initial
This commit is contained in:
15
plugins/externaltools/Makefile.am
Executable file
15
plugins/externaltools/Makefile.am
Executable 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
|
65
plugins/externaltools/data/Makefile.am
Executable file
65
plugins/externaltools/data/Makefile.am
Executable 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
|
9
plugins/externaltools/data/build.desktop.in
Executable file
9
plugins/externaltools/data/build.desktop.in
Executable 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=
|
15
plugins/externaltools/data/build.tool.in
Executable file
15
plugins/externaltools/data/build.tool.in
Executable 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
|
8
plugins/externaltools/data/open-terminal-here-osx.desktop.in
Executable file
8
plugins/externaltools/data/open-terminal-here-osx.desktop.in
Executable 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=
|
16
plugins/externaltools/data/open-terminal-here-osx.tool.in
Executable file
16
plugins/externaltools/data/open-terminal-here-osx.tool.in
Executable 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
|
8
plugins/externaltools/data/open-terminal-here.desktop.in
Executable file
8
plugins/externaltools/data/open-terminal-here.desktop.in
Executable 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=
|
4
plugins/externaltools/data/open-terminal-here.tool.in
Executable file
4
plugins/externaltools/data/open-terminal-here.tool.in
Executable 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 &
|
9
plugins/externaltools/data/remove-trailing-spaces.desktop.in
Executable file
9
plugins/externaltools/data/remove-trailing-spaces.desktop.in
Executable 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=
|
3
plugins/externaltools/data/remove-trailing-spaces.tool.in
Executable file
3
plugins/externaltools/data/remove-trailing-spaces.tool.in
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
sed 's/[[:blank:]]*$//'
|
8
plugins/externaltools/data/run-command.desktop.in
Executable file
8
plugins/externaltools/data/run-command.desktop.in
Executable 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=
|
4
plugins/externaltools/data/run-command.tool.in
Executable file
4
plugins/externaltools/data/run-command.tool.in
Executable 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"`
|
9
plugins/externaltools/externaltools.gedit-plugin.desktop.in
Executable file
9
plugins/externaltools/externaltools.gedit-plugin.desktop.in
Executable 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
|
4
plugins/externaltools/scripts/Makefile.am
Executable file
4
plugins/externaltools/scripts/Makefile.am
Executable file
@@ -0,0 +1,4 @@
|
||||
EXTRA_DIST = gedit-tool-merge.pl
|
||||
|
||||
|
||||
-include $(top_srcdir)/git.mk
|
78
plugins/externaltools/scripts/gedit-tool-merge.pl
Executable file
78
plugins/externaltools/scripts/gedit-tool-merge.pl
Executable 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;
|
23
plugins/externaltools/tools/Makefile.am
Executable file
23
plugins/externaltools/tools/Makefile.am
Executable 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
|
281
plugins/externaltools/tools/__init__.py
Executable file
281
plugins/externaltools/tools/__init__.py
Executable 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:
|
214
plugins/externaltools/tools/capture.py
Executable file
214
plugins/externaltools/tools/capture.py
Executable 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:
|
145
plugins/externaltools/tools/filelookup.py
Executable file
145
plugins/externaltools/tools/filelookup.py
Executable 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:
|
303
plugins/externaltools/tools/functions.py
Executable file
303
plugins/externaltools/tools/functions.py
Executable 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:
|
493
plugins/externaltools/tools/library.py
Executable file
493
plugins/externaltools/tools/library.py
Executable 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:
|
231
plugins/externaltools/tools/linkparsing.py
Executable file
231
plugins/externaltools/tools/linkparsing.py
Executable 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:
|
948
plugins/externaltools/tools/manager.py
Executable file
948
plugins/externaltools/tools/manager.py
Executable 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:
|
224
plugins/externaltools/tools/outputpanel.py
Executable file
224
plugins/externaltools/tools/outputpanel.py
Executable 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:
|
53
plugins/externaltools/tools/outputpanel.ui
Executable file
53
plugins/externaltools/tools/outputpanel.ui
Executable 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>
|
606
plugins/externaltools/tools/tools.ui
Executable file
606
plugins/externaltools/tools/tools.ui
Executable 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>
|
Reference in New Issue
Block a user