This commit is contained in:
Perberos
2011-11-07 16:46:58 -03:00
commit 528c1e5ff5
532 changed files with 709826 additions and 0 deletions

View File

@@ -0,0 +1,15 @@
# External Tools plugin
SUBDIRS = tools data scripts
plugindir = $(GEDIT_PLUGINS_LIBS_DIR)
plugin_in_files = externaltools.gedit-plugin.desktop.in
%.gedit-plugin: %.gedit-plugin.desktop.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*po) ; $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -d -u -c $(top_builddir)/po/.intltool-merge-cache
plugin_DATA = $(plugin_in_files:.gedit-plugin.desktop.in=.gedit-plugin)
EXTRA_DIST = $(plugin_in_files)
CLEANFILES = $(plugin_DATA)
DISTCLEANFILES = $(plugin_DATA)
-include $(top_srcdir)/git.mk

View File

@@ -0,0 +1,65 @@
TOOL_MERGE=$(top_srcdir)/plugins/externaltools/scripts/gedit-tool-merge.pl
tools_in_files = \
build.tool.in \
remove-trailing-spaces.tool.in
tools_in_linux = \
open-terminal-here.tool.in \
run-command.tool.in
tools_in_osx = \
open-terminal-here-osx.tool.in
tools_in_win32 =
install_tools_in_files = $(tools_in_files)
if PLATFORM_OSX
install_tools_in_files += $(tools_in_osx)
else
if PLATFORM_WIN32
install_tools_in_files += $(tools_in_win32)
else
install_tools_in_files += $(tools_in_linux)
endif
endif
desktop_in_files = $(install_tools_in_files:.tool.in=.desktop.in)
desktop_files = $(install_tools_in_files:.tool.in=.desktop)
tools_SCRIPTS = $(install_tools_in_files:.tool.in=)
toolsdir = $(GEDIT_PLUGINS_DATA_DIR)/externaltools/tools
all_tools_in_files = \
$(tools_in_files) \
$(tools_in_linux) \
$(tools_in_osx) \
$(tools_in_win32)
all_desktop_in_files = $(all_tools_in_files:.tool.in=.desktop.in)
all_desktop_files = $(all_tools_in_files:.tool.in=.desktop)
all_tools_files = $(all_tools_in_files:.tool.in=)
@INTLTOOL_DESKTOP_RULE@
# Tools are generated by merging a script file (.tool.in) with a data file
# (.desktop), which happens to be translated using intltool.
$(tools_SCRIPTS): %: %.tool.in %.desktop $(TOOL_MERGE)
perl $(TOOL_MERGE) -o $@ $< $(word 2,$^)
chmod 755 $@
EXTRA_DIST = \
$(all_desktop_in_files) \
$(all_tools_in_files)
CLEANFILES = \
$(all_desktop_files) \
$(all_tools_files)
DISTCLEANFILES = \
$(all_desktop_files) \
$(all_tools_files)
-include $(top_srcdir)/git.mk

View File

@@ -0,0 +1,9 @@
[Gedit Tool]
_Name=Build
_Comment=Run "make" in the document directory
Input=nothing
Output=output-panel
Shortcut=<Control>F8
Applicability=local
Save-files=nothing
Languages=

View File

@@ -0,0 +1,15 @@
#!/bin/sh
EHOME=`echo $HOME | sed "s/#/\#/"`
DIR=$GEDIT_CURRENT_DOCUMENT_DIR
while test "$DIR" != "/"; do
for m in GNUmakefile makefile Makefile; do
if [ -f "${DIR}/${m}" ]; then
echo "Using ${m} from ${DIR}" | sed "s#$EHOME#~#" > /dev/stderr
make -C "${DIR}"
exit
fi
done
DIR=`dirname "${DIR}"`
done
echo "No Makefile found!" > /dev/stderr

View File

@@ -0,0 +1,8 @@
[Gedit Tool]
_Name=Open terminal here
_Comment=Open a terminal in the document location
Input=nothing
Output=output-panel
Applicability=local
Save-files=nothing
Languages=

View File

@@ -0,0 +1,16 @@
#!/usr/bin/osascript
set the_path to system attribute "GEDIT_CURRENT_DOCUMENT_DIR"
set cmd to "cd " & quoted form of the_path
tell application "System Events" to set terminalIsRunning to exists application process "Terminal"
tell application "Terminal"
activate
if terminalIsRunning is true then
do script with command cmd
else
do script with command cmd in window 1
end if
end tell

View File

@@ -0,0 +1,8 @@
[Gedit Tool]
_Name=Open terminal here
_Comment=Open a terminal in the document location
Input=nothing
Output=output-panel
Applicability=local
Save-files=nothing
Languages=

View File

@@ -0,0 +1,4 @@
#!/bin/sh
#TODO: use "mateconftool-2 -g /desktop/mate/applications/terminal/exec"
mate-terminal --working-directory=$GEDIT_CURRENT_DOCUMENT_DIR &

View File

@@ -0,0 +1,9 @@
[Gedit Tool]
_Name=Remove trailing spaces
_Comment=Remove useless trailing spaces in your file
Input=document
Output=replace-document
Shortcut=<Alt>F12
Applicability=all
Save-files=nothing
Languages=

View File

@@ -0,0 +1,3 @@
#!/bin/sh
sed 's/[[:blank:]]*$//'

View File

@@ -0,0 +1,8 @@
[Gedit Tool]
_Name=Run command
_Comment=Execute a custom command and put its output in a new document
Input=nothing
Output=new-document
Applicability=all
Save-files=nothing
Languages=

View File

@@ -0,0 +1,4 @@
#!/bin/sh
#TODO: use "mateconftool-2 -g /desktop/mate/applications/terminal/exec"
exec `matedialog --entry --title="Run command - gedit" --text="Command to run"`

View File

@@ -0,0 +1,9 @@
[Gedit Plugin]
Loader=python
Module=externaltools
IAge=2
_Name=External Tools
_Description=Execute external commands and shell scripts.
Authors=Steve Frécinaux <steve@istique.net>
Copyright=Copyright © 2005 Steve Frécinaux
Website=http://www.gedit.org

View File

@@ -0,0 +1,4 @@
EXTRA_DIST = gedit-tool-merge.pl
-include $(top_srcdir)/git.mk

View File

@@ -0,0 +1,78 @@
#!/usr/bin/perl
# gedit-tool-merge.pl
# This file is part of gedit
#
# Copyright (C) 2006 - Steve Frécinaux <code@istique.net>
#
# gedit is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# gedit is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gedit; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301 USA
# This script merges a script file with a desktop file containing
# metadata about the external tool. This is required in order to
# have translatable tools (bug #342042) since intltool can't extract
# string directly from tool files (a tool file being the combination
# of a script file and a metadata section).
#
# The desktop file is embedded in a comment of the script file, under
# the assumption that any scripting language supports # as a comment
# mark (this is likely to be true since the shebang uses #!). The
# section is placed at the top of the tool file, after the shebang and
# modelines if present.
use strict;
use warnings;
use Getopt::Long;
sub usage {
print <<EOF;
Usage: ${0} [OPTION]... [SCRIPT] [DESKTOP]
Merges a script file with a desktop file, embedding it in a comment.
-o, --output=FILE Specify the output file name. [default: stdout]
EOF
exit;
}
my $output = "";
my $help;
GetOptions ("help|h" => \$help, "output|o=s" => \$output) or &usage;
usage if $help or @ARGV lt 2;
open INFILE, "<", $ARGV[0];
open DFILE, "<", $ARGV[1];
open STDOUT, ">", $output if $output;
# Put shebang and various modelines at the top of the generated file.
$_ = <INFILE>;
print and $_ = <INFILE> if /^#!/;
print and $_ = <INFILE> if /-\*-/;
print and $_ = <INFILE> if /(ex|vi|vim):/;
# Put a blank line before the info block if there is one in INFILE.
print and $_ = <INFILE> if /^\s*$/;
seek INFILE, -length, 1;
# Embed the desktop file...
print "# $_" while <DFILE>;
print "\n";
# ...and write the remaining part of the script.
print while <INFILE>;
close INFILE;
close DFILE;
close STDOUT;

View File

@@ -0,0 +1,23 @@
# Python snippets plugin
plugindir = $(GEDIT_PLUGINS_LIBS_DIR)/externaltools
plugin_PYTHON = \
__init__.py \
capture.py \
library.py \
functions.py \
manager.py \
outputpanel.py \
filelookup.py \
linkparsing.py
uidir = $(GEDIT_PLUGINS_DATA_DIR)/externaltools/ui
ui_DATA = tools.ui \
outputpanel.ui
EXTRA_DIST = $(ui_DATA)
CLEANFILES = *.bak *.gladep
DISTCLEANFILES = *.bak *.gladep
-include $(top_srcdir)/git.mk

View File

@@ -0,0 +1,281 @@
# -*- coding: UTF-8 -*-
# Gedit External Tools plugin
# Copyright (C) 2005-2006 Steve Frécinaux <steve@istique.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
__all__ = ('ExternalToolsPlugin', 'ExternalToolsWindowHelper',
'Manager', 'OutputPanel', 'Capture', 'UniqueById')
import gedit
import gtk
from manager import Manager
from library import ToolLibrary
from outputpanel import OutputPanel
from capture import Capture
from functions import *
class ToolMenu(object):
ACTION_HANDLER_DATA_KEY = "ExternalToolActionHandlerData"
ACTION_ITEM_DATA_KEY = "ExternalToolActionItemData"
def __init__(self, library, window, menupath):
super(ToolMenu, self).__init__()
self._library = library
self._window = window
self._menupath = menupath
self._merge_id = 0
self._action_group = gtk.ActionGroup("ExternalToolsPluginToolActions")
self._signals = []
self.update()
def deactivate(self):
self.remove()
def remove(self):
if self._merge_id != 0:
self._window.get_ui_manager().remove_ui(self._merge_id)
self._window.get_ui_manager().remove_action_group(self._action_group)
self._merge_id = 0
for action in self._action_group.list_actions():
handler = action.get_data(self.ACTION_HANDLER_DATA_KEY)
if handler is not None:
action.disconnect(handler)
action.set_data(self.ACTION_ITEM_DATA_KEY, None)
action.set_data(self.ACTION_HANDLER_DATA_KEY, None)
self._action_group.remove_action(action)
accelmap = gtk.accel_map_get()
for s in self._signals:
accelmap.disconnect(s)
self._signals = []
def _insert_directory(self, directory, path):
manager = self._window.get_ui_manager()
for item in directory.subdirs:
action_name = 'ExternalToolDirectory%X' % id(item)
action = gtk.Action(action_name, item.name.replace('_', '__'), None, None)
self._action_group.add_action(action)
manager.add_ui(self._merge_id, path,
action_name, action_name,
gtk.UI_MANAGER_MENU, False)
self._insert_directory(item, path + '/' + action_name)
for item in directory.tools:
action_name = 'ExternalToolTool%X' % id(item)
action = gtk.Action(action_name, item.name.replace('_', '__'), item.comment, None)
handler = action.connect("activate", capture_menu_action, self._window, item)
action.set_data(self.ACTION_ITEM_DATA_KEY, item)
action.set_data(self.ACTION_HANDLER_DATA_KEY, handler)
# Make sure to replace accel
accelpath = '<Actions>/ExternalToolsPluginToolActions/%s' % (action_name, )
if item.shortcut:
key, mod = gtk.accelerator_parse(item.shortcut)
gtk.accel_map_change_entry(accelpath, key, mod, True)
self._signals.append(gtk.accel_map_get().connect('changed::%s' % (accelpath,), self.on_accelmap_changed, item))
self._action_group.add_action_with_accel(action, item.shortcut)
manager.add_ui(self._merge_id, path,
action_name, action_name,
gtk.UI_MANAGER_MENUITEM, False)
def on_accelmap_changed(self, accelmap, path, key, mod, tool):
tool.shortcut = gtk.accelerator_name(key, mod)
tool.save()
self._window.get_data("ExternalToolsPluginWindowData").update_manager(tool)
def update(self):
self.remove()
self._merge_id = self._window.get_ui_manager().new_merge_id()
self._insert_directory(self._library.tree, self._menupath)
self._window.get_ui_manager().insert_action_group(self._action_group, -1)
self.filter(self._window.get_active_document())
def filter_language(self, language, item):
if not item.languages:
return True
if not language and 'plain' in item.languages:
return True
if language and (language.get_id() in item.languages):
return True
else:
return False
def filter(self, document):
if document is None:
return
titled = document.get_uri() is not None
remote = not document.is_local()
states = {
'all' : True,
'local': titled and not remote,
'remote': titled and remote,
'titled': titled,
'untitled': not titled,
}
language = document.get_language()
for action in self._action_group.list_actions():
item = action.get_data(self.ACTION_ITEM_DATA_KEY)
if item is not None:
action.set_visible(states[item.applicability] and self.filter_language(language, item))
class ExternalToolsWindowHelper(object):
def __init__(self, plugin, window):
super(ExternalToolsWindowHelper, self).__init__()
self._window = window
self._plugin = plugin
self._library = ToolLibrary()
manager = window.get_ui_manager()
self._action_group = gtk.ActionGroup('ExternalToolsPluginActions')
self._action_group.set_translation_domain('gedit')
self._action_group.add_actions([('ExternalToolManager',
None,
_('Manage _External Tools...'),
None,
_("Opens the External Tools Manager"),
lambda action: plugin.open_dialog()),
('ExternalTools',
None,
_('External _Tools'),
None,
_("External tools"),
None)])
manager.insert_action_group(self._action_group, -1)
ui_string = """
<ui>
<menubar name="MenuBar">
<menu name="ToolsMenu" action="Tools">
<placeholder name="ToolsOps_4">
<separator/>
<menu name="ExternalToolsMenu" action="ExternalTools">
<placeholder name="ExternalToolPlaceholder"/>
</menu>
<separator/>
</placeholder>
<placeholder name="ToolsOps_5">
<menuitem name="ExternalToolManager" action="ExternalToolManager"/>
</placeholder>
</menu>
</menubar>
</ui>"""
self._merge_id = manager.add_ui_from_string(ui_string)
self.menu = ToolMenu(self._library, self._window,
"/MenuBar/ToolsMenu/ToolsOps_4/ExternalToolsMenu/ExternalToolPlaceholder")
manager.ensure_update()
# Create output console
self._output_buffer = OutputPanel(self._plugin.get_data_dir(), window)
bottom = window.get_bottom_panel()
bottom.add_item(self._output_buffer.panel,
_("Shell Output"),
gtk.STOCK_EXECUTE)
def update_ui(self):
self.menu.filter(self._window.get_active_document())
self._window.get_ui_manager().ensure_update()
def deactivate(self):
manager = self._window.get_ui_manager()
self.menu.deactivate()
manager.remove_ui(self._merge_id)
manager.remove_action_group(self._action_group)
manager.ensure_update()
bottom = self._window.get_bottom_panel()
bottom.remove_item(self._output_buffer.panel)
def update_manager(self, tool):
self._plugin.update_manager(tool)
class ExternalToolsPlugin(gedit.Plugin):
WINDOW_DATA_KEY = "ExternalToolsPluginWindowData"
def __init__(self):
super(ExternalToolsPlugin, self).__init__()
self._manager = None
self._manager_default_size = None
ToolLibrary().set_locations(os.path.join(self.get_data_dir(), 'tools'))
def activate(self, window):
helper = ExternalToolsWindowHelper(self, window)
window.set_data(self.WINDOW_DATA_KEY, helper)
def deactivate(self, window):
window.get_data(self.WINDOW_DATA_KEY).deactivate()
window.set_data(self.WINDOW_DATA_KEY, None)
def update_ui(self, window):
window.get_data(self.WINDOW_DATA_KEY).update_ui()
def create_configure_dialog(self):
return self.open_dialog()
def open_dialog(self):
if not self._manager:
self._manager = Manager(self.get_data_dir())
if self._manager_default_size:
self._manager.dialog.set_default_size(*self._manager_default_size)
self._manager.dialog.connect('destroy', self.on_manager_destroy)
window = gedit.app_get_default().get_active_window()
self._manager.run(window)
return self._manager.dialog
def update_manager(self, tool):
if not self._manager:
return
self._manager.tool_changed(tool, True)
def on_manager_destroy(self, dialog):
self._manager_default_size = [dialog.allocation.width, dialog.allocation.height]
self._manager = None
# ex:ts=4:et:

View File

@@ -0,0 +1,214 @@
# -*- coding: utf-8 -*-
# Gedit External Tools plugin
# Copyright (C) 2005-2006 Steve Frécinaux <steve@istique.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
__all__ = ('Capture', )
import os, sys, signal
import locale
import subprocess
import gobject
import fcntl
import glib
class Capture(gobject.GObject):
CAPTURE_STDOUT = 0x01
CAPTURE_STDERR = 0x02
CAPTURE_BOTH = 0x03
CAPTURE_NEEDS_SHELL = 0x04
WRITE_BUFFER_SIZE = 0x4000
__gsignals__ = {
'stdout-line' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_STRING,)),
'stderr-line' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_STRING,)),
'begin-execute': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, tuple()),
'end-execute' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_INT,))
}
def __init__(self, command, cwd = None, env = {}):
gobject.GObject.__init__(self)
self.pipe = None
self.env = env
self.cwd = cwd
self.flags = self.CAPTURE_BOTH | self.CAPTURE_NEEDS_SHELL
self.command = command
self.input_text = None
def set_env(self, **values):
self.env.update(**values)
def set_command(self, command):
self.command = command
def set_flags(self, flags):
self.flags = flags
def set_input(self, text):
self.input_text = text
def set_cwd(self, cwd):
self.cwd = cwd
def execute(self):
if self.command is None:
return
# Initialize pipe
popen_args = {
'cwd' : self.cwd,
'shell': self.flags & self.CAPTURE_NEEDS_SHELL,
'env' : self.env
}
if self.input_text is not None:
popen_args['stdin'] = subprocess.PIPE
if self.flags & self.CAPTURE_STDOUT:
popen_args['stdout'] = subprocess.PIPE
if self.flags & self.CAPTURE_STDERR:
popen_args['stderr'] = subprocess.PIPE
self.tried_killing = False
self.idle_write_id = 0
self.read_buffer = ''
try:
self.pipe = subprocess.Popen(self.command, **popen_args)
except OSError, e:
self.pipe = None
self.emit('stderr-line', _('Could not execute command: %s') % (e, ))
return
# Signal
self.emit('begin-execute')
if self.flags & self.CAPTURE_STDOUT:
# Set non blocking
flags = fcntl.fcntl(self.pipe.stdout.fileno(), fcntl.F_GETFL) | os.O_NONBLOCK
fcntl.fcntl(self.pipe.stdout.fileno(), fcntl.F_SETFL, flags)
gobject.io_add_watch(self.pipe.stdout,
gobject.IO_IN | gobject.IO_HUP,
self.on_output)
if self.flags & self.CAPTURE_STDERR:
# Set non blocking
flags = fcntl.fcntl(self.pipe.stderr.fileno(), fcntl.F_GETFL) | os.O_NONBLOCK
fcntl.fcntl(self.pipe.stderr.fileno(), fcntl.F_SETFL, flags)
gobject.io_add_watch(self.pipe.stderr,
gobject.IO_IN | gobject.IO_HUP,
self.on_output)
# IO
if self.input_text is not None:
# Write async, in chunks of something
self.write_buffer = str(self.input_text)
if self.idle_write_chunk():
self.idle_write_id = gobject.idle_add(self.idle_write_chunk)
# Wait for the process to complete
gobject.child_watch_add(self.pipe.pid, self.on_child_end)
def idle_write_chunk(self):
if not self.pipe:
self.idle_write_id = 0
return False
try:
l = len(self.write_buffer)
m = min(l, self.WRITE_BUFFER_SIZE)
self.pipe.stdin.write(self.write_buffer[:m])
if m == l:
self.write_buffer = ''
self.pipe.stdin.close()
self.idle_write_id = 0
return False
else:
self.write_buffer = self.write_buffer[m:]
return True
except IOError:
self.pipe.stdin.close()
self.idle_write_id = 0
return False
def on_output(self, source, condition):
if condition & (glib.IO_IN | glib.IO_PRI):
line = source.read()
if len(line) > 0:
try:
line = unicode(line, 'utf-8')
except:
line = unicode(line,
locale.getdefaultlocale()[1],
'replace')
self.read_buffer += line
lines = self.read_buffer.splitlines(True)
if not lines[-1].endswith("\n"):
self.read_buffer = lines[-1]
lines = lines[0:-1]
else:
self.read_buffer = ''
for line in lines:
if not self.pipe or source == self.pipe.stdout:
self.emit('stdout-line', line)
else:
self.emit('stderr-line', line)
if condition & ~(glib.IO_IN | glib.IO_PRI):
if self.read_buffer:
if source == self.pipe.stdout:
self.emit('stdout-line', self.read_buffer)
else:
self.emit('stderr-line', self.read_buffer)
self.read_buffer = ''
self.pipe = None
return False
else:
return True
def stop(self, error_code = -1):
if self.pipe is not None:
if self.idle_write_id:
gobject.source_remove(self.idle_write_id)
self.idle_write_id = 0
if not self.tried_killing:
os.kill(self.pipe.pid, signal.SIGTERM)
self.tried_killing = True
else:
os.kill(self.pipe.pid, signal.SIGKILL)
def on_child_end(self, pid, error_code):
# In an idle, so it is emitted after all the std*-line signals
# have been intercepted
gobject.idle_add(self.emit, 'end-execute', error_code)
# ex:ts=4:et:

View File

@@ -0,0 +1,145 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2009-2010 Per Arneng <per.arneng@anyplanet.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import os
import gio
import gedit
class FileLookup:
"""
This class is responsible for looking up files given a part or the whole
path of a real file. The lookup is delegated to providers wich use different
methods of trying to find the real file.
"""
def __init__(self):
self.providers = []
self.providers.append(AbsoluteFileLookupProvider())
self.providers.append(CwdFileLookupProvider())
self.providers.append(OpenDocumentRelPathFileLookupProvider())
self.providers.append(OpenDocumentFileLookupProvider())
def lookup(self, path):
"""
Tries to find a file specified by the path parameter. It delegates to
different lookup providers and the first match is returned. If no file
was found then None is returned.
path -- the path to find
"""
found_file = None
for provider in self.providers:
found_file = provider.lookup(path)
if found_file is not None:
break
return found_file
class FileLookupProvider:
"""
The base class of all file lookup providers.
"""
def lookup(self, path):
"""
This method must be implemented by subclasses. Implementors will be
given a path and will try to find a matching file. If no file is found
then None is returned.
"""
raise NotImplementedError("need to implement a lookup method")
class AbsoluteFileLookupProvider(FileLookupProvider):
"""
This file tries to see if the path given is an absolute path and that the
path references a file.
"""
def lookup(self, path):
if os.path.isabs(path) and os.path.isfile(path):
return gio.File(path)
else:
return None
class CwdFileLookupProvider(FileLookupProvider):
"""
This lookup provider tries to find a file specified by the path relative to
the current working directory.
"""
def lookup(self, path):
try:
cwd = os.getcwd()
except OSError:
cwd = os.getenv('HOME')
real_path = os.path.join(cwd, path)
if os.path.isfile(real_path):
return gio.File(real_path)
else:
return None
class OpenDocumentRelPathFileLookupProvider(FileLookupProvider):
"""
Tries to see if the path is relative to any directories where the
currently open documents reside in. Example: If you have a document opened
'/tmp/Makefile' and a lookup is made for 'src/test2.c' then this class
will try to find '/tmp/src/test2.c'.
"""
def lookup(self, path):
if path.startswith('/'):
return None
for doc in gedit.app_get_default().get_documents():
if doc.is_local():
location = doc.get_location()
if location:
rel_path = location.get_parent().get_path()
joined_path = os.path.join(rel_path, path)
if os.path.isfile(joined_path):
return gio.File(joined_path)
return None
class OpenDocumentFileLookupProvider(FileLookupProvider):
"""
Makes a guess that the if the path that was looked for matches the end
of the path of a currently open document then that document is the one
that is looked for. Example: If a document is opened called '/tmp/t.c'
and a lookup is made for 't.c' or 'tmp/t.c' then both will match since
the open document ends with the path that is searched for.
"""
def lookup(self, path):
if path.startswith('/'):
return None
for doc in gedit.app_get_default().get_documents():
if doc.is_local():
location = doc.get_location()
if location and location.get_uri().endswith(path):
return location
return None
# ex:ts=4:et:

View File

@@ -0,0 +1,303 @@
# -*- coding: utf-8 -*-
# Gedit External Tools plugin
# Copyright (C) 2005-2006 Steve Frécinaux <steve@istique.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import os
import gtk
from gtk import gdk
import gio
import gedit
#import gtksourceview
from outputpanel import OutputPanel
from capture import *
def default(val, d):
if val is not None:
return val
else:
return d
def current_word(document):
piter = document.get_iter_at_mark(document.get_insert())
start = piter.copy()
if not piter.starts_word() and (piter.inside_word() or piter.ends_word()):
start.backward_word_start()
if not piter.ends_word() and piter.inside_word():
piter.forward_word_end()
return (start, piter)
# ==== Capture related functions ====
def run_external_tool(window, node):
# Configure capture environment
try:
cwd = os.getcwd()
except OSError:
cwd = os.getenv('HOME');
capture = Capture(node.command, cwd)
capture.env = os.environ.copy()
capture.set_env(GEDIT_CWD = cwd)
view = window.get_active_view()
if view is not None:
# Environment vars relative to current document
document = view.get_buffer()
uri = document.get_uri()
# Current line number
piter = document.get_iter_at_mark(document.get_insert())
capture.set_env(GEDIT_CURRENT_LINE_NUMBER=str(piter.get_line() + 1))
# Current line text
piter.set_line_offset(0)
end = piter.copy()
if not end.ends_line():
end.forward_to_line_end()
capture.set_env(GEDIT_CURRENT_LINE=piter.get_text(end))
# Selected text (only if input is not selection)
if node.input != 'selection' and node.input != 'selection-document':
bounds = document.get_selection_bounds()
if bounds:
capture.set_env(GEDIT_SELECTED_TEXT=bounds[0].get_text(bounds[1]))
bounds = current_word(document)
capture.set_env(GEDIT_CURRENT_WORD=bounds[0].get_text(bounds[1]))
capture.set_env(GEDIT_CURRENT_DOCUMENT_TYPE=document.get_mime_type())
if uri is not None:
gfile = gio.File(uri)
scheme = gfile.get_uri_scheme()
name = os.path.basename(uri)
capture.set_env(GEDIT_CURRENT_DOCUMENT_URI = uri,
GEDIT_CURRENT_DOCUMENT_NAME = name,
GEDIT_CURRENT_DOCUMENT_SCHEME = scheme)
if gedit.utils.uri_has_file_scheme(uri):
path = gfile.get_path()
cwd = os.path.dirname(path)
capture.set_cwd(cwd)
capture.set_env(GEDIT_CURRENT_DOCUMENT_PATH = path,
GEDIT_CURRENT_DOCUMENT_DIR = cwd)
documents_uri = [doc.get_uri()
for doc in window.get_documents()
if doc.get_uri() is not None]
documents_path = [gio.File(uri).get_path()
for uri in documents_uri
if gedit.utils.uri_has_file_scheme(uri)]
capture.set_env(GEDIT_DOCUMENTS_URI = ' '.join(documents_uri),
GEDIT_DOCUMENTS_PATH = ' '.join(documents_path))
flags = capture.CAPTURE_BOTH
if not node.has_hash_bang():
flags |= capture.CAPTURE_NEEDS_SHELL
capture.set_flags(flags)
# Get input text
input_type = node.input
output_type = node.output
# Get the panel
panel = window.get_data("ExternalToolsPluginWindowData")._output_buffer
panel.clear()
if output_type == 'output-panel':
panel.show()
# Assign the error output to the output panel
panel.set_process(capture)
if input_type != 'nothing' and view is not None:
if input_type == 'document':
start, end = document.get_bounds()
elif input_type == 'selection' or input_type == 'selection-document':
try:
start, end = document.get_selection_bounds()
print start, end
except ValueError:
if input_type == 'selection-document':
start, end = document.get_bounds()
if output_type == 'replace-selection':
document.select_range(start, end)
else:
start = document.get_iter_at_mark(document.get_insert())
end = start.copy()
elif input_type == 'line':
start = document.get_iter_at_mark(document.get_insert())
end = start.copy()
if not start.starts_line():
start.set_line_offset(0)
if not end.ends_line():
end.forward_to_line_end()
elif input_type == 'word':
start = document.get_iter_at_mark(document.get_insert())
end = start.copy()
if not start.inside_word():
panel.write(_('You must be inside a word to run this command'),
panel.command_tag)
return
if not start.starts_word():
start.backward_word_start()
if not end.ends_word():
end.forward_word_end()
input_text = document.get_text(start, end)
capture.set_input(input_text)
# Assign the standard output to the chosen "file"
if output_type == 'new-document':
tab = window.create_tab(True)
view = tab.get_view()
document = tab.get_document()
pos = document.get_start_iter()
capture.connect('stdout-line', capture_stdout_line_document, document, pos)
document.begin_user_action()
view.set_editable(False)
view.set_cursor_visible(False)
elif output_type != 'output-panel' and output_type != 'nothing' and view is not None:
document.begin_user_action()
view.set_editable(False)
view.set_cursor_visible(False)
if output_type == 'insert':
pos = document.get_iter_at_mark(document.get_mark('insert'))
elif output_type == 'replace-selection':
document.delete_selection(False, False)
pos = document.get_iter_at_mark(document.get_mark('insert'))
elif output_type == 'replace-document':
document.set_text('')
pos = document.get_end_iter()
else:
pos = document.get_end_iter()
capture.connect('stdout-line', capture_stdout_line_document, document, pos)
elif output_type != 'nothing':
capture.connect('stdout-line', capture_stdout_line_panel, panel)
document.begin_user_action()
capture.connect('stderr-line', capture_stderr_line_panel, panel)
capture.connect('begin-execute', capture_begin_execute_panel, panel, view, node.name)
capture.connect('end-execute', capture_end_execute_panel, panel, view, output_type)
# Run the command
capture.execute()
if output_type != 'nothing':
document.end_user_action()
class MultipleDocumentsSaver:
def __init__(self, window, docs, node):
self._window = window
self._node = node
self._error = False
self._counter = len(docs)
self._signal_ids = {}
self._counter = 0
signals = {}
for doc in docs:
signals[doc] = doc.connect('saving', self.on_document_saving)
gedit.commands.save_document(window, doc)
doc.disconnect(signals[doc])
def on_document_saving(self, doc, size, total_size):
self._counter += 1
self._signal_ids[doc] = doc.connect('saved', self.on_document_saved)
def on_document_saved(self, doc, error):
if error:
self._error = True
doc.disconnect(self._signal_ids[doc])
del self._signal_ids[doc]
self._counter -= 1
if self._counter == 0 and not self._error:
run_external_tool(self._window, self._node)
def capture_menu_action(action, window, node):
if node.save_files == 'document' and window.get_active_document():
MultipleDocumentsSaver(window, [window.get_active_document()], node)
return
elif node.save_files == 'all':
MultipleDocumentsSaver(window, window.get_documents(), node)
return
run_external_tool(window, node)
def capture_stderr_line_panel(capture, line, panel):
if not panel.visible():
panel.show()
panel.write(line, panel.error_tag)
def capture_begin_execute_panel(capture, panel, view, label):
view.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(gdk.Cursor(gdk.WATCH))
panel['stop'].set_sensitive(True)
panel.clear()
panel.write(_("Running tool:"), panel.italic_tag);
panel.write(" %s\n\n" % label, panel.bold_tag);
def capture_end_execute_panel(capture, exit_code, panel, view, output_type):
panel['stop'].set_sensitive(False)
if output_type in ('new-document','replace-document'):
doc = view.get_buffer()
start = doc.get_start_iter()
end = start.copy()
end.forward_chars(300)
mtype = gio.content_type_guess(data=doc.get_text(start, end))
lmanager = gedit.get_language_manager()
language = lmanager.guess_language(doc.get_uri(), mtype)
if language is not None:
doc.set_language(language)
view.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(gdk.Cursor(gdk.XTERM))
view.set_cursor_visible(True)
view.set_editable(True)
if exit_code == 0:
panel.write("\n" + _("Done.") + "\n", panel.italic_tag)
else:
panel.write("\n" + _("Exited") + ":", panel.italic_tag)
panel.write(" %d\n" % exit_code, panel.bold_tag)
def capture_stdout_line_panel(capture, line, panel):
panel.write(line)
def capture_stdout_line_document(capture, line, document, pos):
document.insert(pos, line)
# ex:ts=4:et:

View File

@@ -0,0 +1,493 @@
# -*- coding: utf-8 -*-
# Gedit External Tools plugin
# Copyright (C) 2006 Steve Frécinaux <code@istique.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import os
import re
import locale
import platform
class Singleton(object):
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super(Singleton, cls).__new__(
cls, *args, **kwargs)
cls._instance.__init_once__()
return cls._instance
class ToolLibrary(Singleton):
def __init_once__(self):
self.locations = []
def set_locations(self, datadir):
self.locations = []
if platform.platform() != 'Windows':
for d in self.get_xdg_data_dirs():
self.locations.append(os.path.join(d, 'gedit-2', 'plugins', 'externaltools', 'tools'))
self.locations.append(datadir)
# self.locations[0] is where we save the custom scripts
if platform.platform() == 'Windows':
toolsdir = os.path.expanduser('~/gedit/tools')
else:
userdir = os.getenv('MATE22_USER_DIR')
if userdir:
toolsdir = os.path.join(userdir, 'gedit/tools')
else:
toolsdir = os.path.expanduser('~/.mate2/gedit/tools')
self.locations.insert(0, toolsdir);
if not os.path.isdir(self.locations[0]):
os.makedirs(self.locations[0])
self.tree = ToolDirectory(self, '')
self.import_old_xml_store()
else:
self.tree = ToolDirectory(self, '')
# cf. http://standards.freedesktop.org/basedir-spec/latest/
def get_xdg_data_dirs(self):
dirs = os.getenv('XDG_DATA_DIRS')
if dirs:
dirs = dirs.split(os.pathsep)
else:
dirs = ('/usr/local/share', '/usr/share')
return dirs
# This function is meant to be ran only once, when the tools directory is
# created. It imports eventual tools that have been saved in the old XML
# storage file.
def import_old_xml_store(self):
import xml.etree.ElementTree as et
userdir = os.getenv('MATE22_USER_DIR')
if userdir:
filename = os.path.join(userdir, 'gedit/gedit-tools.xml')
else:
filename = os.path.expanduser('~/.mate2/gedit/gedit-tools.xml')
if not os.path.isfile(filename):
return
print "External tools: importing old tools into the new store..."
xtree = et.parse(filename)
xroot = xtree.getroot()
for xtool in xroot:
for i in self.tree.tools:
if i.name == xtool.get('label'):
tool = i
break
else:
tool = Tool(self.tree)
tool.name = xtool.get('label')
tool.autoset_filename()
self.tree.tools.append(tool)
tool.comment = xtool.get('description')
tool.shortcut = xtool.get('accelerator')
tool.applicability = xtool.get('applicability')
tool.output = xtool.get('output')
tool.input = xtool.get('input')
tool.save_with_script(xtool.text)
def get_full_path(self, path, mode='r', system = True, local = True):
assert (system or local)
if path is None:
return None
if mode == 'r':
if system and local:
locations = self.locations
elif local and not system:
locations = [self.locations[0]]
elif system and not local:
locations = self.locations[1:]
else:
raise ValueError("system and local can't be both set to False")
for i in locations:
p = os.path.join(i, path)
if os.path.lexists(p):
return p
return None
else:
path = os.path.join(self.locations[0], path)
dirname = os.path.dirname(path)
if not os.path.isdir(dirname):
os.mkdir(dirname)
return path
class ToolDirectory(object):
def __init__(self, parent, dirname):
super(ToolDirectory, self).__init__()
self.subdirs = list()
self.tools = list()
if isinstance(parent, ToolDirectory):
self.parent = parent
self.library = parent.library
else:
self.parent = None
self.library = parent
self.dirname = dirname
self._load()
def listdir(self):
elements = dict()
for l in self.library.locations:
d = os.path.join(l, self.dirname)
if not os.path.isdir(d):
continue
for i in os.listdir(d):
elements[i] = None
keys = elements.keys()
keys.sort()
return keys
def _load(self):
for p in self.listdir():
path = os.path.join(self.dirname, p)
full_path = self.library.get_full_path(path)
if os.path.isdir(full_path):
self.subdirs.append(ToolDirectory(self, p))
elif os.path.isfile(full_path) and os.access(full_path, os.X_OK):
self.tools.append(Tool(self, p))
def get_path(self):
if self.parent is None:
return self.dirname
else:
return os.path.join(self.parent.get_path(), self.dirname)
path = property(get_path)
def get_name(self):
return os.path.basename(self.dirname)
name = property(get_name)
def delete_tool(self, tool):
# Only remove it if it lays in $HOME
if tool in self.tools:
path = tool.get_path()
if path is not None:
filename = os.path.join(self.library.locations[0], path)
if os.path.isfile(filename):
os.unlink(filename)
self.tools.remove(tool)
return True
else:
return False
def revert_tool(self, tool):
# Only remove it if it lays in $HOME
filename = os.path.join(self.library.locations[0], tool.get_path())
if tool in self.tools and os.path.isfile(filename):
os.unlink(filename)
tool._load()
return True
else:
return False
class Tool(object):
RE_KEY = re.compile('^([a-zA-Z_][a-zA-Z0-9_.\-]*)(\[([a-zA-Z_@]+)\])?$')
def __init__(self, parent, filename = None):
super(Tool, self).__init__()
self.parent = parent
self.library = parent.library
self.filename = filename
self.changed = False
self._properties = dict()
self._transform = {
'Languages': [self._to_list, self._from_list]
}
self._load()
def _to_list(self, value):
if value.strip() == '':
return []
else:
return map(lambda x: x.strip(), value.split(','))
def _from_list(self, value):
return ','.join(value)
def _parse_value(self, key, value):
if key in self._transform:
return self._transform[key][0](value)
else:
return value
def _load(self):
if self.filename is None:
return
filename = self.library.get_full_path(self.get_path())
if filename is None:
return
fp = file(filename, 'r', 1)
in_block = False
lang = locale.getlocale(locale.LC_MESSAGES)[0]
for line in fp:
if not in_block:
in_block = line.startswith('# [Gedit Tool]')
continue
if line.startswith('##') or line.startswith('# #'): continue
if not line.startswith('# '): break
try:
(key, value) = [i.strip() for i in line[2:].split('=', 1)]
m = self.RE_KEY.match(key)
if m.group(3) is None:
self._properties[m.group(1)] = self._parse_value(m.group(1), value)
elif lang is not None and lang.startswith(m.group(3)):
self._properties[m.group(1)] = self._parse_value(m.group(1), value)
except ValueError:
break
fp.close()
self.changed = False
def _set_property_if_changed(self, key, value):
if value != self._properties.get(key):
self._properties[key] = value
self.changed = True
def is_global(self):
return self.library.get_full_path(self.get_path(), local=False) is not None
def is_local(self):
return self.library.get_full_path(self.get_path(), system=False) is not None
def is_global(self):
return self.library.get_full_path(self.get_path(), local=False) is not None
def get_path(self):
if self.filename is not None:
return os.path.join(self.parent.get_path(), self.filename)
else:
return None
path = property(get_path)
# This command is the one that is meant to be ran
# (later, could have an Exec key or something)
def get_command(self):
return self.library.get_full_path(self.get_path())
command = property(get_command)
def get_applicability(self):
applicability = self._properties.get('Applicability')
if applicability: return applicability
return 'all'
def set_applicability(self, value):
self._set_property_if_changed('Applicability', value)
applicability = property(get_applicability, set_applicability)
def get_name(self):
name = self._properties.get('Name')
if name: return name
return os.path.basename(self.filename)
def set_name(self, value):
self._set_property_if_changed('Name', value)
name = property(get_name, set_name)
def get_shortcut(self):
shortcut = self._properties.get('Shortcut')
if shortcut: return shortcut
return None
def set_shortcut(self, value):
self._set_property_if_changed('Shortcut', value)
shortcut = property(get_shortcut, set_shortcut)
def get_comment(self):
comment = self._properties.get('Comment')
if comment: return comment
return self.filename
def set_comment(self, value):
self._set_property_if_changed('Comment', value)
comment = property(get_comment, set_comment)
def get_input(self):
input = self._properties.get('Input')
if input: return input
return 'nothing'
def set_input(self, value):
self._set_property_if_changed('Input', value)
input = property(get_input, set_input)
def get_output(self):
output = self._properties.get('Output')
if output: return output
return 'output-panel'
def set_output(self, value):
self._set_property_if_changed('Output', value)
output = property(get_output, set_output)
def get_save_files(self):
save_files = self._properties.get('Save-files')
if save_files: return save_files
return 'nothing'
def set_save_files(self, value):
self._set_property_if_changed('Save-files', value)
save_files = property(get_save_files, set_save_files)
def get_languages(self):
languages = self._properties.get('Languages')
if languages: return languages
return []
def set_languages(self, value):
self._set_property_if_changed('Languages', value)
languages = property(get_languages, set_languages)
def has_hash_bang(self):
if self.filename is None:
return True
filename = self.library.get_full_path(self.get_path())
if filename is None:
return True
fp = open(filename, 'r', 1)
for line in fp:
if line.strip() == '':
continue
return line.startswith('#!')
# There is no property for this one because this function is quite
# expensive to perform
def get_script(self):
if self.filename is None:
return ["#!/bin/sh\n"]
filename = self.library.get_full_path(self.get_path())
if filename is None:
return ["#!/bin/sh\n"]
fp = open(filename, 'r', 1)
lines = list()
# before entering the data block
for line in fp:
if line.startswith('# [Gedit Tool]'):
break
lines.append(line)
# in the block:
for line in fp:
if line.startswith('##'): continue
if not (line.startswith('# ') and '=' in line):
# after the block: strip one emtpy line (if present)
if line.strip() != '':
lines.append(line)
break
# after the block
for line in fp:
lines.append(line)
fp.close()
return lines
def _dump_properties(self):
lines = ['# [Gedit Tool]']
for item in self._properties.iteritems():
if item[0] in self._transform:
lines.append('# %s=%s' % (item[0], self._transform[item[0]][1](item[1])))
elif item[1] is not None:
lines.append('# %s=%s' % item)
return '\n'.join(lines) + '\n'
def save_with_script(self, script):
filename = self.library.get_full_path(self.filename, 'w')
fp = open(filename, 'w', 1)
# Make sure to first print header (shebang, modeline), then
# properties, and then actual content
header = []
content = []
inheader = True
# Parse
for line in script:
line = line.rstrip("\n")
if not inheader:
content.append(line)
elif line.startswith('#!'):
# Shebang (should be always present)
header.append(line)
elif line.strip().startswith('#') and ('-*-' in line or 'ex:' in line or 'vi:' in line or 'vim:' in line):
header.append(line)
else:
content.append(line)
inheader = False
# Write out header
for line in header:
fp.write(line + "\n")
fp.write(self._dump_properties())
fp.write("\n")
for line in content:
fp.write(line + "\n")
fp.close()
os.chmod(filename, 0750)
self.changed = False
def save(self):
if self.changed:
self.save_with_script(self.get_script())
def autoset_filename(self):
if self.filename is not None:
return
dirname = self.parent.path
if dirname != '':
dirname += os.path.sep
basename = self.name.lower().replace(' ', '-').replace('/', '-')
if self.library.get_full_path(dirname + basename):
i = 2
while self.library.get_full_path(dirname + "%s-%d" % (basename, i)):
i += 1
basename = "%s-%d" % (basename, i)
self.filename = basename
if __name__ == '__main__':
library = ToolLibrary()
def print_tool(t, indent):
print indent * " " + "%s: %s" % (t.filename, t.name)
def print_dir(d, indent):
print indent * " " + d.dirname + '/'
for i in d.subdirs:
print_dir(i, indent+1)
for i in d.tools:
print_tool(i, indent+1)
print_dir(library.tree, 0)
# ex:ts=4:et:

View File

@@ -0,0 +1,231 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2009-2010 Per Arneng <per.arneng@anyplanet.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import re
class Link:
"""
This class represents a file link from within a string given by the
output of some software tool. A link contains a reference to a file, the
line number within the file and the boundaries within the given output
string that should be marked as a link.
"""
def __init__(self, path, line_nr, start, end):
"""
path -- the path of the file (that could be extracted)
line_nr -- the line nr of the specified file
start -- the index within the string that the link starts at
end -- the index within the string where the link ends at
"""
self.path = path
self.line_nr = int(line_nr)
self.start = start
self.end = end
def __repr__(self):
return "%s[%s](%s:%s)" % (self.path, self.line_nr,
self.start, self.end)
class LinkParser:
"""
Parses a text using different parsing providers with the goal of finding one
or more file links within the text. A typical example could be the output
from a compiler that specifies an error in a specific file. The path of the
file, the line nr and some more info is then returned so that it can be used
to be able to navigate from the error output in to the specific file.
The actual work of parsing the text is done by instances of classes that
inherits from AbstractLinkParser or by regular expressions. To add a new
parser just create a class that inherits from AbstractLinkParser and then
register in this class cunstructor using the method add_parser. If you want
to add a regular expression then just call add_regexp in this class
constructor and provide your regexp string as argument.
"""
def __init__(self):
self._providers = []
self.add_regexp(REGEXP_STANDARD)
self.add_regexp(REGEXP_PYTHON)
self.add_regexp(REGEXP_VALAC)
self.add_regexp(REGEXP_BASH)
self.add_regexp(REGEXP_RUBY)
self.add_regexp(REGEXP_PERL)
self.add_regexp(REGEXP_MCS)
def add_parser(self, parser):
self._providers.append(parser)
def add_regexp(self, regexp):
"""
Adds a regular expression string that should match a link using
re.MULTILINE and re.VERBOSE regexp. The area marked as a link should
be captured by a group named lnk. The path of the link should be
captured by a group named pth. The line number should be captured by
a group named ln. To read more about this look at the documentation
for the RegexpLinkParser constructor.
"""
self.add_parser(RegexpLinkParser(regexp))
def parse(self, text):
"""
Parses the given text and returns a list of links that are parsed from
the text. This method delegates to parser providers that can parse
output from different kinds of formats. If no links are found then an
empty list is returned.
text -- the text to scan for file links. 'text' can not be None.
"""
if text is None:
raise ValueError("text can not be None")
links = []
for provider in self._providers:
links.extend(provider.parse(text))
return links
class AbstractLinkParser(object):
"""The "abstract" base class for link parses"""
def parse(self, text):
"""
This method should be implemented by subclasses. It takes a text as
argument (never None) and then returns a list of Link objects. If no
links are found then an empty list is expected. The Link class is
defined in this module. If you do not override this method then a
NotImplementedError will be thrown.
text -- the text to parse. This argument is never None.
"""
raise NotImplementedError("need to implement a parse method")
class RegexpLinkParser(AbstractLinkParser):
"""
A class that represents parsers that only use one single regular expression.
It can be used by subclasses or by itself. See the constructor documentation
for details about the rules surrouning the regexp.
"""
def __init__(self, regex):
"""
Creates a new RegexpLinkParser based on the given regular expression.
The regular expression is multiline and verbose (se python docs on
compilation flags). The regular expression should contain three named
capturing groups 'lnk', 'pth' and 'ln'. 'lnk' represents the area wich
should be marked as a link in the text. 'pth' is the path that should
be looked for and 'ln' is the line number in that file.
"""
self.re = re.compile(regex, re.MULTILINE | re.VERBOSE)
def parse(self, text):
links = []
for m in re.finditer(self.re, text):
path = m.group("pth")
line_nr = m.group("ln")
start = m.start("lnk")
end = m.end("lnk")
link = Link(path, line_nr, start, end)
links.append(link)
return links
# gcc 'test.c:13: warning: ...'
# javac 'Test.java:13: ...'
# ruby 'test.rb:5: ...'
# scalac 'Test.scala:5: ...'
# 6g (go) 'test.go:9: ...'
REGEXP_STANDARD = r"""
^
(?P<lnk>
(?P<pth> .*[a-z0-9] )
\:
(?P<ln> \d+)
)
\:\s"""
# python ' File "test.py", line 13'
REGEXP_PYTHON = r"""
^\s\sFile\s
(?P<lnk>
\"
(?P<pth> [^\"]+ )
\",\sline\s
(?P<ln> \d+ )
),"""
# python 'test.sh: line 5:'
REGEXP_BASH = r"""
^(?P<lnk>
(?P<pth> .* )
\:\sline\s
(?P<ln> \d+ )
)\:"""
# valac 'Test.vala:13.1-13.3: ...'
REGEXP_VALAC = r"""
^(?P<lnk>
(?P<pth>
.*vala
)
\:
(?P<ln>
\d+
)
\.\d+-\d+\.\d+
)\: """
#ruby
#test.rb:5: ...
# from test.rb:3:in `each'
# fist line parsed by REGEXP_STANDARD
REGEXP_RUBY = r"""
^\s+from\s
(?P<lnk>
(?P<pth>
.*
)
\:
(?P<ln>
\d+
)
)"""
# perl 'syntax error at test.pl line 88, near "$fake_var'
REGEXP_PERL = r"""
\sat\s
(?P<lnk>
(?P<pth> .* )
\sline\s
(?P<ln> \d+ )
)"""
# mcs (C#) 'Test.cs(12,7): error CS0103: The name `fakeMethod'
REGEXP_MCS = r"""
^
(?P<lnk>
(?P<pth> .*\.[cC][sS] )
\(
(?P<ln> \d+ )
,\d+\)
)
\:\s
"""
# ex:ts=4:et:

View File

@@ -0,0 +1,948 @@
# -*- coding: utf-8 -*-
# Gedit External Tools plugin
# Copyright (C) 2005-2006 Steve Frécinaux <steve@istique.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
__all__ = ('Manager', )
import gedit
import gtk
import gtksourceview2 as gsv
import os.path
from library import *
from functions import *
import hashlib
from xml.sax import saxutils
import gobject
class LanguagesPopup(gtk.Window):
COLUMN_NAME = 0
COLUMN_ID = 1
COLUMN_ENABLED = 2
def __init__(self, languages):
gtk.Window.__init__(self, gtk.WINDOW_POPUP)
self.set_default_size(200, 200)
self.props.can_focus = True
self.build()
self.init_languages(languages)
self.show()
self.map()
self.grab_add()
gtk.gdk.keyboard_grab(self.window, False, 0L)
gtk.gdk.pointer_grab(self.window, False, gtk.gdk.BUTTON_PRESS_MASK |
gtk.gdk.BUTTON_RELEASE_MASK |
gtk.gdk.POINTER_MOTION_MASK |
gtk.gdk.ENTER_NOTIFY_MASK |
gtk.gdk.LEAVE_NOTIFY_MASK |
gtk.gdk.PROXIMITY_IN_MASK |
gtk.gdk.PROXIMITY_OUT_MASK, None, None, 0L)
self.view.get_selection().select_path((0,))
def build(self):
self.model = gtk.ListStore(str, str, bool)
self.sw = gtk.ScrolledWindow()
self.sw.show()
self.sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
self.sw.set_shadow_type(gtk.SHADOW_ETCHED_IN)
self.view = gtk.TreeView(self.model)
self.view.show()
self.view.set_headers_visible(False)
column = gtk.TreeViewColumn()
renderer = gtk.CellRendererToggle()
column.pack_start(renderer, False)
column.set_attributes(renderer, active=self.COLUMN_ENABLED)
renderer.connect('toggled', self.on_language_toggled)
renderer = gtk.CellRendererText()
column.pack_start(renderer, True)
column.set_attributes(renderer, text=self.COLUMN_NAME)
self.view.append_column(column)
self.view.set_row_separator_func(self.on_separator)
self.sw.add(self.view)
self.add(self.sw)
def enabled_languages(self, model, path, piter, ret):
enabled = model.get_value(piter, self.COLUMN_ENABLED)
if path == (0,) and enabled:
return True
if enabled:
ret.append(model.get_value(piter, self.COLUMN_ID))
return False
def languages(self):
ret = []
self.model.foreach(self.enabled_languages, ret)
return ret
def on_separator(self, model, piter):
val = model.get_value(piter, self.COLUMN_NAME)
return val == '-'
def init_languages(self, languages):
manager = gsv.LanguageManager()
langs = gedit.language_manager_list_languages_sorted(manager, True)
self.model.append([_('All languages'), None, not languages])
self.model.append(['-', None, False])
self.model.append([_('Plain Text'), 'plain', 'plain' in languages])
self.model.append(['-', None, False])
for lang in langs:
self.model.append([lang.get_name(), lang.get_id(), lang.get_id() in languages])
def correct_all(self, model, path, piter, enabled):
if path == (0,):
return False
model.set_value(piter, self.COLUMN_ENABLED, enabled)
def on_language_toggled(self, renderer, path):
piter = self.model.get_iter(path)
enabled = self.model.get_value(piter, self.COLUMN_ENABLED)
self.model.set_value(piter, self.COLUMN_ENABLED, not enabled)
if path == '0':
self.model.foreach(self.correct_all, False)
else:
self.model.set_value(self.model.get_iter_first(), self.COLUMN_ENABLED, False)
def do_key_press_event(self, event):
if event.keyval == gtk.keysyms.Escape:
self.destroy()
return True
else:
event.window = self.view.get_bin_window()
return self.view.event(event)
def do_key_release_event(self, event):
event.window = self.view.get_bin_window()
return self.view.event(event)
def in_window(self, event, window=None):
if not window:
window = self.window
geometry = window.get_geometry()
origin = window.get_origin()
return event.x_root >= origin[0] and \
event.x_root <= origin[0] + geometry[2] and \
event.y_root >= origin[1] and \
event.y_root <= origin[1] + geometry[3]
def do_destroy(self):
gtk.gdk.keyboard_ungrab(0L)
gtk.gdk.pointer_ungrab(0L)
return gtk.Window.do_destroy(self)
def setup_event(self, event, window):
fr = event.window.get_origin()
to = window.get_origin()
event.window = window
event.x += fr[0] - to[0]
event.y += fr[1] - to[1]
def resolve_widgets(self, root):
res = [root]
if isinstance(root, gtk.Container):
root.forall(lambda x, y: res.extend(self.resolve_widgets(x)), None)
return res
def resolve_windows(self, window):
if not window:
return []
res = [window]
res.extend(window.get_children())
return res
def propagate_mouse_event(self, event):
allwidgets = self.resolve_widgets(self.get_child())
allwidgets.reverse()
orig = [event.x, event.y]
for widget in allwidgets:
windows = self.resolve_windows(widget.window)
windows.reverse()
for window in windows:
if not (window.get_events() & event.type):
continue
if self.in_window(event, window):
self.setup_event(event, window)
if widget.event(event):
return True
return False
def do_button_press_event(self, event):
if not self.in_window(event):
self.destroy()
else:
return self.propagate_mouse_event(event)
def do_button_release_event(self, event):
if not self.in_window(event):
self.destroy()
else:
return self.propagate_mouse_event(event)
def do_scroll_event(self, event):
return self.propagate_mouse_event(event)
def do_motion_notify_event(self, event):
return self.propagate_mouse_event(event)
def do_enter_notify_event(self, event):
return self.propagate_mouse_event(event)
def do_leave_notify_event(self, event):
return self.propagate_mouse_event(event)
def do_proximity_in_event(self, event):
return self.propagate_mouse_event(event)
def do_proximity_out_event(self, event):
return self.propagate_mouse_event(event)
gobject.type_register(LanguagesPopup)
class Manager:
TOOL_COLUMN = 0 # For Tree
NAME_COLUMN = 1 # For Combo
def __init__(self, datadir):
self.datadir = datadir
self.dialog = None
self._languages = {}
self._tool_rows = {}
self.build()
def build(self):
callbacks = {
'on_new_tool_button_clicked' : self.on_new_tool_button_clicked,
'on_remove_tool_button_clicked' : self.on_remove_tool_button_clicked,
'on_tool_manager_dialog_response' : self.on_tool_manager_dialog_response,
'on_tool_manager_dialog_focus_out': self.on_tool_manager_dialog_focus_out,
'on_accelerator_key_press' : self.on_accelerator_key_press,
'on_accelerator_focus_in' : self.on_accelerator_focus_in,
'on_accelerator_focus_out' : self.on_accelerator_focus_out,
'on_languages_button_clicked' : self.on_languages_button_clicked
}
# Load the "main-window" widget from the ui file.
self.ui = gtk.Builder()
self.ui.add_from_file(os.path.join(self.datadir, 'ui', 'tools.ui'))
self.ui.connect_signals(callbacks)
self.dialog = self.ui.get_object('tool-manager-dialog')
self.view = self.ui.get_object('view')
self.__init_tools_model()
self.__init_tools_view()
for name in ['input', 'output', 'applicability', 'save-files']:
self.__init_combobox(name)
self.do_update()
def expand_from_doc(self, doc):
row = None
if doc:
if doc.get_language():
lid = doc.get_language().get_id()
if lid in self._languages:
row = self._languages[lid]
elif 'plain' in self._languages:
row = self._languages['plain']
if not row and None in self._languages:
row = self._languages[None]
if not row:
return
self.view.expand_row(row.get_path(), False)
self.view.get_selection().select_path(row.get_path())
def run(self, window):
if self.dialog == None:
self.build()
# Open up language
self.expand_from_doc(window.get_active_document())
self.dialog.set_transient_for(window)
window.get_group().add_window(self.dialog)
self.dialog.present()
def add_accelerator(self, item):
if not item.shortcut:
return
if item.shortcut in self.accelerators:
if not item in self.accelerators[item.shortcut]:
self.accelerators[item.shortcut].append(item)
else:
self.accelerators[item.shortcut] = [item]
def remove_accelerator(self, item, shortcut=None):
if not shortcut:
shortcut = item.shortcut
if not shortcut in self.accelerators:
return
self.accelerators[shortcut].remove(item)
if not self.accelerators[shortcut]:
del self.accelerators[shortcut]
def add_tool_to_language(self, tool, language):
if isinstance(language, gsv.Language):
lid = language.get_id()
else:
lid = language
if not lid in self._languages:
piter = self.model.append(None, [language])
parent = gtk.TreeRowReference(self.model, self.model.get_path(piter))
self._languages[lid] = parent
else:
parent = self._languages[lid]
piter = self.model.get_iter(parent.get_path())
child = self.model.append(piter, [tool])
if not tool in self._tool_rows:
self._tool_rows[tool] = []
self._tool_rows[tool].append(gtk.TreeRowReference(self.model, self.model.get_path(child)))
return child
def add_tool(self, tool):
manager = gsv.LanguageManager()
ret = None
for lang in tool.languages:
l = manager.get_language(lang)
if l:
ret = self.add_tool_to_language(tool, l)
elif lang == 'plain':
ret = self.add_tool_to_language(tool, 'plain')
if not ret:
ret = self.add_tool_to_language(tool, None)
self.add_accelerator(tool)
return ret
def __init_tools_model(self):
self.tools = ToolLibrary()
self.current_node = None
self.script_hash = None
self.accelerators = dict()
self.model = gtk.TreeStore(object)
self.view.set_model(self.model)
for tool in self.tools.tree.tools:
self.add_tool(tool)
self.model.set_default_sort_func(self.sort_tools)
self.model.set_sort_column_id(-1, gtk.SORT_ASCENDING)
def sort_tools(self, model, iter1, iter2):
# For languages, sort All before everything else, otherwise alphabetical
t1 = model.get_value(iter1, self.TOOL_COLUMN)
t2 = model.get_value(iter2, self.TOOL_COLUMN)
if model.iter_parent(iter1) == None:
if t1 == None:
return -1
if t2 == None:
return 1
def lang_name(lang):
if isinstance(lang, gsv.Language):
return lang.get_name()
else:
return _('Plain Text')
n1 = lang_name(t1)
n2 = lang_name(t2)
else:
n1 = t1.name
n2 = t2.name
return cmp(n1.lower(), n2.lower())
def __init_tools_view(self):
# Tools column
column = gtk.TreeViewColumn('Tools')
renderer = gtk.CellRendererText()
column.pack_start(renderer, False)
renderer.set_property('editable', True)
self.view.append_column(column)
column.set_cell_data_func(renderer, self.get_cell_data_cb)
renderer.connect('edited', self.on_view_label_cell_edited)
renderer.connect('editing-started', self.on_view_label_cell_editing_started)
self.selection_changed_id = self.view.get_selection().connect('changed', self.on_view_selection_changed, None)
def __init_combobox(self, name):
combo = self[name]
combo.set_active(0)
# Convenience function to get an object from its name
def __getitem__(self, key):
return self.ui.get_object(key)
def set_active_by_name(self, combo_name, option_name):
combo = self[combo_name]
model = combo.get_model()
piter = model.get_iter_first()
while piter is not None:
if model.get_value(piter, self.NAME_COLUMN) == option_name:
combo.set_active_iter(piter)
return True
piter = model.iter_next(piter)
return False
def get_selected_tool(self):
model, piter = self.view.get_selection().get_selected()
if piter is not None:
tool = model.get_value(piter, self.TOOL_COLUMN)
if not isinstance(tool, Tool):
tool = None
return piter, tool
else:
return None, None
def compute_hash(self, string):
return hashlib.md5(string).hexdigest()
def save_current_tool(self):
if self.current_node is None:
return
if self.current_node.filename is None:
self.current_node.autoset_filename()
def combo_value(o, name):
combo = o[name]
return combo.get_model().get_value(combo.get_active_iter(), self.NAME_COLUMN)
self.current_node.input = combo_value(self, 'input')
self.current_node.output = combo_value(self, 'output')
self.current_node.applicability = combo_value(self, 'applicability')
self.current_node.save_files = combo_value(self, 'save-files')
buf = self['commands'].get_buffer()
script = buf.get_text(*buf.get_bounds())
h = self.compute_hash(script)
if h != self.script_hash:
# script has changed -> save it
self.current_node.save_with_script([line + "\n" for line in script.splitlines()])
self.script_hash = h
else:
self.current_node.save()
self.update_remove_revert()
def clear_fields(self):
self['accelerator'].set_text('')
buf = self['commands'].get_buffer()
buf.begin_not_undoable_action()
buf.set_text('')
buf.end_not_undoable_action()
for nm in ('input', 'output', 'applicability', 'save-files'):
self[nm].set_active(0)
self['languages_label'].set_text(_('All Languages'))
def fill_languages_button(self):
if not self.current_node or not self.current_node.languages:
self['languages_label'].set_text(_('All Languages'))
else:
manager = gsv.LanguageManager()
langs = []
for lang in self.current_node.languages:
if lang == 'plain':
langs.append(_('Plain Text'))
else:
l = manager.get_language(lang)
if l:
langs.append(l.get_name())
self['languages_label'].set_text(', '.join(langs))
def fill_fields(self):
node = self.current_node
self['accelerator'].set_text(default(node.shortcut, ''))
buf = self['commands'].get_buffer()
script = default(''.join(node.get_script()), '')
buf.begin_not_undoable_action()
buf.set_text(script)
buf.end_not_undoable_action()
self.script_hash = self.compute_hash(script)
contenttype = gio.content_type_guess(data=script)
lmanager = gedit.get_language_manager()
language = lmanager.guess_language(content_type=contenttype)
if language is not None:
buf.set_language(language)
buf.set_highlight_syntax(True)
else:
buf.set_highlight_syntax(False)
for nm in ('input', 'output', 'applicability', 'save-files'):
model = self[nm].get_model()
piter = model.get_iter_first()
self.set_active_by_name(nm,
default(node.__getattribute__(nm.replace('-', '_')),
model.get_value(piter, self.NAME_COLUMN)))
self.fill_languages_button()
def update_remove_revert(self):
piter, node = self.get_selected_tool()
removable = node is not None and node.is_local()
self['remove-tool-button'].set_sensitive(removable)
self['revert-tool-button'].set_sensitive(removable)
if node is not None and node.is_global():
self['remove-tool-button'].hide()
self['revert-tool-button'].show()
else:
self['remove-tool-button'].show()
self['revert-tool-button'].hide()
def do_update(self):
self.update_remove_revert()
piter, node = self.get_selected_tool()
self.current_node = node
if node is not None:
self.fill_fields()
self['tool-table'].set_sensitive(True)
else:
self.clear_fields()
self['tool-table'].set_sensitive(False)
def language_id_from_iter(self, piter):
if not piter:
return None
tool = self.model.get_value(piter, self.TOOL_COLUMN)
if isinstance(tool, Tool):
piter = self.model.iter_parent(piter)
tool = self.model.get_value(piter, self.TOOL_COLUMN)
if isinstance(tool, gsv.Language):
return tool.get_id()
elif tool:
return 'plain'
return None
def selected_language_id(self):
# Find current language if there is any
model, piter = self.view.get_selection().get_selected()
return self.language_id_from_iter(piter)
def on_new_tool_button_clicked(self, button):
self.save_current_tool()
# block handlers while inserting a new item
self.view.get_selection().handler_block(self.selection_changed_id)
self.current_node = Tool(self.tools.tree);
self.current_node.name = _('New tool')
self.tools.tree.tools.append(self.current_node)
lang = self.selected_language_id()
if lang:
self.current_node.languages = [lang]
piter = self.add_tool(self.current_node)
self.view.set_cursor(self.model.get_path(piter), self.view.get_column(self.TOOL_COLUMN), True)
self.fill_fields()
self['tool-table'].set_sensitive(True)
self.view.get_selection().handler_unblock(self.selection_changed_id)
def tool_changed(self, tool, refresh=False):
for row in self._tool_rows[tool]:
self.model.row_changed(row.get_path(), self.model.get_iter(row.get_path()))
if refresh and tool == self.current_node:
self.fill_fields()
self.update_remove_revert()
def on_remove_tool_button_clicked(self, button):
piter, node = self.get_selected_tool()
if not node:
return
if node.is_global():
shortcut = node.shortcut
if node.parent.revert_tool(node):
self.remove_accelerator(node, shortcut)
self.add_accelerator(node)
self['revert-tool-button'].set_sensitive(False)
self.fill_fields()
self.tool_changed(node)
else:
parent = self.model.iter_parent(piter)
language = self.language_id_from_iter(parent)
self.model.remove(piter)
if language in node.languages:
node.languages.remove(language)
self._tool_rows[node] = filter(lambda x: x.valid(), self._tool_rows[node])
if not self._tool_rows[node]:
del self._tool_rows[node]
if node.parent.delete_tool(node):
self.remove_accelerator(node)
self.current_node = None
self.script_hash = None
if self.model.iter_is_valid(piter):
self.view.set_cursor(self.model.get_path(piter), self.view.get_column(self.TOOL_COLUMN), False)
self.view.grab_focus()
path = self._languages[language].get_path()
parent = self.model.get_iter(path)
if not self.model.iter_has_child(parent):
self.model.remove(parent)
del self._languages[language]
def on_view_label_cell_edited(self, cell, path, new_text):
if new_text != '':
piter = self.model.get_iter(path)
tool = self.model.get_value(piter, self.TOOL_COLUMN)
tool.name = new_text
self.save_current_tool()
self.tool_changed(tool)
def on_view_label_cell_editing_started(self, renderer, editable, path):
piter = self.model.get_iter(path)
tool = self.model.get_value(piter, self.TOOL_COLUMN)
if isinstance(editable, gtk.Entry):
editable.set_text(tool.name)
editable.grab_focus()
def on_view_selection_changed(self, selection, userdata):
self.save_current_tool()
self.do_update()
def accelerator_collision(self, name, node):
if not name in self.accelerators:
return []
ret = []
for other in self.accelerators[name]:
if not other.languages or not node.languages:
ret.append(other)
continue
for lang in other.languages:
if lang in node.languages:
ret.append(other)
continue
return ret
def set_accelerator(self, keyval, mod):
# Check whether accelerator already exists
self.remove_accelerator(self.current_node)
name = gtk.accelerator_name(keyval, mod)
if name == '':
self.current_node.shorcut = None
self.save_current_tool()
return True
col = self.accelerator_collision(name, self.current_node)
if col:
dialog = gtk.MessageDialog(self.dialog,
gtk.DIALOG_MODAL,
gtk.MESSAGE_ERROR,
gtk.BUTTONS_OK,
_('This accelerator is already bound to %s') % (', '.join(map(lambda x: x.name, col)),))
dialog.run()
dialog.destroy()
self.add_accelerator(self.current_node)
return False
self.current_node.shortcut = name
self.add_accelerator(self.current_node)
self.save_current_tool()
return True
def on_accelerator_key_press(self, entry, event):
mask = event.state & gtk.accelerator_get_default_mod_mask()
if event.keyval == gtk.keysyms.Escape:
entry.set_text(default(self.current_node.shortcut, ''))
self['commands'].grab_focus()
return True
elif event.keyval == gtk.keysyms.Delete \
or event.keyval == gtk.keysyms.BackSpace:
entry.set_text('')
self.remove_accelerator(self.current_node)
self.current_node.shortcut = None
self['commands'].grab_focus()
return True
elif event.keyval in range(gtk.keysyms.F1, gtk.keysyms.F12 + 1):
# New accelerator
if self.set_accelerator(event.keyval, mask):
entry.set_text(default(self.current_node.shortcut, ''))
self['commands'].grab_focus()
# Capture all `normal characters`
return True
elif gtk.gdk.keyval_to_unicode(event.keyval):
if mask:
# New accelerator
if self.set_accelerator(event.keyval, mask):
entry.set_text(default(self.current_node.shortcut, ''))
self['commands'].grab_focus()
# Capture all `normal characters`
return True
else:
return False
def on_accelerator_focus_in(self, entry, event):
if self.current_node is None:
return
if self.current_node.shortcut:
entry.set_text(_('Type a new accelerator, or press Backspace to clear'))
else:
entry.set_text(_('Type a new accelerator'))
def on_accelerator_focus_out(self, entry, event):
if self.current_node is not None:
entry.set_text(default(self.current_node.shortcut, ''))
self.tool_changed(self.current_node)
def on_tool_manager_dialog_response(self, dialog, response):
if response == gtk.RESPONSE_HELP:
gedit.help_display(self.dialog, 'gedit', 'gedit-external-tools-plugin')
return
self.on_tool_manager_dialog_focus_out(dialog, None)
self.dialog.destroy()
self.dialog = None
self.tools = None
def on_tool_manager_dialog_focus_out(self, dialog, event):
self.save_current_tool()
for window in gedit.app_get_default().get_windows():
helper = window.get_data("ExternalToolsPluginWindowData")
helper.menu.update()
def get_cell_data_cb(self, column, cell, model, piter):
tool = model.get_value(piter, self.TOOL_COLUMN)
if tool == None or not isinstance(tool, Tool):
if tool == None:
label = _('All Languages')
elif not isinstance(tool, gsv.Language):
label = _('Plain Text')
else:
label = tool.get_name()
markup = saxutils.escape(label)
editable = False
else:
escaped = saxutils.escape(tool.name)
if tool.shortcut:
markup = '%s (<b>%s</b>)' % (escaped, saxutils.escape(tool.shortcut))
else:
markup = escaped
editable = True
cell.set_properties(markup=markup, editable=editable)
def tool_in_language(self, tool, lang):
if not lang in self._languages:
return False
ref = self._languages[lang]
parent = ref.get_path()
for row in self._tool_rows[tool]:
path = row.get_path()
if path[0] == parent[0]:
return True
return False
def update_languages(self, popup):
self.current_node.languages = popup.languages()
self.fill_languages_button()
piter, node = self.get_selected_tool()
ret = None
if node:
ref = gtk.TreeRowReference(self.model, self.model.get_path(piter))
# Update languages, make sure to inhibit selection change stuff
self.view.get_selection().handler_block(self.selection_changed_id)
# Remove all rows that are no longer
for row in list(self._tool_rows[self.current_node]):
piter = self.model.get_iter(row.get_path())
language = self.language_id_from_iter(piter)
if (not language and not self.current_node.languages) or \
(language in self.current_node.languages):
continue
# Remove from language
self.model.remove(piter)
self._tool_rows[self.current_node].remove(row)
# If language is empty, remove it
parent = self.model.get_iter(self._languages[language].get_path())
if not self.model.iter_has_child(parent):
self.model.remove(parent)
del self._languages[language]
# Now, add for any that are new
manager = gsv.LanguageManager()
for lang in self.current_node.languages:
if not self.tool_in_language(self.current_node, lang):
l = manager.get_language(lang)
if not l:
l = 'plain'
self.add_tool_to_language(self.current_node, l)
if not self.current_node.languages and not self.tool_in_language(self.current_node, None):
self.add_tool_to_language(self.current_node, None)
# Check if we can still keep the current
if not ref or not ref.valid():
# Change selection to first language
path = self._tool_rows[self.current_node][0].get_path()
piter = self.model.get_iter(path)
parent = self.model.iter_parent(piter)
# Expand parent, select child and scroll to it
self.view.expand_row(self.model.get_path(parent), False)
self.view.get_selection().select_path(path)
self.view.set_cursor(path, self.view.get_column(self.TOOL_COLUMN), False)
self.view.get_selection().handler_unblock(self.selection_changed_id)
def on_languages_button_clicked(self, button):
popup = LanguagesPopup(self.current_node.languages)
popup.set_transient_for(self.dialog)
origin = button.window.get_origin()
popup.move(origin[0], origin[1] - popup.allocation.height)
popup.connect('destroy', self.update_languages)
# ex:et:ts=4:

View File

@@ -0,0 +1,224 @@
# -*- coding: utf-8 -*-
# Gedit External Tools plugin
# Copyright (C) 2005-2006 Steve Frécinaux <steve@istique.net>
# Copyright (C) 2010 Per Arneng <per.arneng@anyplanet.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
__all__ = ('OutputPanel', 'UniqueById')
import gtk, gedit
import pango
import gobject
import os
from weakref import WeakKeyDictionary
from capture import *
from gtk import gdk
import re
import gio
import linkparsing
import filelookup
class UniqueById:
__shared_state = WeakKeyDictionary()
def __init__(self, i):
if i in self.__class__.__shared_state:
self.__dict__ = self.__class__.__shared_state[i]
return True
else:
self.__class__.__shared_state[i] = self.__dict__
return False
def states(self):
return self.__class__.__shared_state
class OutputPanel(UniqueById):
def __init__(self, datadir, window):
if UniqueById.__init__(self, window):
return
callbacks = {
'on_stop_clicked' : self.on_stop_clicked,
'on_view_visibility_notify_event': self.on_view_visibility_notify_event,
'on_view_motion_notify_event': self.on_view_motion_notify_event,
'on_view_button_press_event': self.on_view_button_press_event
}
self.window = window
self.ui = gtk.Builder()
self.ui.add_from_file(os.path.join(datadir, 'ui', 'outputpanel.ui'))
self.ui.connect_signals(callbacks)
self.panel = self["output-panel"]
self['view'].modify_font(pango.FontDescription('Monospace'))
buffer = self['view'].get_buffer()
self.normal_tag = buffer.create_tag('normal')
self.error_tag = buffer.create_tag('error')
self.error_tag.set_property('foreground', 'red')
self.italic_tag = buffer.create_tag('italic')
self.italic_tag.set_property('style', pango.STYLE_OBLIQUE)
self.bold_tag = buffer.create_tag('bold')
self.bold_tag.set_property('weight', pango.WEIGHT_BOLD)
self.invalid_link_tag = buffer.create_tag('invalid_link')
self.link_tag = buffer.create_tag('link')
self.link_tag.set_property('underline', pango.UNDERLINE_SINGLE)
self.link_cursor = gdk.Cursor(gdk.HAND2)
self.normal_cursor = gdk.Cursor(gdk.XTERM)
self.process = None
self.links = []
self.link_parser = linkparsing.LinkParser()
self.file_lookup = filelookup.FileLookup()
def set_process(self, process):
self.process = process
def __getitem__(self, key):
# Convenience function to get an object from its name
return self.ui.get_object(key)
def on_stop_clicked(self, widget, *args):
if self.process is not None:
self.write("\n" + _('Stopped.') + "\n",
self.italic_tag)
self.process.stop(-1)
def scroll_to_end(self):
iter = self['view'].get_buffer().get_end_iter()
self['view'].scroll_to_iter(iter, 0.0)
return False # don't requeue this handler
def clear(self):
self['view'].get_buffer().set_text("")
self.links = []
def visible(self):
panel = self.window.get_bottom_panel()
return panel.props.visible and panel.item_is_active(self.panel)
def write(self, text, tag = None):
buffer = self['view'].get_buffer()
end_iter = buffer.get_end_iter()
insert = buffer.create_mark(None, end_iter, True)
if tag is None:
buffer.insert(end_iter, text)
else:
buffer.insert_with_tags(end_iter, text, tag)
# find all links and apply the appropriate tag for them
links = self.link_parser.parse(text)
for lnk in links:
insert_iter = buffer.get_iter_at_mark(insert)
lnk.start = insert_iter.get_offset() + lnk.start
lnk.end = insert_iter.get_offset() + lnk.end
start_iter = buffer.get_iter_at_offset(lnk.start)
end_iter = buffer.get_iter_at_offset(lnk.end)
tag = None
# if the link points to an existing file then it is a valid link
if self.file_lookup.lookup(lnk.path) is not None:
self.links.append(lnk)
tag = self.link_tag
else:
tag = self.invalid_link_tag
buffer.apply_tag(tag, start_iter, end_iter)
buffer.delete_mark(insert)
gobject.idle_add(self.scroll_to_end)
def show(self):
panel = self.window.get_bottom_panel()
panel.show()
panel.activate_item(self.panel)
def update_cursor_style(self, view, x, y):
if self.get_link_at_location(view, x, y) is not None:
cursor = self.link_cursor
else:
cursor = self.normal_cursor
view.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(cursor)
def on_view_motion_notify_event(self, view, event):
if event.window == view.get_window(gtk.TEXT_WINDOW_TEXT):
self.update_cursor_style(view, int(event.x), int(event.y))
return False
def on_view_visibility_notify_event(self, view, event):
if event.window == view.get_window(gtk.TEXT_WINDOW_TEXT):
x, y, m = event.window.get_pointer()
self.update_cursor_style(view, x, y)
return False
def idle_grab_focus(self):
self.window.get_active_view().grab_focus()
return False
def get_link_at_location(self, view, x, y):
"""
Get the link under a specified x,y coordinate. If no link exists then
None is returned.
"""
# get the offset within the buffer from the x,y coordinates
buff_x, buff_y = view.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT,
x, y)
iter_at_xy = view.get_iter_at_location(buff_x, buff_y)
offset = iter_at_xy.get_offset()
# find the first link that contains the offset
for lnk in self.links:
if offset >= lnk.start and offset <= lnk.end:
return lnk
# no link was found at x,y
return None
def on_view_button_press_event(self, view, event):
if event.button != 1 or event.type != gdk.BUTTON_PRESS or \
event.window != view.get_window(gtk.TEXT_WINDOW_TEXT):
return False
link = self.get_link_at_location(view, int(event.x), int(event.y))
if link is None:
return False
gfile = self.file_lookup.lookup(link.path)
if gfile:
gedit.commands.load_uri(self.window, gfile.get_uri(), None,
link.line_nr)
gobject.idle_add(self.idle_grab_focus)
# ex:ts=4:et:

View File

@@ -0,0 +1,53 @@
<?xml version="1.0"?>
<!-- Generated with glade3
Version: 2.91.3
Date: Sat Nov 18 13:58:59 2006
User: sf
Host: antea
-->
<interface>
<object class="GtkHBox" id="output-panel">
<property name="visible">True</property>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow1">
<property name="visible">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<child>
<object class="GtkTextView" id="view">
<property name="visible">True</property>
<property name="editable">False</property>
<property name="wrap_mode">GTK_WRAP_WORD</property>
<property name="cursor_visible">False</property>
<property name="accepts_tab">False</property>
<signal name="button_press_event" handler="on_view_button_press_event"/>
<signal name="motion_notify_event" handler="on_view_motion_notify_event"/>
<signal name="visibility_notify_event" handler="on_view_visibility_notify_event"/>
</object>
</child>
</object>
</child>
<child>
<object class="GtkVButtonBox" id="vbuttonbox1">
<property name="visible">True</property>
<property name="border_width">6</property>
<property name="spacing">6</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<child>
<object class="GtkButton" id="stop">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="label">gtk-stop</property>
<property name="use_stock">True</property>
<signal handler="on_stop_clicked" name="clicked"/>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
</interface>

View File

@@ -0,0 +1,606 @@
<?xml version="1.0"?>
<interface>
<object class="GtkListStore" id="model_save_files">
<columns>
<!-- column-name gchararray -->
<column type="gchararray"/>
<!-- column-name gchararray -->
<column type="gchararray"/>
</columns>
<data>
<row>
<col id="0" translatable="yes">Nothing</col>
<col id="1">nothing</col>
</row>
<row>
<col id="0" translatable="yes">Current document</col>
<col id="1">document</col>
</row>
<row>
<col id="0" translatable="yes">All documents</col>
<col id="1">all</col>
</row>
</data>
</object>
<object class="GtkListStore" id="model_input">
<columns>
<column type="gchararray"/>
<column type="gchararray"/>
</columns>
<data>
<row>
<col id="0" translatable="yes">Nothing</col>
<col id="1">nothing</col>
</row>
<row>
<col id="0" translatable="yes">Current document</col>
<col id="1">document</col>
</row>
<row>
<col id="0" translatable="yes">Current selection</col>
<col id="1">selection</col>
</row>
<row>
<col id="0" translatable="yes">Current selection (default to document)</col>
<col id="1">selection-document</col>
</row>
<row>
<col id="0" translatable="yes">Current line</col>
<col id="1">line</col>
</row>
<row>
<col id="0" translatable="yes">Current word</col>
<col id="1">word</col>
</row>
</data>
</object>
<object class="GtkListStore" id="model_output">
<columns>
<!-- column-name gchararray -->
<column type="gchararray"/>
<!-- column-name gchararray1 -->
<column type="gchararray"/>
</columns>
<data>
<row>
<col id="0" translatable="yes">Nothing</col>
<col id="1">nothing</col>
</row>
<row>
<col id="0" translatable="yes">Display in bottom pane</col>
<col id="1">output-panel</col>
</row>
<row>
<col id="0" translatable="yes">Create new document</col>
<col id="1">new-document</col>
</row>
<row>
<col id="0" translatable="yes">Append to current document</col>
<col id="1">append-document</col>
</row>
<row>
<col id="0" translatable="yes">Replace current document</col>
<col id="1">replace-document</col>
</row>
<row>
<col id="0" translatable="yes">Replace current selection</col>
<col id="1">replace-selection</col>
</row>
<row>
<col id="0" translatable="yes">Insert at cursor position</col>
<col id="1">insert</col>
</row>
</data>
</object>
<object class="GtkListStore" id="model_applicability">
<columns>
<!-- column-name gchararray -->
<column type="gchararray"/>
<!-- column-name gchararray1 -->
<column type="gchararray"/>
</columns>
<data>
<row>
<col id="0" translatable="yes">All documents</col>
<col id="1">all</col>
</row>
<row>
<col id="0" translatable="yes">All documents except untitled ones</col>
<col id="1">titled</col>
</row>
<row>
<col id="0" translatable="yes">Local files only</col>
<col id="1">local</col>
</row>
<row>
<col id="0" translatable="yes">Remote files only</col>
<col id="1">remote</col>
</row>
<row>
<col id="0" translatable="yes">Untitled documents only</col>
<col id="1">untitled</col>
</row>
</data>
</object>
<object class="GeditDocument" id="commands_buffer">
<property name="highlight-matching-brackets">True</property>
</object>
<object class="GtkDialog" id="tool-manager-dialog">
<property name="title" translatable="yes">External Tools Manager</property>
<property name="default_width">750</property>
<property name="default_height">500</property>
<property name="type_hint">dialog</property>
<property name="skip_taskbar_hint">True</property>
<property name="has_separator">False</property>
<signal name="focus_out_event" handler="on_tool_manager_dialog_focus_out"/>
<signal name="response" handler="on_tool_manager_dialog_response"/>
<child internal-child="vbox">
<object class="GtkVBox" id="tool-manager-dialog-vbox">
<property name="visible">True</property>
<child>
<object class="GtkHPaned" id="paned">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="border_width">6</property>
<property name="position">275</property>
<child>
<object class="GtkVBox" id="vbox2">
<property name="visible">True</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="label20">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_Tools:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">view</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="scrolled_window1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hscrollbar_policy">automatic</property>
<property name="vscrollbar_policy">automatic</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkTreeView" id="view">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">False</property>
<property name="reorderable">True</property>
</object>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox2">
<property name="visible">True</property>
<property name="spacing">6</property>
<child>
<object class="GtkButton" id="new-tool-button">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can_default">True</property>
<property name="receives_default">False</property>
<signal name="clicked" handler="on_new_tool_button_clicked"/>
<child>
<object class="GtkImage" id="new-tool-image">
<property name="visible">True</property>
<property name="stock">gtk-new</property>
<property name="icon-size">4</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="revert-tool-button">
<property name="can_focus">False</property>
<property name="receives_default">False</property>
<signal name="clicked" handler="on_remove_tool_button_clicked"/>
<child>
<object class="GtkImage" id="revert-tool-image">
<property name="visible">True</property>
<property name="stock">gtk-revert-to-saved</property>
<property name="icon-size">4</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="pack_type">end</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkButton" id="remove-tool-button">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can_default">True</property>
<property name="receives_default">False</property>
<signal name="clicked" handler="on_remove_tool_button_clicked"/>
<child>
<object class="GtkImage" id="remove-tool-image">
<property name="visible">True</property>
<property name="stock">gtk-delete</property>
<property name="icon-size">4</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="pack_type">end</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="resize">False</property>
<property name="shrink">False</property>
</packing>
</child>
<child>
<object class="GtkVBox" id="vbox5">
<property name="visible">True</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="title">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="yalign">0.5</property>
<property name="label" translatable="yes">_Edit:</property>
<property name="mnemonic_widget">commands</property>
<property name="use_underline">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox7">
<property name="visible">True</property>
<child>
<object class="GtkLabel" id="label22">
<property name="visible">True</property>
<property name="label" translatable="yes"> </property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkTable" id="tool-table">
<property name="visible">True</property>
<property name="n_rows">6</property>
<property name="n_columns">2</property>
<property name="column_spacing">6</property>
<property name="row_spacing">6</property>
<child>
<object class="GtkEntry" id="accelerator">
<property name="visible">True</property>
<property name="can_focus">True</property>
<signal name="key_press_event" handler="on_accelerator_key_press"/>
<signal name="focus_out_event" handler="on_accelerator_focus_out"/>
<signal name="focus_in_event" handler="on_accelerator_focus_in"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<child>
<object class="GtkComboBox" id="applicability">
<property name="visible">True</property>
<property name="model">model_applicability</property>
<child>
<object class="GtkCellRendererText" id="applicability_renderer"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
<packing>
<property name="position">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<object class="GtkEventBox" id="languages_event_box">
<property name="visible">True</property>
<property name="visible-window">True</property>
<child>
<object class="GtkButton" id="languages_button">
<property name="visible">True</property>
<signal name="clicked" handler="on_languages_button_clicked"/>
<child>
<object class="GtkLabel" id="languages_label">
<property name="visible">True</property>
<property name="label" translatable="yes">All Languages</property>
<property name="xalign">0</property>
<property name="yalign">0.5</property>
<property name="ellipsize">PANGO_ELLIPSIZE_MIDDLE</property>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="position">1</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">5</property>
<property name="bottom_attach">6</property>
<property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="output">
<property name="visible">True</property>
<property name="model">model_output</property>
<child>
<object class="GtkCellRendererText" id="output_renderer"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="input">
<property name="visible">True</property>
<property name="model">model_input</property>
<child>
<object class="GtkCellRendererText" id="input_renderer"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="save-files">
<property name="model">model_save_files</property>
<property name="visible">True</property>
<child>
<object class="GtkCellRendererText" id="renderer1"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label23">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_Applicability:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">applicability</property>
</object>
<packing>
<property name="top_attach">5</property>
<property name="bottom_attach">6</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label8">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_Output:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">output</property>
</object>
<packing>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label7">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_Input:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">input</property>
</object>
<packing>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label6">
<property name="xalign">0</property>
<property name="label" translatable="yes">_Save:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">save-files</property>
<property name="visible">True</property>
</object>
<packing>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_Shortcut Key:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">accelerator</property>
</object>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">automatic</property>
<property name="vscrollbar_policy">automatic</property>
<property name="shadow_type">in</property>
<child>
<object class="GeditView" id="commands">
<property name="buffer">commands_buffer</property>
<property name="visible">True</property>
<property name="auto-indent">True</property>
<property name="insert-spaces-instead-of-tabs">False</property>
<property name="smart-home-end">GTK_SOURCE_SMART_HOME_END_AFTER</property>
<property name="tab-width">2</property>
<property name="highlight-current-line">True</property>
<property name="show-right-margin">False</property>
<property name="show-line-numbers">True</property>
</object>
</child>
</object>
<packing>
<property name="right_attach">2</property>
</packing>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="resize">True</property>
<property name="shrink">False</property>
</packing>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child internal-child="action_area">
<object class="GtkHButtonBox" id="hbuttonbox1">
<property name="visible">True</property>
<property name="layout_style">end</property>
<child>
<object class="GtkButton" id="button1">
<property name="label">gtk-help</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="receives_default">False</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="button2">
<property name="label">gtk-close</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="receives_default">False</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="pack_type">end</property>
<property name="position">0</property>
</packing>
</child>
</object>
</child>
<action-widgets>
<action-widget response="-11">button1</action-widget>
<action-widget response="-7">button2</action-widget>
</action-widgets>
</object>
</interface>