diff --git a/0001-add-chinese-translation.patch b/0001-add-chinese-translation.patch index b1e9b0fb2fc73ce16f2d5d0e90206bbd424b641a..5cd9f0acb112fca2aaec056a92ed821fb9a59232 100644 --- a/0001-add-chinese-translation.patch +++ b/0001-add-chinese-translation.patch @@ -1,4 +1,4 @@ -From 728b7228074f292c4a2f400146cf4de6b1879229 Mon Sep 17 00:00:00 2001 +From 1b4af9d515b3167613e8978515002846a520ce7c Mon Sep 17 00:00:00 2001 From: liaowei Date: Thu, 6 Aug 2020 02:17:02 -0400 Subject: [PATCH] add chinese translation @@ -8,18 +8,18 @@ Subject: [PATCH] add chinese translation 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/po/zh_CN.po b/po/zh_CN.po -index 96066fa..eed8ec1 100644 +index 826dbde..55cdd6b 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po -@@ -503,7 +503,7 @@ msgstr "" +@@ -625,7 +625,7 @@ msgstr "pluma 过滤器编号" - #: ../data/pluma.desktop.in.in.h:1 + #: data/pluma.appdata.xml.in.in:7 data/pluma.desktop.in.in:3 msgid "Pluma" -msgstr "Pluma" +msgstr "文本编辑器" - #: ../data/pluma.desktop.in.in.h:2 ../pluma/pluma-print-job.c:760 - msgid "Text Editor" + #: data/pluma.appdata.xml.in.in:8 + msgid "A Text Editor for the MATE desktop environment" -- -2.18.1 +2.43.0 diff --git a/0001-fix-pluma-utils-Fixed-garbled-toolTips-path-in-tab-p.patch b/0002-fix-pluma-utils-Fixed-garbled-toolTips-path-in-tab-p.patch similarity index 100% rename from 0001-fix-pluma-utils-Fixed-garbled-toolTips-path-in-tab-p.patch rename to 0002-fix-pluma-utils-Fixed-garbled-toolTips-path-in-tab-p.patch diff --git a/pluma-1.22.2.tar.xz b/pluma-1.28.0.tar.xz similarity index 30% rename from pluma-1.22.2.tar.xz rename to pluma-1.28.0.tar.xz index 1f51702462edd74e83c1dfa45d8350bde9678440..e2b6142fa5d4728c956f50f8381fb3b49b1c1fab 100644 Binary files a/pluma-1.22.2.tar.xz and b/pluma-1.28.0.tar.xz differ diff --git a/pluma.spec b/pluma.spec index 2a478ddf1e350bdbe48b673d6386d75a391bbdd7..feeaa2b89f29a0727dd9a4af3040a50c392f22b8 100644 --- a/pluma.spec +++ b/pluma.spec @@ -1,63 +1,39 @@ -# Conditional for release and snapshot builds. Uncomment for release-builds. -%global rel_build 1 - -# This is needed, because src-url contains branched part of versioning-scheme. -%global branch 1.22 - -# Settings used for build from snapshots. -%{!?rel_build:%global commit c1ca209172a8b3a0751ac0a1e2dbec33c1894290} -%{!?rel_build:%global commit_date 20140712} -%{!?rel_build:%global shortcommit %(c=%{commit};echo ${c:0:7})} -%{!?rel_build:%global git_ver git%{commit_date}-%{shortcommit}} -%{!?rel_build:%global git_rel .git%{commit_date}.%{shortcommit}} -%{!?rel_build:%global git_tar %{name}-%{version}-%{git_ver}.tar.xz} - Summary: Text editor for the MATE desktop Name: pluma -Version: %{branch}.2 -Release: 4 - -License: GPLv2+ and LGPLv2+ +Version: 1.28.0 +Release: 1 +License: GPLv2+ and LGPLv2+ URL: http://mate-desktop.org # for downloading the tarball use 'spectool -g -R pluma.spec' # Source for release-builds. Source0: http://pub.mate-desktop.org/releases/%{branch}/%{name}-%{version}.tar.xz - -# https://github.com/mate-desktop/pluma/commit/e88a2ea -Patch1: pluma_0001-Reindent-all-Python-sources-to-ts-4.-Strip-trailing-1.22.patch -# https://github.com/mate-desktop/pluma/pull/433 -Patch2: pluma_0001-quickopen-plugin-change-code-for-Python-2-3-compatib.patch -# https://github.com/mate-desktop/pluma/pull/434 -Patch3: pluma_0001-pythonconsole-plugin-change-source-code-for-Python-2.patch -# https://github.com/mate-desktop/pluma/pull/435 -Patch4: pluma_0001-externaltools-plugin-change-code-for-Python-2-3-comp.patch -# https://github.com/mate-desktop/pluma/pull/436 -Patch5: pluma_0001-snippets-plugin-change-code-for-Python-2-3-compatibi.patch -# https://github.com/mate-desktop/pluma/pull/437 -Patch6: pluma_0001-Switch-to-Python-3.patch -Patch7: 0001-add-chinese-translation.patch -Patch8: 0001-fix-pluma-utils-Fixed-garbled-toolTips-path-in-tab-p.patch +Patch1: 0001-add-chinese-translation.patch +Patch2: 0002-fix-pluma-utils-Fixed-garbled-toolTips-path-in-tab-p.patch BuildRequires: desktop-file-utils BuildRequires: enchant-devel BuildRequires: libpeas-devel BuildRequires: gtk3-devel -BuildRequires: gtksourceview3-devel +BuildRequires: gtksourceview4-devel BuildRequires: iso-codes-devel BuildRequires: libSM-devel +BuildRequires: make BuildRequires: mate-common -BuildRequires: pygobject3-devel +BuildRequires: mate-desktop-devel +BuildRequires: python3-gobject-base BuildRequires: python3-devel +BuildRequires: (python3-setuptools if python3-devel >= 3.12) Requires: %{name}-data = %{version}-%{release} # needed to get a gsettings schema, #959607 Requires: mate-desktop-libs # needed to get a gsettings schema, #959607 -# Requires: caja-schemas +Requires: caja-schemas # the run-command plugin uses zenity Requires: zenity +Requires: libpeas %description pluma is a small, but powerful text editor designed specifically for @@ -82,7 +58,7 @@ This package contains shared data needed for pluma. %package devel Summary: Support for developing plugins for the pluma text editor Requires: %{name}%{?_isa} = %{version}-%{release} -Requires: gtksourceview3-devel +Requires: gtksourceview4-devel %description devel Development files for pluma @@ -91,19 +67,6 @@ Development files for pluma %prep %autosetup -p1 -%if 0%{?rel_build} -# for releases -#NOCONFIGURE=1 ./autogen.sh -%else -# needed for git snapshots -NOCONFIGURE=1 ./autogen.sh -%endif - -# with python3 -sed -i 's|#!\/usr\/bin/\python|#!\/usr\/bin\/python3|g' plugins/externaltools/data/switch-c.tool.in - -# Patch 5 -NOCONFIGURE=1 ./autogen.sh # Fix debug permissions with messy hack find ./*/* -type f -exec chmod 644 {} \; @@ -147,7 +110,7 @@ find %{buildroot} -name '*.a' -exec rm -f {} ';' %{_datadir}/glib-2.0/schemas/org.mate.pluma.plugins.spell.gschema.xml %files data -f %{name}.lang -%doc README COPYING AUTHORS +%doc README.md COPYING AUTHORS %{_datadir}/pluma/ %{_mandir}/man1/pluma.1.* @@ -159,14 +122,8 @@ find %{buildroot} -name '*.a' -exec rm -f {} ';' %changelog -* Thu Jul 20 2023 longcheng - 1.22.2-4 -- fix(pluma-utils):Fixed garbled toolTips path in tab page with GB18030 encoding chinese translation - -* Thu Jul 28 2022 longcheng - 1.22.2-3 -- Add chinese translation - -* Fri Dec 11 2020 douyan - 1.22.2-2 -- modify spec file +* Thu Mar 20 2025 yuanxing - 1.28.0-1 +- Initial build +- KYOS-F: add chinese translation +- KYOS-B: Fixed garbled toolTips path in tab page with GB18030 encoding(#7248) -* Fri Dec 11 2020 douyan - 1.22.2-1 -- package init for openEuler diff --git a/pluma_0001-Reindent-all-Python-sources-to-ts-4.-Strip-trailing-1.22.patch b/pluma_0001-Reindent-all-Python-sources-to-ts-4.-Strip-trailing-1.22.patch deleted file mode 100644 index d33f1a8a76f249fa6623eac762b8d164015bf9ad..0000000000000000000000000000000000000000 --- a/pluma_0001-Reindent-all-Python-sources-to-ts-4.-Strip-trailing-1.22.patch +++ /dev/null @@ -1,13183 +0,0 @@ -From ec7ec9934d6f80f99474ebeca16bce7b52fe78a1 Mon Sep 17 00:00:00 2001 -From: Patrick Monnerat -Date: Fri, 10 May 2019 16:26:39 +0200 -Subject: [PATCH 1/6] Reindent all Python sources to ts=4. Strip trailing - spaces. - ---- - plugins/externaltools/tools/__init__.py | 22 +- - plugins/externaltools/tools/capture.py | 18 +- - plugins/externaltools/tools/functions.py | 44 +- - plugins/externaltools/tools/library.py | 18 +- - plugins/externaltools/tools/linkparsing.py | 2 +- - plugins/externaltools/tools/manager.py | 272 +-- - plugins/externaltools/tools/outputpanel.py | 6 +- - .../pythonconsole/pythonconsole/__init__.py | 2 +- - plugins/quickopen/quickopen/__init__.py | 36 +- - plugins/quickopen/quickopen/popup.py | 877 ++++--- - plugins/quickopen/quickopen/virtualdirs.py | 84 +- - plugins/quickopen/quickopen/windowhelper.py | 218 +- - plugins/snippets/snippets/Completion.py | 304 +-- - plugins/snippets/snippets/Document.py | 1994 ++++++++------- - plugins/snippets/snippets/Exporter.py | 176 +- - plugins/snippets/snippets/Helper.py | 244 +- - plugins/snippets/snippets/Importer.py | 184 +- - plugins/snippets/snippets/LanguageManager.py | 25 +- - plugins/snippets/snippets/Library.py | 1892 +++++++------- - plugins/snippets/snippets/Manager.py | 2176 ++++++++--------- - plugins/snippets/snippets/Parser.py | 466 ++-- - plugins/snippets/snippets/Placeholder.py | 1278 +++++----- - plugins/snippets/snippets/Snippet.py | 657 ++--- - .../snippets/snippets/SubstitutionParser.py | 356 +-- - plugins/snippets/snippets/WindowHelper.py | 248 +- - plugins/snippets/snippets/__init__.py | 94 +- - 28 files changed, 5871 insertions(+), 5856 deletions(-) - mode change 100755 => 100644 plugins/quickopen/quickopen/__init__.py - mode change 100755 => 100644 plugins/quickopen/quickopen/popup.py - mode change 100755 => 100644 plugins/quickopen/quickopen/virtualdirs.py - mode change 100755 => 100644 plugins/quickopen/quickopen/windowhelper.py - mode change 100755 => 100644 plugins/snippets/snippets/Completion.py - mode change 100755 => 100644 plugins/snippets/snippets/Document.py - mode change 100755 => 100644 plugins/snippets/snippets/Exporter.py - mode change 100755 => 100644 plugins/snippets/snippets/Helper.py - mode change 100755 => 100644 plugins/snippets/snippets/Importer.py - mode change 100755 => 100644 plugins/snippets/snippets/LanguageManager.py - mode change 100755 => 100644 plugins/snippets/snippets/Library.py - mode change 100755 => 100644 plugins/snippets/snippets/Manager.py - mode change 100755 => 100644 plugins/snippets/snippets/Parser.py - mode change 100755 => 100644 plugins/snippets/snippets/Placeholder.py - mode change 100755 => 100644 plugins/snippets/snippets/Snippet.py - mode change 100755 => 100644 plugins/snippets/snippets/SubstitutionParser.py - mode change 100755 => 100644 plugins/snippets/snippets/WindowHelper.py - mode change 100755 => 100644 plugins/snippets/snippets/__init__.py - -diff --git a/plugins/externaltools/tools/__init__.py b/plugins/externaltools/tools/__init__.py -index 463c8f5..153d6c6 100755 ---- a/plugins/externaltools/tools/__init__.py -+++ b/plugins/externaltools/tools/__init__.py -@@ -57,12 +57,12 @@ class ToolMenu(object): - action._tool_handler = None - - self._action_group.remove_action(action) -- -+ - accelmap = Gtk.AccelMap.get() - - for s in self._signals: - accelmap.disconnect(s) -- -+ - self._signals = [] - - def _insert_directory(self, directory, path): -@@ -76,7 +76,7 @@ class ToolMenu(object): - manager.add_ui(self._merge_id, path, - action_name, action_name, - Gtk.UIManagerItemType.MENU, False) -- -+ - self._insert_directory(item, path + '/' + action_name) - - for item in directory.tools: -@@ -87,16 +87,16 @@ class ToolMenu(object): - # Attach the item and the handler to the action object - action._tool_item = item - action._tool_handler = handler -- -+ - # Make sure to replace accel - accelpath = '/ExternalToolsPluginToolActions/%s' % (action_name, ) -- -+ - if item.shortcut: - key, mod = Gtk.accelerator_parse(item.shortcut) - Gtk.AccelMap.change_entry(accelpath, key, mod, True) -- -+ - self._signals.append(Gtk.AccelMap.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, -@@ -106,7 +106,7 @@ class ToolMenu(object): - def on_accelmap_changed(self, accelmap, path, key, mod, tool): - tool.shortcut = Gtk.accelerator_name(key, mod) - tool.save() -- -+ - self._plugin.update_manager(tool) - - def update(self): -@@ -119,10 +119,10 @@ class ToolMenu(object): - 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: -@@ -142,7 +142,7 @@ class ToolMenu(object): - 'titled': titled, - 'untitled': not titled, - } -- -+ - language = document.get_language() - - for action in self._action_group.list_actions(): -diff --git a/plugins/externaltools/tools/capture.py b/plugins/externaltools/tools/capture.py -index 487c8db..73ce270 100755 ---- a/plugins/externaltools/tools/capture.py -+++ b/plugins/externaltools/tools/capture.py -@@ -29,7 +29,7 @@ class Capture(GObject.Object): - CAPTURE_STDERR = 0x02 - CAPTURE_BOTH = 0x03 - CAPTURE_NEEDS_SHELL = 0x04 -- -+ - WRITE_BUFFER_SIZE = 0x4000 - - __gsignals__ = { -@@ -73,7 +73,7 @@ class Capture(GObject.Object): - '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: -@@ -84,17 +84,17 @@ class Capture(GObject.Object): - 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 -@@ -132,13 +132,13 @@ class Capture(GObject.Object): - 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 -@@ -165,7 +165,7 @@ class Capture(GObject.Object): - - 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] -diff --git a/plugins/externaltools/tools/functions.py b/plugins/externaltools/tools/functions.py -index e126844..dd4f82b 100755 ---- a/plugins/externaltools/tools/functions.py -+++ b/plugins/externaltools/tools/functions.py -@@ -30,13 +30,13 @@ def default(val, 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 ==== -@@ -56,32 +56,32 @@ def run_external_tool(window, panel, node): - # 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(PLUMA_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(PLUMA_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(PLUMA_SELECTED_TEXT=bounds[0].get_text(bounds[1])) -- -+ - bounds = current_word(document) - capture.set_env(PLUMA_CURRENT_WORD=bounds[0].get_text(bounds[1])) -- -+ - capture.set_env(PLUMA_CURRENT_DOCUMENT_TYPE=document.get_mime_type()) -- -+ - if uri is not None: - gfile = Gio.file_new_for_uri(uri) - scheme = gfile.get_uri_scheme() -@@ -106,7 +106,7 @@ def run_external_tool(window, panel, node): - PLUMA_DOCUMENTS_PATH = ' '.join(documents_path)) - - flags = capture.CAPTURE_BOTH -- -+ - if not node.has_hash_bang(): - flags |= capture.CAPTURE_NEEDS_SHELL - -@@ -131,7 +131,7 @@ def run_external_tool(window, panel, node): - 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': -@@ -142,7 +142,7 @@ def run_external_tool(window, panel, node): - 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() -@@ -196,12 +196,12 @@ def run_external_tool(window, panel, node): - 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('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() - -@@ -222,7 +222,7 @@ class MultipleDocumentsSaver: - signals[doc] = doc.connect('saving', self.on_document_saving) - Pluma.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) -@@ -230,12 +230,12 @@ class MultipleDocumentsSaver: - 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._panel, self._node) - -@@ -275,7 +275,7 @@ def capture_end_execute_panel(capture, exit_code, panel, view, output_type): - mtype, uncertain = Gio.content_type_guess(None, doc.get_text(start, end, False).encode('utf-8')) - lmanager = GtkSource.LanguageManager.get_default() - language = lmanager.guess_language(doc.get_uri(), mtype) -- -+ - if language is not None: - doc.set_language(language) - -diff --git a/plugins/externaltools/tools/library.py b/plugins/externaltools/tools/library.py -index b4e6924..186c33f 100755 ---- a/plugins/externaltools/tools/library.py -+++ b/plugins/externaltools/tools/library.py -@@ -286,64 +286,80 @@ class Tool(object): - 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): -@@ -358,7 +374,6 @@ class Tool(object): - for line in fp: - if line.strip() == '': - continue -- - return line.startswith('#!') - - # There is no property for this one because this function is quite -@@ -404,7 +419,6 @@ class Tool(object): - - 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 -diff --git a/plugins/externaltools/tools/linkparsing.py b/plugins/externaltools/tools/linkparsing.py -index 27b9ba8..33ed614 100755 ---- a/plugins/externaltools/tools/linkparsing.py -+++ b/plugins/externaltools/tools/linkparsing.py -@@ -193,7 +193,7 @@ REGEXP_VALAC = r""" - - #ruby - #test.rb:5: ... --# from test.rb:3:in `each' -+# from test.rb:3:in `each' - # fist line parsed by REGEXP_STANDARD - REGEXP_RUBY = r""" - ^\s+from\s -diff --git a/plugins/externaltools/tools/manager.py b/plugins/externaltools/tools/manager.py -index 24d7d71..4da0deb 100755 ---- a/plugins/externaltools/tools/manager.py -+++ b/plugins/externaltools/tools/manager.py -@@ -34,7 +34,7 @@ class LanguagesPopup(Gtk.Window): - - def __init__(self, languages): - Gtk.Window.__init__(self, type=Gtk.WindowType.POPUP) -- -+ - self.set_default_size(200, 200) - self.props.can_focus = True - -@@ -42,9 +42,9 @@ class LanguagesPopup(Gtk.Window): - self.init_languages(languages) - - self.show() -- -+ - self.grab_add() -- -+ - self.keyboard = None - device_manager = Gdk.Display.get_device_manager(self.get_window().get_display()) - for device in device_manager.list_devices(Gdk.DeviceType.MASTER): -@@ -76,40 +76,40 @@ class LanguagesPopup(Gtk.Window): - - def build(self): - self.model = Gtk.ListStore(str, str, bool) -- -+ - self.sw = Gtk.ScrolledWindow() - self.sw.show() -- -+ - self.sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) - self.sw.set_shadow_type(Gtk.ShadowType.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.get_indices()[0] == 0 and enabled: - return True - -@@ -117,42 +117,42 @@ class LanguagesPopup(Gtk.Window): - 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 = GtkSource.LanguageManager() - langs = [manager.get_language(x) for x in manager.get_language_ids()] - langs.sort(key=lambda x: x.get_name()) -- -+ - 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: -@@ -165,55 +165,55 @@ class LanguagesPopup(Gtk.Window): - 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.get_window() - - geometry = window.get_geometry() - origin = window.get_origin() -- -+ - return event.x_root >= origin[1] and \ - event.x_root <= origin[1] + geometry[2] and \ - event.y_root >= origin[2] and \ - event.y_root <= origin[2] + geometry[3] -- -+ - def do_destroy(self): - if self.keyboard: - self.keyboard.ungrab(Gdk.CURRENT_TIME) - self.pointer.ungrab(Gdk.CURRENT_TIME) -- -+ - 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[1] - to[1] - event.y += fr[2] - to[2] -- -+ - 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, reverse=True): - allwidgets = self.resolve_widgets(self.get_child()) - -@@ -223,19 +223,19 @@ class LanguagesPopup(Gtk.Window): - for widget in allwidgets: - windows = self.resolve_windows(widget.get_window()) - windows.reverse() -- -+ - for window in windows: - if not (window.get_events() & event.type): - continue - -- if self.in_window(event, window): -+ 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() -@@ -250,19 +250,19 @@ class LanguagesPopup(Gtk.Window): - - def do_scroll_event(self, event): - return self.propagate_mouse_event(event, False) -- -+ - 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) - -@@ -281,12 +281,12 @@ class Manager(GObject.Object): - self._size = (0, 0) - self._languages = {} - self._tool_rows = {} -- -+ - self.build() - - def get_final_size(self): - return self._size -- -+ - def build(self): - callbacks = { - 'on_new_tool_button_clicked' : self.on_new_tool_button_clicked, -@@ -305,42 +305,42 @@ class Manager(GObject.Object): - 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()) - -@@ -351,7 +351,7 @@ class Manager(GObject.Object): - 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) -@@ -375,42 +375,42 @@ class Manager(GObject.Object): - lid = language.get_id() - else: - lid = language -- -+ - if not lid in self._languages: - piter = self.model.append(None, [language]) -- -+ - parent = Gtk.TreeRowReference.new(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.new(self.model, self.model.get_path(child))) - return child - - def add_tool(self, tool): - manager = GtkSource.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 -@@ -430,26 +430,26 @@ class Manager(GObject.Object): - # 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, GtkSource.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): -@@ -459,12 +459,12 @@ class Manager(GObject.Object): - 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, None) - - 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): -@@ -491,10 +491,10 @@ class Manager(GObject.Object): - - 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 -@@ -541,27 +541,27 @@ class Manager(GObject.Object): - - 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 = GtkSource.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, '')) -@@ -587,7 +587,7 @@ class Manager(GObject.Object): - 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))) -@@ -620,34 +620,34 @@ class Manager(GObject.Object): - self['tool-table'].set_sensitive(True) - else: - self.clear_fields() -- self['tool-table'].set_sensitive(False) -+ 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, GtkSource.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) - -@@ -656,10 +656,10 @@ class Manager(GObject.Object): - 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) -@@ -671,7 +671,7 @@ class Manager(GObject.Object): - 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() - -@@ -685,29 +685,29 @@ class Manager(GObject.Object): - - 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 -@@ -717,10 +717,10 @@ class Manager(GObject.Object): - 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] -@@ -729,9 +729,9 @@ class Manager(GObject.Object): - 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) - -@@ -742,7 +742,7 @@ class Manager(GObject.Object): - 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() -@@ -750,19 +750,19 @@ class Manager(GObject.Object): - 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): -@@ -775,9 +775,9 @@ class Manager(GObject.Object): - 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.DialogFlags.MODAL, -@@ -787,7 +787,7 @@ class Manager(GObject.Object): - - dialog.run() - dialog.destroy() -- -+ - self.add_accelerator(self.current_node) - return False - -@@ -816,7 +816,7 @@ class Manager(GObject.Object): - 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 Gdk.keyval_to_unicode(event.keyval): -@@ -849,7 +849,7 @@ class Manager(GObject.Object): - return - - self.on_tool_manager_dialog_focus_out(dialog, None) -- -+ - self.dialog.destroy() - self.dialog = None - self.tools = None -@@ -873,7 +873,7 @@ class Manager(GObject.Object): - label = _('Plain Text') - else: - label = tool.get_name() -- -+ - markup = saxutils.escape(label) - editable = False - else: -@@ -885,7 +885,7 @@ class Manager(GObject.Object): - markup = escaped - - editable = True -- -+ - cell.set_properties(markup=markup, editable=editable) - - def tool_in_language(self, tool, lang): -@@ -894,84 +894,84 @@ class Manager(GObject.Object): - - ref = self._languages[lang] - parent = ref.get_path() -- -+ - for row in self._tool_rows[tool]: - path = row.get_path() -- -+ - if path.get_indices()[0] == parent.get_indices()[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 = GtkSource.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.get_window().get_origin() - popup.move(origin[1], origin[2] - popup.get_allocation().height) -- -+ - popup.connect('destroy', self.update_languages) - - # ex:et:ts=4: -diff --git a/plugins/externaltools/tools/outputpanel.py b/plugins/externaltools/tools/outputpanel.py -index 9613d78..e063eb2 100755 ---- a/plugins/externaltools/tools/outputpanel.py -+++ b/plugins/externaltools/tools/outputpanel.py -@@ -129,11 +129,11 @@ class OutputPanel(UniqueById): - # 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) - -@@ -156,7 +156,7 @@ class OutputPanel(UniqueById): - panel.show() - panel.activate_item(self.panel) - -- def update_cursor_style(self, view, x, y): -+ 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: -diff --git a/plugins/pythonconsole/pythonconsole/__init__.py b/plugins/pythonconsole/pythonconsole/__init__.py -index 59ac413..07d13c7 100755 ---- a/plugins/pythonconsole/pythonconsole/__init__.py -+++ b/plugins/pythonconsole/pythonconsole/__init__.py -@@ -8,7 +8,7 @@ - # it under the terms of the GNU General Public License as published by - # the Free Software Foundation; either version 2, 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 -diff --git a/plugins/quickopen/quickopen/__init__.py b/plugins/quickopen/quickopen/__init__.py -old mode 100755 -new mode 100644 -index 54e9598..3ae72a4 ---- a/plugins/quickopen/quickopen/__init__.py -+++ b/plugins/quickopen/quickopen/__init__.py -@@ -21,30 +21,30 @@ from gi.repository import GObject, Peas - from windowhelper import WindowHelper - - class QuickOpenPlugin(GObject.Object, Peas.Activatable): -- __gtype_name__ = "QuickOpenPlugin" -+ __gtype_name__ = "QuickOpenPlugin" - -- object = GObject.Property(type=GObject.Object) -+ object = GObject.Property(type=GObject.Object) - -- def __init__(self): -- GObject.Object.__init__(self) -+ def __init__(self): -+ GObject.Object.__init__(self) - -- self._popup_size = (450, 300) -+ self._popup_size = (450, 300) - -- def do_activate(self): -- window = self.object -- self._helper = WindowHelper(window, self) -+ def do_activate(self): -+ window = self.object -+ self._helper = WindowHelper(window, self) - -- def do_deactivate(self): -- self._helper.deactivate() -- self._helper = None -+ def do_deactivate(self): -+ self._helper.deactivate() -+ self._helper = None - -- def do_update_state(self): -- self._helper.update_ui() -+ def do_update_state(self): -+ self._helper.update_ui() - -- def get_popup_size(self): -- return self._popup_size -+ def get_popup_size(self): -+ return self._popup_size - -- def set_popup_size(self, size): -- self._popup_size = size -+ def set_popup_size(self, size): -+ self._popup_size = size - --# ex:ts=8:et: -+# ex:ts=4:et: -diff --git a/plugins/quickopen/quickopen/popup.py b/plugins/quickopen/quickopen/popup.py -old mode 100755 -new mode 100644 -index b571b68..c6cc801 ---- a/plugins/quickopen/quickopen/popup.py -+++ b/plugins/quickopen/quickopen/popup.py -@@ -24,533 +24,532 @@ from gi.repository import GObject, Gio, GLib, Gdk, Gtk, Pango, Pluma - from virtualdirs import VirtualDirectory - - class Popup(Gtk.Dialog): -- __gtype_name__ = "QuickOpenPopup" -+ __gtype_name__ = "QuickOpenPopup" - -- def __init__(self, window, paths, handler): -- Gtk.Dialog.__init__(self, -- title=_('Quick Open'), -- parent=window, -- flags=Gtk.DialogFlags.DESTROY_WITH_PARENT | Gtk.DialogFlags.MODAL, -- buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)) -+ def __init__(self, window, paths, handler): -+ Gtk.Dialog.__init__(self, -+ title=_('Quick Open'), -+ parent=window, -+ flags=Gtk.DialogFlags.DESTROY_WITH_PARENT | Gtk.DialogFlags.MODAL, -+ buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)) - -- self._open_button = self.add_button(Gtk.STOCK_OPEN, Gtk.ResponseType.ACCEPT) -+ self._open_button = self.add_button(Gtk.STOCK_OPEN, Gtk.ResponseType.ACCEPT) - -- self._handler = handler -- self._build_ui() -+ self._handler = handler -+ self._build_ui() - -- self._size = (0, 0) -- self._dirs = [] -- self._cache = {} -- self._theme = None -- self._cursor = None -- self._shift_start = None -+ self._size = (0, 0) -+ self._dirs = [] -+ self._cache = {} -+ self._theme = None -+ self._cursor = None -+ self._shift_start = None - -- self._busy_cursor = Gdk.Cursor(Gdk.CursorType.WATCH) -+ self._busy_cursor = Gdk.Cursor(Gdk.CursorType.WATCH) - -- accel_group = Gtk.AccelGroup() -- accel_group.connect(Gdk.KEY_l, Gdk.ModifierType.CONTROL_MASK, 0, self.on_focus_entry) -+ accel_group = Gtk.AccelGroup() -+ accel_group.connect(Gdk.KEY_l, Gdk.ModifierType.CONTROL_MASK, 0, self.on_focus_entry) - -- self.add_accel_group(accel_group) -+ self.add_accel_group(accel_group) - -- unique = [] -+ unique = [] - -- for path in paths: -- if not path.get_uri() in unique: -- self._dirs.append(path) -- unique.append(path.get_uri()) -+ for path in paths: -+ if not path.get_uri() in unique: -+ self._dirs.append(path) -+ unique.append(path.get_uri()) - -- def get_final_size(self): -- return self._size -+ def get_final_size(self): -+ return self._size - -- def _build_ui(self): -- vbox = self.get_content_area() -- vbox.set_spacing(3) -+ def _build_ui(self): -+ vbox = self.get_content_area() -+ vbox.set_spacing(3) - -- self._entry = Gtk.Entry() -+ self._entry = Gtk.Entry() - -- self._entry.connect('changed', self.on_changed) -- self._entry.connect('key-press-event', self.on_key_press_event) -+ self._entry.connect('changed', self.on_changed) -+ self._entry.connect('key-press-event', self.on_key_press_event) - -- sw = Gtk.ScrolledWindow() -- sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) -- sw.set_shadow_type(Gtk.ShadowType.OUT) -+ sw = Gtk.ScrolledWindow() -+ sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) -+ sw.set_shadow_type(Gtk.ShadowType.OUT) - -- tv = Gtk.TreeView() -- tv.set_headers_visible(False) -+ tv = Gtk.TreeView() -+ tv.set_headers_visible(False) - -- self._store = Gtk.ListStore(Gio.Icon, str, GObject.Object, Gio.FileType) -- tv.set_model(self._store) -+ self._store = Gtk.ListStore(Gio.Icon, str, GObject.Object, Gio.FileType) -+ tv.set_model(self._store) - -- self._treeview = tv -- tv.connect('row-activated', self.on_row_activated) -+ self._treeview = tv -+ tv.connect('row-activated', self.on_row_activated) - -- renderer = Gtk.CellRendererPixbuf() -- column = Gtk.TreeViewColumn() -- column.pack_start(renderer, False) -- column.add_attribute(renderer, "gicon", 0) -+ renderer = Gtk.CellRendererPixbuf() -+ column = Gtk.TreeViewColumn() -+ column.pack_start(renderer, False) -+ column.add_attribute(renderer, "gicon", 0) - -- renderer = Gtk.CellRendererText() -- column.pack_start(renderer, True) -- column.add_attribute(renderer, "markup", 1) -+ renderer = Gtk.CellRendererText() -+ column.pack_start(renderer, True) -+ column.add_attribute(renderer, "markup", 1) - -- column.set_cell_data_func(renderer, self.on_cell_data_cb, None) -+ column.set_cell_data_func(renderer, self.on_cell_data_cb, None) - -- tv.append_column(column) -- sw.add(tv) -- -- selection = tv.get_selection() -- selection.connect('changed', self.on_selection_changed) -- selection.set_mode(Gtk.SelectionMode.MULTIPLE) -+ tv.append_column(column) -+ sw.add(tv) - -- vbox.pack_start(self._entry, False, False, 0) -- vbox.pack_start(sw, True, True, 0) -+ selection = tv.get_selection() -+ selection.connect('changed', self.on_selection_changed) -+ selection.set_mode(Gtk.SelectionMode.MULTIPLE) - -- lbl = Gtk.Label() -- lbl.set_alignment(0, 0.5) -- lbl.set_ellipsize(Pango.EllipsizeMode.MIDDLE) -- self._info_label = lbl -+ vbox.pack_start(self._entry, False, False, 0) -+ vbox.pack_start(sw, True, True, 0) - -- vbox.pack_start(lbl, False, False, 0) -+ lbl = Gtk.Label() -+ lbl.set_alignment(0, 0.5) -+ lbl.set_ellipsize(Pango.EllipsizeMode.MIDDLE) -+ self._info_label = lbl - -- # Initial selection -- self.on_selection_changed(tv.get_selection()) -- vbox.show_all() -+ vbox.pack_start(lbl, False, False, 0) - -- def on_cell_data_cb(self, column, cell, model, piter, user_data): -- path = model.get_path(piter) -- -- if self._cursor and path == self._cursor.get_path(): -- style = self._treeview.get_style() -- bg = style.bg[Gtk.StateType.PRELIGHT] -- -- cell.set_property('cell-background-gdk', bg) -- cell.set_property('style', Pango.Style.ITALIC) -- else: -- cell.set_property('cell-background-set', False) -- cell.set_property('style-set', False) -+ # Initial selection -+ self.on_selection_changed(tv.get_selection()) -+ vbox.show_all() - -- def _icon_from_stock(self, stock): -- theme = Gtk.icon_theme_get_default() -- size = Gtk.icon_size_lookup(Gtk.IconSize.MENU) -- pixbuf = theme.load_icon(stock, size[0], Gtk.IconLookupFlags.USE_BUILTIN) -+ def on_cell_data_cb(self, column, cell, model, piter, user_data): -+ path = model.get_path(piter) - -- return pixbuf -+ if self._cursor and path == self._cursor.get_path(): -+ style = self._treeview.get_style() -+ bg = style.bg[Gtk.StateType.PRELIGHT] - -- def _list_dir(self, gfile): -- entries = [] -+ cell.set_property('cell-background-gdk', bg) -+ cell.set_property('style', Pango.Style.ITALIC) -+ else: -+ cell.set_property('cell-background-set', False) -+ cell.set_property('style-set', False) - -- try: -- ret = gfile.enumerate_children("standard::*", Gio.FileQueryInfoFlags.NONE, None) -- except GLib.GError: -- pass -+ def _icon_from_stock(self, stock): -+ theme = Gtk.icon_theme_get_default() -+ size = Gtk.icon_size_lookup(Gtk.IconSize.MENU) -+ pixbuf = theme.load_icon(stock, size[0], Gtk.IconLookupFlags.USE_BUILTIN) - -- if isinstance(ret, Gio.FileEnumerator): -- while True: -- entry = ret.next_file(None) -+ return pixbuf - -- if not entry: -- break -+ def _list_dir(self, gfile): -+ entries = [] - -- entries.append((gfile.get_child(entry.get_name()), entry)) -- else: -- entries = ret -+ try: -+ ret = gfile.enumerate_children("standard::*", Gio.FileQueryInfoFlags.NONE, None) -+ except GLib.GError: -+ pass - -- children = [] -+ if isinstance(ret, Gio.FileEnumerator): -+ while True: -+ entry = ret.next_file(None) - -- for entry in entries: -- children.append((entry[0], entry[1].get_name(), entry[1].get_file_type(), entry[1].get_icon())) -+ if not entry: -+ break - -- return children -+ entries.append((gfile.get_child(entry.get_name()), entry)) -+ else: -+ entries = ret - -- def _compare_entries(self, a, b, lpart): -- if lpart in a: -- if lpart in b: -- return cmp(a.index(lpart), b.index(lpart)) -- else: -- return -1 -- elif lpart in b: -- return 1 -- else: -- return 0 -+ children = [] - -- def _match_glob(self, s, glob): -- if glob: -- glob += '*' -+ for entry in entries: -+ children.append((entry[0], entry[1].get_name(), entry[1].get_file_type(), entry[1].get_icon())) - -- return fnmatch.fnmatch(s, glob) -+ return children - -- def do_search_dir(self, parts, d): -- if not parts or not d: -- return [] -+ def _compare_entries(self, a, b, lpart): -+ if lpart in a: -+ if lpart in b: -+ return cmp(a.index(lpart), b.index(lpart)) -+ else: -+ return -1 -+ elif lpart in b: -+ return 1 -+ else: -+ return 0 - -- if not d in self._cache: -- entries = self._list_dir(d) -- entries.sort(lambda x, y: cmp(x[1].lower(), y[1].lower())) -+ def _match_glob(self, s, glob): -+ if glob: -+ glob += '*' - -- self._cache[d] = entries -- else: -- entries = self._cache[d] -+ return fnmatch.fnmatch(s, glob) - -- found = [] -- newdirs = [] -+ def do_search_dir(self, parts, d): -+ if not parts or not d: -+ return [] - -- lpart = parts[0].lower() -+ if not d in self._cache: -+ entries = self._list_dir(d) -+ entries.sort(lambda x, y: cmp(x[1].lower(), y[1].lower())) - -- for entry in entries: -- if not entry: -- continue -+ self._cache[d] = entries -+ else: -+ entries = self._cache[d] - -- lentry = entry[1].lower() -+ found = [] -+ newdirs = [] - -- if not lpart or lpart in lentry or self._match_glob(lentry, lpart): -- if entry[2] == Gio.FileType.DIRECTORY: -- if len(parts) > 1: -- newdirs.append(entry[0]) -- else: -- found.append(entry) -- elif entry[2] == Gio.FileType.REGULAR and \ -- (not lpart or len(parts) == 1): -- found.append(entry) -+ lpart = parts[0].lower() - -- found.sort(lambda a, b: self._compare_entries(a[1].lower(), b[1].lower(), lpart)) -+ for entry in entries: -+ if not entry: -+ continue - -- if lpart == '..': -- newdirs.append(d.get_parent()) -+ lentry = entry[1].lower() - -- for dd in newdirs: -- found.extend(self.do_search_dir(parts[1:], dd)) -+ if not lpart or lpart in lentry or self._match_glob(lentry, lpart): -+ if entry[2] == Gio.FileType.DIRECTORY: -+ if len(parts) > 1: -+ newdirs.append(entry[0]) -+ else: -+ found.append(entry) -+ elif entry[2] == Gio.FileType.REGULAR and \ -+ (not lpart or len(parts) == 1): -+ found.append(entry) - -- return found -+ found.sort(lambda a, b: self._compare_entries(a[1].lower(), b[1].lower(), lpart)) - -- def _replace_insensitive(self, s, find, rep): -- out = '' -- l = s.lower() -- find = find.lower() -- last = 0 -+ if lpart == '..': -+ newdirs.append(d.get_parent()) - -- if len(find) == 0: -- return xml.sax.saxutils.escape(s) -+ for dd in newdirs: -+ found.extend(self.do_search_dir(parts[1:], dd)) - -- while True: -- m = l.find(find, last) -+ return found - -- if m == -1: -- break -- else: -- out += xml.sax.saxutils.escape(s[last:m]) + rep % (xml.sax.saxutils.escape(s[m:m + len(find)]),) -- last = m + len(find) -+ def _replace_insensitive(self, s, find, rep): -+ out = '' -+ l = s.lower() -+ find = find.lower() -+ last = 0 - -- return out + xml.sax.saxutils.escape(s[last:]) -+ if len(find) == 0: -+ return xml.sax.saxutils.escape(s) - -+ while True: -+ m = l.find(find, last) - -- def make_markup(self, parts, path): -- out = [] -+ if m == -1: -+ break -+ else: -+ out += xml.sax.saxutils.escape(s[last:m]) + rep % (xml.sax.saxutils.escape(s[m:m + len(find)]),) -+ last = m + len(find) - -- for i in range(0, len(parts)): -- out.append(self._replace_insensitive(path[i], parts[i], "%s")) -+ return out + xml.sax.saxutils.escape(s[last:]) - -- return os.sep.join(out) -+ def make_markup(self, parts, path): -+ out = [] - -- def _get_icon(self, f): -- query = f.query_info(Gio.FILE_ATTRIBUTE_STANDARD_ICON, Gio.FileQueryInfoFlags.NONE, None) -+ for i in range(0, len(parts)): -+ out.append(self._replace_insensitive(path[i], parts[i], "%s")) - -- if not query: -- return None -- else: -- return query.get_icon() -+ return os.sep.join(out) - -- def _make_parts(self, parent, child, pp): -- parts = [] -+ def _get_icon(self, f): -+ query = f.query_info(Gio.FILE_ATTRIBUTE_STANDARD_ICON, Gio.FileQueryInfoFlags.NONE, None) - -- # We went from parent, to child, using pp -- idx = len(pp) - 1 -+ if not query: -+ return None -+ else: -+ return query.get_icon() - -- while idx >= 0: -- if pp[idx] == '..': -- parts.insert(0, '..') -- else: -- parts.insert(0, child.get_basename()) -- child = child.get_parent() -+ def _make_parts(self, parent, child, pp): -+ parts = [] - -- idx -= 1 -+ # We went from parent, to child, using pp -+ idx = len(pp) - 1 - -- return parts -+ while idx >= 0: -+ if pp[idx] == '..': -+ parts.insert(0, '..') -+ else: -+ parts.insert(0, child.get_basename()) -+ child = child.get_parent() - -- def normalize_relative(self, parts): -- if not parts: -- return [] -+ idx -= 1 - -- out = self.normalize_relative(parts[:-1]) -+ return parts - -- if parts[-1] == '..': -- if not out or (out[-1] == '..') or len(out) == 1: -- out.append('..') -- else: -- del out[-1] -- else: -- out.append(parts[-1]) -+ def normalize_relative(self, parts): -+ if not parts: -+ return [] - -- return out -+ out = self.normalize_relative(parts[:-1]) - -- def _append_to_store(self, item): -- if not item in self._stored_items: -- self._store.append(item) -- self._stored_items[item] = True -+ if parts[-1] == '..': -+ if not out or (out[-1] == '..') or len(out) == 1: -+ out.append('..') -+ else: -+ del out[-1] -+ else: -+ out.append(parts[-1]) - -- def _clear_store(self): -- self._store.clear() -- self._stored_items = {} -+ return out - -- def _show_virtuals(self): -- for d in self._dirs: -- if isinstance(d, VirtualDirectory): -- for entry in d.enumerate_children("standard::*", 0, None): -- self._append_to_store((entry[1].get_icon(), xml.sax.saxutils.escape(entry[1].get_name()), entry[0], entry[1].get_file_type())) -+ def _append_to_store(self, item): -+ if not item in self._stored_items: -+ self._store.append(item) -+ self._stored_items[item] = True - -- def _set_busy(self, busy): -- if busy: -- self.get_window().set_cursor(self._busy_cursor) -- else: -- self.get_window().set_cursor(None) -+ def _clear_store(self): -+ self._store.clear() -+ self._stored_items = {} - -- Gdk.flush() -+ def _show_virtuals(self): -+ for d in self._dirs: -+ if isinstance(d, VirtualDirectory): -+ for entry in d.enumerate_children("standard::*", 0, None): -+ self._append_to_store((entry[1].get_icon(), xml.sax.saxutils.escape(entry[1].get_name()), entry[0], entry[1].get_file_type())) - -- def _remove_cursor(self): -- if self._cursor: -- path = self._cursor.get_path() -- self._cursor = None -+ def _set_busy(self, busy): -+ if busy: -+ self.get_window().set_cursor(self._busy_cursor) -+ else: -+ self.get_window().set_cursor(None) - -- self._store.row_changed(path, self._store.get_iter(path)) -+ Gdk.flush() - -- def do_search(self): -- self._set_busy(True) -- self._remove_cursor() -+ def _remove_cursor(self): -+ if self._cursor: -+ path = self._cursor.get_path() -+ self._cursor = None - -- text = self._entry.get_text().strip() -- self._clear_store() -+ self._store.row_changed(path, self._store.get_iter(path)) - -- if text == '': -- self._show_virtuals() -- else: -- parts = self.normalize_relative(text.split(os.sep)) -- files = [] -+ def do_search(self): -+ self._set_busy(True) -+ self._remove_cursor() - -- for d in self._dirs: -- for entry in self.do_search_dir(parts, d): -- pathparts = self._make_parts(d, entry[0], parts) -- self._append_to_store((entry[3], self.make_markup(parts, pathparts), entry[0], entry[2])) -+ text = self._entry.get_text().strip() -+ self._clear_store() - -- piter = self._store.get_iter_first() -+ if text == '': -+ self._show_virtuals() -+ else: -+ parts = self.normalize_relative(text.split(os.sep)) -+ files = [] - -- if piter: -- self._treeview.get_selection().select_path(self._store.get_path(piter)) -+ for d in self._dirs: -+ for entry in self.do_search_dir(parts, d): -+ pathparts = self._make_parts(d, entry[0], parts) -+ self._append_to_store((entry[3], self.make_markup(parts, pathparts), entry[0], entry[2])) - -- self.get_window().set_cursor(None) -- self._set_busy(False) -+ piter = self._store.get_iter_first() - -- def do_show(self): -- Gtk.Window.do_show(self) -+ if piter: -+ self._treeview.get_selection().select_path(self._store.get_path(piter)) - -- self._entry.grab_focus() -- self._entry.set_text("") -- -- self.do_search() -- -- def on_changed(self, editable): -- self.do_search() -- self.on_selection_changed(self._treeview.get_selection()) -- -- def _shift_extend(self, towhere): -- selection = self._treeview.get_selection() -- -- if not self._shift_start: -- model, rows = selection.get_selected_rows() -- start = rows[0] -- -- self._shift_start = Gtk.TreeRowReference(self._store, start) -- else: -- start = self._shift_start.get_path() -- -- selection.unselect_all() -- selection.select_range(start, towhere) -- -- def _select_index(self, idx, hasctrl, hasshift): -- path = (idx,) -- -- if not (hasctrl or hasshift): -- self._treeview.get_selection().unselect_all() -- -- if hasshift: -- self._shift_extend(path) -- else: -- self._shift_start = None -- -- if not hasctrl: -- self._treeview.get_selection().select_path(path) -- -- self._treeview.scroll_to_cell(path, None, True, 0.5, 0) -- self._remove_cursor() -- -- if hasctrl or hasshift: -- self._cursor = Gtk.TreeRowReference(self._store, path) -- -- piter = self._store.get_iter(path) -- self._store.row_changed(path, piter) -- -- def _move_selection(self, howmany, hasctrl, hasshift): -- num = self._store.iter_n_children(None) -- -- if num == 0: -- return True -- -- # Test for cursor -- path = None -- -- if self._cursor: -- path = self._cursor.get_path() -- else: -- model, rows = self._treeview.get_selection().get_selected_rows() -- -- if len(rows) == 1: -- path = rows[0] -- -- if not path: -- if howmany > 0: -- self._select_index(0, hasctrl, hasshift) -- else: -- self._select_index(num - 1, hasctrl, hasshift) -- else: -- idx = path[0] -- -- if idx + howmany < 0: -- self._select_index(0, hasctrl, hasshift) -- elif idx + howmany >= num: -- self._select_index(num - 1, hasctrl, hasshift) -- else: -- self._select_index(idx + howmany, hasctrl, hasshift) -+ self.get_window().set_cursor(None) -+ self._set_busy(False) - -- return True -+ def do_show(self): -+ Gtk.Window.do_show(self) -+ -+ self._entry.grab_focus() -+ self._entry.set_text("") -+ -+ self.do_search() -+ -+ def on_changed(self, editable): -+ self.do_search() -+ self.on_selection_changed(self._treeview.get_selection()) -+ -+ def _shift_extend(self, towhere): -+ selection = self._treeview.get_selection() -+ -+ if not self._shift_start: -+ model, rows = selection.get_selected_rows() -+ start = rows[0] -+ -+ self._shift_start = Gtk.TreeRowReference(self._store, start) -+ else: -+ start = self._shift_start.get_path() -+ -+ selection.unselect_all() -+ selection.select_range(start, towhere) -+ -+ def _select_index(self, idx, hasctrl, hasshift): -+ path = (idx,) -+ -+ if not (hasctrl or hasshift): -+ self._treeview.get_selection().unselect_all() -+ -+ if hasshift: -+ self._shift_extend(path) -+ else: -+ self._shift_start = None -+ -+ if not hasctrl: -+ self._treeview.get_selection().select_path(path) -+ -+ self._treeview.scroll_to_cell(path, None, True, 0.5, 0) -+ self._remove_cursor() -+ -+ if hasctrl or hasshift: -+ self._cursor = Gtk.TreeRowReference(self._store, path) -+ -+ piter = self._store.get_iter(path) -+ self._store.row_changed(path, piter) -+ -+ def _move_selection(self, howmany, hasctrl, hasshift): -+ num = self._store.iter_n_children(None) - -- def _direct_file(self): -- uri = self._entry.get_text() -- gfile = None -- -- if Pluma.utils_is_valid_uri(uri): -- gfile = Gio.file_new_for_uri(uri) -- elif os.path.isabs(uri): -- f = Gio.file_new_for_uri(uri) -- -- if f.query_exists(): -- gfile = f -- -- return gfile -- -- def _activate(self): -- model, rows = self._treeview.get_selection().get_selected_rows() -- ret = True -- -- for row in rows: -- s = model.get_iter(row) -- info = model.get(s, 2, 3) -- -- if info[1] != Gio.FileType.DIRECTORY: -- ret = ret and self._handler(info[0]) -- else: -- text = self._entry.get_text() -- -- for i in range(len(text) - 1, -1, -1): -- if text[i] == os.sep: -- break -- -- self._entry.set_text(os.path.join(text[:i], os.path.basename(info[0].get_uri())) + os.sep) -- self._entry.set_position(-1) -- self._entry.grab_focus() -- return True -- -- if rows and ret: -- self.destroy() -- -- if not rows: -- gfile = self._direct_file() -- -- if gfile and self._handler(gfile): -- self.destroy() -- else: -- ret = False -- else: -- ret = False -- -- return ret -- -- def toggle_cursor(self): -- if not self._cursor: -- return -- -- path = self._cursor.get_path() -- selection = self._treeview.get_selection() -- -- if selection.path_is_selected(path): -- selection.unselect_path(path) -- else: -- selection.select_path(path) -- -- def on_key_press_event(self, widget, event): -- move_mapping = { -- Gdk.KEY_Down: 1, -- Gdk.KEY_Up: -1, -- Gdk.KEY_Page_Down: 5, -- Gdk.KEY_Page_Up: -5 -- } -- -- if event.keyval == Gdk.KEY_Escape: -- self.destroy() -- return True -- elif event.keyval in move_mapping: -- return self._move_selection(move_mapping[event.keyval], event.state & Gdk.ModifierType.CONTROL_MASK, event.state & Gdk.ModifierType.SHIFT_MASK) -- elif event.keyval in [Gdk.KEY_Return, Gdk.KEY_KP_Enter, Gdk.KEY_Tab, Gdk.KEY_ISO_Left_Tab]: -- return self._activate() -- elif event.keyval == Gdk.KEY_space and event.state & Gdk.ModifierType.CONTROL_MASK: -- self.toggle_cursor() -- -- return False -- -- def on_row_activated(self, view, path, column): -- self._activate() -- -- def do_response(self, response): -- if response != Gtk.ResponseType.ACCEPT or not self._activate(): -- self.destroy() -- -- def do_configure_event(self, event): -- if self.get_realized(): -- alloc = self.get_allocation() -- self._size = (alloc.width, alloc.height) -- -- return Gtk.Dialog.do_configure_event(self, event) -- -- def on_selection_changed(self, selection): -- model, rows = selection.get_selected_rows() -- -- gfile = None -- fname = None -- -- if not rows: -- gfile = self._direct_file() -- elif len(rows) == 1: -- gfile = model.get(model.get_iter(rows[0]), 2)[0] -- else: -- fname = '' -- -- if gfile: -- if gfile.is_native(): -- fname = xml.sax.saxutils.escape(gfile.get_path()) -- else: -- fname = xml.sax.saxutils.escape(gfile.get_uri()) -- -- self._open_button.set_sensitive(fname != None) -- self._info_label.set_markup(fname or '') -- -- def on_focus_entry(self, group, accel, keyval, modifier): -+ if num == 0: -+ return True -+ -+ # Test for cursor -+ path = None -+ -+ if self._cursor: -+ path = self._cursor.get_path() -+ else: -+ model, rows = self._treeview.get_selection().get_selected_rows() -+ -+ if len(rows) == 1: -+ path = rows[0] -+ -+ if not path: -+ if howmany > 0: -+ self._select_index(0, hasctrl, hasshift) -+ else: -+ self._select_index(num - 1, hasctrl, hasshift) -+ else: -+ idx = path[0] -+ -+ if idx + howmany < 0: -+ self._select_index(0, hasctrl, hasshift) -+ elif idx + howmany >= num: -+ self._select_index(num - 1, hasctrl, hasshift) -+ else: -+ self._select_index(idx + howmany, hasctrl, hasshift) -+ -+ return True -+ -+ def _direct_file(self): -+ uri = self._entry.get_text() -+ gfile = None -+ -+ if Pluma.utils_is_valid_uri(uri): -+ gfile = Gio.file_new_for_uri(uri) -+ elif os.path.isabs(uri): -+ f = Gio.file_new_for_uri(uri) -+ -+ if f.query_exists(): -+ gfile = f -+ -+ return gfile -+ -+ def _activate(self): -+ model, rows = self._treeview.get_selection().get_selected_rows() -+ ret = True -+ -+ for row in rows: -+ s = model.get_iter(row) -+ info = model.get(s, 2, 3) -+ -+ if info[1] != Gio.FileType.DIRECTORY: -+ ret = ret and self._handler(info[0]) -+ else: -+ text = self._entry.get_text() -+ -+ for i in range(len(text) - 1, -1, -1): -+ if text[i] == os.sep: -+ break -+ -+ self._entry.set_text(os.path.join(text[:i], os.path.basename(info[0].get_uri())) + os.sep) -+ self._entry.set_position(-1) - self._entry.grab_focus() -+ return True - --# ex:ts=8:et: -+ if rows and ret: -+ self.destroy() -+ -+ if not rows: -+ gfile = self._direct_file() -+ -+ if gfile and self._handler(gfile): -+ self.destroy() -+ else: -+ ret = False -+ else: -+ ret = False -+ -+ return ret -+ -+ def toggle_cursor(self): -+ if not self._cursor: -+ return -+ -+ path = self._cursor.get_path() -+ selection = self._treeview.get_selection() -+ -+ if selection.path_is_selected(path): -+ selection.unselect_path(path) -+ else: -+ selection.select_path(path) -+ -+ def on_key_press_event(self, widget, event): -+ move_mapping = { -+ Gdk.KEY_Down: 1, -+ Gdk.KEY_Up: -1, -+ Gdk.KEY_Page_Down: 5, -+ Gdk.KEY_Page_Up: -5 -+ } -+ -+ if event.keyval == Gdk.KEY_Escape: -+ self.destroy() -+ return True -+ elif event.keyval in move_mapping: -+ return self._move_selection(move_mapping[event.keyval], event.state & Gdk.ModifierType.CONTROL_MASK, event.state & Gdk.ModifierType.SHIFT_MASK) -+ elif event.keyval in [Gdk.KEY_Return, Gdk.KEY_KP_Enter, Gdk.KEY_Tab, Gdk.KEY_ISO_Left_Tab]: -+ return self._activate() -+ elif event.keyval == Gdk.KEY_space and event.state & Gdk.ModifierType.CONTROL_MASK: -+ self.toggle_cursor() -+ -+ return False -+ -+ def on_row_activated(self, view, path, column): -+ self._activate() -+ -+ def do_response(self, response): -+ if response != Gtk.ResponseType.ACCEPT or not self._activate(): -+ self.destroy() -+ -+ def do_configure_event(self, event): -+ if self.get_realized(): -+ alloc = self.get_allocation() -+ self._size = (alloc.width, alloc.height) -+ -+ return Gtk.Dialog.do_configure_event(self, event) -+ -+ def on_selection_changed(self, selection): -+ model, rows = selection.get_selected_rows() -+ -+ gfile = None -+ fname = None -+ -+ if not rows: -+ gfile = self._direct_file() -+ elif len(rows) == 1: -+ gfile = model.get(model.get_iter(rows[0]), 2)[0] -+ else: -+ fname = '' -+ -+ if gfile: -+ if gfile.is_native(): -+ fname = xml.sax.saxutils.escape(gfile.get_path()) -+ else: -+ fname = xml.sax.saxutils.escape(gfile.get_uri()) -+ -+ self._open_button.set_sensitive(fname != None) -+ self._info_label.set_markup(fname or '') -+ -+ def on_focus_entry(self, group, accel, keyval, modifier): -+ self._entry.grab_focus() -+ -+# ex:ts=4:et: -diff --git a/plugins/quickopen/quickopen/virtualdirs.py b/plugins/quickopen/quickopen/virtualdirs.py -old mode 100755 -new mode 100644 -index a2d6985..53d716a ---- a/plugins/quickopen/quickopen/virtualdirs.py -+++ b/plugins/quickopen/quickopen/virtualdirs.py -@@ -20,64 +20,64 @@ - from gi.repository import Gio, Gtk - - class VirtualDirectory(object): -- def __init__(self, name): -- self._name = name -- self._children = [] -+ def __init__(self, name): -+ self._name = name -+ self._children = [] - -- def get_uri(self): -- return 'virtual://' + self._name -+ def get_uri(self): -+ return 'virtual://' + self._name - -- def get_parent(self): -- return None -+ def get_parent(self): -+ return None - -- def enumerate_children(self, attr, flags, callback): -- return self._children -+ def enumerate_children(self, attr, flags, callback): -+ return self._children - -- def append(self, child): -- if not child.is_native(): -- return -+ def append(self, child): -+ if not child.is_native(): -+ return - -- try: -- info = child.query_info("standard::*", Gio.FileQueryInfoFlags.NONE, None) -+ try: -+ info = child.query_info("standard::*", Gio.FileQueryInfoFlags.NONE, None) - -- if info: -- self._children.append((child, info)) -- except: -- pass -+ if info: -+ self._children.append((child, info)) -+ except: -+ pass - - class RecentDocumentsDirectory(VirtualDirectory): -- def __init__(self, maxitems=10): -- VirtualDirectory.__init__(self, 'recent') -+ def __init__(self, maxitems=10): -+ VirtualDirectory.__init__(self, 'recent') - -- self._maxitems = maxitems -- self.fill() -+ self._maxitems = maxitems -+ self.fill() - -- def fill(self): -- manager = Gtk.RecentManager.get_default() -+ def fill(self): -+ manager = Gtk.RecentManager.get_default() - -- items = manager.get_items() -- items.sort(lambda a, b: cmp(b.get_visited(), a.get_visited())) -+ items = manager.get_items() -+ items.sort(lambda a, b: cmp(b.get_visited(), a.get_visited())) - -- added = 0 -+ added = 0 - -- for item in items: -- if item.has_group('pluma'): -- self.append(Gio.file_new_for_uri(item.get_uri())) -- added += 1 -+ for item in items: -+ if item.has_group('pluma'): -+ self.append(Gio.file_new_for_uri(item.get_uri())) -+ added += 1 - -- if added >= self._maxitems: -- break -+ if added >= self._maxitems: -+ break - - class CurrentDocumentsDirectory(VirtualDirectory): -- def __init__(self, window): -- VirtualDirectory.__init__(self, 'documents') -+ def __init__(self, window): -+ VirtualDirectory.__init__(self, 'documents') - -- self.fill(window) -+ self.fill(window) - -- def fill(self, window): -- for doc in window.get_documents(): -- location = doc.get_location() -- if location: -- self.append(location) -+ def fill(self, window): -+ for doc in window.get_documents(): -+ location = doc.get_location() -+ if location: -+ self.append(location) - --# ex:ts=8:et: -+# ex:ts=4:et: -diff --git a/plugins/quickopen/quickopen/windowhelper.py b/plugins/quickopen/quickopen/windowhelper.py -old mode 100755 -new mode 100644 -index c23629c..19e44cb ---- a/plugins/quickopen/quickopen/windowhelper.py -+++ b/plugins/quickopen/quickopen/windowhelper.py -@@ -27,7 +27,7 @@ ui_str = """ - - - -- -+ - - - -@@ -35,157 +35,157 @@ ui_str = """ - """ - - class WindowHelper: -- def __init__(self, window, plugin): -- self._window = window -- self._plugin = plugin -+ def __init__(self, window, plugin): -+ self._window = window -+ self._plugin = plugin - -- self._popup = None -- self._install_menu() -+ self._popup = None -+ self._install_menu() - -- def deactivate(self): -- self._uninstall_menu() -- self._window = None -- self._plugin = None -+ def deactivate(self): -+ self._uninstall_menu() -+ self._window = None -+ self._plugin = None - -- def update_ui(self): -- pass -+ def update_ui(self): -+ pass - -- def _uninstall_menu(self): -- manager = self._window.get_ui_manager() -+ def _uninstall_menu(self): -+ manager = self._window.get_ui_manager() - -- manager.remove_ui(self._ui_id) -- manager.remove_action_group(self._action_group) -+ manager.remove_ui(self._ui_id) -+ manager.remove_action_group(self._action_group) - -- manager.ensure_update() -+ manager.ensure_update() - -- def _install_menu(self): -- manager = self._window.get_ui_manager() -- self._action_group = Gtk.ActionGroup("PlumaQuickOpenPluginActions") -- self._action_group.add_actions([ -- ("QuickOpen", Gtk.STOCK_OPEN, _("Quick open"), -- 'O', _("Quickly open documents"), -- self.on_quick_open_activate) -- ]) -+ def _install_menu(self): -+ manager = self._window.get_ui_manager() -+ self._action_group = Gtk.ActionGroup("PlumaQuickOpenPluginActions") -+ self._action_group.add_actions([ -+ ("QuickOpen", Gtk.STOCK_OPEN, _("Quick open"), -+ 'O', _("Quickly open documents"), -+ self.on_quick_open_activate) -+ ]) - -- manager.insert_action_group(self._action_group, -1) -- self._ui_id = manager.add_ui_from_string(ui_str) -+ manager.insert_action_group(self._action_group, -1) -+ self._ui_id = manager.add_ui_from_string(ui_str) - -- def _create_popup(self): -- paths = [] -+ def _create_popup(self): -+ paths = [] - -- # Open documents -- paths.append(CurrentDocumentsDirectory(self._window)) -+ # Open documents -+ paths.append(CurrentDocumentsDirectory(self._window)) - -- doc = self._window.get_active_document() -+ doc = self._window.get_active_document() - -- # Current document directory -- if doc and doc.is_local(): -- gfile = doc.get_location() -- paths.append(gfile.get_parent()) -+ # Current document directory -+ if doc and doc.is_local(): -+ gfile = doc.get_location() -+ paths.append(gfile.get_parent()) - -- # File browser root directory -- bus = self._window.get_message_bus() -+ # File browser root directory -+ bus = self._window.get_message_bus() - -- try: -- msg = bus.send_sync('/plugins/filebrowser', 'get_root') -+ try: -+ msg = bus.send_sync('/plugins/filebrowser', 'get_root') - -- if msg: -- uri = msg.get_value('uri') -+ if msg: -+ uri = msg.get_value('uri') - -- if uri: -- gfile = Gio.file_new_for_uri(uri) -+ if uri: -+ gfile = Gio.file_new_for_uri(uri) - -- if gfile.is_native(): -- paths.append(gfile) -+ if gfile.is_native(): -+ paths.append(gfile) - -- except StandardError: -- pass -+ except StandardError: -+ pass - -- # Recent documents -- paths.append(RecentDocumentsDirectory()) -+ # Recent documents -+ paths.append(RecentDocumentsDirectory()) - -- # Local bookmarks -- for path in self._local_bookmarks(): -- paths.append(path) -+ # Local bookmarks -+ for path in self._local_bookmarks(): -+ paths.append(path) - -- # Desktop directory -- desktopdir = self._desktop_dir() -+ # Desktop directory -+ desktopdir = self._desktop_dir() - -- if desktopdir: -- paths.append(Gio.file_new_for_path(desktopdir)) -+ if desktopdir: -+ paths.append(Gio.file_new_for_path(desktopdir)) - -- # Home directory -- paths.append(Gio.file_new_for_path(os.path.expanduser('~'))) -+ # Home directory -+ paths.append(Gio.file_new_for_path(os.path.expanduser('~'))) - -- self._popup = Popup(self._window, paths, self.on_activated) -+ self._popup = Popup(self._window, paths, self.on_activated) - -- self._popup.set_default_size(*self._plugin.get_popup_size()) -- self._popup.set_transient_for(self._window) -- self._popup.set_position(Gtk.WindowPosition.CENTER_ON_PARENT) -+ self._popup.set_default_size(*self._plugin.get_popup_size()) -+ self._popup.set_transient_for(self._window) -+ self._popup.set_position(Gtk.WindowPosition.CENTER_ON_PARENT) - -- self._window.get_group().add_window(self._popup) -+ self._window.get_group().add_window(self._popup) - -- self._popup.connect('destroy', self.on_popup_destroy) -+ self._popup.connect('destroy', self.on_popup_destroy) - -- def _local_bookmarks(self): -- filename = os.path.expanduser('~/.gtk-bookmarks') -+ def _local_bookmarks(self): -+ filename = os.path.expanduser('~/.gtk-bookmarks') - -- if not os.path.isfile(filename): -- return [] -+ if not os.path.isfile(filename): -+ return [] - -- paths = [] -+ paths = [] - -- for line in file(filename, 'r').xreadlines(): -- uri = line.strip().split(" ")[0] -- f = Gio.file_new_for_uri(uri) -+ for line in file(filename, 'r').xreadlines(): -+ uri = line.strip().split(" ")[0] -+ f = Gio.file_new_for_uri(uri) - -- if f.is_native(): -- try: -- info = f.query_info(Gio.FILE_ATTRIBUTE_STANDARD_TYPE, Gio.FileQueryInfoFlags.NONE, None) -+ if f.is_native(): -+ try: -+ info = f.query_info(Gio.FILE_ATTRIBUTE_STANDARD_TYPE, Gio.FileQueryInfoFlags.NONE, None) - -- if info and info.get_file_type() == Gio.FileType.DIRECTORY: -- paths.append(f) -- except GLib.GError: -- pass -+ if info and info.get_file_type() == Gio.FileType.DIRECTORY: -+ paths.append(f) -+ except GLib.GError: -+ pass - -- return paths -+ return paths - -- def _desktop_dir(self): -- config = os.getenv('XDG_CONFIG_HOME') -+ def _desktop_dir(self): -+ config = os.getenv('XDG_CONFIG_HOME') - -- if not config: -- config = os.path.expanduser('~/.config') -+ if not config: -+ config = os.path.expanduser('~/.config') - -- config = os.path.join(config, 'user-dirs.dirs') -- desktopdir = None -+ config = os.path.join(config, 'user-dirs.dirs') -+ desktopdir = None - -- if os.path.isfile(config): -- for line in file(config, 'r').xreadlines(): -- line = line.strip() -+ if os.path.isfile(config): -+ for line in file(config, 'r').xreadlines(): -+ line = line.strip() - -- if line.startswith('XDG_DESKTOP_DIR'): -- parts = line.split('=', 1) -- desktopdir = os.path.expandvars(parts[1].strip('"').strip("'")) -- break -+ if line.startswith('XDG_DESKTOP_DIR'): -+ parts = line.split('=', 1) -+ desktopdir = os.path.expandvars(parts[1].strip('"').strip("'")) -+ break - -- if not desktopdir: -- desktopdir = os.path.expanduser('~/Desktop') -+ if not desktopdir: -+ desktopdir = os.path.expanduser('~/Desktop') - -- return desktopdir -+ return desktopdir - -- # Callbacks -- def on_quick_open_activate(self, action): -- if not self._popup: -- self._create_popup() -+ # Callbacks -+ def on_quick_open_activate(self, action): -+ if not self._popup: -+ self._create_popup() - -- self._popup.show() -+ self._popup.show() - -- def on_popup_destroy(self, popup): -- self._plugin.set_popup_size(popup.get_final_size()) -- self._popup = None -+ def on_popup_destroy(self, popup): -+ self._plugin.set_popup_size(popup.get_final_size()) -+ self._popup = None - -- def on_activated(self, gfile): -- Pluma.commands_load_uri(self._window, gfile.get_uri(), None, -1) -- return True -+ def on_activated(self, gfile): -+ Pluma.commands_load_uri(self._window, gfile.get_uri(), None, -1) -+ return True - --# ex:ts=8:et: -+# ex:ts=4:et: -diff --git a/plugins/snippets/snippets/Completion.py b/plugins/snippets/snippets/Completion.py -old mode 100755 -new mode 100644 -index 1788ecd..a860d21 ---- a/plugins/snippets/snippets/Completion.py -+++ b/plugins/snippets/snippets/Completion.py -@@ -5,159 +5,159 @@ from LanguageManager import get_language_manager - from Snippet import Snippet - - class Proposal(GObject.Object, GtkSource.CompletionProposal): -- __gtype_name__ = "PlumaSnippetsProposal" -- -- def __init__(self, snippet): -- GObject.Object.__init__(self) -- self._snippet = Snippet(snippet) -- -- def snippet(self): -- return self._snippet.data -- -- # Interface implementation -- def do_get_markup(self): -- return self._snippet.display() -- -- def do_get_info(self): -- return self._snippet.data['text'] -+ __gtype_name__ = "PlumaSnippetsProposal" -+ -+ def __init__(self, snippet): -+ GObject.Object.__init__(self) -+ self._snippet = Snippet(snippet) -+ -+ def snippet(self): -+ return self._snippet.data -+ -+ # Interface implementation -+ def do_get_markup(self): -+ return self._snippet.display() -+ -+ def do_get_info(self): -+ return self._snippet.data['text'] - - class Provider(GObject.Object, GtkSource.CompletionProvider): -- __gtype_name__ = "PlumaSnippetsProvider" -- -- def __init__(self, name, language_id, handler): -- GObject.Object.__init__(self) -- -- self.name = name -- self.info_widget = None -- self.proposals = [] -- self.language_id = language_id -- self.handler = handler -- self.info_widget = None -- self.mark = None -- -- theme = Gtk.IconTheme.get_default() -- f, w, h = Gtk.icon_size_lookup(Gtk.IconSize.MENU) -- -- self.icon = theme.load_icon(Gtk.STOCK_JUSTIFY_LEFT, w, 0) -- -- def __del__(self): -- if self.mark: -- self.mark.get_buffer().delete_mark(self.mark) -- -- def set_proposals(self, proposals): -- self.proposals = proposals -- -- def mark_position(self, it): -- if not self.mark: -- self.mark = it.get_buffer().create_mark(None, it, True) -- else: -- self.mark.get_buffer().move_mark(self.mark, it) -- -- def get_word(self, context): -- (valid_context, it) = context.get_iter() -- if not valid_context: -- return None -- -- if it.starts_word() or it.starts_line() or not it.ends_word(): -- return None -- -- start = it.copy() -- -- if start.backward_word_start(): -- self.mark_position(start) -- return start.get_text(it) -- else: -- return None -- -- def do_get_start_iter(self, context, proposal): -- if not self.mark or self.mark.get_deleted(): -- return (False, None) -- -- return (True, self.mark.get_buffer().get_iter_at_mark(self.mark)) -- -- def do_match(self, context): -- return True -- -- def get_proposals(self, word): -- if self.proposals: -- proposals = self.proposals -- else: -- proposals = Library().get_snippets(None) -- -- if self.language_id: -- proposals += Library().get_snippets(self.language_id) -- -- # Filter based on the current word -- if word: -- proposals = filter(lambda x: x['tag'].startswith(word), proposals) -- -- return map(lambda x: Proposal(x), proposals) -- -- def do_populate(self, context): -- proposals = self.get_proposals(self.get_word(context)) -- context.add_proposals(self, proposals, True) -- -- def do_get_name(self): -- return self.name -- -- def do_activate_proposal(self, proposal, piter): -- return self.handler(proposal, piter) -- -- def do_get_info_widget(self, proposal): -- if not self.info_widget: -- view = Pluma.View.new_with_buffer(Pluma.Document()) -- manager = get_language_manager() -- -- lang = manager.get_language('snippets') -- view.get_buffer().set_language(lang) -- -- sw = Gtk.ScrolledWindow() -- sw.add(view) -- -- self.info_view = view -- self.info_widget = sw -- -- return self.info_widget -- -- def do_update_info(self, proposal, info): -- buf = self.info_view.get_buffer() -- -- buf.set_text(proposal.get_info()) -- buf.move_mark(buf.get_insert(), buf.get_start_iter()) -- buf.move_mark(buf.get_selection_bound(), buf.get_start_iter()) -- self.info_view.scroll_to_iter(buf.get_start_iter(), 0.0, False, 0.5, 0.5) -- -- def do_get_icon(self): -- return self.icon -- -- def do_get_activation(self): -- return GtkSource.CompletionActivation.USER_REQUESTED -+ __gtype_name__ = "PlumaSnippetsProvider" -+ -+ def __init__(self, name, language_id, handler): -+ GObject.Object.__init__(self) -+ -+ self.name = name -+ self.info_widget = None -+ self.proposals = [] -+ self.language_id = language_id -+ self.handler = handler -+ self.info_widget = None -+ self.mark = None -+ -+ theme = Gtk.IconTheme.get_default() -+ f, w, h = Gtk.icon_size_lookup(Gtk.IconSize.MENU) -+ -+ self.icon = theme.load_icon(Gtk.STOCK_JUSTIFY_LEFT, w, 0) -+ -+ def __del__(self): -+ if self.mark: -+ self.mark.get_buffer().delete_mark(self.mark) -+ -+ def set_proposals(self, proposals): -+ self.proposals = proposals -+ -+ def mark_position(self, it): -+ if not self.mark: -+ self.mark = it.get_buffer().create_mark(None, it, True) -+ else: -+ self.mark.get_buffer().move_mark(self.mark, it) -+ -+ def get_word(self, context): -+ (valid_context, it) = context.get_iter() -+ if not valid_context: -+ return None -+ -+ if it.starts_word() or it.starts_line() or not it.ends_word(): -+ return None -+ -+ start = it.copy() -+ -+ if start.backward_word_start(): -+ self.mark_position(start) -+ return start.get_text(it) -+ else: -+ return None -+ -+ def do_get_start_iter(self, context, proposal): -+ if not self.mark or self.mark.get_deleted(): -+ return (False, None) -+ -+ return (True, self.mark.get_buffer().get_iter_at_mark(self.mark)) -+ -+ def do_match(self, context): -+ return True -+ -+ def get_proposals(self, word): -+ if self.proposals: -+ proposals = self.proposals -+ else: -+ proposals = Library().get_snippets(None) -+ -+ if self.language_id: -+ proposals += Library().get_snippets(self.language_id) -+ -+ # Filter based on the current word -+ if word: -+ proposals = filter(lambda x: x['tag'].startswith(word), proposals) -+ -+ return map(lambda x: Proposal(x), proposals) -+ -+ def do_populate(self, context): -+ proposals = self.get_proposals(self.get_word(context)) -+ context.add_proposals(self, proposals, True) -+ -+ def do_get_name(self): -+ return self.name -+ -+ def do_activate_proposal(self, proposal, piter): -+ return self.handler(proposal, piter) -+ -+ def do_get_info_widget(self, proposal): -+ if not self.info_widget: -+ view = Pluma.View.new_with_buffer(Pluma.Document()) -+ manager = get_language_manager() -+ -+ lang = manager.get_language('snippets') -+ view.get_buffer().set_language(lang) -+ -+ sw = Gtk.ScrolledWindow() -+ sw.add(view) -+ -+ self.info_view = view -+ self.info_widget = sw -+ -+ return self.info_widget -+ -+ def do_update_info(self, proposal, info): -+ buf = self.info_view.get_buffer() -+ -+ buf.set_text(proposal.get_info()) -+ buf.move_mark(buf.get_insert(), buf.get_start_iter()) -+ buf.move_mark(buf.get_selection_bound(), buf.get_start_iter()) -+ self.info_view.scroll_to_iter(buf.get_start_iter(), 0.0, False, 0.5, 0.5) -+ -+ def do_get_icon(self): -+ return self.icon -+ -+ def do_get_activation(self): -+ return GtkSource.CompletionActivation.USER_REQUESTED - - class Defaults(GObject.Object, GtkSource.CompletionProvider): -- __gtype_name__ = "PlumaSnippetsDefaultsProvider" -- -- def __init__(self, handler): -- GObject.Object.__init__(self) -- -- self.handler = handler -- self.proposals = [] -- -- def set_defaults(self, defaults): -- self.proposals = [] -- -- for d in defaults: -- self.proposals.append(GtkSource.CompletionItem.new(d, d, None, None)) -- -- def do_get_name(self): -- return "" -- -- def do_activate_proposal(self, proposal, piter): -- return self.handler(proposal, piter) -- -- def do_populate(self, context): -- context.add_proposals(self, self.proposals, True) -- -- def do_get_activation(self): -- return GtkSource.CompletionActivation.USER_REQUESTED -- --# ex:ts=8:et: -+ __gtype_name__ = "PlumaSnippetsDefaultsProvider" -+ -+ def __init__(self, handler): -+ GObject.Object.__init__(self) -+ -+ self.handler = handler -+ self.proposals = [] -+ -+ def set_defaults(self, defaults): -+ self.proposals = [] -+ -+ for d in defaults: -+ self.proposals.append(GtkSource.CompletionItem.new(d, d, None, None)) -+ -+ def do_get_name(self): -+ return "" -+ -+ def do_activate_proposal(self, proposal, piter): -+ return self.handler(proposal, piter) -+ -+ def do_populate(self, context): -+ context.add_proposals(self, self.proposals, True) -+ -+ def do_get_activation(self): -+ return GtkSource.CompletionActivation.USER_REQUESTED -+ -+# ex:ts=4:et: -diff --git a/plugins/snippets/snippets/Document.py b/plugins/snippets/snippets/Document.py -old mode 100755 -new mode 100644 -index 07faf2a..f1cc065 ---- a/plugins/snippets/snippets/Document.py -+++ b/plugins/snippets/snippets/Document.py -@@ -26,1070 +26,1068 @@ from Placeholder import * - import Completion - - class DynamicSnippet(dict): -- def __init__(self, text): -- self['text'] = text -- self.valid = True -+ def __init__(self, text): -+ self['text'] = text -+ self.valid = True - - class Document: -- TAB_KEY_VAL = (Gdk.KEY_Tab, \ -- Gdk.KEY_ISO_Left_Tab) -- SPACE_KEY_VAL = (Gdk.KEY_space,) -- -- def __init__(self, instance, view): -- self.view = None -- self.instance = instance -- -- self.placeholders = [] -- self.active_snippets = [] -- self.active_placeholder = None -- self.signal_ids = {} -- -- self.ordered_placeholders = [] -- self.update_placeholders = [] -- self.jump_placeholders = [] -- self.language_id = 0 -- self.timeout_update_id = 0 -- -- self.provider = Completion.Provider(_('Snippets'), self.language_id, self.on_proposal_activated) -- -- # Always have a reference to the global snippets -- Library().ref(None) -- self.set_view(view) -- -- # Stop controlling the view. Remove all active snippets, remove references -- # to the view and the plugin instance, disconnect all signal handlers -- def stop(self): -- if self.timeout_update_id != 0: -- GLib.source_remove(self.timeout_update_id) -- self.timeout_update_id = 0 -- del self.update_placeholders[:] -- del self.jump_placeholders[:] -- -- # Always release the reference to the global snippets -- Library().unref(None) -- self.set_view(None) -- self.instance = None -- self.active_placeholder = None -+ TAB_KEY_VAL = (Gdk.KEY_Tab, \ -+ Gdk.KEY_ISO_Left_Tab) -+ SPACE_KEY_VAL = (Gdk.KEY_space,) -+ -+ def __init__(self, instance, view): -+ self.view = None -+ self.instance = instance -+ -+ self.placeholders = [] -+ self.active_snippets = [] -+ self.active_placeholder = None -+ self.signal_ids = {} -+ -+ self.ordered_placeholders = [] -+ self.update_placeholders = [] -+ self.jump_placeholders = [] -+ self.language_id = 0 -+ self.timeout_update_id = 0 -+ -+ self.provider = Completion.Provider(_('Snippets'), self.language_id, self.on_proposal_activated) -+ -+ # Always have a reference to the global snippets -+ Library().ref(None) -+ self.set_view(view) -+ -+ # Stop controlling the view. Remove all active snippets, remove references -+ # to the view and the plugin instance, disconnect all signal handlers -+ def stop(self): -+ if self.timeout_update_id != 0: -+ GLib.source_remove(self.timeout_update_id) -+ self.timeout_update_id = 0 -+ del self.update_placeholders[:] -+ del self.jump_placeholders[:] -+ -+ # Always release the reference to the global snippets -+ Library().unref(None) -+ self.set_view(None) -+ self.instance = None -+ self.active_placeholder = None -+ -+ def disconnect_signal(self, obj, signal): -+ if (obj, signal) in self.signal_ids: -+ obj.disconnect(self.signal_ids[(obj, signal)]) -+ del self.signal_ids[(obj, signal)] -+ -+ def connect_signal(self, obj, signal, cb): -+ self.disconnect_signal(obj, signal) -+ self.signal_ids[(obj, signal)] = obj.connect(signal, cb) -+ -+ def connect_signal_after(self, obj, signal, cb): -+ self.disconnect_signal(obj, signal) -+ self.signal_ids[(obj, signal)] = obj.connect_after(signal, cb) - -- def disconnect_signal(self, obj, signal): -- if (obj, signal) in self.signal_ids: -- obj.disconnect(self.signal_ids[(obj, signal)]) -- del self.signal_ids[(obj, signal)] -- -- def connect_signal(self, obj, signal, cb): -- self.disconnect_signal(obj, signal) -- self.signal_ids[(obj, signal)] = obj.connect(signal, cb) -- -- def connect_signal_after(self, obj, signal, cb): -- self.disconnect_signal(obj, signal) -- self.signal_ids[(obj, signal)] = obj.connect_after(signal, cb) -- -- # Set the view to be controlled. Installs signal handlers and sets current -- # language. If there is already a view set this function will first remove -- # all currently active snippets and disconnect all current signals. So -- # self.set_view(None) will effectively remove all the control from the -- # current view -- def _set_view(self, view): -- if self.view: -- buf = self.view.get_buffer() -- -- # Remove signals -- signals = {self.view: ('key-press-event', 'destroy', -- 'notify::editable', 'drag-data-received', 'expose-event'), -- buf: ('notify::language', 'changed', 'cursor-moved', 'insert-text'), -- self.view.get_completion(): ('hide',)} -- -- for obj, sig in signals.items(): -- if obj: -- for s in sig: -- self.disconnect_signal(obj, s) -- -- # Remove all active snippets -- for snippet in list(self.active_snippets): -- self.deactivate_snippet(snippet, True) -- -- completion = self.view.get_completion() -- if completion: -- completion.remove_provider(self.provider) -- -- self.view = view -- -- if view != None: -- buf = view.get_buffer() -- -- self.connect_signal(view, 'destroy', self.on_view_destroy) -- -- if view.get_editable(): -- self.connect_signal(view, 'key-press-event', self.on_view_key_press) -- -- self.connect_signal(buf, 'notify::language', self.on_notify_language) -- self.connect_signal(view, 'notify::editable', self.on_notify_editable) -- self.connect_signal(view, 'drag-data-received', self.on_drag_data_received) -- self.connect_signal_after(view, 'draw', self.on_draw) -- -- self.update_language() -- -- completion = view.get_completion() -- completion.add_provider(self.provider) -- elif self.language_id != 0: -- langid = self.language_id -- -- self.language_id = None; -- self.provider.language_id = self.language_id -- -- if self.instance: -- self.instance.language_changed(self) -- -- Library().unref(langid) -- -- def set_view(self, view): -- if view == self.view: -- return -- -- self._set_view(view) -- -- # Call this whenever the language in the view changes. This makes sure that -- # the correct language is used when finding snippets -- def update_language(self): -- lang = self.view.get_buffer().get_language() -- -- if lang == None and self.language_id == None: -- return -- elif lang and lang.get_id() == self.language_id: -- return -- -- langid = self.language_id -- -- if lang: -- self.language_id = lang.get_id() -- else: -- self.language_id = None -+ # Set the view to be controlled. Installs signal handlers and sets current -+ # language. If there is already a view set this function will first remove -+ # all currently active snippets and disconnect all current signals. So -+ # self.set_view(None) will effectively remove all the control from the -+ # current view -+ def _set_view(self, view): -+ if self.view: -+ buf = self.view.get_buffer() -+ -+ # Remove signals -+ signals = {self.view: ('key-press-event', 'destroy', -+ 'notify::editable', 'drag-data-received', 'expose-event'), -+ buf: ('notify::language', 'changed', 'cursor-moved', 'insert-text'), -+ self.view.get_completion(): ('hide',)} -+ -+ for obj, sig in signals.items(): -+ if obj: -+ for s in sig: -+ self.disconnect_signal(obj, s) - -- if self.instance: -- self.instance.language_changed(self) -+ # Remove all active snippets -+ for snippet in list(self.active_snippets): -+ self.deactivate_snippet(snippet, True) -+ -+ completion = self.view.get_completion() -+ if completion: -+ completion.remove_provider(self.provider) -+ -+ self.view = view - -- if langid != 0: -- Library().unref(langid) -+ if view != None: -+ buf = view.get_buffer() - -- Library().ref(self.language_id) -- self.provider.language_id = self.language_id -+ self.connect_signal(view, 'destroy', self.on_view_destroy) -+ -+ if view.get_editable(): -+ self.connect_signal(view, 'key-press-event', self.on_view_key_press) - -- def accelerator_activate(self, keyval, mod): -- if not self.view or not self.view.get_editable(): -- return False -+ self.connect_signal(buf, 'notify::language', self.on_notify_language) -+ self.connect_signal(view, 'notify::editable', self.on_notify_editable) -+ self.connect_signal(view, 'drag-data-received', self.on_drag_data_received) -+ self.connect_signal_after(view, 'draw', self.on_draw) -+ -+ self.update_language() -+ -+ completion = view.get_completion() -+ completion.add_provider(self.provider) -+ elif self.language_id != 0: -+ langid = self.language_id - -- accelerator = Gtk.accelerator_name(keyval, mod) -- snippets = Library().from_accelerator(accelerator, \ -- self.language_id) -+ self.language_id = None; -+ self.provider.language_id = self.language_id -+ -+ if self.instance: -+ self.instance.language_changed(self) - -- snippets_debug('Accel!') -+ Library().unref(langid) - -- if len(snippets) == 0: -- return False -- elif len(snippets) == 1: -- self.apply_snippet(snippets[0]) -- else: -- # Do the fancy completion dialog -- provider = Completion.Provider(_('Snippets'), self.language_id, self.on_proposal_activated) -- provider.set_proposals(snippets) -+ def set_view(self, view): -+ if view == self.view: -+ return - -- cm.show([provider], cm.create_context(None)) -+ self._set_view(view) - -- return True -+ # Call this whenever the language in the view changes. This makes sure that -+ # the correct language is used when finding snippets -+ def update_language(self): -+ lang = self.view.get_buffer().get_language() -+ -+ if lang == None and self.language_id == None: -+ return -+ elif lang and lang.get_id() == self.language_id: -+ return -+ -+ langid = self.language_id -+ -+ if lang: -+ self.language_id = lang.get_id() -+ else: -+ self.language_id = None - -- def first_snippet_inserted(self): -- buf = self.view.get_buffer() -- -- self.connect_signal(buf, 'changed', self.on_buffer_changed) -- self.connect_signal(buf, 'cursor-moved', self.on_buffer_cursor_moved) -- self.connect_signal_after(buf, 'insert-text', self.on_buffer_insert_text) -- -- def last_snippet_removed(self): -- buf = self.view.get_buffer() -- self.disconnect_signal(buf, 'changed') -- self.disconnect_signal(buf, 'cursor-moved') -- self.disconnect_signal(buf, 'insert-text') -- -- def current_placeholder(self): -- buf = self.view.get_buffer() -- -- piter = buf.get_iter_at_mark(buf.get_insert()) -- found = [] -- -- for placeholder in self.placeholders: -- begin = placeholder.begin_iter() -- end = placeholder.end_iter() -- -- if piter.compare(begin) >= 0 and piter.compare(end) <= 0: -- found.append(placeholder) -- -- if self.active_placeholder in found: -- return self.active_placeholder -- elif len(found) > 0: -- return found[0] -- else: -- return None -- -- def advance_placeholder(self, direction): -- # Returns (CurrentPlaceholder, NextPlaceholder), depending on direction -- buf = self.view.get_buffer() -- -- piter = buf.get_iter_at_mark(buf.get_insert()) -- found = current = next = None -- length = len(self.placeholders) -- -- placeholders = list(self.placeholders) -- -- if self.active_placeholder: -- begin = self.active_placeholder.begin_iter() -- end = self.active_placeholder.end_iter() -- -- if piter.compare(begin) >= 0 and piter.compare(end) <= 0: -- current = self.active_placeholder -- currentIndex = placeholders.index(self.active_placeholder) -- -- if direction == 1: -- # w = piter, x = begin, y = end, z = found -- nearest = lambda w, x, y, z: (w.compare(x) <= 0 and (not z or \ -- x.compare(z.begin_iter()) < 0)) -- indexer = lambda x: x < length - 1 -- else: -- # w = piter, x = begin, y = end, z = prev -- nearest = lambda w, x, y, z: (w.compare(x) >= 0 and (not z or \ -- x.compare(z.begin_iter()) >= 0)) -- indexer = lambda x: x > 0 -- -- for index in range(0, length): -- placeholder = placeholders[index] -- begin = placeholder.begin_iter() -- end = placeholder.end_iter() -- -- # Find the nearest placeholder -- if nearest(piter, begin, end, found): -- foundIndex = index -- found = placeholder -- -- # Find the current placeholder -- if piter.compare(begin) >= 0 and \ -- piter.compare(end) <= 0 and \ -- current == None: -- currentIndex = index -- current = placeholder -- -- if current and current != found and \ -- (current.begin_iter().compare(found.begin_iter()) == 0 or \ -- current.end_iter().compare(found.begin_iter()) == 0) and \ -- self.active_placeholder and \ -- current.begin_iter().compare(self.active_placeholder.begin_iter()) == 0: -- # if current and found are at the same place, then -- # resolve the 'hugging' problem -- current = self.active_placeholder -- currentIndex = placeholders.index(current) -- -- if current: -- if indexer(currentIndex): -- next = placeholders[currentIndex + direction] -- elif found: -- next = found -- elif length > 0: -- next = self.placeholders[0] -- -- return current, next -- -- def next_placeholder(self): -- return self.advance_placeholder(1) -- -- def previous_placeholder(self): -- return self.advance_placeholder(-1) -- -- def cursor_on_screen(self): -- buf = self.view.get_buffer() -- self.view.scroll_mark_onscreen(buf.get_insert()) -- -- def set_active_placeholder(self, placeholder): -- self.active_placeholder = placeholder -- -- def goto_placeholder(self, current, next): -- last = None -- -- if current: -- # Signal this placeholder to end action -- self.view.get_completion().hide() -- current.leave() -- -- if current.__class__ == PlaceholderEnd: -- last = current -- -- self.set_active_placeholder(next) -- -- if next: -- next.enter() -- -- if next.__class__ == PlaceholderEnd: -- last = next -- elif len(next.defaults) > 1 and next.get_text() == next.default: -- provider = Completion.Defaults(self.on_default_activated) -- provider.set_defaults(next.defaults) -- -- cm = self.view.get_completion() -- cm.show([provider], cm.create_context(None)) -- -- if last: -- # This is the end of the placeholder, remove the snippet etc -- for snippet in list(self.active_snippets): -- if snippet.placeholders[0] == last: -- self.deactivate_snippet(snippet) -- break -- -- self.cursor_on_screen() -- -- return next != None -- -- def skip_to_next_placeholder(self): -- (current, next) = self.next_placeholder() -- return self.goto_placeholder(current, next) -- -- def skip_to_previous_placeholder(self): -- (current, prev) = self.previous_placeholder() -- return self.goto_placeholder(current, prev) -- -- def env_get_selected_text(self, buf): -- bounds = buf.get_selection_bounds() -- -- if bounds: -- return buf.get_text(bounds[0], bounds[1], False) -- else: -- return '' -- -- def env_get_current_word(self, buf): -- start, end = buffer_word_boundary(buf) -- -- return buf.get_text(start, end, False) -- -- def env_get_current_line(self, buf): -- start, end = buffer_line_boundary(buf) -- -- return buf.get_text(start, end, False) -- -- def env_get_current_line_number(self, buf): -- start, end = buffer_line_boundary(buf) -- -- return str(start.get_line() + 1) -- -- def env_get_document_uri(self, buf): -- location = buf.get_location() -- -- if location: -- return location.get_uri() -- else: -- return '' -- -- def env_get_document_name(self, buf): -- location = buf.get_location() -- -- if location: -- return location.get_basename() -- else: -- return '' -+ if self.instance: -+ self.instance.language_changed(self) -+ -+ if langid != 0: -+ Library().unref(langid) - -- def env_get_document_scheme(self, buf): -- location = buf.get_location() -- -- if location: -- return location.get_uri_scheme() -- else: -- return '' -+ Library().ref(self.language_id) -+ self.provider.language_id = self.language_id -+ -+ def accelerator_activate(self, keyval, mod): -+ if not self.view or not self.view.get_editable(): -+ return False -+ -+ accelerator = Gtk.accelerator_name(keyval, mod) -+ snippets = Library().from_accelerator(accelerator, \ -+ self.language_id) -+ -+ snippets_debug('Accel!') -+ -+ if len(snippets) == 0: -+ return False -+ elif len(snippets) == 1: -+ self.apply_snippet(snippets[0]) -+ else: -+ # Do the fancy completion dialog -+ provider = Completion.Provider(_('Snippets'), self.language_id, self.on_proposal_activated) -+ provider.set_proposals(snippets) -+ -+ cm.show([provider], cm.create_context(None)) -+ -+ return True -+ -+ def first_snippet_inserted(self): -+ buf = self.view.get_buffer() -+ -+ self.connect_signal(buf, 'changed', self.on_buffer_changed) -+ self.connect_signal(buf, 'cursor-moved', self.on_buffer_cursor_moved) -+ self.connect_signal_after(buf, 'insert-text', self.on_buffer_insert_text) - -- def env_get_document_path(self, buf): -- location = buf.get_location() -- -- if location: -- return location.get_path() -- else: -- return '' -+ def last_snippet_removed(self): -+ buf = self.view.get_buffer() -+ self.disconnect_signal(buf, 'changed') -+ self.disconnect_signal(buf, 'cursor-moved') -+ self.disconnect_signal(buf, 'insert-text') - -- def env_get_document_dir(self, buf): -- location = buf.get_location() -+ def current_placeholder(self): -+ buf = self.view.get_buffer() - -- if location: -- return location.get_parent().get_path() or '' -- else: -- return '' -+ piter = buf.get_iter_at_mark(buf.get_insert()) -+ found = [] -+ -+ for placeholder in self.placeholders: -+ begin = placeholder.begin_iter() -+ end = placeholder.end_iter() -+ -+ if piter.compare(begin) >= 0 and piter.compare(end) <= 0: -+ found.append(placeholder) -+ -+ if self.active_placeholder in found: -+ return self.active_placeholder -+ elif len(found) > 0: -+ return found[0] -+ else: -+ return None -+ -+ def advance_placeholder(self, direction): -+ # Returns (CurrentPlaceholder, NextPlaceholder), depending on direction -+ buf = self.view.get_buffer() - -- def env_get_document_type(self, buf): -- typ = buf.get_mime_type() -- -- if typ: -- return typ -- else: -- return '' -- -- def env_get_documents_uri(self, buf): -- toplevel = self.view.get_toplevel() -- -- if isinstance(toplevel, Pluma.Window): -- documents_uri = [doc.get_location().get_uri() -- for doc in toplevel.get_documents() -- if doc.get_location() is not None] -- else: -- documents_uri = [] -- -- return ' '.join(documents_uri) -- -- def env_get_documents_path(self, buf): -- toplevel = self.view.get_toplevel() -- -- if isinstance(toplevel, Pluma.Window): -- documents_location = [doc.get_location() -- for doc in toplevel.get_documents() -- if doc.get_location() is not None] -- -- documents_path = [location.get_path() -- for location in documents_location -- if Pluma.utils_uri_has_file_scheme(location.get_uri())] -- else: -- documents_path = [] -- -- return ' '.join(documents_path) -- -- def update_environment(self): -- buf = self.view.get_buffer() -- -- variables = {'PLUMA_SELECTED_TEXT': self.env_get_selected_text, -- 'PLUMA_CURRENT_WORD': self.env_get_current_word, -- 'PLUMA_CURRENT_LINE': self.env_get_current_line, -- 'PLUMA_CURRENT_LINE_NUMBER': self.env_get_current_line_number, -- 'PLUMA_CURRENT_DOCUMENT_URI': self.env_get_document_uri, -- 'PLUMA_CURRENT_DOCUMENT_NAME': self.env_get_document_name, -- 'PLUMA_CURRENT_DOCUMENT_SCHEME': self.env_get_document_scheme, -- 'PLUMA_CURRENT_DOCUMENT_PATH': self.env_get_document_path, -- 'PLUMA_CURRENT_DOCUMENT_DIR': self.env_get_document_dir, -- 'PLUMA_CURRENT_DOCUMENT_TYPE': self.env_get_document_type, -- 'PLUMA_DOCUMENTS_URI': self.env_get_documents_uri, -- 'PLUMA_DOCUMENTS_PATH': self.env_get_documents_path, -- } -- -- for var in variables: -- os.environ[var] = variables[var](buf) -- -- def uses_current_word(self, snippet): -- matches = re.findall('(\\\\*)\\$PLUMA_CURRENT_WORD', snippet['text']) -- -- for match in matches: -- if len(match) % 2 == 0: -- return True -- -- return False -- -- def uses_current_line(self, snippet): -- matches = re.findall('(\\\\*)\\$PLUMA_CURRENT_LINE', snippet['text']) -- -- for match in matches: -- if len(match) % 2 == 0: -- return True -- -- return False -- -- def apply_snippet(self, snippet, start = None, end = None): -- if not snippet.valid: -- return False -- -- buf = self.view.get_buffer() -- s = Snippet(snippet) -- -- if not start: -- start = buf.get_iter_at_mark(buf.get_insert()) -- -- if not end: -- end = buf.get_iter_at_mark(buf.get_selection_bound()) -- -- if start.equal(end) and self.uses_current_word(s): -- # There is no tab trigger and no selection and the snippet uses -- # the current word. Set start and end to the word boundary so that -- # it will be removed -- start, end = buffer_word_boundary(buf) -- elif start.equal(end) and self.uses_current_line(s): -- # There is no tab trigger and no selection and the snippet uses -- # the current line. Set start and end to the line boundary so that -- # it will be removed -- start, end = buffer_line_boundary(buf) -- -- # Set environmental variables -- self.update_environment() -- -- # You know, we could be in an end placeholder -- (current, next) = self.next_placeholder() -- if current and current.__class__ == PlaceholderEnd: -- self.goto_placeholder(current, None) -- -- buf.begin_user_action() -- -- # Remove the tag, selection or current word -- buf.delete(start, end) -- -- # Insert the snippet -- holders = len(self.placeholders) -- -- if len(self.active_snippets) == 0: -- self.first_snippet_inserted() -- -- sn = s.insert_into(self, start) -- self.active_snippets.append(sn) -- -- # Put cursor at first tab placeholder -- keys = filter(lambda x: x > 0, sn.placeholders.keys()) -- -- if len(keys) == 0: -- if 0 in sn.placeholders: -- self.goto_placeholder(self.active_placeholder, sn.placeholders[0]) -- else: -- buf.place_cursor(sn.begin_iter()) -- else: -- self.goto_placeholder(self.active_placeholder, sn.placeholders[keys[0]]) -+ piter = buf.get_iter_at_mark(buf.get_insert()) -+ found = current = next = None -+ length = len(self.placeholders) -+ -+ placeholders = list(self.placeholders) -+ -+ if self.active_placeholder: -+ begin = self.active_placeholder.begin_iter() -+ end = self.active_placeholder.end_iter() -+ -+ if piter.compare(begin) >= 0 and piter.compare(end) <= 0: -+ current = self.active_placeholder -+ currentIndex = placeholders.index(self.active_placeholder) -+ -+ if direction == 1: -+ # w = piter, x = begin, y = end, z = found -+ nearest = lambda w, x, y, z: (w.compare(x) <= 0 and (not z or \ -+ x.compare(z.begin_iter()) < 0)) -+ indexer = lambda x: x < length - 1 -+ else: -+ # w = piter, x = begin, y = end, z = prev -+ nearest = lambda w, x, y, z: (w.compare(x) >= 0 and (not z or \ -+ x.compare(z.begin_iter()) >= 0)) -+ indexer = lambda x: x > 0 -+ -+ for index in range(0, length): -+ placeholder = placeholders[index] -+ begin = placeholder.begin_iter() -+ end = placeholder.end_iter() -+ -+ # Find the nearest placeholder -+ if nearest(piter, begin, end, found): -+ foundIndex = index -+ found = placeholder -+ -+ # Find the current placeholder -+ if piter.compare(begin) >= 0 and \ -+ piter.compare(end) <= 0 and \ -+ current == None: -+ currentIndex = index -+ current = placeholder -+ -+ if current and current != found and \ -+ (current.begin_iter().compare(found.begin_iter()) == 0 or \ -+ current.end_iter().compare(found.begin_iter()) == 0) and \ -+ self.active_placeholder and \ -+ current.begin_iter().compare(self.active_placeholder.begin_iter()) == 0: -+ # if current and found are at the same place, then -+ # resolve the 'hugging' problem -+ current = self.active_placeholder -+ currentIndex = placeholders.index(current) -+ -+ if current: -+ if indexer(currentIndex): -+ next = placeholders[currentIndex + direction] -+ elif found: -+ next = found -+ elif length > 0: -+ next = self.placeholders[0] -+ -+ return current, next -+ -+ def next_placeholder(self): -+ return self.advance_placeholder(1) -+ -+ def previous_placeholder(self): -+ return self.advance_placeholder(-1) -+ -+ def cursor_on_screen(self): -+ buf = self.view.get_buffer() -+ self.view.scroll_mark_onscreen(buf.get_insert()) -+ -+ def set_active_placeholder(self, placeholder): -+ self.active_placeholder = placeholder -+ -+ def goto_placeholder(self, current, next): -+ last = None -+ -+ if current: -+ # Signal this placeholder to end action -+ self.view.get_completion().hide() -+ current.leave() -+ -+ if current.__class__ == PlaceholderEnd: -+ last = current -+ -+ self.set_active_placeholder(next) -+ -+ if next: -+ next.enter() -+ -+ if next.__class__ == PlaceholderEnd: -+ last = next -+ elif len(next.defaults) > 1 and next.get_text() == next.default: -+ provider = Completion.Defaults(self.on_default_activated) -+ provider.set_defaults(next.defaults) -+ -+ cm = self.view.get_completion() -+ cm.show([provider], cm.create_context(None)) -+ -+ if last: -+ # This is the end of the placeholder, remove the snippet etc -+ for snippet in list(self.active_snippets): -+ if snippet.placeholders[0] == last: -+ self.deactivate_snippet(snippet) -+ break -+ -+ self.cursor_on_screen() -+ -+ return next != None -+ -+ def skip_to_next_placeholder(self): -+ (current, next) = self.next_placeholder() -+ return self.goto_placeholder(current, next) -+ -+ def skip_to_previous_placeholder(self): -+ (current, prev) = self.previous_placeholder() -+ return self.goto_placeholder(current, prev) -+ -+ def env_get_selected_text(self, buf): -+ bounds = buf.get_selection_bounds() -+ -+ if bounds: -+ return buf.get_text(bounds[0], bounds[1], False) -+ else: -+ return '' -+ -+ def env_get_current_word(self, buf): -+ start, end = buffer_word_boundary(buf) -+ -+ return buf.get_text(start, end, False) -+ -+ def env_get_current_line(self, buf): -+ start, end = buffer_line_boundary(buf) -+ -+ return buf.get_text(start, end, False) -+ -+ def env_get_current_line_number(self, buf): -+ start, end = buffer_line_boundary(buf) -+ return str(start.get_line() + 1) -+ -+ def env_get_document_uri(self, buf): -+ location = buf.get_location() -+ -+ if location: -+ return location.get_uri() -+ else: -+ return '' -+ -+ def env_get_document_name(self, buf): -+ location = buf.get_location() -+ -+ if location: -+ return location.get_basename() -+ else: -+ return '' -+ -+ def env_get_document_scheme(self, buf): -+ location = buf.get_location() -+ -+ if location: -+ return location.get_uri_scheme() -+ else: -+ return '' -+ -+ def env_get_document_path(self, buf): -+ location = buf.get_location() -+ -+ if location: -+ return location.get_path() -+ else: -+ return '' -+ -+ def env_get_document_dir(self, buf): -+ location = buf.get_location() -+ -+ if location: -+ return location.get_parent().get_path() or '' -+ else: -+ return '' -+ -+ def env_get_document_type(self, buf): -+ typ = buf.get_mime_type() -+ -+ if typ: -+ return typ -+ else: -+ return '' -+ -+ def env_get_documents_uri(self, buf): -+ toplevel = self.view.get_toplevel() -+ -+ if isinstance(toplevel, Pluma.Window): -+ documents_uri = [doc.get_location().get_uri() -+ for doc in toplevel.get_documents() -+ if doc.get_location() is not None] -+ else: -+ documents_uri = [] -+ -+ return ' '.join(documents_uri) -+ -+ def env_get_documents_path(self, buf): -+ toplevel = self.view.get_toplevel() - -- if sn in self.active_snippets: -- # Check if we can get end_iter in view without moving the -- # current cursor position out of view -- cur = buf.get_iter_at_mark(buf.get_insert()) -- last = sn.end_iter() -+ if isinstance(toplevel, Pluma.Window): -+ documents_location = [doc.get_location() -+ for doc in toplevel.get_documents() -+ if doc.get_location() is not None] - -- curloc = self.view.get_iter_location(cur) -- lastloc = self.view.get_iter_location(last) -+ documents_path = [location.get_path() -+ for location in documents_location -+ if Pluma.utils_uri_has_file_scheme(location.get_uri())] -+ else: -+ documents_path = [] - -- if (lastloc.y + lastloc.height) - curloc.y <= \ -- self.view.get_visible_rect().height: -- self.view.scroll_mark_onscreen(sn.end_mark) -+ return ' '.join(documents_path) - -- buf.end_user_action() -- self.view.grab_focus() -+ def update_environment(self): -+ buf = self.view.get_buffer() - -+ variables = {'PLUMA_SELECTED_TEXT': self.env_get_selected_text, -+ 'PLUMA_CURRENT_WORD': self.env_get_current_word, -+ 'PLUMA_CURRENT_LINE': self.env_get_current_line, -+ 'PLUMA_CURRENT_LINE_NUMBER': self.env_get_current_line_number, -+ 'PLUMA_CURRENT_DOCUMENT_URI': self.env_get_document_uri, -+ 'PLUMA_CURRENT_DOCUMENT_NAME': self.env_get_document_name, -+ 'PLUMA_CURRENT_DOCUMENT_SCHEME': self.env_get_document_scheme, -+ 'PLUMA_CURRENT_DOCUMENT_PATH': self.env_get_document_path, -+ 'PLUMA_CURRENT_DOCUMENT_DIR': self.env_get_document_dir, -+ 'PLUMA_CURRENT_DOCUMENT_TYPE': self.env_get_document_type, -+ 'PLUMA_DOCUMENTS_URI': self.env_get_documents_uri, -+ 'PLUMA_DOCUMENTS_PATH': self.env_get_documents_path, -+ } -+ -+ for var in variables: -+ os.environ[var] = variables[var](buf) -+ -+ def uses_current_word(self, snippet): -+ matches = re.findall('(\\\\*)\\$PLUMA_CURRENT_WORD', snippet['text']) -+ -+ for match in matches: -+ if len(match) % 2 == 0: - return True - -- def get_tab_tag(self, buf, end = None): -- if not end: -- end = buf.get_iter_at_mark(buf.get_insert()) -+ return False - -- start = end.copy() -- -- word = None -- -- if start.backward_word_start(): -- # Check if we were at a word start ourselves -- tmp = start.copy() -- tmp.forward_word_end() -- -- if tmp.equal(end): -- word = buf.get_text(start, end, False) -- else: -- start = end.copy() -- else: -- start = end.copy() -- -- if not word or word == '': -- if start.backward_char(): -- word = start.get_char() -+ def uses_current_line(self, snippet): -+ matches = re.findall('(\\\\*)\\$PLUMA_CURRENT_LINE', snippet['text']) - -- if word.isalnum() or word.isspace(): -- return (None, None, None) -- else: -- return (None, None, None) -+ for match in matches: -+ if len(match) % 2 == 0: -+ return True - -- return (word, start, end) -+ return False - -- def parse_and_run_snippet(self, data, iter): -- self.apply_snippet(DynamicSnippet(data), iter, iter) -+ def apply_snippet(self, snippet, start = None, end = None): -+ if not snippet.valid: -+ return False - -- def run_snippet_trigger(self, trigger, bounds): -- if not self.view: -- return False -+ buf = self.view.get_buffer() -+ s = Snippet(snippet) - -- snippets = Library().from_tag(trigger, self.language_id) -- buf = self.view.get_buffer() -+ if not start: -+ start = buf.get_iter_at_mark(buf.get_insert()) - -- if snippets: -- if len(snippets) == 1: -- return self.apply_snippet(snippets[0], bounds[0], bounds[1]) -- else: -- # Do the fancy completion dialog -- provider = Completion.Provider(_('Snippets'), self.language_id, self.on_proposal_activated) -- provider.set_proposals(snippets) -+ if not end: -+ end = buf.get_iter_at_mark(buf.get_selection_bound()) - -- cm = self.view.get_completion() -- cm.show([provider], cm.create_context(None)) -+ if start.equal(end) and self.uses_current_word(s): -+ # There is no tab trigger and no selection and the snippet uses -+ # the current word. Set start and end to the word boundary so that -+ # it will be removed -+ start, end = buffer_word_boundary(buf) -+ elif start.equal(end) and self.uses_current_line(s): -+ # There is no tab trigger and no selection and the snippet uses -+ # the current line. Set start and end to the line boundary so that -+ # it will be removed -+ start, end = buffer_line_boundary(buf) - -- return True -+ # Set environmental variables -+ self.update_environment() - -- return False -- -- def run_snippet(self): -- if not self.view: -- return False -+ # You know, we could be in an end placeholder -+ (current, next) = self.next_placeholder() -+ if current and current.__class__ == PlaceholderEnd: -+ self.goto_placeholder(current, None) - -- buf = self.view.get_buffer() -+ buf.begin_user_action() - -- # get the word preceding the current insertion position -- (word, start, end) = self.get_tab_tag(buf) -+ # Remove the tag, selection or current word -+ buf.delete(start, end) - -- if not word: -- return self.skip_to_next_placeholder() -+ # Insert the snippet -+ holders = len(self.placeholders) - -- if not self.run_snippet_trigger(word, (start, end)): -- return self.skip_to_next_placeholder() -- else: -- return True -+ if len(self.active_snippets) == 0: -+ self.first_snippet_inserted() - -- def deactivate_snippet(self, snippet, force = False): -- buf = self.view.get_buffer() -- remove = [] -- ordered_remove = [] -+ sn = s.insert_into(self, start) -+ self.active_snippets.append(sn) - -- for tabstop in snippet.placeholders: -- if tabstop == -1: -- placeholders = snippet.placeholders[-1] -- else: -- placeholders = [snippet.placeholders[tabstop]] -- -- for placeholder in placeholders: -- if placeholder in self.placeholders: -- if placeholder in self.update_placeholders: -- placeholder.update_contents() -- -- self.update_placeholders.remove(placeholder) -- elif placeholder in self.jump_placeholders: -- placeholder[0].leave() -+ # Put cursor at first tab placeholder -+ keys = filter(lambda x: x > 0, sn.placeholders.keys()) - -- remove.append(placeholder) -- elif placeholder in self.ordered_placeholders: -- ordered_remove.append(placeholder) -+ if len(keys) == 0: -+ if 0 in sn.placeholders: -+ self.goto_placeholder(self.active_placeholder, sn.placeholders[0]) -+ else: -+ buf.place_cursor(sn.begin_iter()) -+ else: -+ self.goto_placeholder(self.active_placeholder, sn.placeholders[keys[0]]) - -- for placeholder in remove: -- if placeholder == self.active_placeholder: -- self.active_placeholder = None -+ if sn in self.active_snippets: -+ # Check if we can get end_iter in view without moving the -+ # current cursor position out of view -+ cur = buf.get_iter_at_mark(buf.get_insert()) -+ last = sn.end_iter() - -- self.placeholders.remove(placeholder) -- self.ordered_placeholders.remove(placeholder) -+ curloc = self.view.get_iter_location(cur) -+ lastloc = self.view.get_iter_location(last) - -- placeholder.remove(force) -+ if (lastloc.y + lastloc.height) - curloc.y <= \ -+ self.view.get_visible_rect().height: -+ self.view.scroll_mark_onscreen(sn.end_mark) - -- for placeholder in ordered_remove: -- self.ordered_placeholders.remove(placeholder) -- placeholder.remove(force) -+ buf.end_user_action() -+ self.view.grab_focus() - -- snippet.deactivate() -- self.active_snippets.remove(snippet) -+ return True - -- if len(self.active_snippets) == 0: -- self.last_snippet_removed() -+ def get_tab_tag(self, buf, end = None): -+ if not end: -+ end = buf.get_iter_at_mark(buf.get_insert()) - -- self.view.queue_draw() -+ start = end.copy() -+ word = None - -- def update_snippet_contents(self): -- self.timeout_update_id = 0 -+ if start.backward_word_start(): -+ # Check if we were at a word start ourselves -+ tmp = start.copy() -+ tmp.forward_word_end() - -- for placeholder in self.update_placeholders: -- placeholder.update_contents() -+ if tmp.equal(end): -+ word = buf.get_text(start, end, False) -+ else: -+ start = end.copy() -+ else: -+ start = end.copy() - -- for placeholder in self.jump_placeholders: -- self.goto_placeholder(placeholder[0], placeholder[1]) -- -- del self.update_placeholders[:] -- del self.jump_placeholders[:] -- -- return False -- -- # Callbacks -- def on_view_destroy(self, view): -- self.stop() -- return -- -- def on_buffer_cursor_moved(self, buf): -- piter = buf.get_iter_at_mark(buf.get_insert()) -- -- # Check for all snippets if the cursor is outside its scope -- for snippet in list(self.active_snippets): -- if snippet.begin_mark.get_deleted() or snippet.end_mark.get_deleted(): -- self.deactivate(snippet) -- else: -- begin = snippet.begin_iter() -- end = snippet.end_iter() -- -- if piter.compare(begin) < 0 or piter.compare(end) > 0: -- # Oh no! Remove the snippet this instant!! -- self.deactivate_snippet(snippet) -- -- current = self.current_placeholder() -- -- if current != self.active_placeholder: -- self.jump_placeholders.append((self.active_placeholder, current)) -- -- if self.timeout_update_id == 0: -- self.timeout_update_id = GLib.timeout_add(0, -- self.update_snippet_contents) -- -- def on_buffer_changed(self, buf): -- for snippet in list(self.active_snippets): -- begin = snippet.begin_iter() -- end = snippet.end_iter() -- -- if begin.compare(end) >= 0: -- # Begin collapsed on end, just remove it -- self.deactivate_snippet(snippet) -- -- current = self.current_placeholder() -- -- if current: -- if not current in self.update_placeholders: -- self.update_placeholders.append(current) -- -- if self.timeout_update_id == 0: -- self.timeout_update_id = GLib.timeout_add(0, \ -- self.update_snippet_contents) -- -- def on_buffer_insert_text(self, buf, piter, text, length): -- ctx = get_buffer_context(buf) -- -- # do nothing special if there is no context and no active -- # placeholder -- if (not ctx) and (not self.active_placeholder): -- return -- -- if not ctx: -- ctx = self.active_placeholder -- -- if not ctx in self.ordered_placeholders: -- return -- -- # move any marks that were incorrectly moved by this insertion -- # back to where they belong -- begin = ctx.begin_iter() -- end = ctx.end_iter() -- idx = self.ordered_placeholders.index(ctx) -- -- for placeholder in self.ordered_placeholders: -- if placeholder == ctx: -- continue -- -- ob = placeholder.begin_iter() -- oe = placeholder.end_iter() -- -- if ob.compare(begin) == 0 and ((not oe) or oe.compare(end) == 0): -- oidx = self.ordered_placeholders.index(placeholder) -- -- if oidx > idx and ob: -- buf.move_mark(placeholder.begin, end) -- elif oidx < idx and oe: -- buf.move_mark(placeholder.end, begin) -- elif ob.compare(begin) >= 0 and ob.compare(end) < 0 and (oe and oe.compare(end) >= 0): -- buf.move_mark(placeholder.begin, end) -- elif (oe and oe.compare(begin) > 0) and ob.compare(begin) <= 0: -- buf.move_mark(placeholder.end, begin) -- -- def on_notify_language(self, buf, spec): -- self.update_language() -- -- def on_notify_editable(self, view, spec): -- self._set_view(view) -- -- def on_view_key_press(self, view, event): -- library = Library() -- -- state = event.get_state() -- -- if not (state & Gdk.ModifierType.CONTROL_MASK) and \ -- not (state & Gdk.ModifierType.MOD1_MASK) and \ -- event.keyval in self.TAB_KEY_VAL: -- if not state & Gdk.ModifierType.SHIFT_MASK: -- return self.run_snippet() -- else: -- return self.skip_to_previous_placeholder() -- elif not library.loaded and \ -- library.valid_accelerator(event.keyval, state): -- library.ensure_files() -- library.ensure(self.language_id) -- self.accelerator_activate(event.keyval, \ -- state & Gtk.accelerator_get_default_mod_mask()) -- -- return False -- -- def path_split(self, path, components=[]): -- head, tail = os.path.split(path) -- -- if not tail and head: -- return [head] + components -- elif tail: -- return self.path_split(head, [tail] + components) -- else: -- return components -- -- def relative_path(self, first, second, mime): -- prot1 = re.match('(^[a-z]+:\/\/|\/)(.*)', first) -- prot2 = re.match('(^[a-z]+:\/\/|\/)(.*)', second) -- -- if not prot1 or not prot2: -- return second -- -- # Different protocols -- if prot1.group(1) != prot2.group(1): -- return second -- -- # Split on backslash -- path1 = self.path_split(prot1.group(2)) -- path2 = self.path_split(prot2.group(2)) -- -- # Remove as long as common -- while path1 and path2 and path1[0] == path2[0]: -- path1.pop(0) -- path2.pop(0) -- -- # If we need to ../ more than 3 times, then just return -- # the absolute path -- if len(path1) - 1 > 3: -- return second -- -- if mime.startswith('x-directory'): -- # directory, special case -- if not path2: -- result = './' -- else: -- result = '../' * (len(path1) - 1) -- else: -- # Insert ../ -- result = '../' * (len(path1) - 1) -- -- if not path2: -- result = os.path.basename(second) -- -- if path2: -- result += os.path.join(*path2) -- -- return result -- -- def apply_uri_snippet(self, snippet, mime, uri): -- # Remove file scheme -- gfile = Gio.file_new_for_uri(uri) -- pathname = '' -- dirname = '' -- ruri = '' -- -- if Pluma.utils_uri_has_file_scheme(uri): -- pathname = gfile.get_path() -- dirname = gfile.get_parent().get_path() -- -- name = os.path.basename(uri) -- scheme = gfile.get_uri_scheme() -- -- os.environ['PLUMA_DROP_DOCUMENT_URI'] = uri -- os.environ['PLUMA_DROP_DOCUMENT_NAME'] = name -- os.environ['PLUMA_DROP_DOCUMENT_SCHEME'] = scheme -- os.environ['PLUMA_DROP_DOCUMENT_PATH'] = pathname -- os.environ['PLUMA_DROP_DOCUMENT_DIR'] = dirname -- os.environ['PLUMA_DROP_DOCUMENT_TYPE'] = mime -- -- buf = self.view.get_buffer() -- location = buf.get_location() -- if location: -- ruri = location.get_uri() -- -- relpath = self.relative_path(ruri, uri, mime) -- -- os.environ['PLUMA_DROP_DOCUMENT_RELATIVE_PATH'] = relpath -- -- mark = buf.get_mark('gtk_drag_target') -- -- if not mark: -- mark = buf.get_insert() -- -- piter = buf.get_iter_at_mark(mark) -- self.apply_snippet(snippet, piter, piter) -- -- def in_bounds(self, x, y): -- rect = self.view.get_visible_rect() -- rect.x, rect.y = self.view.buffer_to_window_coords(Gtk.TextWindowType.WIDGET, rect.x, rect.y) -- -- return not (x < rect.x or x > rect.x + rect.width or y < rect.y or y > rect.y + rect.height) -- -- def on_drag_data_received(self, view, context, x, y, data, info, timestamp): -- uris = drop_get_uris(data) -- if not uris: -- return -- -- if not self.in_bounds(x, y): -- return -- -- uris.reverse() -- stop = False -- -- for uri in uris: -- try: -- mime = Gio.content_type_guess(uri) -- except: -- mime = None -- -- if not mime: -- continue -- -- snippets = Library().from_drop_target(mime, self.language_id) -- -- if snippets: -- stop = True -- self.apply_uri_snippet(snippets[0], mime, uri) -- -- if stop: -- context.finish(True, False, timestamp) -- view.stop_emission('drag-data-received') -- view.get_toplevel().present() -- view.grab_focus() -- -- def find_uri_target(self, context): -- lst = Gtk.target_list_add_uri_targets((), 0) -- -- return self.view.drag_dest_find_target(context, lst) -- -- def on_proposal_activated(self, proposal, piter): -- buf = self.view.get_buffer() -- bounds = buf.get_selection_bounds() -- -- if bounds: -- self.apply_snippet(proposal.snippet(), None, None) -- else: -- (word, start, end) = self.get_tab_tag(buf, piter) -- self.apply_snippet(proposal.snippet(), start, end) -+ if not word or word == '': -+ if start.backward_char(): -+ word = start.get_char() -+ -+ if word.isalnum() or word.isspace(): -+ return (None, None, None) -+ else: -+ return (None, None, None) -+ -+ return (word, start, end) -+ -+ def parse_and_run_snippet(self, data, iter): -+ self.apply_snippet(DynamicSnippet(data), iter, iter) -+ -+ def run_snippet_trigger(self, trigger, bounds): -+ if not self.view: -+ return False -+ -+ snippets = Library().from_tag(trigger, self.language_id) -+ buf = self.view.get_buffer() -+ -+ if snippets: -+ if len(snippets) == 1: -+ return self.apply_snippet(snippets[0], bounds[0], bounds[1]) -+ else: -+ # Do the fancy completion dialog -+ provider = Completion.Provider(_('Snippets'), self.language_id, self.on_proposal_activated) -+ provider.set_proposals(snippets) -+ -+ cm = self.view.get_completion() -+ cm.show([provider], cm.create_context(None)) - - return True -- -- def on_default_activated(self, proposal, piter): -- buf = self.view.get_buffer() -- bounds = buf.get_selection_bounds() -- -- if bounds: -- buf.begin_user_action() -- buf.delete(bounds[0], bounds[1]) -- buf.insert(bounds[0], proposal.props.label) -- buf.end_user_action() -- -- return True -+ -+ return False -+ -+ def run_snippet(self): -+ if not self.view: -+ return False -+ -+ buf = self.view.get_buffer() -+ -+ # get the word preceding the current insertion position -+ (word, start, end) = self.get_tab_tag(buf) -+ -+ if not word: -+ return self.skip_to_next_placeholder() -+ -+ if not self.run_snippet_trigger(word, (start, end)): -+ return self.skip_to_next_placeholder() -+ else: -+ return True -+ -+ def deactivate_snippet(self, snippet, force = False): -+ buf = self.view.get_buffer() -+ remove = [] -+ ordered_remove = [] -+ -+ for tabstop in snippet.placeholders: -+ if tabstop == -1: -+ placeholders = snippet.placeholders[-1] -+ else: -+ placeholders = [snippet.placeholders[tabstop]] -+ -+ for placeholder in placeholders: -+ if placeholder in self.placeholders: -+ if placeholder in self.update_placeholders: -+ placeholder.update_contents() -+ -+ self.update_placeholders.remove(placeholder) -+ elif placeholder in self.jump_placeholders: -+ placeholder[0].leave() -+ -+ remove.append(placeholder) -+ elif placeholder in self.ordered_placeholders: -+ ordered_remove.append(placeholder) -+ -+ for placeholder in remove: -+ if placeholder == self.active_placeholder: -+ self.active_placeholder = None -+ -+ self.placeholders.remove(placeholder) -+ self.ordered_placeholders.remove(placeholder) -+ -+ placeholder.remove(force) -+ -+ for placeholder in ordered_remove: -+ self.ordered_placeholders.remove(placeholder) -+ placeholder.remove(force) -+ -+ snippet.deactivate() -+ self.active_snippets.remove(snippet) -+ -+ if len(self.active_snippets) == 0: -+ self.last_snippet_removed() -+ -+ self.view.queue_draw() -+ -+ def update_snippet_contents(self): -+ self.timeout_update_id = 0 -+ -+ for placeholder in self.update_placeholders: -+ placeholder.update_contents() -+ -+ for placeholder in self.jump_placeholders: -+ self.goto_placeholder(placeholder[0], placeholder[1]) -+ -+ del self.update_placeholders[:] -+ del self.jump_placeholders[:] -+ -+ return False -+ -+ # Callbacks -+ def on_view_destroy(self, view): -+ self.stop() -+ return -+ -+ def on_buffer_cursor_moved(self, buf): -+ piter = buf.get_iter_at_mark(buf.get_insert()) -+ -+ # Check for all snippets if the cursor is outside its scope -+ for snippet in list(self.active_snippets): -+ if snippet.begin_mark.get_deleted() or snippet.end_mark.get_deleted(): -+ self.deactivate(snippet) -+ else: -+ begin = snippet.begin_iter() -+ end = snippet.end_iter() -+ -+ if piter.compare(begin) < 0 or piter.compare(end) > 0: -+ # Oh no! Remove the snippet this instant!! -+ self.deactivate_snippet(snippet) -+ -+ current = self.current_placeholder() -+ -+ if current != self.active_placeholder: -+ self.jump_placeholders.append((self.active_placeholder, current)) -+ -+ if self.timeout_update_id == 0: -+ self.timeout_update_id = GLib.timeout_add(0, -+ self.update_snippet_contents) -+ -+ def on_buffer_changed(self, buf): -+ for snippet in list(self.active_snippets): -+ begin = snippet.begin_iter() -+ end = snippet.end_iter() -+ -+ if begin.compare(end) >= 0: -+ # Begin collapsed on end, just remove it -+ self.deactivate_snippet(snippet) -+ -+ current = self.current_placeholder() -+ -+ if current: -+ if not current in self.update_placeholders: -+ self.update_placeholders.append(current) -+ -+ if self.timeout_update_id == 0: -+ self.timeout_update_id = GLib.timeout_add(0, \ -+ self.update_snippet_contents) -+ -+ def on_buffer_insert_text(self, buf, piter, text, length): -+ ctx = get_buffer_context(buf) -+ -+ # do nothing special if there is no context and no active -+ # placeholder -+ if (not ctx) and (not self.active_placeholder): -+ return -+ -+ if not ctx: -+ ctx = self.active_placeholder -+ -+ if not ctx in self.ordered_placeholders: -+ return -+ -+ # move any marks that were incorrectly moved by this insertion -+ # back to where they belong -+ begin = ctx.begin_iter() -+ end = ctx.end_iter() -+ idx = self.ordered_placeholders.index(ctx) -+ -+ for placeholder in self.ordered_placeholders: -+ if placeholder == ctx: -+ continue -+ -+ ob = placeholder.begin_iter() -+ oe = placeholder.end_iter() -+ -+ if ob.compare(begin) == 0 and ((not oe) or oe.compare(end) == 0): -+ oidx = self.ordered_placeholders.index(placeholder) -+ -+ if oidx > idx and ob: -+ buf.move_mark(placeholder.begin, end) -+ elif oidx < idx and oe: -+ buf.move_mark(placeholder.end, begin) -+ elif ob.compare(begin) >= 0 and ob.compare(end) < 0 and (oe and oe.compare(end) >= 0): -+ buf.move_mark(placeholder.begin, end) -+ elif (oe and oe.compare(begin) > 0) and ob.compare(begin) <= 0: -+ buf.move_mark(placeholder.end, begin) -+ -+ def on_notify_language(self, buf, spec): -+ self.update_language() -+ -+ def on_notify_editable(self, view, spec): -+ self._set_view(view) -+ -+ def on_view_key_press(self, view, event): -+ library = Library() -+ -+ state = event.get_state() -+ -+ if not (state & Gdk.ModifierType.CONTROL_MASK) and \ -+ not (state & Gdk.ModifierType.MOD1_MASK) and \ -+ event.keyval in self.TAB_KEY_VAL: -+ if not state & Gdk.ModifierType.SHIFT_MASK: -+ return self.run_snippet() -+ else: -+ return self.skip_to_previous_placeholder() -+ elif not library.loaded and \ -+ library.valid_accelerator(event.keyval, state): -+ library.ensure_files() -+ library.ensure(self.language_id) -+ self.accelerator_activate(event.keyval, \ -+ state & Gtk.accelerator_get_default_mod_mask()) -+ -+ return False -+ -+ def path_split(self, path, components=[]): -+ head, tail = os.path.split(path) -+ -+ if not tail and head: -+ return [head] + components -+ elif tail: -+ return self.path_split(head, [tail] + components) -+ else: -+ return components -+ -+ def relative_path(self, first, second, mime): -+ prot1 = re.match('(^[a-z]+:\/\/|\/)(.*)', first) -+ prot2 = re.match('(^[a-z]+:\/\/|\/)(.*)', second) -+ -+ if not prot1 or not prot2: -+ return second -+ -+ # Different protocols -+ if prot1.group(1) != prot2.group(1): -+ return second -+ -+ # Split on backslash -+ path1 = self.path_split(prot1.group(2)) -+ path2 = self.path_split(prot2.group(2)) -+ -+ # Remove as long as common -+ while path1 and path2 and path1[0] == path2[0]: -+ path1.pop(0) -+ path2.pop(0) -+ -+ # If we need to ../ more than 3 times, then just return -+ # the absolute path -+ if len(path1) - 1 > 3: -+ return second -+ -+ if mime.startswith('x-directory'): -+ # directory, special case -+ if not path2: -+ result = './' -+ else: -+ result = '../' * (len(path1) - 1) -+ else: -+ # Insert ../ -+ result = '../' * (len(path1) - 1) -+ -+ if not path2: -+ result = os.path.basename(second) -+ -+ if path2: -+ result += os.path.join(*path2) -+ -+ return result -+ -+ def apply_uri_snippet(self, snippet, mime, uri): -+ # Remove file scheme -+ gfile = Gio.file_new_for_uri(uri) -+ pathname = '' -+ dirname = '' -+ ruri = '' -+ -+ if Pluma.utils_uri_has_file_scheme(uri): -+ pathname = gfile.get_path() -+ dirname = gfile.get_parent().get_path() -+ -+ name = os.path.basename(uri) -+ scheme = gfile.get_uri_scheme() -+ -+ os.environ['PLUMA_DROP_DOCUMENT_URI'] = uri -+ os.environ['PLUMA_DROP_DOCUMENT_NAME'] = name -+ os.environ['PLUMA_DROP_DOCUMENT_SCHEME'] = scheme -+ os.environ['PLUMA_DROP_DOCUMENT_PATH'] = pathname -+ os.environ['PLUMA_DROP_DOCUMENT_DIR'] = dirname -+ os.environ['PLUMA_DROP_DOCUMENT_TYPE'] = mime -+ -+ buf = self.view.get_buffer() -+ location = buf.get_location() -+ if location: -+ ruri = location.get_uri() -+ -+ relpath = self.relative_path(ruri, uri, mime) -+ -+ os.environ['PLUMA_DROP_DOCUMENT_RELATIVE_PATH'] = relpath -+ -+ mark = buf.get_mark('gtk_drag_target') -+ -+ if not mark: -+ mark = buf.get_insert() -+ -+ piter = buf.get_iter_at_mark(mark) -+ self.apply_snippet(snippet, piter, piter) -+ -+ def in_bounds(self, x, y): -+ rect = self.view.get_visible_rect() -+ rect.x, rect.y = self.view.buffer_to_window_coords(Gtk.TextWindowType.WIDGET, rect.x, rect.y) -+ -+ return not (x < rect.x or x > rect.x + rect.width or y < rect.y or y > rect.y + rect.height) -+ -+ def on_drag_data_received(self, view, context, x, y, data, info, timestamp): -+ uris = drop_get_uris(data) -+ if not uris: -+ return -+ -+ if not self.in_bounds(x, y): -+ return -+ -+ uris.reverse() -+ stop = False -+ -+ for uri in uris: -+ try: -+ mime = Gio.content_type_guess(uri) -+ except: -+ mime = None -+ -+ if not mime: -+ continue -+ -+ snippets = Library().from_drop_target(mime, self.language_id) -+ -+ if snippets: -+ stop = True -+ self.apply_uri_snippet(snippets[0], mime, uri) -+ -+ if stop: -+ context.finish(True, False, timestamp) -+ view.stop_emission('drag-data-received') -+ view.get_toplevel().present() -+ view.grab_focus() -+ -+ def find_uri_target(self, context): -+ lst = Gtk.target_list_add_uri_targets((), 0) -+ -+ return self.view.drag_dest_find_target(context, lst) -+ -+ def on_proposal_activated(self, proposal, piter): -+ buf = self.view.get_buffer() -+ bounds = buf.get_selection_bounds() -+ -+ if bounds: -+ self.apply_snippet(proposal.snippet(), None, None) -+ else: -+ (word, start, end) = self.get_tab_tag(buf, piter) -+ self.apply_snippet(proposal.snippet(), start, end) -+ -+ return True -+ -+ def on_default_activated(self, proposal, piter): -+ buf = self.view.get_buffer() -+ bounds = buf.get_selection_bounds() -+ -+ if bounds: -+ buf.begin_user_action() -+ buf.delete(bounds[0], bounds[1]) -+ buf.insert(bounds[0], proposal.props.label) -+ buf.end_user_action() -+ -+ return True -+ else: -+ return False -+ -+ def iter_coords(self, piter): -+ rect = self.view.get_iter_location(piter) -+ rect.x, rect.y = self.view.buffer_to_window_coords(Gtk.TextWindowType.TEXT, rect.x, rect.y) -+ -+ return rect -+ -+ def placeholder_in_area(self, placeholder, area): -+ start = placeholder.begin_iter() -+ end = placeholder.end_iter() -+ -+ if not start or not end: -+ return False -+ -+ # Test if start is before bottom, and end is after top -+ start_rect = self.iter_coords(start) -+ end_rect = self.iter_coords(end) -+ -+ return start_rect.y <= area.y + area.height and \ -+ end_rect.y + end_rect.height >= area.y -+ -+ def draw_placeholder_rect(self, ctx, placeholder): -+ start = placeholder.begin_iter() -+ start_rect = self.iter_coords(start) -+ start_line = start.get_line() -+ -+ end = placeholder.end_iter() -+ end_rect = self.iter_coords(end) -+ end_line = end.get_line() -+ -+ line = start.copy() -+ line.set_line_offset(0) -+ geom = self.view.get_window(Gtk.TextWindowType.TEXT).get_geometry() -+ -+ ctx.translate(0.5, 0.5) -+ -+ while line.get_line() <= end_line: -+ ypos, height = self.view.get_line_yrange(line) -+ x_, ypos = self.view.window_to_buffer_coords(Gtk.TextWindowType.TEXT, 0, ypos) -+ -+ if line.get_line() == start_line and line.get_line() == end_line: -+ # Simply draw a box, both are on the same line -+ ctx.rectangle(start_rect.x, start_rect.y, end_rect.x - start_rect.x, start_rect.height - 1) -+ ctx.stroke() -+ elif line.get_line() == start_line or line.get_line() == end_line: -+ if line.get_line() == start_line: -+ rect = start_rect - else: -- return False -- -- def iter_coords(self, piter): -- rect = self.view.get_iter_location(piter) -- rect.x, rect.y = self.view.buffer_to_window_coords(Gtk.TextWindowType.TEXT, rect.x, rect.y) -- -- return rect -- -- def placeholder_in_area(self, placeholder, area): -- start = placeholder.begin_iter() -- end = placeholder.end_iter() -- -- if not start or not end: -- return False -- -- # Test if start is before bottom, and end is after top -- start_rect = self.iter_coords(start) -- end_rect = self.iter_coords(end) -- -- return start_rect.y <= area.y + area.height and \ -- end_rect.y + end_rect.height >= area.y -- -- def draw_placeholder_rect(self, ctx, placeholder): -- start = placeholder.begin_iter() -- start_rect = self.iter_coords(start) -- start_line = start.get_line() -- -- end = placeholder.end_iter() -- end_rect = self.iter_coords(end) -- end_line = end.get_line() -- -- line = start.copy() -- line.set_line_offset(0) -- geom = self.view.get_window(Gtk.TextWindowType.TEXT).get_geometry() -- -- ctx.translate(0.5, 0.5) -- -- while line.get_line() <= end_line: -- ypos, height = self.view.get_line_yrange(line) -- x_, ypos = self.view.window_to_buffer_coords(Gtk.TextWindowType.TEXT, 0, ypos) -- -- if line.get_line() == start_line and line.get_line() == end_line: -- # Simply draw a box, both are on the same line -- ctx.rectangle(start_rect.x, start_rect.y, end_rect.x - start_rect.x, start_rect.height - 1) -- ctx.stroke() -- elif line.get_line() == start_line or line.get_line() == end_line: -- if line.get_line() == start_line: -- rect = start_rect -- else: -- rect = end_rect -- -- ctx.move_to(0, rect.y + rect.height - 1) -- ctx.rel_line_to(rect.x, 0) -- ctx.rel_line_to(0, -rect.height + 1) -- ctx.rel_line_to(geom[2], 0) -- ctx.stroke() -- -- if not line.forward_line(): -- break -- -- def draw_placeholder_bar(self, ctx, placeholder): -- start = placeholder.begin_iter() -- start_rect = self.iter_coords(start) -- -- ctx.translate(0.5, 0.5) -- extend_width = 2.5 -- -- ctx.move_to(start_rect.x - extend_width, start_rect.y) -- ctx.rel_line_to(extend_width * 2, 0) -- -- ctx.move_to(start_rect.x, start_rect.y) -- ctx.rel_line_to(0, start_rect.height - 1) -- -- ctx.rel_move_to(-extend_width, 0) -- ctx.rel_line_to(extend_width * 2, 0) -+ rect = end_rect -+ -+ ctx.move_to(0, rect.y + rect.height - 1) -+ ctx.rel_line_to(rect.x, 0) -+ ctx.rel_line_to(0, -rect.height + 1) -+ ctx.rel_line_to(geom[2], 0) - ctx.stroke() - -- def draw_placeholder(self, ctx, placeholder): -- if isinstance(placeholder, PlaceholderEnd): -- return -+ if not line.forward_line(): -+ break - -- buf = self.view.get_buffer() -+ def draw_placeholder_bar(self, ctx, placeholder): -+ start = placeholder.begin_iter() -+ start_rect = self.iter_coords(start) - -- col = self.view.get_style_context().get_color(Gtk.StateFlags.INSENSITIVE) -- col.alpha = 0.5 -- Gdk.cairo_set_source_rgba(ctx, col) -- -- if placeholder.tabstop > 0: -- ctx.set_dash([], 0) -- else: -- ctx.set_dash([2], 0) -+ ctx.translate(0.5, 0.5) -+ extend_width = 2.5 - -- start = placeholder.begin_iter() -- end = placeholder.end_iter() -+ ctx.move_to(start_rect.x - extend_width, start_rect.y) -+ ctx.rel_line_to(extend_width * 2, 0) - -- if start.equal(end): -- self.draw_placeholder_bar(ctx, placeholder) -- else: -- self.draw_placeholder_rect(ctx, placeholder) -+ ctx.move_to(start_rect.x, start_rect.y) -+ ctx.rel_line_to(0, start_rect.height - 1) -+ -+ ctx.rel_move_to(-extend_width, 0) -+ ctx.rel_line_to(extend_width * 2, 0) -+ ctx.stroke() -+ -+ def draw_placeholder(self, ctx, placeholder): -+ if isinstance(placeholder, PlaceholderEnd): -+ return -+ -+ buf = self.view.get_buffer() -+ -+ col = self.view.get_style_context().get_color(Gtk.StateFlags.INSENSITIVE) -+ col.alpha = 0.5 -+ Gdk.cairo_set_source_rgba(ctx, col) -+ -+ if placeholder.tabstop > 0: -+ ctx.set_dash([], 0) -+ else: -+ ctx.set_dash([2], 0) -+ -+ start = placeholder.begin_iter() -+ end = placeholder.end_iter() -+ -+ if start.equal(end): -+ self.draw_placeholder_bar(ctx, placeholder) -+ else: -+ self.draw_placeholder_rect(ctx, placeholder) - -- def on_draw(self, view, ctx): -- window = view.get_window(Gtk.TextWindowType.TEXT) -+ def on_draw(self, view, ctx): -+ window = view.get_window(Gtk.TextWindowType.TEXT) - -- if not Gtk.cairo_should_draw_window(ctx, window): -- return False -+ if not Gtk.cairo_should_draw_window(ctx, window): -+ return False - -- ctx.set_line_width(1.0) -- Gtk.cairo_transform_to_window(ctx, view, window) -- clipped, clip = Gdk.cairo_get_clip_rectangle(ctx) -+ ctx.set_line_width(1.0) -+ Gtk.cairo_transform_to_window(ctx, view, window) -+ clipped, clip = Gdk.cairo_get_clip_rectangle(ctx) - -- if not clipped: -- return False -+ if not clipped: -+ return False - -- for placeholder in self.ordered_placeholders: -- if not self.placeholder_in_area(placeholder, clip): -- continue -+ for placeholder in self.ordered_placeholders: -+ if not self.placeholder_in_area(placeholder, clip): -+ continue - -- ctx.save() -- self.draw_placeholder(ctx, placeholder) -- ctx.restore() -+ ctx.save() -+ self.draw_placeholder(ctx, placeholder) -+ ctx.restore() - -- return False -+ return False - --# ex:ts=8:et: -+# ex:ts=4:et: -diff --git a/plugins/snippets/snippets/Exporter.py b/plugins/snippets/snippets/Exporter.py -old mode 100755 -new mode 100644 -index 18369a6..850c3a4 ---- a/plugins/snippets/snippets/Exporter.py -+++ b/plugins/snippets/snippets/Exporter.py -@@ -8,91 +8,91 @@ import xml.etree.ElementTree as et - from Helper import * - - class Exporter: -- def __init__(self, filename, snippets): -- self.filename = filename -- self.set_snippets(snippets) -- -- def set_snippets(self, snippets): -- self.snippets = {} -- -- for snippet in snippets: -- lang = snippet.language() -- -- if lang in self.snippets: -- self.snippets[lang].append(snippet) -- else: -- self.snippets[lang] = [snippet] -- -- def export_xml(self, dirname, language, snippets): -- # Create the root snippets node -- root = et.Element('snippets') -- -- # Create filename based on language -- if language: -- filename = os.path.join(dirname, language + '.xml') -- -- # Set the language attribute -- root.attrib['language'] = language -- else: -- filename = os.path.join(dirname, 'global.xml') -- -- # Add all snippets to the root node -- for snippet in snippets: -- root.append(snippet.to_xml()) -- -- # Write xml -- write_xml(root, filename, ('text', 'accelerator')) -- -- def export_archive(self, cmd): -- dirname = tempfile.mkdtemp() -- -- # Save current working directory and change to temporary directory -- curdir = os.getcwd() -- -- try: -- os.chdir(dirname) -- -- # Write snippet xml files -- for language, snippets in self.snippets.items(): -- self.export_xml(dirname, language , snippets) -- -- # Archive files -- status = os.system('%s "%s" *.xml' % (cmd, self.filename)) -- finally: -- os.chdir(curdir) -- -- if status != 0: -- return _('The archive "%s" could not be created' % self.filename) -- -- # Remove the temporary directory -- shutil.rmtree(dirname) -- -- def export_targz(self): -- self.export_archive('tar -c --gzip -f') -- -- def export_tarbz2(self): -- self.export_archive('tar -c --bzip2 -f') -- -- def export_tar(self): -- self.export_archive('tar -cf') -- -- def run(self): -- dirname = os.path.dirname(self.filename) -- if not os.path.exists(dirname): -- return _('Target directory "%s" does not exist') % dirname -- -- if not os.path.isdir(dirname): -- return _('Target directory "%s" is not a valid directory') % dirname -- -- (root, ext) = os.path.splitext(self.filename) -- -- actions = {'.tar.gz': self.export_targz, -- '.tar.bz2': self.export_tarbz2, -- '.tar': self.export_tar} -- -- for k, v in actions.items(): -- if self.filename.endswith(k): -- return v() -- -- return self.export_targz() --# ex:ts=8:et: -+ def __init__(self, filename, snippets): -+ self.filename = filename -+ self.set_snippets(snippets) -+ -+ def set_snippets(self, snippets): -+ self.snippets = {} -+ -+ for snippet in snippets: -+ lang = snippet.language() -+ -+ if lang in self.snippets: -+ self.snippets[lang].append(snippet) -+ else: -+ self.snippets[lang] = [snippet] -+ -+ def export_xml(self, dirname, language, snippets): -+ # Create the root snippets node -+ root = et.Element('snippets') -+ -+ # Create filename based on language -+ if language: -+ filename = os.path.join(dirname, language + '.xml') -+ -+ # Set the language attribute -+ root.attrib['language'] = language -+ else: -+ filename = os.path.join(dirname, 'global.xml') -+ -+ # Add all snippets to the root node -+ for snippet in snippets: -+ root.append(snippet.to_xml()) -+ -+ # Write xml -+ write_xml(root, filename, ('text', 'accelerator')) -+ -+ def export_archive(self, cmd): -+ dirname = tempfile.mkdtemp() -+ -+ # Save current working directory and change to temporary directory -+ curdir = os.getcwd() -+ -+ try: -+ os.chdir(dirname) -+ -+ # Write snippet xml files -+ for language, snippets in self.snippets.items(): -+ self.export_xml(dirname, language , snippets) -+ -+ # Archive files -+ status = os.system('%s "%s" *.xml' % (cmd, self.filename)) -+ finally: -+ os.chdir(curdir) -+ -+ if status != 0: -+ return _('The archive "%s" could not be created' % self.filename) -+ -+ # Remove the temporary directory -+ shutil.rmtree(dirname) -+ -+ def export_targz(self): -+ self.export_archive('tar -c --gzip -f') -+ -+ def export_tarbz2(self): -+ self.export_archive('tar -c --bzip2 -f') -+ -+ def export_tar(self): -+ self.export_archive('tar -cf') -+ -+ def run(self): -+ dirname = os.path.dirname(self.filename) -+ if not os.path.exists(dirname): -+ return _('Target directory "%s" does not exist') % dirname -+ -+ if not os.path.isdir(dirname): -+ return _('Target directory "%s" is not a valid directory') % dirname -+ -+ (root, ext) = os.path.splitext(self.filename) -+ -+ actions = {'.tar.gz': self.export_targz, -+ '.tar.bz2': self.export_tarbz2, -+ '.tar': self.export_tar} -+ -+ for k, v in actions.items(): -+ if self.filename.endswith(k): -+ return v() -+ -+ return self.export_targz() -+# ex:ts=4:et: -diff --git a/plugins/snippets/snippets/Helper.py b/plugins/snippets/snippets/Helper.py -old mode 100755 -new mode 100644 -index d8a1967..6d440d0 ---- a/plugins/snippets/snippets/Helper.py -+++ b/plugins/snippets/snippets/Helper.py -@@ -23,164 +23,164 @@ import re - from gi.repository import Gtk - - def message_dialog(par, typ, msg): -- d = Gtk.MessageDialog(par, Gtk.DialogFlags.MODAL, typ, Gtk.ButtonsType.OK, msg) -- d.set_property('use-markup', True) -+ d = Gtk.MessageDialog(par, Gtk.DialogFlags.MODAL, typ, Gtk.ButtonsType.OK, msg) -+ d.set_property('use-markup', True) - -- d.run() -- d.destroy() -+ d.run() -+ d.destroy() - - def compute_indentation(view, piter): -- line = piter.get_line() -- start = view.get_buffer().get_iter_at_line(line) -- end = start.copy() -- -+ line = piter.get_line() -+ start = view.get_buffer().get_iter_at_line(line) -+ end = start.copy() -+ -+ ch = end.get_char() -+ -+ while (ch.isspace() and ch != '\r' and ch != '\n' and \ -+ end.compare(piter) < 0): -+ if not end.forward_char(): -+ break; -+ - ch = end.get_char() -- -- while (ch.isspace() and ch != '\r' and ch != '\n' and \ -- end.compare(piter) < 0): -- if not end.forward_char(): -- break; -- -- ch = end.get_char() -- -- if start.equal(end): -- return '' -- -- return start.get_slice(end) -+ -+ if start.equal(end): -+ return '' -+ -+ return start.get_slice(end) - - def markup_escape(text): -- return saxutils.escape(text) -+ return saxutils.escape(text) - - def spaces_instead_of_tabs(view, text): -- if not view.get_insert_spaces_instead_of_tabs(): -- return text -+ if not view.get_insert_spaces_instead_of_tabs(): -+ return text - -- return text.replace("\t", view.get_tab_width() * ' ') -+ return text.replace("\t", view.get_tab_width() * ' ') - - def insert_with_indent(view, piter, text, indentfirst = True, context = None): -- text = spaces_instead_of_tabs(view, text) -- lines = text.split('\n') -- buf = view.get_buffer() -+ text = spaces_instead_of_tabs(view, text) -+ lines = text.split('\n') -+ buf = view.get_buffer() -+ -+ buf._snippets_context = context - -- buf._snippets_context = context -+ if len(lines) == 1: -+ view.get_buffer().insert(piter, text) -+ else: -+ # Compute indentation -+ indent = compute_indentation(view, piter) -+ text = '' - -- if len(lines) == 1: -- view.get_buffer().insert(piter, text) -- else: -- # Compute indentation -- indent = compute_indentation(view, piter) -- text = '' -+ for i in range(0, len(lines)): -+ if indentfirst or i > 0: -+ text += indent + lines[i] + '\n' -+ else: -+ text += lines[i] + '\n' - -- for i in range(0, len(lines)): -- if indentfirst or i > 0: -- text += indent + lines[i] + '\n' -- else: -- text += lines[i] + '\n' -- -- buf.insert(piter, text[:-1]) -+ buf.insert(piter, text[:-1]) - -- buf._snippets_context = None -+ buf._snippets_context = None - - def get_buffer_context(buf): -- if hasattr(buf, "_snippets_context"): -- return buf._snippets_context -- return None -+ if hasattr(buf, "_snippets_context"): -+ return buf._snippets_context -+ return None - - def snippets_debug(*s): -- return -+ return - - def write_xml(node, f, cdata_nodes=()): -- assert node is not None -+ assert node is not None - -- if not hasattr(f, "write"): -- f = open(f, "wb") -+ if not hasattr(f, "write"): -+ f = open(f, "wb") - -- # Encoding -- f.write("\n") -+ # Encoding -+ f.write("\n") - -- _write_node(node, f, cdata_nodes) -+ _write_node(node, f, cdata_nodes) - - def _write_indent(file, text, indent): -- file.write(' ' * indent + text) -+ file.write(' ' * indent + text) - - def _write_node(node, file, cdata_nodes=(), indent=0): -- # write XML to file -- tag = node.tag -- -- if node is Comment: -- _write_indent(file, "\n" % saxutils.escape(node.text.encode('utf-8')), indent) -- elif node is ProcessingInstruction: -- _write_indent(file, "\n" % saxutils.escape(node.text.encode('utf-8')), indent) -- else: -- items = node.items() -- -- if items or node.text or len(node): -- _write_indent(file, "<" + tag.encode('utf-8'), indent) -- -- if items: -- items.sort() # lexical order -- for k, v in items: -- file.write(" %s=%s" % (k.encode('utf-8'), saxutils.quoteattr(v.encode('utf-8')))) -- if node.text or len(node): -- file.write(">") -- if node.text and node.text.strip() != "": -- if tag in cdata_nodes: -- file.write(_cdata(node.text)) -- else: -- file.write(saxutils.escape(node.text.encode('utf-8'))) -- else: -- file.write("\n") -- -- for n in node: -- _write_node(n, file, cdata_nodes, indent + 1) -- -- if not len(node): -- file.write("\n") -- else: -- _write_indent(file, "\n", \ -- indent) -- else: -- file.write(" />\n") -- -- if node.tail and node.tail.strip() != "": -- file.write(saxutils.escape(node.tail.encode('utf-8'))) -+ # write XML to file -+ tag = node.tag -+ -+ if node is Comment: -+ _write_indent(file, "\n" % saxutils.escape(node.text.encode('utf-8')), indent) -+ elif node is ProcessingInstruction: -+ _write_indent(file, "\n" % saxutils.escape(node.text.encode('utf-8')), indent) -+ else: -+ items = node.items() -+ -+ if items or node.text or len(node): -+ _write_indent(file, "<" + tag.encode('utf-8'), indent) -+ -+ if items: -+ items.sort() # lexical order -+ for k, v in items: -+ file.write(" %s=%s" % (k.encode('utf-8'), saxutils.quoteattr(v.encode('utf-8')))) -+ if node.text or len(node): -+ file.write(">") -+ if node.text and node.text.strip() != "": -+ if tag in cdata_nodes: -+ file.write(_cdata(node.text)) -+ else: -+ file.write(saxutils.escape(node.text.encode('utf-8'))) -+ else: -+ file.write("\n") -+ -+ for n in node: -+ _write_node(n, file, cdata_nodes, indent + 1) -+ -+ if not len(node): -+ file.write("\n") -+ else: -+ _write_indent(file, "\n", \ -+ indent) -+ else: -+ file.write(" />\n") -+ -+ if node.tail and node.tail.strip() != "": -+ file.write(saxutils.escape(node.tail.encode('utf-8'))) - - def _cdata(text, replace=string.replace): -- text = text.encode('utf-8') -- return '', ']]]]>') + ']]>' -+ text = text.encode('utf-8') -+ return '', ']]]]>') + ']]>' - - def buffer_word_boundary(buf): -- iter = buf.get_iter_at_mark(buf.get_insert()) -- start = iter.copy() -- -- if not iter.starts_word() and (iter.inside_word() or iter.ends_word()): -- start.backward_word_start() -- -- if not iter.ends_word() and iter.inside_word(): -- iter.forward_word_end() -- -- return (start, iter) -+ iter = buf.get_iter_at_mark(buf.get_insert()) -+ start = iter.copy() -+ -+ if not iter.starts_word() and (iter.inside_word() or iter.ends_word()): -+ start.backward_word_start() -+ -+ if not iter.ends_word() and iter.inside_word(): -+ iter.forward_word_end() -+ -+ return (start, iter) - - def buffer_line_boundary(buf): -- iter = buf.get_iter_at_mark(buf.get_insert()) -- start = iter.copy() -- start.set_line_offset(0) -- -- if not iter.ends_line(): -- iter.forward_to_line_end() -- -- return (start, iter) -+ iter = buf.get_iter_at_mark(buf.get_insert()) -+ start = iter.copy() -+ start.set_line_offset(0) -+ -+ if not iter.ends_line(): -+ iter.forward_to_line_end() -+ -+ return (start, iter) - - def drop_get_uris(selection): -- uris = [] -- if selection.targets_include_uri(): -- data = selection.get_data() -- lines = re.split('\\s*[\\n\\r]+\\s*', data.strip()) -+ uris = [] -+ if selection.targets_include_uri(): -+ data = selection.get_data() -+ lines = re.split('\\s*[\\n\\r]+\\s*', data.strip()) - -- for line in lines: -- if not line.startswith('#'): -- uris.append(line) -+ for line in lines: -+ if not line.startswith('#'): -+ uris.append(line) - -- return uris -+ return uris - --# ex:ts=8:et: -+# ex:ts=4:et: -diff --git a/plugins/snippets/snippets/Importer.py b/plugins/snippets/snippets/Importer.py -old mode 100755 -new mode 100644 -index b2e8723..c1d211e ---- a/plugins/snippets/snippets/Importer.py -+++ b/plugins/snippets/snippets/Importer.py -@@ -6,95 +6,95 @@ import shutil - from Library import * - - class Importer: -- def __init__(self, filename): -- self.filename = filename -- -- def import_destination(self, filename): -- userdir = Library().userdir -- -- filename = os.path.basename(filename) -- (root, ext) = os.path.splitext(filename) -- -- filename = os.path.join(userdir, root + ext) -- i = 1 -- -- while os.path.exists(filename): -- filename = os.path.join(userdir, root + '_' + str(i) + ext) -- i += 1 -- -- return filename -- -- def import_file(self, filename): -- if not os.path.exists(filename): -- return _('File "%s" does not exist') % filename -- -- if not os.path.isfile(filename): -- return _('File "%s" is not a valid snippets file') % filename -- -- # Find destination for file to copy to -- dest = self.import_destination(filename) -- -- # Copy file -- shutil.copy(filename, dest) -- -- # Add library -- if not Library().add_user_library(dest): -- return _('Imported file "%s" is not a valid snippets file') % os.path.basename(dest) -- -- def import_xml(self): -- return self.import_file(self.filename) -- -- def import_archive(self, cmd): -- dirname = tempfile.mkdtemp() -- status = os.system('cd %s; %s "%s"' % (dirname, cmd, self.filename)) -- -- if status != 0: -- return _('The archive "%s" could not be extracted' % self.filename) -- -- errors = [] -- -- # Now import all the files from the archive -- for f in os.listdir(dirname): -- f = os.path.join(dirname, f) -- -- if os.path.isfile(f): -- if self.import_file(f): -- errors.append(os.path.basename(f)) -- else: -- sys.stderr.write('Skipping %s, not a valid snippets file' % os.path.basename(f)) -- -- # Remove the temporary directory -- shutil.rmtree(dirname) -- -- if len(errors) > 0: -- return _('The following files could not be imported: %s') % ', '.join(errors) -- -- def import_targz(self): -- self.import_archive('tar -x --gzip -f') -- -- def import_tarbz2(self): -- self.import_archive('tar -x --bzip2 -f') -- -- def import_tar(self): -- self.import_archive('tar -xf') -- -- def run(self): -- if not os.path.exists(self.filename): -- return _('File "%s" does not exist') % self.filename -- -- if not os.path.isfile(self.filename): -- return _('File "%s" is not a valid snippets archive') % self.filename -- -- (root, ext) = os.path.splitext(self.filename) -- -- actions = {'.tar.gz': self.import_targz, -- '.tar.bz2': self.import_tarbz2, -- '.xml': self.import_xml, -- '.tar': self.import_tar} -- -- for k, v in actions.items(): -- if self.filename.endswith(k): -- return v() -- -- return _('File "%s" is not a valid snippets archive') % self.filename --# ex:ts=8:et: -+ def __init__(self, filename): -+ self.filename = filename -+ -+ def import_destination(self, filename): -+ userdir = Library().userdir -+ -+ filename = os.path.basename(filename) -+ (root, ext) = os.path.splitext(filename) -+ -+ filename = os.path.join(userdir, root + ext) -+ i = 1 -+ -+ while os.path.exists(filename): -+ filename = os.path.join(userdir, root + '_' + str(i) + ext) -+ i += 1 -+ -+ return filename -+ -+ def import_file(self, filename): -+ if not os.path.exists(filename): -+ return _('File "%s" does not exist') % filename -+ -+ if not os.path.isfile(filename): -+ return _('File "%s" is not a valid snippets file') % filename -+ -+ # Find destination for file to copy to -+ dest = self.import_destination(filename) -+ -+ # Copy file -+ shutil.copy(filename, dest) -+ -+ # Add library -+ if not Library().add_user_library(dest): -+ return _('Imported file "%s" is not a valid snippets file') % os.path.basename(dest) -+ -+ def import_xml(self): -+ return self.import_file(self.filename) -+ -+ def import_archive(self, cmd): -+ dirname = tempfile.mkdtemp() -+ status = os.system('cd %s; %s "%s"' % (dirname, cmd, self.filename)) -+ -+ if status != 0: -+ return _('The archive "%s" could not be extracted' % self.filename) -+ -+ errors = [] -+ -+ # Now import all the files from the archive -+ for f in os.listdir(dirname): -+ f = os.path.join(dirname, f) -+ -+ if os.path.isfile(f): -+ if self.import_file(f): -+ errors.append(os.path.basename(f)) -+ else: -+ sys.stderr.write('Skipping %s, not a valid snippets file' % os.path.basename(f)) -+ -+ # Remove the temporary directory -+ shutil.rmtree(dirname) -+ -+ if len(errors) > 0: -+ return _('The following files could not be imported: %s') % ', '.join(errors) -+ -+ def import_targz(self): -+ self.import_archive('tar -x --gzip -f') -+ -+ def import_tarbz2(self): -+ self.import_archive('tar -x --bzip2 -f') -+ -+ def import_tar(self): -+ self.import_archive('tar -xf') -+ -+ def run(self): -+ if not os.path.exists(self.filename): -+ return _('File "%s" does not exist') % self.filename -+ -+ if not os.path.isfile(self.filename): -+ return _('File "%s" is not a valid snippets archive') % self.filename -+ -+ (root, ext) = os.path.splitext(self.filename) -+ -+ actions = {'.tar.gz': self.import_targz, -+ '.tar.bz2': self.import_tarbz2, -+ '.xml': self.import_xml, -+ '.tar': self.import_tar} -+ -+ for k, v in actions.items(): -+ if self.filename.endswith(k): -+ return v() -+ -+ return _('File "%s" is not a valid snippets archive') % self.filename -+# ex:ts=4:et: -diff --git a/plugins/snippets/snippets/LanguageManager.py b/plugins/snippets/snippets/LanguageManager.py -old mode 100755 -new mode 100644 -index 1fb4347..e738333 ---- a/plugins/snippets/snippets/LanguageManager.py -+++ b/plugins/snippets/snippets/LanguageManager.py -@@ -7,15 +7,16 @@ global manager - manager = None - - def get_language_manager(): -- global manager -- -- if not manager: -- dirs = [] -- -- for d in Library().systemdirs: -- dirs.append(os.path.join(d, 'lang')) -- -- manager = GtkSource.LanguageManager() -- manager.set_search_path(dirs + manager.get_search_path()) -- -- return manager -+ global manager -+ -+ if not manager: -+ dirs = [] -+ -+ for d in Library().systemdirs: -+ dirs.append(os.path.join(d, 'lang')) -+ -+ manager = GtkSource.LanguageManager() -+ manager.set_search_path(dirs + manager.get_search_path()) -+ -+ return manager -+# ex:ts=4:et: -diff --git a/plugins/snippets/snippets/Library.py b/plugins/snippets/snippets/Library.py -old mode 100755 -new mode 100644 -index 5b3773a..f152082 ---- a/plugins/snippets/snippets/Library.py -+++ b/plugins/snippets/snippets/Library.py -@@ -27,973 +27,973 @@ import xml.etree.ElementTree as et - from Helper import * - - class NamespacedId: -- def __init__(self, namespace, id): -- if not id: -- self.id = None -- else: -- if namespace: -- self.id = namespace + '-' -- else: -- self.id = 'global-' -- -- self.id += id -+ def __init__(self, namespace, id): -+ if not id: -+ self.id = None -+ else: -+ if namespace: -+ self.id = namespace + '-' -+ else: -+ self.id = 'global-' -+ -+ self.id += id - - class SnippetData: -- PROPS = {'tag': '', 'text': '', 'description': 'New snippet', -- 'accelerator': '', 'drop-targets': ''} -+ PROPS = {'tag': '', 'text': '', 'description': 'New snippet', -+ 'accelerator': '', 'drop-targets': ''} - -- def __init__(self, node, library): -- self.priv_id = node.attrib.get('id') -+ def __init__(self, node, library): -+ self.priv_id = node.attrib.get('id') - -- self.set_library(library) -- self.valid = False -- self.set_node(node) -+ self.set_library(library) -+ self.valid = False -+ self.set_node(node) - -- def can_modify(self): -- return (self.library and (isinstance(self.library(), SnippetsUserFile))) -+ def can_modify(self): -+ return (self.library and (isinstance(self.library(), SnippetsUserFile))) - -- def set_library(self, library): -- if library: -- self.library = weakref.ref(library) -- else: -- self.library = None -+ def set_library(self, library): -+ if library: -+ self.library = weakref.ref(library) -+ else: -+ self.library = None -+ -+ self.id = NamespacedId(self.language(), self.priv_id).id -+ -+ def set_node(self, node): -+ if self.can_modify(): -+ self.node = node -+ else: -+ self.node = None -+ -+ self.init_snippet_data(node) -+ -+ def init_snippet_data(self, node): -+ if node == None: -+ return -+ -+ self.override = node.attrib.get('override') - -- self.id = NamespacedId(self.language(), self.priv_id).id -+ self.properties = {} -+ props = SnippetData.PROPS.copy() -+ -+ # Store all properties present -+ for child in node: -+ if child.tag in props: -+ del props[child.tag] -+ -+ # Normalize accelerator -+ if child.tag == 'accelerator' and child.text != None: -+ keyval, mod = Gtk.accelerator_parse(child.text) -+ -+ if Gtk.accelerator_valid(keyval, mod): -+ child.text = Gtk.accelerator_name(keyval, mod) -+ else: -+ child.text = '' - -- def set_node(self, node): - if self.can_modify(): -- self.node = node -- else: -- self.node = None -- -- self.init_snippet_data(node) -- -- def init_snippet_data(self, node): -- if node == None: -- return -- -- self.override = node.attrib.get('override') -- -- self.properties = {} -- props = SnippetData.PROPS.copy() -- -- # Store all properties present -- for child in node: -- if child.tag in props: -- del props[child.tag] -- -- # Normalize accelerator -- if child.tag == 'accelerator' and child.text != None: -- keyval, mod = Gtk.accelerator_parse(child.text) -- -- if Gtk.accelerator_valid(keyval, mod): -- child.text = Gtk.accelerator_name(keyval, mod) -- else: -- child.text = '' -- -- if self.can_modify(): -- self.properties[child.tag] = child -- else: -- self.properties[child.tag] = child.text or '' -- -- # Create all the props that were not found so we stay consistent -- for prop in props: -- if self.can_modify(): -- child = et.SubElement(node, prop) -- -- child.text = props[prop] -- self.properties[prop] = child -- else: -- self.properties[prop] = props[prop] -- -- self.check_validation() -- -- def check_validation(self): -- if not self['tag'] and not self['accelerator'] and not self['drop-targets']: -- return False -- -- library = Library() -- keyval, mod = Gtk.accelerator_parse(self['accelerator']) -- -- self.valid = library.valid_tab_trigger(self['tag']) and \ -- (not self['accelerator'] or library.valid_accelerator(keyval, mod)) -- -- def _format_prop(self, prop, value): -- if prop == 'drop-targets' and value != '': -- return re.split('\\s*[,;]\\s*', value) -+ self.properties[child.tag] = child - else: -- return value -- -- def __getitem__(self, prop): -- if prop in self.properties: -- if self.can_modify(): -- return self._format_prop(prop, self.properties[prop].text or '') -- else: -- return self._format_prop(prop, self.properties[prop] or '') -- -- return self._format_prop(prop, '') -- -- def __setitem__(self, prop, value): -- if not prop in self.properties: -- return -- -- if isinstance(value, list): -- value = ','.join(value) -- -- if not self.can_modify() and self.properties[prop] != value: -- # ohoh, this is not can_modify, but it needs to be changed... -- # make sure it is transfered to the changes file and set all the -- # fields. -- # This snippet data container will effectively become the container -- # for the newly created node, but transparently to whoever uses -- # it -- self._override() -- -- if self.can_modify() and self.properties[prop].text != value: -- if self.library(): -- self.library().tainted = True -- -- oldvalue = self.properties[prop].text -- self.properties[prop].text = value -- -- if prop == 'tag' or prop == 'accelerator' or prop == 'drop-targets': -- container = Library().container(self.language()) -- container.prop_changed(self, prop, oldvalue) -- -- self.check_validation() -- -- def language(self): -- if self.library and self.library(): -- return self.library().language -- else: -- return None -- -- def is_override(self): -- return self.override and Library().overridden[self.override] -- -- def to_xml(self): -- return self._create_xml() -- -- def _create_xml(self, parent=None, update=False, attrib={}): -- # Create a new node -- if parent != None: -- element = et.SubElement(parent, 'snippet', attrib) -- else: -- element = et.Element('snippet') -- -- # Create all the properties -- for p in self.properties: -- prop = et.SubElement(element, p) -- prop.text = self[p] -- -- if update: -- self.properties[p] = prop -- -- return element -- -- def _override(self): -- # Find the user file -- target = Library().get_user_library(self.language()) -- -- # Create a new node there with override -- element = self._create_xml(target.root, True, {'override': self.id}) -- -- # Create an override snippet data, feed it element so that it stores -- # all the values and then set the node to None so that it only contains -- # the values in .properties -- override = SnippetData(element, self.library()) -- override.set_node(None) -- override.id = self.id -- -- # Set our node to the new element -- self.node = element -- -- # Set the override to our id -- self.override = self.id -- self.id = None -- -- # Set the new library -- self.set_library(target) -- -- # The library is tainted because we added this snippet -- target.tainted = True -- -- # Add the override -- Library().overridden[self.override] = override -- -- def revert(self, snippet): -- userlib = self.library() -- self.set_library(snippet.library()) -- -- userlib.remove(self.node) -- -- self.set_node(None) -- -- # Copy the properties -- self.properties = snippet.properties -- -- # Set the id -- self.id = snippet.id -- -- # Reset the override flag -- self.override = None -+ self.properties[child.tag] = child.text or '' -+ -+ # Create all the props that were not found so we stay consistent -+ for prop in props: -+ if self.can_modify(): -+ child = et.SubElement(node, prop) -+ -+ child.text = props[prop] -+ self.properties[prop] = child -+ else: -+ self.properties[prop] = props[prop] -+ -+ self.check_validation() -+ -+ def check_validation(self): -+ if not self['tag'] and not self['accelerator'] and not self['drop-targets']: -+ return False -+ -+ library = Library() -+ keyval, mod = Gtk.accelerator_parse(self['accelerator']) -+ -+ self.valid = library.valid_tab_trigger(self['tag']) and \ -+ (not self['accelerator'] or library.valid_accelerator(keyval, mod)) -+ -+ def _format_prop(self, prop, value): -+ if prop == 'drop-targets' and value != '': -+ return re.split('\\s*[,;]\\s*', value) -+ else: -+ return value -+ -+ def __getitem__(self, prop): -+ if prop in self.properties: -+ if self.can_modify(): -+ return self._format_prop(prop, self.properties[prop].text or '') -+ else: -+ return self._format_prop(prop, self.properties[prop] or '') -+ -+ return self._format_prop(prop, '') -+ -+ def __setitem__(self, prop, value): -+ if not prop in self.properties: -+ return -+ -+ if isinstance(value, list): -+ value = ','.join(value) -+ -+ if not self.can_modify() and self.properties[prop] != value: -+ # ohoh, this is not can_modify, but it needs to be changed... -+ # make sure it is transfered to the changes file and set all the -+ # fields. -+ # This snippet data container will effectively become the container -+ # for the newly created node, but transparently to whoever uses -+ # it -+ self._override() -+ -+ if self.can_modify() and self.properties[prop].text != value: -+ if self.library(): -+ self.library().tainted = True -+ -+ oldvalue = self.properties[prop].text -+ self.properties[prop].text = value -+ -+ if prop == 'tag' or prop == 'accelerator' or prop == 'drop-targets': -+ container = Library().container(self.language()) -+ container.prop_changed(self, prop, oldvalue) -+ -+ self.check_validation() -+ -+ def language(self): -+ if self.library and self.library(): -+ return self.library().language -+ else: -+ return None -+ -+ def is_override(self): -+ return self.override and Library().overridden[self.override] -+ -+ def to_xml(self): -+ return self._create_xml() -+ -+ def _create_xml(self, parent=None, update=False, attrib={}): -+ # Create a new node -+ if parent != None: -+ element = et.SubElement(parent, 'snippet', attrib) -+ else: -+ element = et.Element('snippet') -+ -+ # Create all the properties -+ for p in self.properties: -+ prop = et.SubElement(element, p) -+ prop.text = self[p] -+ -+ if update: -+ self.properties[p] = prop -+ -+ return element -+ -+ def _override(self): -+ # Find the user file -+ target = Library().get_user_library(self.language()) -+ -+ # Create a new node there with override -+ element = self._create_xml(target.root, True, {'override': self.id}) -+ -+ # Create an override snippet data, feed it element so that it stores -+ # all the values and then set the node to None so that it only contains -+ # the values in .properties -+ override = SnippetData(element, self.library()) -+ override.set_node(None) -+ override.id = self.id -+ -+ # Set our node to the new element -+ self.node = element -+ -+ # Set the override to our id -+ self.override = self.id -+ self.id = None -+ -+ # Set the new library -+ self.set_library(target) -+ -+ # The library is tainted because we added this snippet -+ target.tainted = True -+ -+ # Add the override -+ Library().overridden[self.override] = override -+ -+ def revert(self, snippet): -+ userlib = self.library() -+ self.set_library(snippet.library()) -+ -+ userlib.remove(self.node) -+ -+ self.set_node(None) -+ -+ # Copy the properties -+ self.properties = snippet.properties -+ -+ # Set the id -+ self.id = snippet.id -+ -+ # Reset the override flag -+ self.override = None - - class SnippetsTreeBuilder(et.TreeBuilder): -- def __init__(self, start=None, end=None): -- et.TreeBuilder.__init__(self) -- self.set_start(start) -- self.set_end(end) -- -- def set_start(self, start): -- self._start_cb = start -- -- def set_end(self, end): -- self._end_cb = end -- -- def start(self, tag, attrs): -- result = et.TreeBuilder.start(self, tag, attrs) -- -- if self._start_cb: -- self._start_cb(result) -- -- return result -- -- def end(self, tag): -- result = et.TreeBuilder.end(self, tag) -- -- if self._end_cb: -- self._end_cb(result) -- -- return result -+ def __init__(self, start=None, end=None): -+ et.TreeBuilder.__init__(self) -+ self.set_start(start) -+ self.set_end(end) -+ -+ def set_start(self, start): -+ self._start_cb = start -+ -+ def set_end(self, end): -+ self._end_cb = end -+ -+ def start(self, tag, attrs): -+ result = et.TreeBuilder.start(self, tag, attrs) -+ -+ if self._start_cb: -+ self._start_cb(result) -+ -+ return result -+ -+ def end(self, tag): -+ result = et.TreeBuilder.end(self, tag) -+ -+ if self._end_cb: -+ self._end_cb(result) -+ -+ return result - - class LanguageContainer: -- def __init__(self, language): -- self.language = language -- self.snippets = [] -- self.snippets_by_prop = {'tag': {}, 'accelerator': {}, 'drop-targets': {}} -- self.accel_group = Gtk.AccelGroup() -- self._refs = 0 -- -- def _add_prop(self, snippet, prop, value=0): -- if value == 0: -- value = snippet[prop] -- -- if not value or value == '': -- return -- -- snippets_debug('Added ', prop ,' ', value, ' to ', str(self.language)) -- -- if prop == 'accelerator': -- keyval, mod = Gtk.accelerator_parse(value) -- self.accel_group.connect(keyval, mod, 0, \ -- Library().accelerator_activated) -- -- snippets = self.snippets_by_prop[prop] -- -- if not isinstance(value, list): -- value = [value] -- -- for val in value: -- if val in snippets: -- snippets[val].append(snippet) -- else: -- snippets[val] = [snippet] -- -- def _remove_prop(self, snippet, prop, value=0): -- if value == 0: -- value = snippet[prop] -- -- if not value or value == '': -- return -- -- snippets_debug('Removed ', prop, ' ', value, ' from ', str(self.language)) -- -- if prop == 'accelerator': -- keyval, mod = Gtk.accelerator_parse(value) -- self.accel_group.disconnect_key(keyval, mod) -- -- snippets = self.snippets_by_prop[prop] -- -- if not isinstance(value, list): -- value = [value] -- -- for val in value: -- try: -- snippets[val].remove(snippet) -- except: -- True -- -- def append(self, snippet): -- tag = snippet['tag'] -- accelerator = snippet['accelerator'] -- -- self.snippets.append(snippet) -- -- self._add_prop(snippet, 'tag') -- self._add_prop(snippet, 'accelerator') -- self._add_prop(snippet, 'drop-targets') -- -- return snippet -- -- def remove(self, snippet): -- try: -- self.snippets.remove(snippet) -- except: -- True -- -- self._remove_prop(snippet, 'tag') -- self._remove_prop(snippet, 'accelerator') -- self._remove_prop(snippet, 'drop-targets') -- -- def prop_changed(self, snippet, prop, oldvalue): -- snippets_debug('PROP CHANGED (', prop, ')', oldvalue) -- -- self._remove_prop(snippet, prop, oldvalue) -- self._add_prop(snippet, prop) -- -- def from_prop(self, prop, value): -- snippets = self.snippets_by_prop[prop] -- -- if prop == 'drop-targets': -- s = [] -- -- # FIXME: change this to use -- # matevfs.mime_type_get_equivalence when it comes -- # available -- for key, val in snippets.items(): -- if not value.startswith(key): -- continue -- -- for snippet in snippets[key]: -- if not snippet in s: -- s.append(snippet) -- -- return s -- else: -- if value in snippets: -- return snippets[value] -- else: -- return [] -- -- def ref(self): -- self._refs += 1 -- -- return True -- -- def unref(self): -- if self._refs > 0: -- self._refs -= 1 -- -- return self._refs != 0 -+ def __init__(self, language): -+ self.language = language -+ self.snippets = [] -+ self.snippets_by_prop = {'tag': {}, 'accelerator': {}, 'drop-targets': {}} -+ self.accel_group = Gtk.AccelGroup() -+ self._refs = 0 -+ -+ def _add_prop(self, snippet, prop, value=0): -+ if value == 0: -+ value = snippet[prop] -+ -+ if not value or value == '': -+ return -+ -+ snippets_debug('Added ', prop ,' ', value, ' to ', str(self.language)) -+ -+ if prop == 'accelerator': -+ keyval, mod = Gtk.accelerator_parse(value) -+ self.accel_group.connect(keyval, mod, 0, \ -+ Library().accelerator_activated) -+ -+ snippets = self.snippets_by_prop[prop] -+ -+ if not isinstance(value, list): -+ value = [value] -+ -+ for val in value: -+ if val in snippets: -+ snippets[val].append(snippet) -+ else: -+ snippets[val] = [snippet] -+ -+ def _remove_prop(self, snippet, prop, value=0): -+ if value == 0: -+ value = snippet[prop] -+ -+ if not value or value == '': -+ return -+ -+ snippets_debug('Removed ', prop, ' ', value, ' from ', str(self.language)) -+ -+ if prop == 'accelerator': -+ keyval, mod = Gtk.accelerator_parse(value) -+ self.accel_group.disconnect_key(keyval, mod) -+ -+ snippets = self.snippets_by_prop[prop] -+ -+ if not isinstance(value, list): -+ value = [value] -+ -+ for val in value: -+ try: -+ snippets[val].remove(snippet) -+ except: -+ True -+ -+ def append(self, snippet): -+ tag = snippet['tag'] -+ accelerator = snippet['accelerator'] -+ -+ self.snippets.append(snippet) -+ -+ self._add_prop(snippet, 'tag') -+ self._add_prop(snippet, 'accelerator') -+ self._add_prop(snippet, 'drop-targets') -+ -+ return snippet -+ -+ def remove(self, snippet): -+ try: -+ self.snippets.remove(snippet) -+ except: -+ True -+ -+ self._remove_prop(snippet, 'tag') -+ self._remove_prop(snippet, 'accelerator') -+ self._remove_prop(snippet, 'drop-targets') -+ -+ def prop_changed(self, snippet, prop, oldvalue): -+ snippets_debug('PROP CHANGED (', prop, ')', oldvalue) -+ -+ self._remove_prop(snippet, prop, oldvalue) -+ self._add_prop(snippet, prop) -+ -+ def from_prop(self, prop, value): -+ snippets = self.snippets_by_prop[prop] -+ -+ if prop == 'drop-targets': -+ s = [] -+ -+ # FIXME: change this to use -+ # matevfs.mime_type_get_equivalence when it comes -+ # available -+ for key, val in snippets.items(): -+ if not value.startswith(key): -+ continue -+ -+ for snippet in snippets[key]: -+ if not snippet in s: -+ s.append(snippet) -+ -+ return s -+ else: -+ if value in snippets: -+ return snippets[value] -+ else: -+ return [] -+ -+ def ref(self): -+ self._refs += 1 -+ -+ return True -+ -+ def unref(self): -+ if self._refs > 0: -+ self._refs -= 1 -+ -+ return self._refs != 0 - - class SnippetsSystemFile: -- def __init__(self, path=None): -- self.path = path -- self.loaded = False -- self.language = None -- self.ok = True -- self.need_id = True -- -- def load_error(self, message): -- sys.stderr.write("An error occurred loading " + self.path + ":\n") -- sys.stderr.write(message + "\nSnippets in this file will not be " \ -- "available, please correct or remove the file.\n") -- -- def _add_snippet(self, element): -- if not self.need_id or element.attrib.get('id'): -- self.loading_elements.append(element) -- -- def set_language(self, element): -- self.language = element.attrib.get('language') -- -- if self.language: -- self.language = self.language.lower() -- -- def _set_root(self, element): -- self.set_language(element) -- -- def _preprocess_element(self, element): -- if not self.loaded: -- if not element.tag == "snippets": -- self.load_error("Root element should be `snippets' instead " \ -- "of `%s'" % element.tag) -- return False -- else: -- self._set_root(element) -- self.loaded = True -- elif element.tag != 'snippet' and not self.insnippet: -- self.load_error("Element should be `snippet' instead of `%s'" \ -- % element.tag) -- return False -- else: -- self.insnippet = True -- -- return True -- -- def _process_element(self, element): -- if element.tag == 'snippet': -- self._add_snippet(element) -- self.insnippet = False -- -- return True -- -- def ensure(self): -- if not self.ok or self.loaded: -- return -- -- self.load() -- -- def parse_xml(self, readsize=16384): -- if not self.path: -- return -- -- elements = [] -- -- builder = SnippetsTreeBuilder( \ -- lambda node: elements.append((node, True)), \ -- lambda node: elements.append((node, False))) -- -- parser = et.XMLTreeBuilder(target=builder) -- self.insnippet = False -- -- try: -- f = open(self.path, "r") -- -- while True: -- data = f.read(readsize) -- -- if not data: -- break -- -- parser.feed(data) -- -- for element in elements: -- yield element -- -- del elements[:] -- -- f.close() -- except IOError: -- self.ok = False -- -- def load(self): -- if not self.ok: -- return -- -- snippets_debug("Loading library (" + str(self.language) + "): " + \ -- self.path) -- -- self.loaded = False -- self.ok = False -- self.loading_elements = [] -- -- for element in self.parse_xml(): -- if element[1]: -- if not self._preprocess_element(element[0]): -- del self.loading_elements[:] -- return -- else: -- if not self._process_element(element[0]): -- del self.loading_elements[:] -- return -- -- for element in self.loading_elements: -- snippet = Library().add_snippet(self, element) -- -- del self.loading_elements[:] -- self.ok = True -- -- # This function will get the language for a file by just inspecting the -- # root element of the file. This is provided so that a cache can be built -- # for which file contains which language. -- # It returns the name of the language -- def ensure_language(self): -- if not self.loaded: -- self.ok = False -- -- for element in self.parse_xml(256): -- if element[1]: -- if element[0].tag == 'snippets': -- self.set_language(element[0]) -- self.ok = True -- -- break -- -- def unload(self): -- snippets_debug("Unloading library (" + str(self.language) + "): " + \ -- self.path) -- self.language = None -- self.loaded = False -- self.ok = True -+ def __init__(self, path=None): -+ self.path = path -+ self.loaded = False -+ self.language = None -+ self.ok = True -+ self.need_id = True -+ -+ def load_error(self, message): -+ sys.stderr.write("An error occurred loading " + self.path + ":\n") -+ sys.stderr.write(message + "\nSnippets in this file will not be " \ -+ "available, please correct or remove the file.\n") -+ -+ def _add_snippet(self, element): -+ if not self.need_id or element.attrib.get('id'): -+ self.loading_elements.append(element) -+ -+ def set_language(self, element): -+ self.language = element.attrib.get('language') -+ -+ if self.language: -+ self.language = self.language.lower() -+ -+ def _set_root(self, element): -+ self.set_language(element) -+ -+ def _preprocess_element(self, element): -+ if not self.loaded: -+ if not element.tag == "snippets": -+ self.load_error("Root element should be `snippets' instead " \ -+ "of `%s'" % element.tag) -+ return False -+ else: -+ self._set_root(element) -+ self.loaded = True -+ elif element.tag != 'snippet' and not self.insnippet: -+ self.load_error("Element should be `snippet' instead of `%s'" \ -+ % element.tag) -+ return False -+ else: -+ self.insnippet = True -+ -+ return True -+ -+ def _process_element(self, element): -+ if element.tag == 'snippet': -+ self._add_snippet(element) -+ self.insnippet = False -+ -+ return True -+ -+ def ensure(self): -+ if not self.ok or self.loaded: -+ return -+ -+ self.load() -+ -+ def parse_xml(self, readsize=16384): -+ if not self.path: -+ return -+ -+ elements = [] -+ -+ builder = SnippetsTreeBuilder( \ -+ lambda node: elements.append((node, True)), \ -+ lambda node: elements.append((node, False))) -+ -+ parser = et.XMLTreeBuilder(target=builder) -+ self.insnippet = False -+ -+ try: -+ f = open(self.path, "r") -+ -+ while True: -+ data = f.read(readsize) -+ -+ if not data: -+ break -+ -+ parser.feed(data) -+ -+ for element in elements: -+ yield element -+ -+ del elements[:] -+ -+ f.close() -+ except IOError: -+ self.ok = False -+ -+ def load(self): -+ if not self.ok: -+ return -+ -+ snippets_debug("Loading library (" + str(self.language) + "): " + \ -+ self.path) -+ -+ self.loaded = False -+ self.ok = False -+ self.loading_elements = [] -+ -+ for element in self.parse_xml(): -+ if element[1]: -+ if not self._preprocess_element(element[0]): -+ del self.loading_elements[:] -+ return -+ else: -+ if not self._process_element(element[0]): -+ del self.loading_elements[:] -+ return -+ -+ for element in self.loading_elements: -+ snippet = Library().add_snippet(self, element) -+ -+ del self.loading_elements[:] -+ self.ok = True -+ -+ # This function will get the language for a file by just inspecting the -+ # root element of the file. This is provided so that a cache can be built -+ # for which file contains which language. -+ # It returns the name of the language -+ def ensure_language(self): -+ if not self.loaded: -+ self.ok = False -+ -+ for element in self.parse_xml(256): -+ if element[1]: -+ if element[0].tag == 'snippets': -+ self.set_language(element[0]) -+ self.ok = True -+ -+ break -+ -+ def unload(self): -+ snippets_debug("Unloading library (" + str(self.language) + "): " + \ -+ self.path) -+ self.language = None -+ self.loaded = False -+ self.ok = True - - class SnippetsUserFile(SnippetsSystemFile): -- def __init__(self, path=None): -- SnippetsSystemFile.__init__(self, path) -- self.tainted = False -- self.need_id = False -- -- def _set_root(self, element): -- SnippetsSystemFile._set_root(self, element) -- self.root = element -- -- def add_prop(self, node, tag, data): -- if data[tag]: -- prop = et.SubElement(node, tag) -- prop.text = data[tag] -- -- return prop -- else: -- return None -- -- def new_snippet(self, properties=None): -- if (not self.ok) or self.root == None: -- return None -- -- element = et.SubElement(self.root, 'snippet') -- -- if properties: -- for prop in properties: -- sub = et.SubElement(element, prop) -- sub.text = properties[prop] -- -- self.tainted = True -- -- return Library().add_snippet(self, element) -- -- def set_language(self, element): -- SnippetsSystemFile.set_language(self, element) -- -- filename = os.path.basename(self.path).lower() -- -- if not self.language and filename == "global.xml": -- self.modifier = True -- elif self.language and filename == self.language + ".xml": -- self.modifier = True -- else: -- self.modifier = False -- -- def create_root(self, language): -- if self.loaded: -- snippets_debug('Not creating root, already loaded') -- return -- -- if language: -- root = et.Element('snippets', {'language': language}) -- self.path = os.path.join(Library().userdir, language.lower() + '.xml') -- else: -- root = et.Element('snippets') -- self.path = os.path.join(Library().userdir, 'global.xml') -- -- self._set_root(root) -- self.loaded = True -- self.ok = True -- self.tainted = True -- self.save() -- -- def remove(self, element): -- try: -- self.root.remove(element) -- self.tainted = True -- except: -- return -- -- try: -- first = self.root[0] -- except: -- # No more elements, this library is useless now -- Library().remove_library(self) -- -- def save(self): -- if not self.ok or self.root == None or not self.tainted: -- return -- -- path = os.path.dirname(self.path) -- -- try: -- if not os.path.isdir(path): -- os.makedirs(path, 0755) -- except OSError: -- # TODO: this is bad... -- sys.stderr.write("Error in making dirs\n") -- -- try: -- write_xml(self.root, self.path, ('text', 'accelerator')) -- self.tainted = False -- except IOError: -- # Couldn't save, what to do -- sys.stderr.write("Could not save user snippets file to " + \ -- self.path + "\n") -- -- def unload(self): -- SnippetsSystemFile.unload(self) -- self.root = None -+ def __init__(self, path=None): -+ SnippetsSystemFile.__init__(self, path) -+ self.tainted = False -+ self.need_id = False -+ -+ def _set_root(self, element): -+ SnippetsSystemFile._set_root(self, element) -+ self.root = element -+ -+ def add_prop(self, node, tag, data): -+ if data[tag]: -+ prop = et.SubElement(node, tag) -+ prop.text = data[tag] -+ -+ return prop -+ else: -+ return None -+ -+ def new_snippet(self, properties=None): -+ if (not self.ok) or self.root == None: -+ return None -+ -+ element = et.SubElement(self.root, 'snippet') -+ -+ if properties: -+ for prop in properties: -+ sub = et.SubElement(element, prop) -+ sub.text = properties[prop] -+ -+ self.tainted = True -+ -+ return Library().add_snippet(self, element) -+ -+ def set_language(self, element): -+ SnippetsSystemFile.set_language(self, element) -+ -+ filename = os.path.basename(self.path).lower() -+ -+ if not self.language and filename == "global.xml": -+ self.modifier = True -+ elif self.language and filename == self.language + ".xml": -+ self.modifier = True -+ else: -+ self.modifier = False -+ -+ def create_root(self, language): -+ if self.loaded: -+ snippets_debug('Not creating root, already loaded') -+ return -+ -+ if language: -+ root = et.Element('snippets', {'language': language}) -+ self.path = os.path.join(Library().userdir, language.lower() + '.xml') -+ else: -+ root = et.Element('snippets') -+ self.path = os.path.join(Library().userdir, 'global.xml') -+ -+ self._set_root(root) -+ self.loaded = True -+ self.ok = True -+ self.tainted = True -+ self.save() -+ -+ def remove(self, element): -+ try: -+ self.root.remove(element) -+ self.tainted = True -+ except: -+ return -+ -+ try: -+ first = self.root[0] -+ except: -+ # No more elements, this library is useless now -+ Library().remove_library(self) -+ -+ def save(self): -+ if not self.ok or self.root == None or not self.tainted: -+ return -+ -+ path = os.path.dirname(self.path) -+ -+ try: -+ if not os.path.isdir(path): -+ os.makedirs(path, 0755) -+ except OSError: -+ # TODO: this is bad... -+ sys.stderr.write("Error in making dirs\n") -+ -+ try: -+ write_xml(self.root, self.path, ('text', 'accelerator')) -+ self.tainted = False -+ except IOError: -+ # Couldn't save, what to do -+ sys.stderr.write("Could not save user snippets file to " + \ -+ self.path + "\n") -+ -+ def unload(self): -+ SnippetsSystemFile.unload(self) -+ self.root = None - - 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 Library(Singleton): -- def __init_once__(self): -- self._accelerator_activated_cb = [] -- self.loaded = False -- self.check_buffer = Gtk.TextBuffer() -- -- def set_dirs(self, userdir, systemdirs): -- self.userdir = userdir -- self.systemdirs = systemdirs -- -- self.libraries = {} -- self.containers = {} -- self.overridden = {} -- self.loaded_ids = [] -- -- self.loaded = False -- -- def add_accelerator_callback(self, cb): -- self._accelerator_activated_cb.append(cb) -- -- def remove_accelerator_callback(self, cb): -- self._accelerator_activated_cb.remove(cb) -- -- def accelerator_activated(self, group, obj, keyval, mod): -- ret = False -- -- for cb in self._accelerator_activated_cb: -- ret = cb(group, obj, keyval, mod) -- -- if ret: -- break -- -- return ret -- -- def add_snippet(self, library, element): -- container = self.container(library.language) -- overrided = self.overrided(library, element) -- -- if overrided: -- overrided.set_library(library) -- snippets_debug('Snippet is overriden: ' + overrided['description']) -- return None -- -- snippet = SnippetData(element, library) -- -- if snippet.id in self.loaded_ids: -- snippets_debug('Not added snippet ' + str(library.language) + \ -- '::' + snippet['description'] + ' (duplicate)') -- return None -- -- snippet = container.append(snippet) -- snippets_debug('Added snippet ' + str(library.language) + '::' + \ -- snippet['description']) -- -- if snippet and snippet.override: -- self.add_override(snippet) -- -- if snippet.id: -- self.loaded_ids.append(snippet.id) -- -- return snippet -- -- def container(self, language): -- language = self.normalize_language(language) -- -- if not language in self.containers: -- self.containers[language] = LanguageContainer(language) -- -- return self.containers[language] -- -- def get_user_library(self, language): -- target = None -- -- if language in self.libraries: -- for library in self.libraries[language]: -- if isinstance(library, SnippetsUserFile) and library.modifier: -- target = library -- elif not isinstance(library, SnippetsUserFile): -- break -- -- if not target: -- # Create a new user file then -- snippets_debug('Creating a new user file for language ' + \ -- str(language)) -- target = SnippetsUserFile() -- target.create_root(language) -- self.add_library(target) -- -- return target -- -- def new_snippet(self, language, properties=None): -- language = self.normalize_language(language) -- library = self.get_user_library(language) -- -- return library.new_snippet(properties) -- -- def revert_snippet(self, snippet): -- # This will revert the snippet to the one it overrides -- if not snippet.can_modify() or not snippet.override in self.overridden: -- # It can't be reverted, shouldn't happen, but oh.. -- return -- -- # The snippet in self.overriden only contains the property contents and -- # the library it belongs to -- revertto = self.overridden[snippet.override] -- del self.overridden[snippet.override] -- -- if revertto: -- snippet.revert(revertto) -- -- if revertto.id: -- self.loaded_ids.append(revertto.id) -- -- def remove_snippet(self, snippet): -- if not snippet.can_modify() or snippet.is_override(): -- return -- -- # Remove from the library -- userlib = snippet.library() -- userlib.remove(snippet.node) -- -- # Remove from the container -- container = self.containers[userlib.language] -+ _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 Library(Singleton): -+ def __init_once__(self): -+ self._accelerator_activated_cb = [] -+ self.loaded = False -+ self.check_buffer = Gtk.TextBuffer() -+ -+ def set_dirs(self, userdir, systemdirs): -+ self.userdir = userdir -+ self.systemdirs = systemdirs -+ -+ self.libraries = {} -+ self.containers = {} -+ self.overridden = {} -+ self.loaded_ids = [] -+ -+ self.loaded = False -+ -+ def add_accelerator_callback(self, cb): -+ self._accelerator_activated_cb.append(cb) -+ -+ def remove_accelerator_callback(self, cb): -+ self._accelerator_activated_cb.remove(cb) -+ -+ def accelerator_activated(self, group, obj, keyval, mod): -+ ret = False -+ -+ for cb in self._accelerator_activated_cb: -+ ret = cb(group, obj, keyval, mod) -+ -+ if ret: -+ break -+ -+ return ret -+ -+ def add_snippet(self, library, element): -+ container = self.container(library.language) -+ overrided = self.overrided(library, element) -+ -+ if overrided: -+ overrided.set_library(library) -+ snippets_debug('Snippet is overriden: ' + overrided['description']) -+ return None -+ -+ snippet = SnippetData(element, library) -+ -+ if snippet.id in self.loaded_ids: -+ snippets_debug('Not added snippet ' + str(library.language) + \ -+ '::' + snippet['description'] + ' (duplicate)') -+ return None -+ -+ snippet = container.append(snippet) -+ snippets_debug('Added snippet ' + str(library.language) + '::' + \ -+ snippet['description']) -+ -+ if snippet and snippet.override: -+ self.add_override(snippet) -+ -+ if snippet.id: -+ self.loaded_ids.append(snippet.id) -+ -+ return snippet -+ -+ def container(self, language): -+ language = self.normalize_language(language) -+ -+ if not language in self.containers: -+ self.containers[language] = LanguageContainer(language) -+ -+ return self.containers[language] -+ -+ def get_user_library(self, language): -+ target = None -+ -+ if language in self.libraries: -+ for library in self.libraries[language]: -+ if isinstance(library, SnippetsUserFile) and library.modifier: -+ target = library -+ elif not isinstance(library, SnippetsUserFile): -+ break -+ -+ if not target: -+ # Create a new user file then -+ snippets_debug('Creating a new user file for language ' + \ -+ str(language)) -+ target = SnippetsUserFile() -+ target.create_root(language) -+ self.add_library(target) -+ -+ return target -+ -+ def new_snippet(self, language, properties=None): -+ language = self.normalize_language(language) -+ library = self.get_user_library(language) -+ -+ return library.new_snippet(properties) -+ -+ def revert_snippet(self, snippet): -+ # This will revert the snippet to the one it overrides -+ if not snippet.can_modify() or not snippet.override in self.overridden: -+ # It can't be reverted, shouldn't happen, but oh.. -+ return -+ -+ # The snippet in self.overriden only contains the property contents and -+ # the library it belongs to -+ revertto = self.overridden[snippet.override] -+ del self.overridden[snippet.override] -+ -+ if revertto: -+ snippet.revert(revertto) -+ -+ if revertto.id: -+ self.loaded_ids.append(revertto.id) -+ -+ def remove_snippet(self, snippet): -+ if not snippet.can_modify() or snippet.is_override(): -+ return -+ -+ # Remove from the library -+ userlib = snippet.library() -+ userlib.remove(snippet.node) -+ -+ # Remove from the container -+ container = self.containers[userlib.language] -+ container.remove(snippet) -+ -+ def overrided(self, library, element): -+ id = NamespacedId(library.language, element.attrib.get('id')).id -+ -+ if id in self.overridden: -+ snippet = SnippetData(element, None) -+ snippet.set_node(None) -+ -+ self.overridden[id] = snippet -+ return snippet -+ else: -+ return None -+ -+ def add_override(self, snippet): -+ snippets_debug('Add override:', snippet.override) -+ if not snippet.override in self.overridden: -+ self.overridden[snippet.override] = None -+ -+ def add_library(self, library): -+ library.ensure_language() -+ -+ if not library.ok: -+ snippets_debug('Library in wrong format, ignoring') -+ return False -+ -+ snippets_debug('Adding library (' + str(library.language) + '): ' + \ -+ library.path) -+ -+ if library.language in self.libraries: -+ # Make sure all the user files are before the system files -+ if isinstance(library, SnippetsUserFile): -+ self.libraries[library.language].insert(0, library) -+ else: -+ self.libraries[library.language].append(library) -+ else: -+ self.libraries[library.language] = [library] -+ -+ return True -+ -+ def remove_library(self, library): -+ if not library.ok: -+ return -+ -+ if library.path and os.path.isfile(library.path): -+ os.unlink(library.path) -+ -+ try: -+ self.libraries[library.language].remove(library) -+ except KeyError: -+ True -+ -+ container = self.containers[library.language] -+ -+ for snippet in list(container.snippets): -+ if snippet.library() == library: - container.remove(snippet) -- -- def overrided(self, library, element): -- id = NamespacedId(library.language, element.attrib.get('id')).id -- -- if id in self.overridden: -- snippet = SnippetData(element, None) -- snippet.set_node(None) -- -- self.overridden[id] = snippet -- return snippet -- else: -- return None -- -- def add_override(self, snippet): -- snippets_debug('Add override:', snippet.override) -- if not snippet.override in self.overridden: -- self.overridden[snippet.override] = None -- -- def add_library(self, library): -- library.ensure_language() -- -- if not library.ok: -- snippets_debug('Library in wrong format, ignoring') -- return False -- -- snippets_debug('Adding library (' + str(library.language) + '): ' + \ -- library.path) -- -- if library.language in self.libraries: -- # Make sure all the user files are before the system files -- if isinstance(library, SnippetsUserFile): -- self.libraries[library.language].insert(0, library) -- else: -- self.libraries[library.language].append(library) -+ -+ def add_user_library(self, path): -+ library = SnippetsUserFile(path) -+ return self.add_library(library) -+ -+ def add_system_library(self, path): -+ library = SnippetsSystemFile(path) -+ return self.add_library(library) -+ -+ def find_libraries(self, path, searched, addcb): -+ snippets_debug("Finding in: " + path) -+ -+ if not os.path.isdir(path): -+ return searched -+ -+ files = os.listdir(path) -+ searched.append(path) -+ -+ for f in files: -+ f = os.path.realpath(os.path.join(path, f)) -+ -+ # Determine what language this file provides snippets for -+ if os.path.isfile(f): -+ addcb(f) -+ -+ return searched -+ -+ def normalize_language(self, language): -+ if language: -+ return language.lower() -+ -+ return language -+ -+ def remove_container(self, language): -+ for snippet in self.containers[language].snippets: -+ if snippet.id in self.loaded_ids: -+ self.loaded_ids.remove(snippet.id) -+ -+ if snippet.override in self.overridden: -+ del self.overridden[snippet.override] -+ -+ del self.containers[language] -+ -+ def get_accel_group(self, language): -+ language = self.normalize_language(language) -+ container = self.container(language) -+ -+ self.ensure(language) -+ return container.accel_group -+ -+ def save(self, language): -+ language = self.normalize_language(language) -+ -+ if language in self.libraries: -+ for library in self.libraries[language]: -+ if isinstance(library, SnippetsUserFile): -+ library.save() - else: -- self.libraries[library.language] = [library] -- -- return True -- -- def remove_library(self, library): -- if not library.ok: -- return -- -- if library.path and os.path.isfile(library.path): -- os.unlink(library.path) -- -- try: -- self.libraries[library.language].remove(library) -- except KeyError: -- True -- -- container = self.containers[library.language] -- -- for snippet in list(container.snippets): -- if snippet.library() == library: -- container.remove(snippet) -- -- def add_user_library(self, path): -- library = SnippetsUserFile(path) -- return self.add_library(library) -- -- def add_system_library(self, path): -- library = SnippetsSystemFile(path) -- return self.add_library(library) -- -- def find_libraries(self, path, searched, addcb): -- snippets_debug("Finding in: " + path) -- -- if not os.path.isdir(path): -- return searched -- -- files = os.listdir(path) -- searched.append(path) -- -- for f in files: -- f = os.path.realpath(os.path.join(path, f)) -- -- # Determine what language this file provides snippets for -- if os.path.isfile(f): -- addcb(f) -- -- return searched -- -- def normalize_language(self, language): -- if language: -- return language.lower() -- -- return language -- -- def remove_container(self, language): -- for snippet in self.containers[language].snippets: -- if snippet.id in self.loaded_ids: -- self.loaded_ids.remove(snippet.id) -- -- if snippet.override in self.overridden: -- del self.overridden[snippet.override] -- -- del self.containers[language] -- -- def get_accel_group(self, language): -- language = self.normalize_language(language) -- container = self.container(language) -- -- self.ensure(language) -- return container.accel_group -- -- def save(self, language): -- language = self.normalize_language(language) -- -- if language in self.libraries: -- for library in self.libraries[language]: -- if isinstance(library, SnippetsUserFile): -- library.save() -- else: -- break -- -- def ref(self, language): -- language = self.normalize_language(language) -- -- snippets_debug('Ref:', language) -- self.container(language).ref() -- -- def unref(self, language): -- language = self.normalize_language(language) -- -- snippets_debug('Unref:', language) -- -- if language in self.containers: -- if not self.containers[language].unref() and \ -- language in self.libraries: -- -- for library in self.libraries[language]: -- library.unload() -- -- self.remove_container(language) -- -- def ensure(self, language): -- self.ensure_files() -- language = self.normalize_language(language) -- -- # Ensure language as well as the global snippets (None) -- for lang in (None, language): -- if lang in self.libraries: -- # Ensure the container exists -- self.container(lang) -- -- for library in self.libraries[lang]: -- library.ensure() -- -- def ensure_files(self): -- if self.loaded: -- return -- -- searched = [] -- searched = self.find_libraries(self.userdir, searched, \ -- self.add_user_library) -- -- for d in self.systemdirs: -- searched = self.find_libraries(d, searched, \ -- self.add_system_library) -+ break - -- self.loaded = True -+ def ref(self, language): -+ language = self.normalize_language(language) -+ -+ snippets_debug('Ref:', language) -+ self.container(language).ref() -+ -+ def unref(self, language): -+ language = self.normalize_language(language) -+ -+ snippets_debug('Unref:', language) -+ -+ if language in self.containers: -+ if not self.containers[language].unref() and \ -+ language in self.libraries: -+ -+ for library in self.libraries[language]: -+ library.unload() -+ -+ self.remove_container(language) -+ -+ def ensure(self, language): -+ self.ensure_files() -+ language = self.normalize_language(language) -+ -+ # Ensure language as well as the global snippets (None) -+ for lang in (None, language): -+ if lang in self.libraries: -+ # Ensure the container exists -+ self.container(lang) -+ -+ for library in self.libraries[lang]: -+ library.ensure() -+ -+ def ensure_files(self): -+ if self.loaded: -+ return -+ -+ searched = [] -+ searched = self.find_libraries(self.userdir, searched, \ -+ self.add_user_library) -+ -+ for d in self.systemdirs: -+ searched = self.find_libraries(d, searched, \ -+ self.add_system_library) -+ -+ self.loaded = True -+ -+ def valid_accelerator(self, keyval, mod): -+ mod &= Gtk.accelerator_get_default_mod_mask() -+ -+ return (mod and (Gdk.keyval_to_unicode(keyval) or \ -+ keyval in range(Gdk.KEY_F1, Gdk.KEY_F12 + 1))) -+ -+ def valid_tab_trigger(self, trigger): -+ if not trigger: -+ return True -+ -+ if trigger.isdigit(): -+ return False -+ -+ self.check_buffer.set_text(trigger) -+ -+ start, end = self.check_buffer.get_bounds() -+ text = self.check_buffer.get_text(start, end, False) -+ -+ s = start.copy() -+ e = end.copy() -+ -+ end.backward_word_start() -+ start.forward_word_end() -+ -+ return (s.equal(end) and e.equal(start)) or (len(text) == 1 and not (text.isalnum() or text.isspace())) -+ -+ # Snippet getters -+ # =============== -+ def _from_prop(self, prop, value, language=None): -+ self.ensure_files() -+ -+ result = [] -+ language = self.normalize_language(language) -+ -+ if not language in self.containers: -+ return [] -+ -+ self.ensure(language) -+ result = self.containers[language].from_prop(prop, value) -+ -+ if len(result) == 0 and language and None in self.containers: -+ result = self.containers[None].from_prop(prop, value) -+ -+ return result -+ -+ # Get snippets for a given language -+ def get_snippets(self, language=None): -+ self.ensure_files() -+ language = self.normalize_language(language) -+ -+ if not language in self.libraries: -+ return [] -+ -+ snippets = [] -+ self.ensure(language) -+ -+ return list(self.containers[language].snippets) -+ -+ # Get snippets for a given accelerator -+ def from_accelerator(self, accelerator, language=None): -+ return self._from_prop('accelerator', accelerator, language) -+ -+ # Get snippets for a given tag -+ def from_tag(self, tag, language=None): -+ return self._from_prop('tag', tag, language) -+ -+ # Get snippets for a given drop target -+ def from_drop_target(self, drop_target, language=None): -+ return self._from_prop('drop-targets', drop_target, language) - -- def valid_accelerator(self, keyval, mod): -- mod &= Gtk.accelerator_get_default_mod_mask() -- -- return (mod and (Gdk.keyval_to_unicode(keyval) or \ -- keyval in range(Gdk.KEY_F1, Gdk.KEY_F12 + 1))) -- -- def valid_tab_trigger(self, trigger): -- if not trigger: -- return True -- -- if trigger.isdigit(): -- return False -- -- self.check_buffer.set_text(trigger) -- -- start, end = self.check_buffer.get_bounds() -- text = self.check_buffer.get_text(start, end, False) -- -- s = start.copy() -- e = end.copy() -- -- end.backward_word_start() -- start.forward_word_end() -- -- return (s.equal(end) and e.equal(start)) or (len(text) == 1 and not (text.isalnum() or text.isspace())) -- -- # Snippet getters -- # =============== -- def _from_prop(self, prop, value, language=None): -- self.ensure_files() -- -- result = [] -- language = self.normalize_language(language) -- -- if not language in self.containers: -- return [] -- -- self.ensure(language) -- result = self.containers[language].from_prop(prop, value) -- -- if len(result) == 0 and language and None in self.containers: -- result = self.containers[None].from_prop(prop, value) -- -- return result -- -- # Get snippets for a given language -- def get_snippets(self, language=None): -- self.ensure_files() -- language = self.normalize_language(language) -- -- if not language in self.libraries: -- return [] -- -- snippets = [] -- self.ensure(language) -- -- return list(self.containers[language].snippets) -- -- # Get snippets for a given accelerator -- def from_accelerator(self, accelerator, language=None): -- return self._from_prop('accelerator', accelerator, language) -- -- # Get snippets for a given tag -- def from_tag(self, tag, language=None): -- return self._from_prop('tag', tag, language) -- -- # Get snippets for a given drop target -- def from_drop_target(self, drop_target, language=None): -- return self._from_prop('drop-targets', drop_target, language) -- --# ex:ts=8:et: -+# ex:ts=4:et: -diff --git a/plugins/snippets/snippets/Manager.py b/plugins/snippets/snippets/Manager.py -old mode 100755 -new mode 100644 -index f496b1c..9760fa7 ---- a/plugins/snippets/snippets/Manager.py -+++ b/plugins/snippets/snippets/Manager.py -@@ -30,1119 +30,1119 @@ from Document import Document - from LanguageManager import get_language_manager - - class Manager: -- NAME_COLUMN = 0 -- SORT_COLUMN = 1 -- LANG_COLUMN = 2 -- SNIPPET_COLUMN = 3 -- TARGET_URI = 105 -- -- model = None -- drag_icons = ('mate-mime-application-x-tarz', 'mate-package', 'package') -- default_export_name = _('Snippets archive') + '.tar.gz' -- dragging = False -- dnd_target_list = [Gtk.TargetEntry.new('text/uri-list', 0, TARGET_URI)] -- -- def __init__(self, datadir): -- self.datadir = datadir -- self.snippet = None -- self.dlg = None -- self._temp_export = None -- self.snippets_doc = None -- self.manager = None -- self.default_size = None -- -- self.key_press_id = 0 -- self.run() -- -- def get_language_snippets(self, path, name = None): -- library = Library() -- -- name = self.get_language(path) -- nodes = library.get_snippets(name) -- -- return nodes -- -- def add_new_snippet_node(self, parent): -- return self.model.append(parent, ('' + _('Add a new snippet...') + \ -- '', '', None, None)) -- -- def fill_language(self, piter, expand=True): -- # Remove all children -- child = self.model.iter_children(piter) -- -- while child and self.model.remove(child): -- True -- -- path = self.model.get_path(piter) -- nodes = self.get_language_snippets(path) -- language = self.get_language(path) -- -- Library().ref(language) -- -- if nodes: -- for node in nodes: -- self.add_snippet(piter, node) -- else: -- # Add node that tells there are no snippets currently -- self.add_new_snippet_node(piter) -+ NAME_COLUMN = 0 -+ SORT_COLUMN = 1 -+ LANG_COLUMN = 2 -+ SNIPPET_COLUMN = 3 -+ TARGET_URI = 105 -+ -+ model = None -+ drag_icons = ('mate-mime-application-x-tarz', 'mate-package', 'package') -+ default_export_name = _('Snippets archive') + '.tar.gz' -+ dragging = False -+ dnd_target_list = [Gtk.TargetEntry.new('text/uri-list', 0, TARGET_URI)] -+ -+ def __init__(self, datadir): -+ self.datadir = datadir -+ self.snippet = None -+ self.dlg = None -+ self._temp_export = None -+ self.snippets_doc = None -+ self.manager = None -+ self.default_size = None -+ -+ self.key_press_id = 0 -+ self.run() -+ -+ def get_language_snippets(self, path, name = None): -+ library = Library() -+ -+ name = self.get_language(path) -+ nodes = library.get_snippets(name) -+ -+ return nodes -+ -+ def add_new_snippet_node(self, parent): -+ return self.model.append(parent, ('' + _('Add a new snippet...') + \ -+ '', '', None, None)) -+ -+ def fill_language(self, piter, expand=True): -+ # Remove all children -+ child = self.model.iter_children(piter) -+ -+ while child and self.model.remove(child): -+ True -+ -+ path = self.model.get_path(piter) -+ nodes = self.get_language_snippets(path) -+ language = self.get_language(path) -+ -+ Library().ref(language) -+ -+ if nodes: -+ for node in nodes: -+ self.add_snippet(piter, node) -+ else: -+ # Add node that tells there are no snippets currently -+ self.add_new_snippet_node(piter) -+ -+ if expand: -+ self.tree_view.expand_row(path, False) -+ -+ def build_model(self, force_reload = False): -+ window = Pluma.App.get_default().get_active_window() -+ -+ if window: -+ view = window.get_active_view() -+ -+ if not view: -+ current_lang = None -+ else: -+ current_lang = view.get_buffer().get_language() -+ source_view = self['source_view_snippet'] - -- if expand: -- self.tree_view.expand_row(path, False) -+ else: -+ current_lang = None - -- def build_model(self, force_reload = False): -- window = Pluma.App.get_default().get_active_window() -- -- if window: -- view = window.get_active_view() -+ tree_view = self['tree_view_snippets'] -+ expand = None - -- if not view: -- current_lang = None -- else: -- current_lang = view.get_buffer().get_language() -- source_view = self['source_view_snippet'] -+ if not self.model or force_reload: -+ self.model = Gtk.TreeStore(str, str, GObject.Object, object) -+ self.model.set_sort_column_id(self.SORT_COLUMN, Gtk.SortType.ASCENDING) -+ manager = get_language_manager() -+ langs = [manager.get_language(x) for x in manager.get_language_ids()] -+ langs.sort(key=lambda x: x.get_name()) - -- else: -- current_lang = None -- -- tree_view = self['tree_view_snippets'] -- expand = None -- -- if not self.model or force_reload: -- self.model = Gtk.TreeStore(str, str, GObject.Object, object) -- self.model.set_sort_column_id(self.SORT_COLUMN, Gtk.SortType.ASCENDING) -- manager = get_language_manager() -- langs = [manager.get_language(x) for x in manager.get_language_ids()] -- langs.sort(key=lambda x: x.get_name()) -- -- piter = self.model.append(None, (_('Global'), '', None, None)) -- # Add dummy node -- self.model.append(piter, ('', '', None, None)) -- -- nm = None -- -- if current_lang: -- nm = current_lang.get_name() -- -- for lang in langs: -- name = lang.get_name() -- parent = self.model.append(None, (name, name, lang, None)) -- -- # Add dummy node -- self.model.append(parent, ('', '', None, None)) -- -- if (nm == name): -- expand = parent -- else: -- if current_lang: -- piter = self.model.get_iter_first() -- nm = current_lang.get_name() -- -- while piter: -- lang = self.model.get_value(piter, \ -- self.SORT_COLUMN) -- -- if lang == nm: -- expand = piter -- break; -- -- piter = self.model.iter_next(piter) -- -- tree_view.set_model(self.model) -- -- if not expand: -- expand = self.model.get_iter_first() -- -- tree_view.expand_row(self.model.get_path(expand), False) -- self.select_iter(expand) -- -- def get_cell_data_pixbuf_cb(self, column, cell, model, iter, data): -- snippet = model.get_value(iter, self.SNIPPET_COLUMN) -- -- if snippet and not snippet.valid: -- cell.set_property('stock-id', Gtk.STOCK_DIALOG_ERROR) -- else: -- cell.set_property('stock-id', None) -- -- cell.set_property('xalign', 1.0) -- -- def get_cell_data_cb(self, column, cell, model, iter, data): -- snippet = model.get_value(iter, self.SNIPPET_COLUMN) -- -- cell.set_property('editable', snippet != None) -- cell.set_property('markup', model.get_value(iter, self.NAME_COLUMN)) -- -- def on_tree_view_drag_data_get(self, widget, context, selection_data, info, time): -- gfile = Gio.file_new_for_path(self._temp_export) -- selection_data.set_uris([gfile.get_uri()]) -- -- def on_tree_view_drag_begin(self, widget, context): -- self.dragging = True -- -- if self._temp_export: -- shutil.rmtree(os.path.dirname(self._temp_export)) -- self._temp_export = None -- -- if self.dnd_name: -- Gtk.drag_set_icon_name(context, self.dnd_name, 0, 0) -- -- dirname = tempfile.mkdtemp() -- filename = os.path.join(dirname, self.default_export_name) -- -- # Generate temporary file name -- self.export_snippets(filename, False) -- self._temp_export = filename -- -- def on_tree_view_drag_end(self, widget, context): -- self.dragging = False -- -- def on_tree_view_drag_data_received(self, widget, context, x, y, selection, info, timestamp): -- uris = selection.get_uris() -- -- self.import_snippets(uris) -- -- def on_tree_view_drag_motion(self, widget, context, x, y, timestamp): -- # Return False if we are dragging -- if self.dragging: -- return False -- -- # Check uri target -- if not Gtk.targets_include_uri(context.targets): -- return False -- -- # Check action -- action = None -- if context.suggested_action == Gdk.DragAction.COPY: -- action = Gdk.DragAction.COPY -- else: -- for act in context.actions: -- if act == Gdk.DragAction.COPY: -- action = Gdk.DragAction.COPY -- break -- -- if action == Gdk.DragAction.COPY: -- context.drag_status(Gdk.DragAction.COPY, timestamp) -- return True -- else: -- return False -- -- def build_dnd(self): -- tv = self.tree_view -- -- # Set it as a drag source for exporting snippets -- tv.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, self.dnd_target_list, Gdk.DragAction.DEFAULT | Gdk.DragAction.COPY) -- -- # Set it as a drag destination for importing snippets -- tv.drag_dest_set(Gtk.DestDefaults.HIGHLIGHT | Gtk.DestDefaults.DROP, -- self.dnd_target_list, Gdk.DragAction.DEFAULT | Gdk.DragAction.COPY) -- -- tv.connect('drag_data_get', self.on_tree_view_drag_data_get) -- tv.connect('drag_begin', self.on_tree_view_drag_begin) -- tv.connect('drag_end', self.on_tree_view_drag_end) -- tv.connect('drag_data_received', self.on_tree_view_drag_data_received) -- tv.connect('drag_motion', self.on_tree_view_drag_motion) -- -- theme = Gtk.IconTheme.get_for_screen(tv.get_screen()) -- -- self.dnd_name = None -- for name in self.drag_icons: -- icon = theme.lookup_icon(name, Gtk.IconSize.DND, 0) -- -- if icon: -- self.dnd_name = name -- break -- -- def build_tree_view(self): -- self.tree_view = self['tree_view_snippets'] -- -- self.column = Gtk.TreeViewColumn(None) -- -- self.renderer = Gtk.CellRendererText() -- self.column.pack_start(self.renderer, False) -- self.column.set_cell_data_func(self.renderer, self.get_cell_data_cb, None) -- -- renderer = Gtk.CellRendererPixbuf() -- self.column.pack_start(renderer, True) -- self.column.set_cell_data_func(renderer, self.get_cell_data_pixbuf_cb, None) -- -- self.tree_view.append_column(self.column) -- -- self.renderer.connect('edited', self.on_cell_edited) -- self.renderer.connect('editing-started', self.on_cell_editing_started) -- -- selection = self.tree_view.get_selection() -- selection.set_mode(Gtk.SelectionMode.MULTIPLE) -- selection.connect('changed', self.on_tree_view_selection_changed) -- -- self.build_dnd() -- -- def build(self): -- self.builder = Gtk.Builder() -- self.builder.add_from_file(os.path.join(self.datadir, 'ui', 'snippets.ui')) -- -- handlers_dic = { -- 'on_dialog_snippets_response': self.on_dialog_snippets_response, -- 'on_dialog_snippets_destroy': self.on_dialog_snippets_destroy, -- 'on_button_new_snippet_clicked': self.on_button_new_snippet_clicked, -- 'on_button_import_snippets_clicked': self.on_button_import_snippets_clicked, -- 'on_button_export_snippets_clicked': self.on_button_export_snippets_clicked, -- 'on_button_remove_snippet_clicked': self.on_button_remove_snippet_clicked, -- 'on_entry_tab_trigger_focus_out': self.on_entry_tab_trigger_focus_out, -- 'on_entry_tab_trigger_changed': self.on_entry_tab_trigger_changed, -- 'on_entry_accelerator_focus_out': self.on_entry_accelerator_focus_out, -- 'on_entry_accelerator_focus_in': self.on_entry_accelerator_focus_in, -- 'on_entry_accelerator_key_press': self.on_entry_accelerator_key_press, -- 'on_source_view_snippet_focus_out': self.on_source_view_snippet_focus_out, -- 'on_tree_view_snippets_row_expanded': self.on_tree_view_snippets_row_expanded, -- 'on_tree_view_snippets_key_press': self.on_tree_view_snippets_key_press} -- -- self.builder.connect_signals(handlers_dic) -- -- self.build_tree_view() -- self.build_model() -- -- image = self['image_remove'] -- image.set_from_stock(Gtk.STOCK_REMOVE, Gtk.IconSize.SMALL_TOOLBAR) -+ piter = self.model.append(None, (_('Global'), '', None, None)) -+ # Add dummy node -+ self.model.append(piter, ('', '', None, None)) - -- source_view = self['source_view_snippet'] -- manager = get_language_manager() -- lang = manager.get_language('snippets') -- -- if lang: -- source_view.get_buffer().set_highlight_syntax(True) -- source_view.get_buffer().set_language(lang) -- self.snippets_doc = Document(None, source_view) -- -- combo = self['combo_drop_targets'] -- -- entry = combo.get_child() -- entry.connect('focus-out-event', self.on_entry_drop_targets_focus_out) -- entry.connect('drag-data-received', self.on_entry_drop_targets_drag_data_received) -- -- lst = entry.drag_dest_get_target_list() -- lst.add_uri_targets(self.TARGET_URI) -- -- self.dlg = self['dialog_snippets'] -- -- if self.default_size: -- self.dlg.set_default_size(*self.default_size) -- -- def __getitem__(self, key): -- return self.builder.get_object(key) -- -- def is_filled(self, piter): -- if not self.model.iter_has_child(piter): -- return True -- -- child = self.model.iter_children(piter) -- nm = self.model.get_value(child, self.NAME_COLUMN) -- lang = self.model.get_value(child, self.LANG_COLUMN) -- snippet = self.model.get_value(child, self.SNIPPET_COLUMN) -- -- return (lang or snippet or nm) -- -- def fill_if_needed(self, piter, expand=True): -- if not self.is_filled(piter): -- self.fill_language(piter, expand) -- -- def find_iter(self, parent, snippet): -- self.fill_if_needed(parent) -- piter = self.model.iter_children(parent) -- -- while (piter): -- node = self.model.get_value(piter, self.SNIPPET_COLUMN) -- -- if node == snippet.data: -- return piter -- -- piter = self.model.iter_next(piter) -- -- return None -- -- def selected_snippets_state(self): -- snippets = self.selected_snippets(False) -- override = False -- remove = False -- system = False -- -- for snippet in snippets: -- if not snippet: -- continue -- -- if snippet.is_override(): -- override = True -- elif snippet.can_modify(): -- remove = True -- else: -- system = True -- -- # No need to continue if both are found -- if override and remove: -- break -- -- return (override, remove, system) -- -- def update_buttons(self): -- button_remove = self['button_remove_snippet'] -- button_new = self['button_new_snippet'] -- image_remove = self['image_remove'] -- -- button_new.set_sensitive(self.language_path != None) -- override, remove, system = self.selected_snippets_state() -- -- if not (override ^ remove) or system: -- button_remove.set_sensitive(False) -- image_remove.set_from_stock(Gtk.STOCK_DELETE, Gtk.IconSize.BUTTON) -- else: -- button_remove.set_sensitive(True) -- -- if override: -- image_remove.set_from_stock(Gtk.STOCK_UNDO, Gtk.IconSize.BUTTON) -- tooltip = _('Revert selected snippet') -- else: -- image_remove.set_from_stock(Gtk.STOCK_DELETE, Gtk.IconSize.BUTTON) -- tooltip = _('Delete selected snippet') -- -- button_remove.set_tooltip_text(tooltip) -- -- def snippet_changed(self, piter = None): -- if piter: -- node = self.model.get_value(piter, self.SNIPPET_COLUMN) -- s = Snippet(node) -- else: -- s = self.snippet -- piter = self.find_iter(self.model.get_iter(self.language_path), s) -+ nm = None -+ -+ if current_lang: -+ nm = current_lang.get_name() -+ -+ for lang in langs: -+ name = lang.get_name() -+ parent = self.model.append(None, (name, name, lang, None)) -+ -+ # Add dummy node -+ self.model.append(parent, ('', '', None, None)) -+ -+ if (nm == name): -+ expand = parent -+ else: -+ if current_lang: -+ piter = self.model.get_iter_first() -+ nm = current_lang.get_name() -+ -+ while piter: -+ lang = self.model.get_value(piter, \ -+ self.SORT_COLUMN) -+ -+ if lang == nm: -+ expand = piter -+ break; -+ -+ piter = self.model.iter_next(piter) -+ -+ tree_view.set_model(self.model) -+ -+ if not expand: -+ expand = self.model.get_iter_first() -+ -+ tree_view.expand_row(self.model.get_path(expand), False) -+ self.select_iter(expand) -+ -+ def get_cell_data_pixbuf_cb(self, column, cell, model, iter, data): -+ snippet = model.get_value(iter, self.SNIPPET_COLUMN) -+ -+ if snippet and not snippet.valid: -+ cell.set_property('stock-id', Gtk.STOCK_DIALOG_ERROR) -+ else: -+ cell.set_property('stock-id', None) -+ -+ cell.set_property('xalign', 1.0) -+ -+ def get_cell_data_cb(self, column, cell, model, iter, data): -+ snippet = model.get_value(iter, self.SNIPPET_COLUMN) -+ -+ cell.set_property('editable', snippet != None) -+ cell.set_property('markup', model.get_value(iter, self.NAME_COLUMN)) -+ -+ def on_tree_view_drag_data_get(self, widget, context, selection_data, info, time): -+ gfile = Gio.file_new_for_path(self._temp_export) -+ selection_data.set_uris([gfile.get_uri()]) -+ -+ def on_tree_view_drag_begin(self, widget, context): -+ self.dragging = True -+ -+ if self._temp_export: -+ shutil.rmtree(os.path.dirname(self._temp_export)) -+ self._temp_export = None -+ -+ if self.dnd_name: -+ Gtk.drag_set_icon_name(context, self.dnd_name, 0, 0) -+ -+ dirname = tempfile.mkdtemp() -+ filename = os.path.join(dirname, self.default_export_name) -+ -+ # Generate temporary file name -+ self.export_snippets(filename, False) -+ self._temp_export = filename -+ -+ def on_tree_view_drag_end(self, widget, context): -+ self.dragging = False -+ -+ def on_tree_view_drag_data_received(self, widget, context, x, y, selection, info, timestamp): -+ uris = selection.get_uris() -+ -+ self.import_snippets(uris) -+ -+ def on_tree_view_drag_motion(self, widget, context, x, y, timestamp): -+ # Return False if we are dragging -+ if self.dragging: -+ return False -+ -+ # Check uri target -+ if not Gtk.targets_include_uri(context.targets): -+ return False -+ -+ # Check action -+ action = None -+ if context.suggested_action == Gdk.DragAction.COPY: -+ action = Gdk.DragAction.COPY -+ else: -+ for act in context.actions: -+ if act == Gdk.DragAction.COPY: -+ action = Gdk.DragAction.COPY -+ break -+ -+ if action == Gdk.DragAction.COPY: -+ context.drag_status(Gdk.DragAction.COPY, timestamp) -+ return True -+ else: -+ return False -+ -+ def build_dnd(self): -+ tv = self.tree_view -+ -+ # Set it as a drag source for exporting snippets -+ tv.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, self.dnd_target_list, Gdk.DragAction.DEFAULT | Gdk.DragAction.COPY) -+ -+ # Set it as a drag destination for importing snippets -+ tv.drag_dest_set(Gtk.DestDefaults.HIGHLIGHT | Gtk.DestDefaults.DROP, -+ self.dnd_target_list, Gdk.DragAction.DEFAULT | Gdk.DragAction.COPY) -+ -+ tv.connect('drag_data_get', self.on_tree_view_drag_data_get) -+ tv.connect('drag_begin', self.on_tree_view_drag_begin) -+ tv.connect('drag_end', self.on_tree_view_drag_end) -+ tv.connect('drag_data_received', self.on_tree_view_drag_data_received) -+ tv.connect('drag_motion', self.on_tree_view_drag_motion) -+ -+ theme = Gtk.IconTheme.get_for_screen(tv.get_screen()) -+ -+ self.dnd_name = None -+ for name in self.drag_icons: -+ icon = theme.lookup_icon(name, Gtk.IconSize.DND, 0) -+ -+ if icon: -+ self.dnd_name = name -+ break -+ -+ def build_tree_view(self): -+ self.tree_view = self['tree_view_snippets'] -+ -+ self.column = Gtk.TreeViewColumn(None) -+ -+ self.renderer = Gtk.CellRendererText() -+ self.column.pack_start(self.renderer, False) -+ self.column.set_cell_data_func(self.renderer, self.get_cell_data_cb, None) -+ -+ renderer = Gtk.CellRendererPixbuf() -+ self.column.pack_start(renderer, True) -+ self.column.set_cell_data_func(renderer, self.get_cell_data_pixbuf_cb, None) -+ -+ self.tree_view.append_column(self.column) -+ -+ self.renderer.connect('edited', self.on_cell_edited) -+ self.renderer.connect('editing-started', self.on_cell_editing_started) -+ -+ selection = self.tree_view.get_selection() -+ selection.set_mode(Gtk.SelectionMode.MULTIPLE) -+ selection.connect('changed', self.on_tree_view_selection_changed) -+ -+ self.build_dnd() -+ -+ def build(self): -+ self.builder = Gtk.Builder() -+ self.builder.add_from_file(os.path.join(self.datadir, 'ui', 'snippets.ui')) -+ -+ handlers_dic = { -+ 'on_dialog_snippets_response': self.on_dialog_snippets_response, -+ 'on_dialog_snippets_destroy': self.on_dialog_snippets_destroy, -+ 'on_button_new_snippet_clicked': self.on_button_new_snippet_clicked, -+ 'on_button_import_snippets_clicked': self.on_button_import_snippets_clicked, -+ 'on_button_export_snippets_clicked': self.on_button_export_snippets_clicked, -+ 'on_button_remove_snippet_clicked': self.on_button_remove_snippet_clicked, -+ 'on_entry_tab_trigger_focus_out': self.on_entry_tab_trigger_focus_out, -+ 'on_entry_tab_trigger_changed': self.on_entry_tab_trigger_changed, -+ 'on_entry_accelerator_focus_out': self.on_entry_accelerator_focus_out, -+ 'on_entry_accelerator_focus_in': self.on_entry_accelerator_focus_in, -+ 'on_entry_accelerator_key_press': self.on_entry_accelerator_key_press, -+ 'on_source_view_snippet_focus_out': self.on_source_view_snippet_focus_out, -+ 'on_tree_view_snippets_row_expanded': self.on_tree_view_snippets_row_expanded, -+ 'on_tree_view_snippets_key_press': self.on_tree_view_snippets_key_press} - -- if piter: -- nm = s.display() -- -- self.model.set_value(piter, self.NAME_COLUMN, nm) -- self.model.set_value(piter, self.SORT_COLUMN, nm) -- self.update_buttons() -- self.entry_tab_trigger_update_valid() -+ self.builder.connect_signals(handlers_dic) - -+ self.build_tree_view() -+ self.build_model() -+ -+ image = self['image_remove'] -+ image.set_from_stock(Gtk.STOCK_REMOVE, Gtk.IconSize.SMALL_TOOLBAR) -+ -+ source_view = self['source_view_snippet'] -+ manager = get_language_manager() -+ lang = manager.get_language('snippets') -+ -+ if lang: -+ source_view.get_buffer().set_highlight_syntax(True) -+ source_view.get_buffer().set_language(lang) -+ self.snippets_doc = Document(None, source_view) -+ -+ combo = self['combo_drop_targets'] -+ -+ entry = combo.get_child() -+ entry.connect('focus-out-event', self.on_entry_drop_targets_focus_out) -+ entry.connect('drag-data-received', self.on_entry_drop_targets_drag_data_received) -+ -+ lst = entry.drag_dest_get_target_list() -+ lst.add_uri_targets(self.TARGET_URI) -+ -+ self.dlg = self['dialog_snippets'] -+ -+ if self.default_size: -+ self.dlg.set_default_size(*self.default_size) -+ -+ def __getitem__(self, key): -+ return self.builder.get_object(key) -+ -+ def is_filled(self, piter): -+ if not self.model.iter_has_child(piter): -+ return True -+ -+ child = self.model.iter_children(piter) -+ nm = self.model.get_value(child, self.NAME_COLUMN) -+ lang = self.model.get_value(child, self.LANG_COLUMN) -+ snippet = self.model.get_value(child, self.SNIPPET_COLUMN) -+ -+ return (lang or snippet or nm) -+ -+ def fill_if_needed(self, piter, expand=True): -+ if not self.is_filled(piter): -+ self.fill_language(piter, expand) -+ -+ def find_iter(self, parent, snippet): -+ self.fill_if_needed(parent) -+ piter = self.model.iter_children(parent) -+ -+ while (piter): -+ node = self.model.get_value(piter, self.SNIPPET_COLUMN) -+ -+ if node == snippet.data: - return piter - -- def add_snippet(self, parent, snippet): -- piter = self.model.append(parent, ('', '', None, snippet)) -- -- return self.snippet_changed(piter) -+ piter = self.model.iter_next(piter) - -- def run(self): -- if not self.dlg: -- self.build() -- self.dlg.show() -+ return None -+ -+ def selected_snippets_state(self): -+ snippets = self.selected_snippets(False) -+ override = False -+ remove = False -+ system = False -+ -+ for snippet in snippets: -+ if not snippet: -+ continue -+ -+ if snippet.is_override(): -+ override = True -+ elif snippet.can_modify(): -+ remove = True -+ else: -+ system = True -+ -+ # No need to continue if both are found -+ if override and remove: -+ break -+ -+ return (override, remove, system) -+ -+ def update_buttons(self): -+ button_remove = self['button_remove_snippet'] -+ button_new = self['button_new_snippet'] -+ image_remove = self['image_remove'] -+ -+ button_new.set_sensitive(self.language_path != None) -+ override, remove, system = self.selected_snippets_state() -+ -+ if not (override ^ remove) or system: -+ button_remove.set_sensitive(False) -+ image_remove.set_from_stock(Gtk.STOCK_DELETE, Gtk.IconSize.BUTTON) -+ else: -+ button_remove.set_sensitive(True) -+ -+ if override: -+ image_remove.set_from_stock(Gtk.STOCK_UNDO, Gtk.IconSize.BUTTON) -+ tooltip = _('Revert selected snippet') -+ else: -+ image_remove.set_from_stock(Gtk.STOCK_DELETE, Gtk.IconSize.BUTTON) -+ tooltip = _('Delete selected snippet') -+ -+ button_remove.set_tooltip_text(tooltip) -+ -+ def snippet_changed(self, piter = None): -+ if piter: -+ node = self.model.get_value(piter, self.SNIPPET_COLUMN) -+ s = Snippet(node) -+ else: -+ s = self.snippet -+ piter = self.find_iter(self.model.get_iter(self.language_path), s) -+ -+ if piter: -+ nm = s.display() -+ -+ self.model.set_value(piter, self.NAME_COLUMN, nm) -+ self.model.set_value(piter, self.SORT_COLUMN, nm) -+ self.update_buttons() -+ self.entry_tab_trigger_update_valid() -+ -+ return piter -+ -+ def add_snippet(self, parent, snippet): -+ piter = self.model.append(parent, ('', '', None, snippet)) -+ -+ return self.snippet_changed(piter) -+ -+ def run(self): -+ if not self.dlg: -+ self.build() -+ self.dlg.show() -+ else: -+ self.build_model() -+ self.dlg.present() -+ -+ def snippet_from_iter(self, model, piter): -+ parent = model.iter_parent(piter) -+ -+ if parent: -+ return model.get_value(piter, self.SNIPPET_COLUMN) -+ else: -+ return None -+ -+ def language_snippets(self, model, parent, as_path=False): -+ self.fill_if_needed(parent, False) -+ piter = model.iter_children(parent) -+ snippets = [] -+ -+ if not piter: -+ return snippets -+ -+ while piter: -+ snippet = self.snippet_from_iter(model, piter) -+ -+ if snippet: -+ if as_path: -+ snippets.append(model.get_path(piter)) - else: -- self.build_model() -- self.dlg.present() -+ snippets.append(snippet) -+ -+ piter = model.iter_next(piter) -+ -+ return snippets - -- def snippet_from_iter(self, model, piter): -+ def selected_snippets(self, include_languages=True, as_path=False): -+ selection = self.tree_view.get_selection() -+ (model, paths) = selection.get_selected_rows() -+ snippets = [] -+ -+ if paths and len(paths) != 0: -+ for p in paths: -+ piter = model.get_iter(p) - parent = model.iter_parent(piter) -- -- if parent: -- return model.get_value(piter, self.SNIPPET_COLUMN) -- else: -- return None -- -- def language_snippets(self, model, parent, as_path=False): -- self.fill_if_needed(parent, False) -- piter = model.iter_children(parent) -- snippets = [] -- -+ - if not piter: -- return snippets -- -- while piter: -- snippet = self.snippet_from_iter(model, piter) -- -- if snippet: -- if as_path: -- snippets.append(model.get_path(piter)) -- else: -- snippets.append(snippet) -- -- piter = model.iter_next(piter) -- -- return snippets -- -- def selected_snippets(self, include_languages=True, as_path=False): -- selection = self.tree_view.get_selection() -- (model, paths) = selection.get_selected_rows() -- snippets = [] -- -- if paths and len(paths) != 0: -- for p in paths: -- piter = model.get_iter(p) -- parent = model.iter_parent(piter) -- -- if not piter: -- continue -- -- if parent: -- snippet = self.snippet_from_iter(model, piter) -- -- if not snippet: -- continue -- -- if as_path: -- snippets.append(p) -- else: -- snippets.append(snippet) -- elif include_languages: -- snippets += self.language_snippets(model, piter, as_path) -- -- return snippets -- -- def selected_snippet(self): -- selection = self.tree_view.get_selection() -- (model, paths) = selection.get_selected_rows() -- -- if len(paths) == 1: -- piter = model.get_iter(paths[0]) -- parent = model.iter_parent(piter) -- snippet = self.snippet_from_iter(model, piter) -- -- return parent, piter, snippet -- else: -- return None, None, None -+ continue - -- def selection_changed(self): -- if not self.snippet: -- sens = False -+ if parent: -+ snippet = self.snippet_from_iter(model, piter) - -- self['entry_tab_trigger'].set_text('') -- self['entry_accelerator'].set_text('') -- buf = self['source_view_snippet'].get_buffer() -- buf.begin_not_undoable_action() -- buf.set_text('') -- buf.end_not_undoable_action() -- self['combo_drop_targets'].get_child().set_text('') -+ if not snippet: -+ continue - -+ if as_path: -+ snippets.append(p) -+ else: -+ snippets.append(snippet) -+ elif include_languages: -+ snippets += self.language_snippets(model, piter, as_path) -+ -+ return snippets -+ -+ def selected_snippet(self): -+ selection = self.tree_view.get_selection() -+ (model, paths) = selection.get_selected_rows() -+ -+ if len(paths) == 1: -+ piter = model.get_iter(paths[0]) -+ parent = model.iter_parent(piter) -+ snippet = self.snippet_from_iter(model, piter) -+ -+ return parent, piter, snippet -+ else: -+ return None, None, None -+ -+ def selection_changed(self): -+ if not self.snippet: -+ sens = False -+ -+ self['entry_tab_trigger'].set_text('') -+ self['entry_accelerator'].set_text('') -+ buf = self['source_view_snippet'].get_buffer() -+ buf.begin_not_undoable_action() -+ buf.set_text('') -+ buf.end_not_undoable_action() -+ self['combo_drop_targets'].get_child().set_text('') -+ -+ else: -+ sens = True -+ -+ self['entry_tab_trigger'].set_text(self.snippet['tag']) -+ self['entry_accelerator'].set_text( \ -+ self.snippet.accelerator_display()) -+ self['combo_drop_targets'].get_child().set_text(', '.join(self.snippet['drop-targets'])) -+ -+ buf = self['source_view_snippet'].get_buffer() -+ buf.begin_not_undoable_action() -+ buf.set_text(self.snippet['text']) -+ buf.end_not_undoable_action() -+ -+ -+ for name in ['source_view_snippet', 'label_tab_trigger', -+ 'entry_tab_trigger', 'label_accelerator', -+ 'entry_accelerator', 'label_drop_targets', -+ 'combo_drop_targets']: -+ self[name].set_sensitive(sens) -+ -+ self.update_buttons() -+ -+ def select_iter(self, piter, unselect=True): -+ selection = self.tree_view.get_selection() -+ -+ if unselect: -+ selection.unselect_all() -+ -+ selection.select_iter(piter) -+ -+ self.tree_view.scroll_to_cell(self.model.get_path(piter), None, \ -+ True, 0.5, 0.5) -+ -+ def get_language(self, path): -+ if path.get_indices()[0] == 0: -+ return None -+ else: -+ return self.model.get_value(self.model.get_iter(path), \ -+ self.LANG_COLUMN).get_id() -+ -+ def new_snippet(self, properties=None): -+ if not self.language_path: -+ return None -+ -+ snippet = Library().new_snippet(self.get_language(self.language_path), properties) -+ -+ return Snippet(snippet) -+ -+ def get_dummy(self, parent): -+ if not self.model.iter_n_children(parent) == 1: -+ return None -+ -+ dummy = self.model.iter_children(parent) -+ -+ if not self.model.get_value(dummy, self.SNIPPET_COLUMN): -+ return dummy -+ -+ return None -+ -+ def unref_languages(self): -+ piter = self.model.get_iter_first() -+ library = Library() -+ -+ while piter: -+ if self.is_filled(piter): -+ language = self.get_language(self.model.get_path(piter)) -+ library.save(language) -+ -+ library.unref(language) -+ -+ piter = self.model.iter_next(piter) -+ -+ # Callbacks -+ def on_dialog_snippets_destroy(self, dlg): -+ # Remove temporary drag export -+ if self._temp_export: -+ shutil.rmtree(os.path.dirname(self._temp_export)) -+ self._temp_export = None -+ -+ if self.snippets_doc: -+ self.snippets_doc.stop() -+ -+ self.manager = None -+ self.unref_languages() -+ self.snippet = None -+ self.model = None -+ self.dlg = None -+ -+ def on_dialog_snippets_response(self, dlg, resp): -+ -+ alloc = dlg.get_allocation() -+ self.default_size = [alloc.width, alloc.height] -+ -+ if resp == Gtk.ResponseType.HELP: -+ Pluma.help_display(self, 'pluma', 'pluma-snippets-plugin') -+ return -+ -+ self.dlg.destroy() -+ -+ def on_cell_editing_started(self, renderer, editable, path): -+ piter = self.model.get_iter(path) -+ -+ if not self.model.iter_parent(piter): -+ renderer.stop_editing(True) -+ editable.remove_widget() -+ elif isinstance(editable, Gtk.Entry): -+ if self.snippet: -+ editable.set_text(self.snippet['description']) -+ else: -+ # This is the `Add a new snippet...` item -+ editable.set_text('') -+ -+ editable.grab_focus() -+ -+ def on_cell_edited(self, cell, path, new_text): -+ if new_text != '': -+ piter = self.model.get_iter(path) -+ node = self.model.get_value(piter, self.SNIPPET_COLUMN) -+ -+ if node: -+ if node == self.snippet.data: -+ s = self.snippet - else: -- sens = True -- -- self['entry_tab_trigger'].set_text(self.snippet['tag']) -- self['entry_accelerator'].set_text( \ -- self.snippet.accelerator_display()) -- self['combo_drop_targets'].get_child().set_text(', '.join(self.snippet['drop-targets'])) -- -- buf = self['source_view_snippet'].get_buffer() -- buf.begin_not_undoable_action() -- buf.set_text(self.snippet['text']) -- buf.end_not_undoable_action() -- -- -- for name in ['source_view_snippet', 'label_tab_trigger', -- 'entry_tab_trigger', 'label_accelerator', -- 'entry_accelerator', 'label_drop_targets', -- 'combo_drop_targets']: -- self[name].set_sensitive(sens) -- -- self.update_buttons() -- -- def select_iter(self, piter, unselect=True): -- selection = self.tree_view.get_selection() -- -- if unselect: -- selection.unselect_all() -- -- selection.select_iter(piter) -- -- self.tree_view.scroll_to_cell(self.model.get_path(piter), None, \ -- True, 0.5, 0.5) -- -- def get_language(self, path): -- if path.get_indices()[0] == 0: -- return None -- else: -- return self.model.get_value(self.model.get_iter(path), \ -- self.LANG_COLUMN).get_id() -- -- def new_snippet(self, properties=None): -- if not self.language_path: -- return None -- -- snippet = Library().new_snippet(self.get_language(self.language_path), properties) -- -- return Snippet(snippet) -- -- def get_dummy(self, parent): -- if not self.model.iter_n_children(parent) == 1: -- return None -- -- dummy = self.model.iter_children(parent) -- -- if not self.model.get_value(dummy, self.SNIPPET_COLUMN): -- return dummy -- -- return None -- -- def unref_languages(self): -- piter = self.model.get_iter_first() -- library = Library() -- -- while piter: -- if self.is_filled(piter): -- language = self.get_language(self.model.get_path(piter)) -- library.save(language) -- -- library.unref(language) -- -- piter = self.model.iter_next(piter) -- -- # Callbacks -- def on_dialog_snippets_destroy(self, dlg): -- # Remove temporary drag export -- if self._temp_export: -- shutil.rmtree(os.path.dirname(self._temp_export)) -- self._temp_export = None -- -- if self.snippets_doc: -- self.snippets_doc.stop() -- -- self.manager = None -- self.unref_languages() -- self.snippet = None -- self.model = None -- self.dlg = None -- -- def on_dialog_snippets_response(self, dlg, resp): -- -- alloc = dlg.get_allocation() -- self.default_size = [alloc.width, alloc.height] -- -- if resp == Gtk.ResponseType.HELP: -- Pluma.help_display(self, 'pluma', 'pluma-snippets-plugin') -- return -- -- self.dlg.destroy() -- -- def on_cell_editing_started(self, renderer, editable, path): -- piter = self.model.get_iter(path) -- -- if not self.model.iter_parent(piter): -- renderer.stop_editing(True) -- editable.remove_widget() -- elif isinstance(editable, Gtk.Entry): -- if self.snippet: -- editable.set_text(self.snippet['description']) -- else: -- # This is the `Add a new snippet...` item -- editable.set_text('') -- -- editable.grab_focus() -- -- def on_cell_edited(self, cell, path, new_text): -- if new_text != '': -- piter = self.model.get_iter(path) -- node = self.model.get_value(piter, self.SNIPPET_COLUMN) -- -- if node: -- if node == self.snippet.data: -- s = self.snippet -- else: -- s = Snippet(node) -- -- s['description'] = new_text -- self.snippet_changed(piter) -- self.select_iter(piter) -- else: -- # This is the `Add a new snippet...` item -- # We create a new snippet -- snippet = self.new_snippet({'description': new_text}) -- -- if snippet: -- self.model.set_value(piter, self.SNIPPET_COLUMN, snippet.data) -- self.snippet_changed(piter) -- self.snippet = snippet -- self.selection_changed() -- -- def on_entry_accelerator_focus_out(self, entry, event): -- if not self.snippet: -- return -- -- entry.set_text(self.snippet.accelerator_display()) -- -- def entry_tab_trigger_update_valid(self): -- entry = self['entry_tab_trigger'] -- text = entry.get_text() -- -- if text and not Library().valid_tab_trigger(text): -- img = self['image_tab_trigger'] -- img.set_from_stock(Gtk.STOCK_DIALOG_ERROR, Gtk.IconSize.BUTTON) -- img.show() -- -- #self['hbox_tab_trigger'].set_spacing(3) -- tip = _('This is not a valid Tab trigger. Triggers can either contain letters or a single (non-alphanumeric) character like: {, [, etc.') -- -- entry.set_tooltip_text(tip) -- img.set_tooltip_text(tip) -- else: -- self['image_tab_trigger'].hide() -- #self['hbox_tab_trigger'].set_spacing(0) -- entry.set_tooltip_text(_('Single word the snippet is activated with after pressing Tab')) -- -- return False -+ s = Snippet(node) - -- def on_entry_tab_trigger_focus_out(self, entry, event): -- if not self.snippet: -- return -- -- text = entry.get_text() -- -- # save tag -- self.snippet['tag'] = text -- self.snippet_changed() -- -- def on_entry_drop_targets_focus_out(self, entry, event): -- if not self.snippet: -- return -- -- text = entry.get_text() -- -- # save drop targets -- self.snippet['drop-targets'] = text -- self.snippet_changed() -- -- def on_entry_tab_trigger_changed(self, entry): -- self.entry_tab_trigger_update_valid() -- -- def on_source_view_snippet_focus_out(self, source_view, event): -- if not self.snippet: -- return -- -- buf = source_view.get_buffer() -- text = buf.get_text(buf.get_start_iter(), \ -- buf.get_end_iter(), False) -- -- self.snippet['text'] = text -- self.snippet_changed() -- -- def on_button_new_snippet_clicked(self, button): -- snippet = self.new_snippet() -- -- if not snippet: -- return -- -- parent = self.model.get_iter(self.language_path) -- path = self.model.get_path(parent) -- -- dummy = self.get_dummy(parent) -- -- if dummy: -- # Remove the dummy -- self.model.remove(dummy) -- -- # Add the snippet -- piter = self.add_snippet(parent, snippet.data) -+ s['description'] = new_text -+ self.snippet_changed(piter) - self.select_iter(piter) -+ else: -+ # This is the `Add a new snippet...` item -+ # We create a new snippet -+ snippet = self.new_snippet({'description': new_text}) - -- if not self.tree_view.row_expanded(path): -- self.tree_view.expand_row(path, False) -- self.select_iter(piter) -- -- self.tree_view.grab_focus() -- -- path = self.model.get_path(piter) -- self.tree_view.set_cursor(path, self.column, True) -- -- def file_filter(self, name, pattern): -- fil = Gtk.FileFilter() -- fil.set_name(name) -- -- for p in pattern: -- fil.add_pattern(p) -- -- return fil -- -- def import_snippets(self, filenames): -- success = True -- -- for filename in filenames: -- if not Pluma.utils_uri_has_file_scheme(filename): -- continue -- -- # Remove file:// -- gfile = Gio.file_new_for_uri(filename) -- filename = gfile.get_path() -- -- importer = Importer(filename) -- error = importer.run() -- -- if error: -- message = _('The following error occurred while importing: %s') % error -- success = False -- message_dialog(self.dlg, Gtk.MessageType.ERROR, message) -- -- self.build_model(True) -- -- if success: -- message = _('Import successfully completed') -- message_dialog(self.dlg, Gtk.MessageType.INFO, message) -- -- def on_import_response(self, dialog, response): -- if response == Gtk.ResponseType.CANCEL or response == Gtk.ResponseType.CLOSE: -- dialog.destroy() -- return -- -- f = dialog.get_uris() -- dialog.destroy() -- -- self.import_snippets(f) -- -- def on_button_import_snippets_clicked(self, button): -- dlg = Gtk.FileChooserDialog(parent=self.dlg, title=_("Import snippets"), -- action=Gtk.FileChooserAction.OPEN, -- buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, -- Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) -- -- dlg.add_filter(self.file_filter(_('All supported archives'), ('*.gz','*.bz2','*.tar', '*.xml'))) -- dlg.add_filter(self.file_filter(_('Gzip compressed archive'), ('*.tar.gz',))) -- dlg.add_filter(self.file_filter(_('Bzip2 compressed archive'), ('*.tar.bz2',))) -- dlg.add_filter(self.file_filter(_('Single snippets file'), ('*.xml',))) -- dlg.add_filter(self.file_filter(_('All files'), '*')) -- -- dlg.connect('response', self.on_import_response) -- dlg.set_local_only(True) -- -- dlg.show() -- -- def export_snippets_real(self, filename, snippets, show_dialogs=True): -- export = Exporter(filename, snippets) -- error = export.run() -- -- if error: -- message = _('The following error occurred while exporting: %s') % error -- msgtype = Gtk.MessageType.ERROR -- retval = False -- else: -- message = _('Export successfully completed') -- msgtype = Gtk.MessageType.INFO -- retval = True -- -- if show_dialogs: -- message_dialog(self.dlg, msgtype, message) -- -- return retval -- -- def on_export_response(self, dialog, response): -- filename = dialog.get_filename() -- snippets = dialog._export_snippets -- -- dialog.destroy() -- -- if response != Gtk.ResponseType.OK: -- return -- -- self.export_snippets_real(filename, snippets); -- -- def export_snippets(self, filename=None, show_dialogs=True): -- snippets = self.selected_snippets() -- -- if not snippets or len(snippets) == 0: -- return False -- -- usersnippets = [] -- systemsnippets = [] -- -- # Iterate through snippets and look for system snippets -- for snippet in snippets: -- if snippet.can_modify(): -- usersnippets.append(snippet) -- else: -- systemsnippets.append(snippet) -- -- export_snippets = snippets -- -- if len(systemsnippets) != 0 and show_dialogs: -- # Ask if system snippets should also be exported -- message = _('Do you want to include selected system snippets in your export?') -- mes = Gtk.MessageDialog(flags=Gtk.DialogFlags.MODAL, -- type=Gtk.MessageType.QUESTION, -- buttons=Gtk.ButtonsType.YES_NO, -- message_format=message) -- mes.set_property('use-markup', True) -- resp = mes.run() -- mes.destroy() -- -- if resp == Gtk.ResponseType.NO: -- export_snippets = usersnippets -- elif resp != Gtk.ResponseType.YES: -- return False -- -- if len(export_snippets) == 0 and show_dialogs: -- message = _('There are no snippets selected to be exported') -- message_dialog(self.dlg, Gtk.MessageType.INFO, message) -- return False -- -- if not filename: -- dlg = Gtk.FileChooserDialog(parent=self.dlg, title=_('Export snippets'), -- action=Gtk.FileChooserAction.SAVE, -- buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, -- Gtk.STOCK_SAVE, Gtk.ResponseType.OK)) -- -- dlg._export_snippets = export_snippets -- dlg.add_filter(self.file_filter(_('All supported archives'), ('*.gz','*.bz2','*.tar'))) -- dlg.add_filter(self.file_filter(_('Gzip compressed archive'), ('*.tar.gz',))) -- dlg.add_filter(self.file_filter(_('Bzip2 compressed archive'), ('*.tar.bz2',))) -- -- dlg.add_filter(self.file_filter(_('All files'), '*')) -- dlg.set_do_overwrite_confirmation(True) -- dlg.set_current_name(self.default_export_name) -- -- dlg.connect('response', self.on_export_response) -- dlg.set_local_only(True) -- -- dlg.show() -- return True -- else: -- return self.export_snippets_real(filename, export_snippets, show_dialogs) -- -- def on_button_export_snippets_clicked(self, button): -- snippets = self.selected_snippets() -- -- if not snippets or len(snippets) == 0: -- return -- -- usersnippets = [] -- systemsnippets = [] -- -- # Iterate through snippets and look for system snippets -- for snippet in snippets: -- if snippet.can_modify(): -- usersnippets.append(snippet) -- else: -- systemsnippets.append(snippet) -- -- dlg = Gtk.FileChooserDialog(parent=self.dlg, title=_('Export snippets'), -- action=Gtk.FileChooserAction.SAVE, -- buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, -- Gtk.STOCK_SAVE, Gtk.ResponseType.OK)) -- -- dlg._export_snippets = snippets -- -- if len(systemsnippets) != 0: -- # Ask if system snippets should also be exported -- message = _('Do you want to include selected system snippets in your export?') -- mes = Gtk.MessageDialog(flags=Gtk.DialogFlags.MODAL, -- type=Gtk.MessageType.QUESTION, -- buttons=Gtk.ButtonsType.YES_NO, -- message_format=message) -- mes.set_property('use-markup', True) -- resp = mes.run() -- mes.destroy() -- -- if resp == Gtk.ResponseType.NO: -- dlg._export_snippets = usersnippets -- elif resp != Gtk.ResponseType.YES: -- dlg.destroy() -- return -- -- if len(dlg._export_snippets) == 0: -- dlg.destroy() -- -- message = _('There are no snippets selected to be exported') -- message_dialog(self.dlg, Gtk.MessageType.INFO, message) -- return -- -- dlg.add_filter(self.file_filter(_('All supported archives'), ('*.gz','*.bz2','*.tar'))) -- dlg.add_filter(self.file_filter(_('Gzip compressed archive'), ('*.tar.gz',))) -- dlg.add_filter(self.file_filter(_('Bzip2 compressed archive'), ('*.tar.bz2',))) -- -- dlg.add_filter(self.file_filter(_('All files'), '*')) -- dlg.set_do_overwrite_confirmation(True) -- dlg.set_current_name(self.default_export_name) -- -- dlg.connect('response', self.on_export_response) -- dlg.set_local_only(True) -- -- dlg.show() -- -- def remove_snippet_revert(self, path, piter): -- node = self.snippet_from_iter(self.model, piter) -- Library().revert_snippet(node) -- -- return piter -- -- def remove_snippet_delete(self, path, piter): -- node = self.snippet_from_iter(self.model, piter) -- parent = self.model.iter_parent(piter) -- -- Library().remove_snippet(node) -- idx = path.get_indices() -- -- if self.model.remove(piter): -- return piter -- elif idx[-1] != 0: -- self.select_iter(self.model.get_iter((idx[0], idx[1] - 1))) -- else: -- dummy = self.add_new_snippet_node(parent) -- self.tree_view.expand_row(self.model.get_path(parent), False) -- return dummy -- -- def on_button_remove_snippet_clicked(self, button): -- override, remove, system = self.selected_snippets_state() -- -- if not (override ^ remove) or system: -- return -- -- paths = self.selected_snippets(include_languages=False, as_path=True) -- -- if override: -- action = self.remove_snippet_revert -- else: -- action = self.remove_snippet_delete -- -- # Remove selection -- self.tree_view.get_selection().unselect_all() -- -- # Create tree row references -- references = [] -- for path in paths: -- references.append(Gtk.TreeRowReference(self.model, path)) -- -- # Remove/revert snippets -- select = None -- for reference in references: -- path = reference.get_path() -- piter = self.model.get_iter(path) -- -- res = action(path, piter) -- -- if res: -- select = res -- -- if select: -- self.select_iter(select) -- -- self.selection_changed() -- -- def set_accelerator(self, keyval, mod): -- accelerator = Gtk.accelerator_name(keyval, mod) -- self.snippet['accelerator'] = accelerator -- -- return True -- -- def on_entry_accelerator_key_press(self, entry, event): -- source_view = self['source_view_snippet'] -+ if snippet: -+ self.model.set_value(piter, self.SNIPPET_COLUMN, snippet.data) -+ self.snippet_changed(piter) -+ self.snippet = snippet -+ self.selection_changed() - -- if event.keyval == Gdk.keyval_from_name('Escape'): -- # Reset -- entry.set_text(self.snippet.accelerator_display()) -- self.tree_view.grab_focus() -- -- return True -- elif event.keyval == Gdk.keyval_from_name('Delete') or \ -- event.keyval == Gdk.keyval_from_name('BackSpace'): -- # Remove the accelerator -- entry.set_text('') -- self.snippet['accelerator'] = '' -- self.tree_view.grab_focus() -- -- self.snippet_changed() -- return True -- elif Library().valid_accelerator(event.keyval, event.state): -- # New accelerator -- self.set_accelerator(event.keyval, \ -- event.state & Gtk.accelerator_get_default_mod_mask()) -- entry.set_text(self.snippet.accelerator_display()) -- self.snippet_changed() -- self.tree_view.grab_focus() -+ def on_entry_accelerator_focus_out(self, entry, event): -+ if not self.snippet: -+ return - -- else: -- return True -- -- def on_entry_accelerator_focus_in(self, entry, event): -- if self.snippet['accelerator']: -- entry.set_text(_('Type a new shortcut, or press Backspace to clear')) -- else: -- entry.set_text(_('Type a new shortcut')) -- -- def update_language_path(self): -- model, paths = self.tree_view.get_selection().get_selected_rows() -- -- # Check if all have the same language parent -- current_parent = None -+ entry.set_text(self.snippet.accelerator_display()) - -- for path in paths: -- piter = model.get_iter(path) -- parent = model.iter_parent(piter) -- -- if parent: -- path = model.get_path(parent) -- -- if current_parent != None and current_parent != path: -- current_parent = None -- break -- else: -- current_parent = path -- -- self.language_path = current_parent -- -- def on_tree_view_selection_changed(self, selection): -- parent, piter, node = self.selected_snippet() -- -- if self.snippet: -- self.on_entry_tab_trigger_focus_out(self['entry_tab_trigger'], -- None) -- self.on_source_view_snippet_focus_out(self['source_view_snippet'], -- None) -- self.on_entry_drop_targets_focus_out(self['combo_drop_targets'].get_child(), -- None) -- -- self.update_language_path() -- -- if node: -- self.snippet = Snippet(node) -- else: -- self.snippet = None -+ def entry_tab_trigger_update_valid(self): -+ entry = self['entry_tab_trigger'] -+ text = entry.get_text() -+ -+ if text and not Library().valid_tab_trigger(text): -+ img = self['image_tab_trigger'] -+ img.set_from_stock(Gtk.STOCK_DIALOG_ERROR, Gtk.IconSize.BUTTON) -+ img.show() -+ -+ #self['hbox_tab_trigger'].set_spacing(3) -+ tip = _('This is not a valid Tab trigger. Triggers can either contain letters or a single (non-alphanumeric) character like: {, [, etc.') -+ -+ entry.set_tooltip_text(tip) -+ img.set_tooltip_text(tip) -+ else: -+ self['image_tab_trigger'].hide() -+ #self['hbox_tab_trigger'].set_spacing(0) -+ entry.set_tooltip_text(_('Single word the snippet is activated with after pressing Tab')) -+ -+ return False -+ -+ def on_entry_tab_trigger_focus_out(self, entry, event): -+ if not self.snippet: -+ return -+ -+ text = entry.get_text() -+ -+ # save tag -+ self.snippet['tag'] = text -+ self.snippet_changed() -+ -+ def on_entry_drop_targets_focus_out(self, entry, event): -+ if not self.snippet: -+ return -+ -+ text = entry.get_text() -+ -+ # save drop targets -+ self.snippet['drop-targets'] = text -+ self.snippet_changed() -+ -+ def on_entry_tab_trigger_changed(self, entry): -+ self.entry_tab_trigger_update_valid() -+ -+ def on_source_view_snippet_focus_out(self, source_view, event): -+ if not self.snippet: -+ return -+ -+ buf = source_view.get_buffer() -+ text = buf.get_text(buf.get_start_iter(), \ -+ buf.get_end_iter(), False) -+ -+ self.snippet['text'] = text -+ self.snippet_changed() -+ -+ def on_button_new_snippet_clicked(self, button): -+ snippet = self.new_snippet() -+ -+ if not snippet: -+ return -+ -+ parent = self.model.get_iter(self.language_path) -+ path = self.model.get_path(parent) -+ -+ dummy = self.get_dummy(parent) -+ -+ if dummy: -+ # Remove the dummy -+ self.model.remove(dummy) -+ -+ # Add the snippet -+ piter = self.add_snippet(parent, snippet.data) -+ self.select_iter(piter) -+ -+ if not self.tree_view.row_expanded(path): -+ self.tree_view.expand_row(path, False) -+ self.select_iter(piter) -+ -+ self.tree_view.grab_focus() -+ -+ path = self.model.get_path(piter) -+ self.tree_view.set_cursor(path, self.column, True) -+ -+ def file_filter(self, name, pattern): -+ fil = Gtk.FileFilter() -+ fil.set_name(name) -+ -+ for p in pattern: -+ fil.add_pattern(p) -+ -+ return fil -+ -+ def import_snippets(self, filenames): -+ success = True -+ -+ for filename in filenames: -+ if not Pluma.utils_uri_has_file_scheme(filename): -+ continue -+ -+ # Remove file:// -+ gfile = Gio.file_new_for_uri(filename) -+ filename = gfile.get_path() -+ -+ importer = Importer(filename) -+ error = importer.run() -+ -+ if error: -+ message = _('The following error occurred while importing: %s') % error -+ success = False -+ message_dialog(self.dlg, Gtk.MessageType.ERROR, message) -+ -+ self.build_model(True) -+ -+ if success: -+ message = _('Import successfully completed') -+ message_dialog(self.dlg, Gtk.MessageType.INFO, message) - -- self.selection_changed() -+ def on_import_response(self, dialog, response): -+ if response == Gtk.ResponseType.CANCEL or response == Gtk.ResponseType.CLOSE: -+ dialog.destroy() -+ return - -- def iter_after(self, target, after): -- if not after: -- return True -+ f = dialog.get_uris() -+ dialog.destroy() - -- tp = self.model.get_path(target) -- ap = self.model.get_path(after) -- -- if tp[0] > ap[0] or (tp[0] == ap[0] and (len(ap) == 1 or tp[1] > ap[1])): -- return True -- -+ self.import_snippets(f) -+ -+ def on_button_import_snippets_clicked(self, button): -+ dlg = Gtk.FileChooserDialog(parent=self.dlg, title=_("Import snippets"), -+ action=Gtk.FileChooserAction.OPEN, -+ buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, -+ Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) -+ -+ dlg.add_filter(self.file_filter(_('All supported archives'), ('*.gz','*.bz2','*.tar', '*.xml'))) -+ dlg.add_filter(self.file_filter(_('Gzip compressed archive'), ('*.tar.gz',))) -+ dlg.add_filter(self.file_filter(_('Bzip2 compressed archive'), ('*.tar.bz2',))) -+ dlg.add_filter(self.file_filter(_('Single snippets file'), ('*.xml',))) -+ dlg.add_filter(self.file_filter(_('All files'), '*')) -+ -+ dlg.connect('response', self.on_import_response) -+ dlg.set_local_only(True) -+ -+ dlg.show() -+ -+ def export_snippets_real(self, filename, snippets, show_dialogs=True): -+ export = Exporter(filename, snippets) -+ error = export.run() -+ -+ if error: -+ message = _('The following error occurred while exporting: %s') % error -+ msgtype = Gtk.MessageType.ERROR -+ retval = False -+ else: -+ message = _('Export successfully completed') -+ msgtype = Gtk.MessageType.INFO -+ retval = True -+ -+ if show_dialogs: -+ message_dialog(self.dlg, msgtype, message) -+ -+ return retval -+ -+ def on_export_response(self, dialog, response): -+ filename = dialog.get_filename() -+ snippets = dialog._export_snippets -+ -+ dialog.destroy() -+ -+ if response != Gtk.ResponseType.OK: -+ return -+ -+ self.export_snippets_real(filename, snippets); -+ -+ def export_snippets(self, filename=None, show_dialogs=True): -+ snippets = self.selected_snippets() -+ -+ if not snippets or len(snippets) == 0: -+ return False -+ -+ usersnippets = [] -+ systemsnippets = [] -+ -+ # Iterate through snippets and look for system snippets -+ for snippet in snippets: -+ if snippet.can_modify(): -+ usersnippets.append(snippet) -+ else: -+ systemsnippets.append(snippet) -+ -+ export_snippets = snippets -+ -+ if len(systemsnippets) != 0 and show_dialogs: -+ # Ask if system snippets should also be exported -+ message = _('Do you want to include selected system snippets in your export?') -+ mes = Gtk.MessageDialog(flags=Gtk.DialogFlags.MODAL, -+ type=Gtk.MessageType.QUESTION, -+ buttons=Gtk.ButtonsType.YES_NO, -+ message_format=message) -+ mes.set_property('use-markup', True) -+ resp = mes.run() -+ mes.destroy() -+ -+ if resp == Gtk.ResponseType.NO: -+ export_snippets = usersnippets -+ elif resp != Gtk.ResponseType.YES: - return False -- -- def on_tree_view_snippets_key_press(self, treeview, event): -- if event.keyval == Gdk.keyval_from_name('Delete'): -- self.on_button_remove_snippet_clicked(None) -- return True -- -- def on_tree_view_snippets_row_expanded(self, treeview, piter, path): -- # Check if it is already filled -- self.fill_if_needed(piter) -- self.select_iter(piter) -- -- def on_entry_drop_targets_drag_data_received(self, entry, context, x, y, selection_data, info, timestamp): -- uris = drop_get_uris(selection_data) -- -- if not uris: -- return -- -- if entry.get_text(): -- mimes = [entry.get_text()] -- else: -- mimes = [] -- -- for uri in uris: -- try: -- mime = Gio.content_type_guess(uri) -- except: -- mime = None -- -- if mime: -- mimes.append(mime) -- -- entry.set_text(', '.join(mimes)) -- self.on_entry_drop_targets_focus_out(entry, None) -- context.finish(True, False, timestamp) -- -- entry.stop_emission('drag_data_received') --# ex:ts=8:et: -+ -+ if len(export_snippets) == 0 and show_dialogs: -+ message = _('There are no snippets selected to be exported') -+ message_dialog(self.dlg, Gtk.MessageType.INFO, message) -+ return False -+ -+ if not filename: -+ dlg = Gtk.FileChooserDialog(parent=self.dlg, title=_('Export snippets'), -+ action=Gtk.FileChooserAction.SAVE, -+ buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, -+ Gtk.STOCK_SAVE, Gtk.ResponseType.OK)) -+ -+ dlg._export_snippets = export_snippets -+ dlg.add_filter(self.file_filter(_('All supported archives'), ('*.gz','*.bz2','*.tar'))) -+ dlg.add_filter(self.file_filter(_('Gzip compressed archive'), ('*.tar.gz',))) -+ dlg.add_filter(self.file_filter(_('Bzip2 compressed archive'), ('*.tar.bz2',))) -+ -+ dlg.add_filter(self.file_filter(_('All files'), '*')) -+ dlg.set_do_overwrite_confirmation(True) -+ dlg.set_current_name(self.default_export_name) -+ -+ dlg.connect('response', self.on_export_response) -+ dlg.set_local_only(True) -+ -+ dlg.show() -+ return True -+ else: -+ return self.export_snippets_real(filename, export_snippets, show_dialogs) -+ -+ def on_button_export_snippets_clicked(self, button): -+ snippets = self.selected_snippets() -+ -+ if not snippets or len(snippets) == 0: -+ return -+ -+ usersnippets = [] -+ systemsnippets = [] -+ -+ # Iterate through snippets and look for system snippets -+ for snippet in snippets: -+ if snippet.can_modify(): -+ usersnippets.append(snippet) -+ else: -+ systemsnippets.append(snippet) -+ -+ dlg = Gtk.FileChooserDialog(parent=self.dlg, title=_('Export snippets'), -+ action=Gtk.FileChooserAction.SAVE, -+ buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, -+ Gtk.STOCK_SAVE, Gtk.ResponseType.OK)) -+ -+ dlg._export_snippets = snippets -+ -+ if len(systemsnippets) != 0: -+ # Ask if system snippets should also be exported -+ message = _('Do you want to include selected system snippets in your export?') -+ mes = Gtk.MessageDialog(flags=Gtk.DialogFlags.MODAL, -+ type=Gtk.MessageType.QUESTION, -+ buttons=Gtk.ButtonsType.YES_NO, -+ message_format=message) -+ mes.set_property('use-markup', True) -+ resp = mes.run() -+ mes.destroy() -+ -+ if resp == Gtk.ResponseType.NO: -+ dlg._export_snippets = usersnippets -+ elif resp != Gtk.ResponseType.YES: -+ dlg.destroy() -+ return -+ -+ if len(dlg._export_snippets) == 0: -+ dlg.destroy() -+ -+ message = _('There are no snippets selected to be exported') -+ message_dialog(self.dlg, Gtk.MessageType.INFO, message) -+ return -+ -+ dlg.add_filter(self.file_filter(_('All supported archives'), ('*.gz','*.bz2','*.tar'))) -+ dlg.add_filter(self.file_filter(_('Gzip compressed archive'), ('*.tar.gz',))) -+ dlg.add_filter(self.file_filter(_('Bzip2 compressed archive'), ('*.tar.bz2',))) -+ -+ dlg.add_filter(self.file_filter(_('All files'), '*')) -+ dlg.set_do_overwrite_confirmation(True) -+ dlg.set_current_name(self.default_export_name) -+ -+ dlg.connect('response', self.on_export_response) -+ dlg.set_local_only(True) -+ -+ dlg.show() -+ -+ def remove_snippet_revert(self, path, piter): -+ node = self.snippet_from_iter(self.model, piter) -+ Library().revert_snippet(node) -+ -+ return piter -+ -+ def remove_snippet_delete(self, path, piter): -+ node = self.snippet_from_iter(self.model, piter) -+ parent = self.model.iter_parent(piter) -+ -+ Library().remove_snippet(node) -+ idx = path.get_indices() -+ -+ if self.model.remove(piter): -+ return piter -+ elif idx[-1] != 0: -+ self.select_iter(self.model.get_iter((idx[0], idx[1] - 1))) -+ else: -+ dummy = self.add_new_snippet_node(parent) -+ self.tree_view.expand_row(self.model.get_path(parent), False) -+ return dummy -+ -+ def on_button_remove_snippet_clicked(self, button): -+ override, remove, system = self.selected_snippets_state() -+ -+ if not (override ^ remove) or system: -+ return -+ -+ paths = self.selected_snippets(include_languages=False, as_path=True) -+ -+ if override: -+ action = self.remove_snippet_revert -+ else: -+ action = self.remove_snippet_delete -+ -+ # Remove selection -+ self.tree_view.get_selection().unselect_all() -+ -+ # Create tree row references -+ references = [] -+ for path in paths: -+ references.append(Gtk.TreeRowReference(self.model, path)) -+ -+ # Remove/revert snippets -+ select = None -+ for reference in references: -+ path = reference.get_path() -+ piter = self.model.get_iter(path) -+ -+ res = action(path, piter) -+ -+ if res: -+ select = res -+ -+ if select: -+ self.select_iter(select) -+ -+ self.selection_changed() -+ -+ def set_accelerator(self, keyval, mod): -+ accelerator = Gtk.accelerator_name(keyval, mod) -+ self.snippet['accelerator'] = accelerator -+ -+ return True -+ -+ def on_entry_accelerator_key_press(self, entry, event): -+ source_view = self['source_view_snippet'] -+ -+ if event.keyval == Gdk.keyval_from_name('Escape'): -+ # Reset -+ entry.set_text(self.snippet.accelerator_display()) -+ self.tree_view.grab_focus() -+ -+ return True -+ elif event.keyval == Gdk.keyval_from_name('Delete') or \ -+ event.keyval == Gdk.keyval_from_name('BackSpace'): -+ # Remove the accelerator -+ entry.set_text('') -+ self.snippet['accelerator'] = '' -+ self.tree_view.grab_focus() -+ -+ self.snippet_changed() -+ return True -+ elif Library().valid_accelerator(event.keyval, event.state): -+ # New accelerator -+ self.set_accelerator(event.keyval, \ -+ event.state & Gtk.accelerator_get_default_mod_mask()) -+ entry.set_text(self.snippet.accelerator_display()) -+ self.snippet_changed() -+ self.tree_view.grab_focus() -+ -+ else: -+ return True -+ -+ def on_entry_accelerator_focus_in(self, entry, event): -+ if self.snippet['accelerator']: -+ entry.set_text(_('Type a new shortcut, or press Backspace to clear')) -+ else: -+ entry.set_text(_('Type a new shortcut')) -+ -+ def update_language_path(self): -+ model, paths = self.tree_view.get_selection().get_selected_rows() -+ -+ # Check if all have the same language parent -+ current_parent = None -+ -+ for path in paths: -+ piter = model.get_iter(path) -+ parent = model.iter_parent(piter) -+ -+ if parent: -+ path = model.get_path(parent) -+ -+ if current_parent != None and current_parent != path: -+ current_parent = None -+ break -+ else: -+ current_parent = path -+ -+ self.language_path = current_parent -+ -+ def on_tree_view_selection_changed(self, selection): -+ parent, piter, node = self.selected_snippet() -+ -+ if self.snippet: -+ self.on_entry_tab_trigger_focus_out(self['entry_tab_trigger'], -+ None) -+ self.on_source_view_snippet_focus_out(self['source_view_snippet'], -+ None) -+ self.on_entry_drop_targets_focus_out(self['combo_drop_targets'].get_child(), -+ None) -+ -+ self.update_language_path() -+ -+ if node: -+ self.snippet = Snippet(node) -+ else: -+ self.snippet = None -+ -+ self.selection_changed() -+ -+ def iter_after(self, target, after): -+ if not after: -+ return True -+ -+ tp = self.model.get_path(target) -+ ap = self.model.get_path(after) -+ -+ if tp[0] > ap[0] or (tp[0] == ap[0] and (len(ap) == 1 or tp[1] > ap[1])): -+ return True -+ -+ return False -+ -+ def on_tree_view_snippets_key_press(self, treeview, event): -+ if event.keyval == Gdk.keyval_from_name('Delete'): -+ self.on_button_remove_snippet_clicked(None) -+ return True -+ -+ def on_tree_view_snippets_row_expanded(self, treeview, piter, path): -+ # Check if it is already filled -+ self.fill_if_needed(piter) -+ self.select_iter(piter) -+ -+ def on_entry_drop_targets_drag_data_received(self, entry, context, x, y, selection_data, info, timestamp): -+ uris = drop_get_uris(selection_data) -+ -+ if not uris: -+ return -+ -+ if entry.get_text(): -+ mimes = [entry.get_text()] -+ else: -+ mimes = [] -+ -+ for uri in uris: -+ try: -+ mime = Gio.content_type_guess(uri) -+ except: -+ mime = None -+ -+ if mime: -+ mimes.append(mime) -+ -+ entry.set_text(', '.join(mimes)) -+ self.on_entry_drop_targets_focus_out(entry, None) -+ context.finish(True, False, timestamp) -+ -+ entry.stop_emission('drag_data_received') -+# ex:ts=4:et: -diff --git a/plugins/snippets/snippets/Parser.py b/plugins/snippets/snippets/Parser.py -old mode 100755 -new mode 100644 -index 0c638df..280ce0c ---- a/plugins/snippets/snippets/Parser.py -+++ b/plugins/snippets/snippets/Parser.py -@@ -21,239 +21,239 @@ import sys - from SubstitutionParser import SubstitutionParser - - class Token: -- def __init__(self, klass, data): -- self.klass = klass -- self.data = data -- -- def __str__(self): -- return '%s: [%s]' % (self.klass, self.data) -- -- def __eq__(self, other): -- return self.klass == other.klass and self.data == other.data -- -- def __ne__(self, other): -- return not self.__eq__(other) -+ def __init__(self, klass, data): -+ self.klass = klass -+ self.data = data -+ -+ def __str__(self): -+ return '%s: [%s]' % (self.klass, self.data) -+ -+ def __eq__(self, other): -+ return self.klass == other.klass and self.data == other.data -+ -+ def __ne__(self, other): -+ return not self.__eq__(other) - - class Parser: -- SREG_ENV = '[A-Z_]+' -- SREG_ID = '[0-9]+' -- -- REG_ESCAPE = re.compile('(\\$(%s|\\(|\\{|<|%s)|`|\\\\)' % (SREG_ENV, SREG_ID)) -- -- def __init__(self, **kwargs): -- for k, v in kwargs.items(): -- setattr(self, k, v) -- -- self.position = 0 -- self.data_length = len(self.data) -- -- self.RULES = (self._match_env, self._match_regex, self._match_placeholder, self._match_shell, self._match_eval, self._text) -- -- def remains(self): -- return self.data[self.position:] -- -- def next_char(self): -- if self.position + 1 >= self.data_length: -- return '' -- else: -- return self.data[self.position + 1] -- -- def char(self): -- if self.position >= self.data_length: -- return '' -- else: -- return self.data[self.position] -- -- def token(self): -- self.tktext = '' -- -- while self.position < self.data_length: -- try: -- # Get first character -- func = {'$': self._rule, -- '`': self._try_match_shell}[self.char()] -- except: -- func = self._text -- -- # Detect end of text token -- if func != self._text and self.tktext != '': -- return Token('text', self.tktext) -- -- tk = func() -- -- if tk: -- return tk -- -- if self.tktext != '': -- return Token('text', self.tktext) -- -- def _need_escape(self): -- text = self.remains()[1:] -- -- if text == '': -- return False -- -- return self.REG_ESCAPE.match(text) -- -- def _escape(self): -- if not self._need_escape(): -- return -- -- # Increase position with 1 -- self.position += 1 -- -- def _text(self): -- if self.char() == '\\': -- self._escape() -- -- self.tktext += self.char() -- self.position += 1 -- -- def _rule(self): -- for rule in self.RULES: -- res = rule() -- -- if res: -- return res -- -- def _match_env(self): -- text = self.remains() -- match = re.match('\\$(%s)' % self.SREG_ENV, text) or re.match('\\${(%s)}' % self.SREG_ENV, text) -- -- if match: -- self.position += len(match.group(0)) -- return Token('environment', match.group(1)) -- -- def _parse_list(self, lst): -- pos = 0 -- length = len(lst) -- items = [] -- last = None -- -- while pos < length: -- char = lst[pos] -- next = pos < length - 1 and lst[pos + 1] -- -- if char == '\\' and (next == ',' or next == ']'): -- char = next -- pos += 1 -- elif char == ',': -- if last != None: -- items.append(last) -- -- last = None -- pos += 1 -- continue -- -- last = (last != None and last + char) or char -- pos += 1 -- -+ SREG_ENV = '[A-Z_]+' -+ SREG_ID = '[0-9]+' -+ -+ REG_ESCAPE = re.compile('(\\$(%s|\\(|\\{|<|%s)|`|\\\\)' % (SREG_ENV, SREG_ID)) -+ -+ def __init__(self, **kwargs): -+ for k, v in kwargs.items(): -+ setattr(self, k, v) -+ -+ self.position = 0 -+ self.data_length = len(self.data) -+ -+ self.RULES = (self._match_env, self._match_regex, self._match_placeholder, self._match_shell, self._match_eval, self._text) -+ -+ def remains(self): -+ return self.data[self.position:] -+ -+ def next_char(self): -+ if self.position + 1 >= self.data_length: -+ return '' -+ else: -+ return self.data[self.position + 1] -+ -+ def char(self): -+ if self.position >= self.data_length: -+ return '' -+ else: -+ return self.data[self.position] -+ -+ def token(self): -+ self.tktext = '' -+ -+ while self.position < self.data_length: -+ try: -+ # Get first character -+ func = {'$': self._rule, -+ '`': self._try_match_shell}[self.char()] -+ except: -+ func = self._text -+ -+ # Detect end of text token -+ if func != self._text and self.tktext != '': -+ return Token('text', self.tktext) -+ -+ tk = func() -+ -+ if tk: -+ return tk -+ -+ if self.tktext != '': -+ return Token('text', self.tktext) -+ -+ def _need_escape(self): -+ text = self.remains()[1:] -+ -+ if text == '': -+ return False -+ -+ return self.REG_ESCAPE.match(text) -+ -+ def _escape(self): -+ if not self._need_escape(): -+ return -+ -+ # Increase position with 1 -+ self.position += 1 -+ -+ def _text(self): -+ if self.char() == '\\': -+ self._escape() -+ -+ self.tktext += self.char() -+ self.position += 1 -+ -+ def _rule(self): -+ for rule in self.RULES: -+ res = rule() -+ -+ if res: -+ return res -+ -+ def _match_env(self): -+ text = self.remains() -+ match = re.match('\\$(%s)' % self.SREG_ENV, text) or re.match('\\${(%s)}' % self.SREG_ENV, text) -+ -+ if match: -+ self.position += len(match.group(0)) -+ return Token('environment', match.group(1)) -+ -+ def _parse_list(self, lst): -+ pos = 0 -+ length = len(lst) -+ items = [] -+ last = None -+ -+ while pos < length: -+ char = lst[pos] -+ next = pos < length - 1 and lst[pos + 1] -+ -+ if char == '\\' and (next == ',' or next == ']'): -+ char = next -+ pos += 1 -+ elif char == ',': - if last != None: -- items.append(last) -- -- return items -- -- def _parse_default(self, default): -- match = re.match('^\\s*(\\\\)?(\\[((\\\\]|[^\\]])+)\\]\\s*)$', default) -- -- if not match: -- return [default] -- -- groups = match.groups() -- -- if groups[0]: -- return [groups[1]] -- -- return self._parse_list(groups[2]) -- -- def _match_placeholder(self): -- text = self.remains() -- -- match = re.match('\\${(%s)(:((\\\\\\}|[^}])+))?}' % self.SREG_ID, text) or re.match('\\$(%s)' % self.SREG_ID, text) -- -- if not match: -- return None -- -- groups = match.groups() -- default = '' -- tabstop = int(groups[0]) -- self.position += len(match.group(0)) -- -- if len(groups) > 1 and groups[2]: -- default = self._parse_default(groups[2].replace('\\}', '}')) -- -- return Token('placeholder', {'tabstop': tabstop, 'default': default}) -- -- def _match_shell(self): -- text = self.remains() -- match = re.match('`((%s):)?((\\\\`|[^`])+?)`' % self.SREG_ID, text) or re.match('\\$\\(((%s):)?((\\\\\\)|[^\\)])+?)\\)' % self.SREG_ID, text) -- -- if not match: -- return None -- -- groups = match.groups() -- tabstop = (groups[1] and int(groups[1])) or -1 -- self.position += len(match.group(0)) -- -- if text[0] == '`': -- contents = groups[2].replace('\\`', '`') -- else: -- contents = groups[2].replace('\\)', ')') -- -- return Token('shell', {'tabstop': tabstop, 'contents': contents}) -- -- def _try_match_shell(self): -- return self._match_shell() or self._text() -- -- def _eval_options(self, options): -- reg = re.compile(self.SREG_ID) -- tabstop = -1 -- depend = [] -- -- options = options.split(':') -- -- for opt in options: -- if reg.match(opt): -- tabstop = int(opt) -- else: -- depend += self._parse_list(opt[1:-1]) -- -- return (tabstop, depend) -- -- def _match_eval(self): -- text = self.remains() -- -- options = '((%s)|\\[([0-9, ]+)\\])' % self.SREG_ID -- match = re.match('\\$<((%s:)*)((\\\\>|[^>])+?)>' % options, text) -- -- if not match: -- return None -- -- groups = match.groups() -- (tabstop, depend) = (groups[0] and self._eval_options(groups[0][:-1])) or (-1, []) -- self.position += len(match.group(0)) -- -- return Token('eval', {'tabstop': tabstop, 'dependencies': depend, 'contents': groups[5].replace('\\>', '>')}) -- -- def _match_regex(self): -- text = self.remains() -- -- content = '((?:\\\\[/]|\\\\}|[^/}])+)' -- match = re.match('\\${(?:(%s):)?\\s*(%s|\\$([A-Z_]+))?[/]%s[/]%s(?:[/]([a-zA-Z]*))?}' % (self.SREG_ID, self.SREG_ID, content, content), text) -- -- if not match: -- return None -- -- groups = match.groups() -- tabstop = (groups[0] and int(groups[0])) or -1 -- inp = (groups[2] or (groups[1] and int(groups[1]))) or '' -- -- pattern = re.sub('\\\\([/}])', '\\1', groups[3]) -- substitution = re.sub('\\\\([/}])', '\\1', groups[4]) -- modifiers = groups[5] or '' -- -- self.position += len(match.group(0)) -- -- return Token('regex', {'tabstop': tabstop, 'input': inp, 'pattern': pattern, 'substitution': substitution, 'modifiers': modifiers}) -- --# ex:ts=8:et: -+ items.append(last) -+ -+ last = None -+ pos += 1 -+ continue -+ -+ last = (last != None and last + char) or char -+ pos += 1 -+ -+ if last != None: -+ items.append(last) -+ -+ return items -+ -+ def _parse_default(self, default): -+ match = re.match('^\\s*(\\\\)?(\\[((\\\\]|[^\\]])+)\\]\\s*)$', default) -+ -+ if not match: -+ return [default] -+ -+ groups = match.groups() -+ -+ if groups[0]: -+ return [groups[1]] -+ -+ return self._parse_list(groups[2]) -+ -+ def _match_placeholder(self): -+ text = self.remains() -+ -+ match = re.match('\\${(%s)(:((\\\\\\}|[^}])+))?}' % self.SREG_ID, text) or re.match('\\$(%s)' % self.SREG_ID, text) -+ -+ if not match: -+ return None -+ -+ groups = match.groups() -+ default = '' -+ tabstop = int(groups[0]) -+ self.position += len(match.group(0)) -+ -+ if len(groups) > 1 and groups[2]: -+ default = self._parse_default(groups[2].replace('\\}', '}')) -+ -+ return Token('placeholder', {'tabstop': tabstop, 'default': default}) -+ -+ def _match_shell(self): -+ text = self.remains() -+ match = re.match('`((%s):)?((\\\\`|[^`])+?)`' % self.SREG_ID, text) or re.match('\\$\\(((%s):)?((\\\\\\)|[^\\)])+?)\\)' % self.SREG_ID, text) -+ -+ if not match: -+ return None -+ -+ groups = match.groups() -+ tabstop = (groups[1] and int(groups[1])) or -1 -+ self.position += len(match.group(0)) -+ -+ if text[0] == '`': -+ contents = groups[2].replace('\\`', '`') -+ else: -+ contents = groups[2].replace('\\)', ')') -+ -+ return Token('shell', {'tabstop': tabstop, 'contents': contents}) -+ -+ def _try_match_shell(self): -+ return self._match_shell() or self._text() -+ -+ def _eval_options(self, options): -+ reg = re.compile(self.SREG_ID) -+ tabstop = -1 -+ depend = [] -+ -+ options = options.split(':') -+ -+ for opt in options: -+ if reg.match(opt): -+ tabstop = int(opt) -+ else: -+ depend += self._parse_list(opt[1:-1]) -+ -+ return (tabstop, depend) -+ -+ def _match_eval(self): -+ text = self.remains() -+ -+ options = '((%s)|\\[([0-9, ]+)\\])' % self.SREG_ID -+ match = re.match('\\$<((%s:)*)((\\\\>|[^>])+?)>' % options, text) -+ -+ if not match: -+ return None -+ -+ groups = match.groups() -+ (tabstop, depend) = (groups[0] and self._eval_options(groups[0][:-1])) or (-1, []) -+ self.position += len(match.group(0)) -+ -+ return Token('eval', {'tabstop': tabstop, 'dependencies': depend, 'contents': groups[5].replace('\\>', '>')}) -+ -+ def _match_regex(self): -+ text = self.remains() -+ -+ content = '((?:\\\\[/]|\\\\}|[^/}])+)' -+ match = re.match('\\${(?:(%s):)?\\s*(%s|\\$([A-Z_]+))?[/]%s[/]%s(?:[/]([a-zA-Z]*))?}' % (self.SREG_ID, self.SREG_ID, content, content), text) -+ -+ if not match: -+ return None -+ -+ groups = match.groups() -+ tabstop = (groups[0] and int(groups[0])) or -1 -+ inp = (groups[2] or (groups[1] and int(groups[1]))) or '' -+ -+ pattern = re.sub('\\\\([/}])', '\\1', groups[3]) -+ substitution = re.sub('\\\\([/}])', '\\1', groups[4]) -+ modifiers = groups[5] or '' -+ -+ self.position += len(match.group(0)) -+ -+ return Token('regex', {'tabstop': tabstop, 'input': inp, 'pattern': pattern, 'substitution': substitution, 'modifiers': modifiers}) -+ -+# ex:ts=4:et: -diff --git a/plugins/snippets/snippets/Placeholder.py b/plugins/snippets/snippets/Placeholder.py -old mode 100755 -new mode 100644 -index 5fa6e55..9edf099 ---- a/plugins/snippets/snippets/Placeholder.py -+++ b/plugins/snippets/snippets/Placeholder.py -@@ -29,671 +29,671 @@ from Helper import * - - # These are places in a view where the cursor can go and do things - class Placeholder: -- def __init__(self, view, tabstop, defaults, begin): -- self.ok = True -- self.done = False -- self.buf = view.get_buffer() -- self.view = view -- self.has_references = False -- self.mirrors = [] -- self.leave_mirrors = [] -- self.tabstop = tabstop -- self.set_default(defaults) -- self.prev_contents = self.default -- self.set_mark_gravity() -- -- if begin: -- self.begin = self.buf.create_mark(None, begin, self.mark_gravity[0]) -- else: -- self.begin = None -- -- self.end = None -- -- def __str__(self): -- return '%s (%s)' % (str(self.__class__), str(self.default)) -- -- def set_mark_gravity(self): -- self.mark_gravity = [True, False] -- -- def set_default(self, defaults): -- self.default = None -- self.defaults = [] -- -- if not defaults: -- return -- -- for d in defaults: -- dm = self.expand_environment(d) -- -- if dm: -- self.defaults.append(dm) -- -- if not self.default: -- self.default = dm -- -- if dm != d: -- break -- -- -- def literal(self, s): -- return repr(s) -- -- def format_environment(self, s): -- return s -- -- def re_environment(self, m): -- if m.group(1) or not m.group(2) in os.environ: -- return '$' + m.group(2) -- else: -- return self.format_environment(os.environ[m.group(2)]) -- -- def expand_environment(self, text): -- if not text: -- return text -- -- return re.sub('(\\\\)?\\$([A-Z_]+)', self.re_environment, text) -- -- def get_iter(self, mark): -- if mark and not mark.get_deleted(): -- return self.buf.get_iter_at_mark(mark) -- else: -- return None -- -- def begin_iter(self): -- return self.get_iter(self.begin) -- -- def end_iter(self): -- return self.get_iter(self.end) -- -- def run_last(self, placeholders): -- begin = self.begin_iter() -- self.end = self.buf.create_mark(None, begin, self.mark_gravity[1]) -- -- if self.default: -- insert_with_indent(self.view, begin, self.default, False, self) -- -- def remove(self, force = False): -- if self.begin and not self.begin.get_deleted(): -- self.buf.delete_mark(self.begin) -- -- if self.end and not self.end.get_deleted(): -- self.buf.delete_mark(self.end) -- -- # Do something on beginning this placeholder -- def enter(self): -- if not self.begin or self.begin.get_deleted(): -- return -- -- self.buf.move_mark(self.buf.get_insert(), self.begin_iter()) -- -- if self.end: -- self.buf.move_mark(self.buf.get_selection_bound(), self.end_iter()) -- else: -- self.buf.move_mark(self.buf.get_selection_bound(), self.begin_iter()) -- -- def get_text(self): -- if self.begin and self.end: -- biter = self.begin_iter() -- eiter = self.end_iter() -- -- if biter and eiter: -- return self.buf.get_text(self.begin_iter(), self.end_iter(), False) -- else: -- return '' -- else: -- return '' -- -- def add_mirror(self, mirror, onleave = False): -- mirror.has_references = True -- -- if onleave: -- self.leave_mirrors.append(mirror) -- else: -- self.mirrors.append(mirror) -- -- def set_text(self, text): -- if self.begin.get_deleted() or self.end.get_deleted(): -- return -- -- # Set from self.begin to self.end to text! -- self.buf.begin_user_action() -- # Remove everything between self.begin and self.end -- begin = self.begin_iter() -- self.buf.delete(begin, self.end_iter()) -- -- # Insert the text from the mirror -- insert_with_indent(self.view, begin, text, True, self) -- self.buf.end_user_action() -- -- self.update_contents() -- -- def update_contents(self): -- prev = self.prev_contents -- self.prev_contents = self.get_text() -- -- if prev != self.get_text(): -- for mirror in self.mirrors: -- if not mirror.update(self): -- return -- -- def update_leave_mirrors(self): -- # Notify mirrors -- for mirror in self.leave_mirrors: -- if not mirror.update(self): -- return -- -- # Do something on ending this placeholder -- def leave(self): -- self.update_leave_mirrors() -- -- def find_mirrors(self, text, placeholders): -- mirrors = [] -- -- while (True): -- m = re.search('(\\\\)?\\$(?:{([0-9]+)}|([0-9]+))', text) -- -- if not m: -- break -- -- # Skip escaped mirrors -- if m.group(1): -- text = text[m.end():] -- continue -- -- tabstop = int(m.group(2) or m.group(3)) -- -- if tabstop in placeholders: -- if not tabstop in mirrors: -- mirrors.append(tabstop) -- -- text = text[m.end():] -- else: -- self.ok = False -- return None -- -- return mirrors -- --# This is an placeholder which inserts a mirror of another Placeholder -+ def __init__(self, view, tabstop, defaults, begin): -+ self.ok = True -+ self.done = False -+ self.buf = view.get_buffer() -+ self.view = view -+ self.has_references = False -+ self.mirrors = [] -+ self.leave_mirrors = [] -+ self.tabstop = tabstop -+ self.set_default(defaults) -+ self.prev_contents = self.default -+ self.set_mark_gravity() -+ -+ if begin: -+ self.begin = self.buf.create_mark(None, begin, self.mark_gravity[0]) -+ else: -+ self.begin = None -+ -+ self.end = None -+ -+ def __str__(self): -+ return '%s (%s)' % (str(self.__class__), str(self.default)) -+ -+ def set_mark_gravity(self): -+ self.mark_gravity = [True, False] -+ -+ def set_default(self, defaults): -+ self.default = None -+ self.defaults = [] -+ -+ if not defaults: -+ return -+ -+ for d in defaults: -+ dm = self.expand_environment(d) -+ -+ if dm: -+ self.defaults.append(dm) -+ -+ if not self.default: -+ self.default = dm -+ -+ if dm != d: -+ break -+ -+ def literal(self, s): -+ return repr(s) -+ -+ def format_environment(self, s): -+ return s -+ -+ def re_environment(self, m): -+ if m.group(1) or not m.group(2) in os.environ: -+ return '$' + m.group(2) -+ else: -+ return self.format_environment(os.environ[m.group(2)]) -+ -+ def expand_environment(self, text): -+ if not text: -+ return text -+ -+ return re.sub('(\\\\)?\\$([A-Z_]+)', self.re_environment, text) -+ -+ def get_iter(self, mark): -+ if mark and not mark.get_deleted(): -+ return self.buf.get_iter_at_mark(mark) -+ else: -+ return None -+ -+ def begin_iter(self): -+ return self.get_iter(self.begin) -+ -+ def end_iter(self): -+ return self.get_iter(self.end) -+ -+ def run_last(self, placeholders): -+ begin = self.begin_iter() -+ self.end = self.buf.create_mark(None, begin, self.mark_gravity[1]) -+ -+ if self.default: -+ insert_with_indent(self.view, begin, self.default, False, self) -+ -+ def remove(self, force = False): -+ if self.begin and not self.begin.get_deleted(): -+ self.buf.delete_mark(self.begin) -+ -+ if self.end and not self.end.get_deleted(): -+ self.buf.delete_mark(self.end) -+ -+ # Do something on beginning this placeholder -+ def enter(self): -+ if not self.begin or self.begin.get_deleted(): -+ return -+ -+ self.buf.move_mark(self.buf.get_insert(), self.begin_iter()) -+ -+ if self.end: -+ self.buf.move_mark(self.buf.get_selection_bound(), self.end_iter()) -+ else: -+ self.buf.move_mark(self.buf.get_selection_bound(), self.begin_iter()) -+ -+ def get_text(self): -+ if self.begin and self.end: -+ biter = self.begin_iter() -+ eiter = self.end_iter() -+ -+ if biter and eiter: -+ return self.buf.get_text(self.begin_iter(), self.end_iter(), False) -+ else: -+ return '' -+ else: -+ return '' -+ -+ def add_mirror(self, mirror, onleave = False): -+ mirror.has_references = True -+ -+ if onleave: -+ self.leave_mirrors.append(mirror) -+ else: -+ self.mirrors.append(mirror) -+ -+ def set_text(self, text): -+ if self.begin.get_deleted() or self.end.get_deleted(): -+ return -+ -+ # Set from self.begin to self.end to text! -+ self.buf.begin_user_action() -+ # Remove everything between self.begin and self.end -+ begin = self.begin_iter() -+ self.buf.delete(begin, self.end_iter()) -+ -+ # Insert the text from the mirror -+ insert_with_indent(self.view, begin, text, True, self) -+ self.buf.end_user_action() -+ -+ self.update_contents() -+ -+ def update_contents(self): -+ prev = self.prev_contents -+ self.prev_contents = self.get_text() -+ -+ if prev != self.get_text(): -+ for mirror in self.mirrors: -+ if not mirror.update(self): -+ return -+ -+ def update_leave_mirrors(self): -+ # Notify mirrors -+ for mirror in self.leave_mirrors: -+ if not mirror.update(self): -+ return -+ -+ # Do something on ending this placeholder -+ def leave(self): -+ self.update_leave_mirrors() -+ -+ def find_mirrors(self, text, placeholders): -+ mirrors = [] -+ -+ while (True): -+ m = re.search('(\\\\)?\\$(?:{([0-9]+)}|([0-9]+))', text) -+ -+ if not m: -+ break -+ -+ # Skip escaped mirrors -+ if m.group(1): -+ text = text[m.end():] -+ continue -+ -+ tabstop = int(m.group(2) or m.group(3)) -+ -+ if tabstop in placeholders: -+ if not tabstop in mirrors: -+ mirrors.append(tabstop) -+ -+ text = text[m.end():] -+ else: -+ self.ok = False -+ return None -+ -+ return mirrors -+ -+# This is an placeholder which inserts a mirror of another Placeholder - class PlaceholderMirror(Placeholder): -- def __init__(self, view, tabstop, begin): -- Placeholder.__init__(self, view, -1, None, begin) -- self.mirror_stop = tabstop -- -- def update(self, mirror): -- self.set_text(mirror.get_text()) -- return True -- -- def run_last(self, placeholders): -- Placeholder.run_last(self, placeholders) -- -- if self.mirror_stop in placeholders: -- mirror = placeholders[self.mirror_stop] -- -- mirror.add_mirror(self) -- -- if mirror.default: -- self.set_text(mirror.default) -- else: -- self.ok = False -+ def __init__(self, view, tabstop, begin): -+ Placeholder.__init__(self, view, -1, None, begin) -+ self.mirror_stop = tabstop -+ -+ def update(self, mirror): -+ self.set_text(mirror.get_text()) -+ return True -+ -+ def run_last(self, placeholders): -+ Placeholder.run_last(self, placeholders) -+ -+ if self.mirror_stop in placeholders: -+ mirror = placeholders[self.mirror_stop] -+ -+ mirror.add_mirror(self) -+ -+ if mirror.default: -+ self.set_text(mirror.default) -+ else: -+ self.ok = False - - # This placeholder indicates the end of a snippet - class PlaceholderEnd(Placeholder): -- def __init__(self, view, begin, default): -- Placeholder.__init__(self, view, 0, default, begin) -- -- def run_last(self, placeholders): -- Placeholder.run_last(self, placeholders) -- -- # Remove the begin mark and set the begin mark -- # to the end mark, this is needed so the end placeholder won't contain -- # any text -- -- if not self.default: -- self.mark_gravity[0] = False -- self.buf.delete_mark(self.begin) -- self.begin = self.buf.create_mark(None, self.end_iter(), self.mark_gravity[0]) -- -- def enter(self): -- if self.begin and not self.begin.get_deleted(): -- self.buf.move_mark(self.buf.get_insert(), self.begin_iter()) -- -- if self.end and not self.end.get_deleted(): -- self.buf.move_mark(self.buf.get_selection_bound(), self.end_iter()) -- -- def leave(self): -- self.enter() -- --# This placeholder is used to expand a command with embedded mirrors -+ def __init__(self, view, begin, default): -+ Placeholder.__init__(self, view, 0, default, begin) -+ -+ def run_last(self, placeholders): -+ Placeholder.run_last(self, placeholders) -+ -+ # Remove the begin mark and set the begin mark -+ # to the end mark, this is needed so the end placeholder won't contain -+ # any text -+ -+ if not self.default: -+ self.mark_gravity[0] = False -+ self.buf.delete_mark(self.begin) -+ self.begin = self.buf.create_mark(None, self.end_iter(), self.mark_gravity[0]) -+ -+ def enter(self): -+ if self.begin and not self.begin.get_deleted(): -+ self.buf.move_mark(self.buf.get_insert(), self.begin_iter()) -+ -+ if self.end and not self.end.get_deleted(): -+ self.buf.move_mark(self.buf.get_selection_bound(), self.end_iter()) -+ -+ def leave(self): -+ self.enter() -+ -+# This placeholder is used to expand a command with embedded mirrors - class PlaceholderExpand(Placeholder): -- def __init__(self, view, tabstop, begin, s): -- Placeholder.__init__(self, view, tabstop, None, begin) -- -- self.mirror_text = {0: ''} -- self.timeout_id = None -- self.cmd = s -- self.instant_update = False -- -- def __str__(self): -- s = Placeholder.__str__(self) -- -- return s + ' ' + self.cmd -- -- def get_mirrors(self, placeholders): -- return self.find_mirrors(self.cmd, placeholders) -- -- # Check if all substitution placeholders are accounted for -- def run_last(self, placeholders): -- Placeholder.run_last(self, placeholders) -- -- self.ok = True -- mirrors = self.get_mirrors(placeholders) -- -- if mirrors: -- allDefault = True -- -- for mirror in mirrors: -- p = placeholders[mirror] -- p.add_mirror(self, not self.instant_update) -- self.mirror_text[p.tabstop] = p.default -- -- if not p.default and not isinstance(p, PlaceholderExpand): -- allDefault = False -- -- if allDefault: -- self.update(None) -- self.default = self.get_text() or None -- else: -- self.update(None) -- self.default = self.get_text() or None -- -- if self.tabstop == -1: -- self.done = True -- -- def re_placeholder(self, m, formatter): -- if m.group(1): -- return '"$' + m.group(2) + '"' -- else: -- if m.group(3): -- index = int(m.group(3)) -- else: -- index = int(m.group(4)) -- -- return formatter(self.mirror_text[index]) -- -- def remove_timeout(self): -- if self.timeout_id != None: -- GLib.source_remove(self.timeout_id) -- self.timeout_id = None -- -- def install_timeout(self): -- self.remove_timeout() -- self.timeout_id = GLib.timeout_add(1000, self.timeout_cb) -+ def __init__(self, view, tabstop, begin, s): -+ Placeholder.__init__(self, view, tabstop, None, begin) - -- def timeout_cb(self): -- self.timeout_id = None -- -- return False -- -- def format_environment(self, text): -- return self.literal(text) -- -- def substitute(self, text, formatter = None): -- formatter = formatter or self.literal -- -- # substitute all mirrors, but also environmental variables -- text = re.sub('(\\\\)?\\$({([0-9]+)}|([0-9]+))', lambda m: self.re_placeholder(m, formatter), -- text) -- -- return self.expand_environment(text) -- -- def run_update(self): -- text = self.substitute(self.cmd) -- -- if text: -- ret = self.expand(text) -- -- if ret: -- self.update_leave_mirrors() -- else: -- ret = True -- -- return ret -- -- def update(self, mirror): -- text = None -- -- if mirror: -- self.mirror_text[mirror.tabstop] = mirror.get_text() -- -- # Check if all substitutions have been made -- for tabstop in self.mirror_text: -- if tabstop == 0: -- continue -- -- if self.mirror_text[tabstop] == None: -- return False -- -- return self.run_update() -- -- def expand(self, text): -- return True -+ self.mirror_text = {0: ''} -+ self.timeout_id = None -+ self.cmd = s -+ self.instant_update = False -+ -+ def __str__(self): -+ s = Placeholder.__str__(self) -+ -+ return s + ' ' + self.cmd -+ -+ def get_mirrors(self, placeholders): -+ return self.find_mirrors(self.cmd, placeholders) -+ -+ # Check if all substitution placeholders are accounted for -+ def run_last(self, placeholders): -+ Placeholder.run_last(self, placeholders) -+ -+ self.ok = True -+ mirrors = self.get_mirrors(placeholders) -+ -+ if mirrors: -+ allDefault = True -+ -+ for mirror in mirrors: -+ p = placeholders[mirror] -+ p.add_mirror(self, not self.instant_update) -+ self.mirror_text[p.tabstop] = p.default -+ -+ if not p.default and not isinstance(p, PlaceholderExpand): -+ allDefault = False -+ -+ if allDefault: -+ self.update(None) -+ self.default = self.get_text() or None -+ else: -+ self.update(None) -+ self.default = self.get_text() or None -+ -+ if self.tabstop == -1: -+ self.done = True -+ -+ def re_placeholder(self, m, formatter): -+ if m.group(1): -+ return '"$' + m.group(2) + '"' -+ else: -+ if m.group(3): -+ index = int(m.group(3)) -+ else: -+ index = int(m.group(4)) -+ -+ return formatter(self.mirror_text[index]) -+ -+ def remove_timeout(self): -+ if self.timeout_id != None: -+ GLib.source_remove(self.timeout_id) -+ self.timeout_id = None -+ -+ def install_timeout(self): -+ self.remove_timeout() -+ self.timeout_id = GLib.timeout_add(1000, self.timeout_cb) -+ -+ def timeout_cb(self): -+ self.timeout_id = None -+ -+ return False -+ -+ def format_environment(self, text): -+ return self.literal(text) -+ -+ def substitute(self, text, formatter = None): -+ formatter = formatter or self.literal -+ -+ # substitute all mirrors, but also environmental variables -+ text = re.sub('(\\\\)?\\$({([0-9]+)}|([0-9]+))', lambda m: self.re_placeholder(m, formatter), -+ text) -+ -+ return self.expand_environment(text) -+ -+ def run_update(self): -+ text = self.substitute(self.cmd) -+ -+ if text: -+ ret = self.expand(text) -+ -+ if ret: -+ self.update_leave_mirrors() -+ else: -+ ret = True -+ -+ return ret -+ -+ def update(self, mirror): -+ text = None -+ -+ if mirror: -+ self.mirror_text[mirror.tabstop] = mirror.get_text() -+ -+ # Check if all substitutions have been made -+ for tabstop in self.mirror_text: -+ if tabstop == 0: -+ continue -+ -+ if self.mirror_text[tabstop] == None: -+ return False -+ -+ return self.run_update() -+ -+ def expand(self, text): -+ return True - - # The shell placeholder executes commands in a subshell - class PlaceholderShell(PlaceholderExpand): -- def __init__(self, view, tabstop, begin, s): -- PlaceholderExpand.__init__(self, view, tabstop, begin, s) -- -- self.shell = None -- self.remove_me = False -- -- def close_shell(self): -- self.shell.stdout.close() -- self.shell = None -- -- def timeout_cb(self): -- PlaceholderExpand.timeout_cb(self) -- self.remove_timeout() -- -- if not self.shell: -- return False -+ def __init__(self, view, tabstop, begin, s): -+ PlaceholderExpand.__init__(self, view, tabstop, begin, s) - -- GLib.source_remove(self.watch_id) -- self.close_shell() -+ self.shell = None -+ self.remove_me = False - -- if self.remove_me: -- PlaceholderExpand.remove(self) -+ def close_shell(self): -+ self.shell.stdout.close() -+ self.shell = None - -- message_dialog(None, Gtk.MessageType.ERROR, 'Execution of the shell ' \ -- 'command (%s) exceeded the maximum time; ' \ -- 'execution aborted.' % self.command) -- -- return False -- -- def process_close(self): -- self.close_shell() -- self.remove_timeout() -+ def timeout_cb(self): -+ PlaceholderExpand.timeout_cb(self) -+ self.remove_timeout() - -- self.set_text(str.join('', self.shell_output).rstrip('\n')) -- -- if self.default == None: -- self.default = self.get_text() -- self.leave() -- -- if self.remove_me: -- PlaceholderExpand.remove(self, True) -- -- def process_cb(self, source, condition): -- if condition & GObject.IO_IN: -- line = source.readline() -- -- if len(line) > 0: -- try: -- line = unicode(line, 'utf-8') -- except: -- line = unicode(line, locale.getdefaultlocale()[1], -- 'replace') -- -- self.shell_output += line -- self.install_timeout() -- -- return True -- -- self.process_close() -- return False -- -- def literal_replace(self, match): -- return "\\%s" % (match.group(0)) -- -- def literal(self, text): -- return '"' + re.sub('([\\\\"])', self.literal_replace, text) + '"' -- -- def expand(self, text): -+ if not self.shell: -+ return False -+ -+ GLib.source_remove(self.watch_id) -+ self.close_shell() -+ -+ if self.remove_me: -+ PlaceholderExpand.remove(self) -+ -+ message_dialog(None, Gtk.MessageType.ERROR, 'Execution of the shell ' \ -+ 'command (%s) exceeded the maximum time; ' \ -+ 'execution aborted.' % self.command) -+ -+ return False -+ -+ def process_close(self): -+ self.close_shell() -+ self.remove_timeout() -+ -+ self.set_text(str.join('', self.shell_output).rstrip('\n')) -+ -+ if self.default == None: -+ self.default = self.get_text() -+ self.leave() -+ -+ if self.remove_me: -+ PlaceholderExpand.remove(self, True) -+ -+ def process_cb(self, source, condition): -+ if condition & GObject.IO_IN: -+ line = source.readline() -+ -+ if len(line) > 0: -+ try: -+ line = unicode(line, 'utf-8') -+ except: -+ line = unicode(line, locale.getdefaultlocale()[1], -+ 'replace') -+ -+ self.shell_output += line -+ self.install_timeout() -+ -+ return True -+ -+ self.process_close() -+ return False -+ -+ def literal_replace(self, match): -+ return "\\%s" % (match.group(0)) -+ -+ def literal(self, text): -+ return '"' + re.sub('([\\\\"])', self.literal_replace, text) + '"' -+ -+ def expand(self, text): -+ self.remove_timeout() -+ -+ if self.shell: -+ GLib.source_remove(self.watch_id) -+ self.close_shell() -+ -+ popen_args = { -+ 'cwd' : None, -+ 'shell': True, -+ 'env' : os.environ, -+ 'stdout': subprocess.PIPE -+ } -+ -+ self.command = text -+ self.shell = subprocess.Popen(text, **popen_args) -+ self.shell_output = '' -+ self.watch_id = GLib.io_add_watch(self.shell.stdout, GObject.IO_IN | \ -+ GObject.IO_HUP, self.process_cb) -+ self.install_timeout() -+ -+ return True -+ -+ def remove(self, force = False): -+ if not force and self.shell: -+ # Still executing shell command -+ self.remove_me = True -+ else: -+ if force: - self.remove_timeout() - - if self.shell: -- GLib.source_remove(self.watch_id) -- self.close_shell() -- -- popen_args = { -- 'cwd' : None, -- 'shell': True, -- 'env' : os.environ, -- 'stdout': subprocess.PIPE -- } -- -- self.command = text -- self.shell = subprocess.Popen(text, **popen_args) -- self.shell_output = '' -- self.watch_id = GLib.io_add_watch(self.shell.stdout, GObject.IO_IN | \ -- GObject.IO_HUP, self.process_cb) -- self.install_timeout() -- -- return True -- -- def remove(self, force = False): -- if not force and self.shell: -- # Still executing shell command -- self.remove_me = True -- else: -- if force: -- self.remove_timeout() -- -- if self.shell: -- self.close_shell() -- -- PlaceholderExpand.remove(self, force) -+ self.close_shell() -+ -+ PlaceholderExpand.remove(self, force) - - class TimeoutError(Exception): -- def __init__(self, value): -- self.value = value -- -- def __str__(self): -- return repr(self.value) -+ def __init__(self, value): -+ self.value = value -+ -+ def __str__(self): -+ return repr(self.value) - - # The python placeholder evaluates commands in python - class PlaceholderEval(PlaceholderExpand): -- def __init__(self, view, tabstop, refs, begin, s, namespace): -- PlaceholderExpand.__init__(self, view, tabstop, begin, s) -- -- self.fdread = 0 -- self.remove_me = False -- self.namespace = namespace -- -- self.refs = [] -- -- if refs: -- for ref in refs: -- self.refs.append(int(ref.strip())) -- -- def get_mirrors(self, placeholders): -- mirrors = PlaceholderExpand.get_mirrors(self, placeholders) -- -- if not self.ok: -- return None -- -- for ref in self.refs: -- if ref in placeholders: -- if ref not in mirrors: -- mirrors.append(ref) -- else: -- self.ok = False -- return None -- -- return mirrors -- -- # SIGALRM is not supported on all platforms (e.g. windows). Timeout -- # with SIGALRM will not be used on those platforms. This will -- # potentially block pluma if you have a placeholder which gets stuck, -- # but it's better than not supporting them at all. At some point we -- # might have proper thread support and we can fix this in a better way -- def timeout_supported(self): -- return hasattr(signal, 'SIGALRM') -- -- def timeout_cb(self, signum = 0, frame = 0): -- raise TimeoutError, "Operation timed out (>2 seconds)" -- -- def install_timeout(self): -- if not self.timeout_supported(): -- return -- -- if self.timeout_id != None: -- self.remove_timeout() -- -- self.timeout_id = signal.signal(signal.SIGALRM, self.timeout_cb) -- signal.alarm(2) -- -- def remove_timeout(self): -- if not self.timeout_supported(): -- return -- -- if self.timeout_id != None: -- signal.alarm(0) -- -- signal.signal(signal.SIGALRM, self.timeout_id) -- -- self.timeout_id = None -- -- def expand(self, text): -+ def __init__(self, view, tabstop, refs, begin, s, namespace): -+ PlaceholderExpand.__init__(self, view, tabstop, begin, s) -+ -+ self.fdread = 0 -+ self.remove_me = False -+ self.namespace = namespace -+ -+ self.refs = [] -+ -+ if refs: -+ for ref in refs: -+ self.refs.append(int(ref.strip())) -+ -+ def get_mirrors(self, placeholders): -+ mirrors = PlaceholderExpand.get_mirrors(self, placeholders) -+ -+ if not self.ok: -+ return None -+ -+ for ref in self.refs: -+ if ref in placeholders: -+ if ref not in mirrors: -+ mirrors.append(ref) -+ else: -+ self.ok = False -+ return None -+ -+ return mirrors -+ -+ # SIGALRM is not supported on all platforms (e.g. windows). Timeout -+ # with SIGALRM will not be used on those platforms. This will -+ # potentially block pluma if you have a placeholder which gets stuck, -+ # but it's better than not supporting them at all. At some point we -+ # might have proper thread support and we can fix this in a better way -+ def timeout_supported(self): -+ return hasattr(signal, 'SIGALRM') -+ -+ def timeout_cb(self, signum = 0, frame = 0): -+ raise TimeoutError, "Operation timed out (>2 seconds)" -+ -+ def install_timeout(self): -+ if not self.timeout_supported(): -+ return -+ -+ if self.timeout_id != None: -+ self.remove_timeout() -+ -+ self.timeout_id = signal.signal(signal.SIGALRM, self.timeout_cb) -+ signal.alarm(2) -+ -+ def remove_timeout(self): -+ if not self.timeout_supported(): -+ return -+ -+ if self.timeout_id != None: -+ signal.alarm(0) -+ -+ signal.signal(signal.SIGALRM, self.timeout_id) -+ -+ self.timeout_id = None -+ -+ def expand(self, text): -+ self.remove_timeout() -+ -+ text = text.strip() -+ self.command = text -+ -+ if not self.command or self.command == '': -+ self.set_text('') -+ return -+ -+ text = "def process_snippet():\n\t" + "\n\t".join(text.split("\n")) -+ -+ if 'process_snippet' in self.namespace: -+ del self.namespace['process_snippet'] -+ -+ try: -+ exec text in self.namespace -+ except: -+ traceback.print_exc() -+ -+ if 'process_snippet' in self.namespace: -+ try: -+ # Install a sigalarm signal. This is a HACK to make sure -+ # pluma doesn't get freezed by someone creating a python -+ # placeholder which for instance loops indefinately. Since -+ # the code is executed synchronously it will hang pluma. With -+ # the alarm signal we raise an exception and catch this -+ # (see below). We show an error message and return False. -+ # ___this is a HACK___ and should be fixed properly (I just -+ # don't know how) -+ self.install_timeout() -+ result = self.namespace['process_snippet']() -+ self.remove_timeout() -+ except TimeoutError: - self.remove_timeout() - -- text = text.strip() -- self.command = text -+ message_dialog(None, Gtk.MessageType.ERROR, \ -+ _('Execution of the Python command (%s) exceeds the maximum ' \ -+ 'time, execution aborted.') % self.command) - -- if not self.command or self.command == '': -- self.set_text('') -- return -+ return False -+ except Exception, detail: -+ self.remove_timeout() - -- text = "def process_snippet():\n\t" + "\n\t".join(text.split("\n")) -- -- if 'process_snippet' in self.namespace: -- del self.namespace['process_snippet'] -+ message_dialog(None, Gtk.MessageType.ERROR, -+ _('Execution of the Python command (%s) failed: %s') % -+ (self.command, detail)) - -- try: -- exec text in self.namespace -- except: -- traceback.print_exc() -- -- if 'process_snippet' in self.namespace: -- try: -- # Install a sigalarm signal. This is a HACK to make sure -- # pluma doesn't get freezed by someone creating a python -- # placeholder which for instance loops indefinately. Since -- # the code is executed synchronously it will hang pluma. With -- # the alarm signal we raise an exception and catch this -- # (see below). We show an error message and return False. -- # ___this is a HACK___ and should be fixed properly (I just -- # don't know how) -- self.install_timeout() -- result = self.namespace['process_snippet']() -- self.remove_timeout() -- except TimeoutError: -- self.remove_timeout() -- -- message_dialog(None, Gtk.MessageType.ERROR, \ -- _('Execution of the Python command (%s) exceeds the maximum ' \ -- 'time, execution aborted.') % self.command) -- -- return False -- except Exception, detail: -- self.remove_timeout() -- -- message_dialog(None, Gtk.MessageType.ERROR, -- _('Execution of the Python command (%s) failed: %s') % -- (self.command, detail)) -- -- return False -- -- if result == None: -- # sys.stderr.write("%s:\n>> %s\n" % (_('The following python code, run in a snippet, does not return a value'), "\n>> ".join(self.command.split("\n")))) -- result = '' -- -- self.set_text(str(result)) -- -- return True -+ return False -+ -+ if result == None: -+ # sys.stderr.write("%s:\n>> %s\n" % (_('The following python code, run in a snippet, does not return a value'), "\n>> ".join(self.command.split("\n")))) -+ result = '' -+ -+ self.set_text(str(result)) -+ -+ return True - - # Regular expression placeholder - class PlaceholderRegex(PlaceholderExpand): -- def __init__(self, view, tabstop, begin, inp, pattern, substitution, modifiers): -- PlaceholderExpand.__init__(self, view, tabstop, begin, '') -- -- self.instant_update = True -- self.inp = inp -- self.pattern = pattern -- self.substitution = substitution -- -- self.init_modifiers(modifiers) -- -- def init_modifiers(self, modifiers): -- mods = {'I': re.I, -- 'L': re.L, -- 'M': re.M, -- 'S': re.S, -- 'U': re.U, -- 'X': re.X} -- -- self.modifiers = 0 -- -- for modifier in modifiers: -- if modifier in mods: -- self.modifiers |= mods[modifier] -- -- def get_mirrors(self, placeholders): -- mirrors = self.find_mirrors(self.pattern, placeholders) + self.find_mirrors(self.substitution, placeholders) -- -- if isinstance(self.inp, int): -- if self.inp not in placeholders: -- self.ok = False -- return None -- elif self.inp not in mirrors: -- mirrors.append(self.inp) -- -- return mirrors -- -- def literal(self, s): -- return re.escape(s) -- -- def get_input(self): -- if isinstance(self.inp, int): -- return self.mirror_text[self.inp] -- elif self.inp in os.environ: -- return os.environ[self.inp] -- else: -- return '' -- -- def run_update(self): -- pattern = self.substitute(self.pattern) -- substitution = self.substitute(self.substitution, SubstitutionParser.escape_substitution) -- -- if pattern: -- return self.expand(pattern, substitution) -- -- return True -- -- def expand(self, pattern, substitution): -- # Try to compile pattern -- try: -- regex = re.compile(pattern, self.modifiers) -- except re.error, message: -- sys.stderr.write('Could not compile regular expression: %s\n%s\n' % (pattern, message)) -- return False -- -- inp = self.get_input() -- match = regex.search(inp) -- -- if not match: -- self.set_text(inp) -- else: -- groups = match.groupdict() -- -- idx = 0 -- for group in match.groups(): -- groups[str(idx + 1)] = group -- idx += 1 -- -- groups['0'] = match.group(0) -- -- parser = SubstitutionParser(substitution, groups) -- self.set_text(parser.parse()) -- -- return True --# ex:ts=8:et: -+ def __init__(self, view, tabstop, begin, inp, pattern, substitution, modifiers): -+ PlaceholderExpand.__init__(self, view, tabstop, begin, '') -+ -+ self.instant_update = True -+ self.inp = inp -+ self.pattern = pattern -+ self.substitution = substitution -+ -+ self.init_modifiers(modifiers) -+ -+ def init_modifiers(self, modifiers): -+ mods = {'I': re.I, -+ 'L': re.L, -+ 'M': re.M, -+ 'S': re.S, -+ 'U': re.U, -+ 'X': re.X} -+ -+ self.modifiers = 0 -+ -+ for modifier in modifiers: -+ if modifier in mods: -+ self.modifiers |= mods[modifier] -+ -+ def get_mirrors(self, placeholders): -+ mirrors = self.find_mirrors(self.pattern, placeholders) + self.find_mirrors(self.substitution, placeholders) -+ -+ if isinstance(self.inp, int): -+ if self.inp not in placeholders: -+ self.ok = False -+ return None -+ elif self.inp not in mirrors: -+ mirrors.append(self.inp) -+ -+ return mirrors -+ -+ def literal(self, s): -+ return re.escape(s) -+ -+ def get_input(self): -+ if isinstance(self.inp, int): -+ return self.mirror_text[self.inp] -+ elif self.inp in os.environ: -+ return os.environ[self.inp] -+ else: -+ return '' -+ -+ def run_update(self): -+ pattern = self.substitute(self.pattern) -+ substitution = self.substitute(self.substitution, SubstitutionParser.escape_substitution) -+ -+ if pattern: -+ return self.expand(pattern, substitution) -+ -+ return True -+ -+ def expand(self, pattern, substitution): -+ # Try to compile pattern -+ try: -+ regex = re.compile(pattern, self.modifiers) -+ except re.error, message: -+ sys.stderr.write('Could not compile regular expression: %s\n%s\n' % (pattern, message)) -+ return False -+ -+ inp = self.get_input() -+ match = regex.search(inp) -+ -+ if not match: -+ self.set_text(inp) -+ else: -+ groups = match.groupdict() -+ -+ idx = 0 -+ for group in match.groups(): -+ groups[str(idx + 1)] = group -+ idx += 1 -+ -+ groups['0'] = match.group(0) -+ -+ parser = SubstitutionParser(substitution, groups) -+ self.set_text(parser.parse()) -+ -+ return True -+ -+# ex:ts=4:et: -diff --git a/plugins/snippets/snippets/Snippet.py b/plugins/snippets/snippets/Snippet.py -old mode 100755 -new mode 100644 -index 2d7f67d..192b036 ---- a/plugins/snippets/snippets/Snippet.py -+++ b/plugins/snippets/snippets/Snippet.py -@@ -23,333 +23,334 @@ from Parser import Parser, Token - from Helper import * - - class EvalUtilities: -- def __init__(self, view=None): -- self.view = view -- self._init_namespace() -- -- def _init_namespace(self): -- self.namespace = { -- '__builtins__': __builtins__, -- 'align': self.util_align, -- 'readfile': self.util_readfile, -- 'filesize': self.util_filesize -- } -- -- def _real_len(self, s, tablen = 0): -- if tablen == 0: -- tablen = self.view.get_tab_width() -- -- return len(s.expandtabs(tablen)) -- -- def _filename_to_uri(self, filename): -- gfile = Gio.file_new_for_path(filename) -- -- return gfile.get_uri() -- -- def util_readfile(self, filename): -- stream = Gio.file_new_for_path(filename).read() -- -- if not stream: -- return '' -- -- res = stream.read() -- stream.close() -- -- return res -- -- def util_filesize(self, filename): -- gfile = Gio.file_new_for_path(filename) -- info = gfile.query_info(Gio.FILE_ATTRIBUTE_STANDARD_SIZE) -- -- if not info: -- return 0 -- -- return info.get_size() -- -- def util_align(self, items): -- maxlen = [] -- tablen = self.view.get_tab_width() -- -- for row in range(0, len(items)): -- for col in range(0, len(items[row]) - 1): -- if row == 0: -- maxlen.append(0) -- -- items[row][col] += "\t" -- rl = self._real_len(items[row][col], tablen) -- -- if (rl > maxlen[col]): -- maxlen[col] = rl -- -- result = '' -- -- for row in range(0, len(items)): -- for col in range(0, len(items[row]) - 1): -- item = items[row][col] -- -- result += item + ("\t" * ((maxlen[col] - \ -- self._real_len(item, tablen)) / tablen)) -- -- result += items[row][len(items[row]) - 1] -- -- if row != len(items) - 1: -- result += "\n" -- -- return result -+ def __init__(self, view=None): -+ self.view = view -+ self._init_namespace() -+ -+ def _init_namespace(self): -+ self.namespace = { -+ '__builtins__': __builtins__, -+ 'align': self.util_align, -+ 'readfile': self.util_readfile, -+ 'filesize': self.util_filesize -+ } -+ -+ def _real_len(self, s, tablen = 0): -+ if tablen == 0: -+ tablen = self.view.get_tab_width() -+ -+ return len(s.expandtabs(tablen)) -+ -+ def _filename_to_uri(self, filename): -+ gfile = Gio.file_new_for_path(filename) -+ -+ return gfile.get_uri() -+ -+ def util_readfile(self, filename): -+ stream = Gio.file_new_for_path(filename).read() -+ -+ if not stream: -+ return '' -+ -+ res = stream.read() -+ stream.close() -+ -+ return res -+ -+ def util_filesize(self, filename): -+ gfile = Gio.file_new_for_path(filename) -+ info = gfile.query_info(Gio.FILE_ATTRIBUTE_STANDARD_SIZE) -+ -+ if not info: -+ return 0 -+ -+ return info.get_size() -+ -+ def util_align(self, items): -+ maxlen = [] -+ tablen = self.view.get_tab_width() -+ -+ for row in range(0, len(items)): -+ for col in range(0, len(items[row]) - 1): -+ if row == 0: -+ maxlen.append(0) -+ -+ items[row][col] += "\t" -+ rl = self._real_len(items[row][col], tablen) -+ -+ if (rl > maxlen[col]): -+ maxlen[col] = rl -+ -+ result = '' -+ -+ for row in range(0, len(items)): -+ for col in range(0, len(items[row]) - 1): -+ item = items[row][col] -+ -+ result += item + ("\t" * ((maxlen[col] - \ -+ self._real_len(item, tablen)) / tablen)) -+ -+ result += items[row][len(items[row]) - 1] -+ -+ if row != len(items) - 1: -+ result += "\n" -+ -+ return result - - class Snippet: -- def __init__(self, data): -- self.data = data -- -- def __getitem__(self, prop): -- return self.data[prop] -- -- def __setitem__(self, prop, value): -- self.data[prop] = value -- -- def accelerator_display(self): -- accel = self['accelerator'] -- -- if accel: -- keyval, mod = Gtk.accelerator_parse(accel) -- accel = Gtk.accelerator_get_label(keyval, mod) -- -- return accel or '' -- -- def display(self): -- nm = markup_escape(self['description']) -- -- tag = self['tag'] -- accel = self.accelerator_display() -- detail = [] -- -- if tag and tag != '': -- detail.append(tag) -- -- if accel and accel != '': -- detail.append(accel) -- -- if not detail: -- return nm -- else: -- return nm + ' (' + markup_escape(str.join(', ', detail)) + \ -- ')' -- -- def _add_placeholder(self, placeholder): -- if placeholder.tabstop in self.placeholders: -- if placeholder.tabstop == -1: -- self.placeholders[-1].append(placeholder) -- self.plugin_data.ordered_placeholders.append(placeholder) -- elif placeholder.tabstop == -1: -- self.placeholders[-1] = [placeholder] -- self.plugin_data.ordered_placeholders.append(placeholder) -- else: -- self.placeholders[placeholder.tabstop] = placeholder -- self.plugin_data.ordered_placeholders.append(placeholder) -- -- def _insert_text(self, text): -- # Insert text keeping indentation in mind -- indented = unicode.join('\n' + unicode(self._indent), spaces_instead_of_tabs(self._view, text).split('\n')) -- self._view.get_buffer().insert(self._insert_iter(), indented) -- -- def _insert_iter(self): -- return self._view.get_buffer().get_iter_at_mark(self._insert_mark) -- -- def _create_environment(self, data): -- val = ((data in os.environ) and os.environ[data]) or '' -- -- # Get all the current indentation -- all_indent = compute_indentation(self._view, self._insert_iter()) -- -- # Substract initial indentation to get the snippet indentation -- indent = all_indent[len(self._indent):] -- -- # Keep indentation -- return unicode.join('\n' + unicode(indent), val.split('\n')) -- -- def _create_placeholder(self, data): -- tabstop = data['tabstop'] -- begin = self._insert_iter() -- -- if tabstop == 0: -- # End placeholder -- return PlaceholderEnd(self._view, begin, data['default']) -- elif tabstop in self.placeholders: -- # Mirror placeholder -- return PlaceholderMirror(self._view, tabstop, begin) -- else: -- # Default placeholder -- return Placeholder(self._view, tabstop, data['default'], begin) -- -- def _create_shell(self, data): -- begin = self._insert_iter() -- return PlaceholderShell(self._view, data['tabstop'], begin, data['contents']) -- -- def _create_eval(self, data): -- begin = self._insert_iter() -- return PlaceholderEval(self._view, data['tabstop'], data['dependencies'], begin, data['contents'], self._utils.namespace) -- -- def _create_regex(self, data): -- begin = self._insert_iter() -- return PlaceholderRegex(self._view, data['tabstop'], begin, data['input'], data['pattern'], data['substitution'], data['modifiers']) -- -- def _create_text(self, data): -- return data -- -- def _invalid_placeholder(self, placeholder, remove): -- buf = self._view.get_buffer() -- -- # Remove the text because this placeholder is invalid -- if placeholder.default and remove: -- buf.delete(placeholder.begin_iter(), placeholder.end_iter()) -- -- placeholder.remove() -- -- if placeholder.tabstop == -1: -- index = self.placeholders[-1].index(placeholder) -- del self.placeholders[-1][index] -- else: -- del self.placeholders[placeholder.tabstop] -- -- self.plugin_data.ordered_placeholders.remove(placeholder) -- -- def _parse(self, plugin_data): -- # Initialize current variables -- self._view = plugin_data.view -- self._indent = compute_indentation(self._view, self._view.get_buffer().get_iter_at_mark(self.begin_mark)) -- self._utils = EvalUtilities(self._view) -- self.placeholders = {} -- self._insert_mark = self.end_mark -- self.plugin_data = plugin_data -- -- # Create parser -- parser = Parser(data=self['text']) -- -- # Parse tokens -- while (True): -- token = parser.token() -- -- if not token: -- break -- -- try: -- val = {'environment': self._create_environment, -- 'placeholder': self._create_placeholder, -- 'shell': self._create_shell, -- 'eval': self._create_eval, -- 'regex': self._create_regex, -- 'text': self._create_text}[token.klass](token.data) -- except: -- sys.stderr.write('Token class not supported: %s\n' % token.klass) -- continue -- -- if isinstance(val, basestring): -- # Insert text -- self._insert_text(val) -- else: -- # Insert placeholder -- self._add_placeholder(val) -- -- # Create end placeholder if there isn't one yet -- if 0 not in self.placeholders: -- self.placeholders[0] = PlaceholderEnd(self._view, self.end_iter(), None) -- self.plugin_data.ordered_placeholders.append(self.placeholders[0]) -- -- # Make sure run_last is ran for all placeholders and remove any -- # non `ok` placeholders -- for tabstop in self.placeholders.copy(): -- ph = (tabstop == -1 and list(self.placeholders[-1])) or [self.placeholders[tabstop]] -- -- for placeholder in ph: -- placeholder.run_last(self.placeholders) -- -- if not placeholder.ok or placeholder.done: -- self._invalid_placeholder(placeholder, not placeholder.ok) -- -- # Remove all the Expand placeholders which have a tabstop because -- # they can be used to mirror, but they shouldn't be real tabstops -- # (if they have mirrors installed). This is problably a bit of -- # a dirty hack :) -- if -1 not in self.placeholders: -- self.placeholders[-1] = [] -- -- for tabstop in self.placeholders.copy(): -- placeholder = self.placeholders[tabstop] -- -- if tabstop != -1: -- if isinstance(placeholder, PlaceholderExpand) and \ -- placeholder.has_references: -- # Add to anonymous placeholders -- self.placeholders[-1].append(placeholder) -- -- # Remove placeholder -- del self.placeholders[tabstop] -- -- self.plugin_data = None -- -- def insert_into(self, plugin_data, insert): -- buf = plugin_data.view.get_buffer() -- last_index = 0 -- -- # Find closest mark at current insertion, so that we may insert -- # our marks in the correct order -- (current, next) = plugin_data.next_placeholder() -- -- if current: -- # Insert AFTER current -- last_index = plugin_data.placeholders.index(current) + 1 -- elif next: -- # Insert BEFORE next -- last_index = plugin_data.placeholders.index(next) -- else: -- # Insert at first position -- last_index = 0 -- -- # lastIndex now contains the position of the last mark -- # Create snippet bounding marks -- self.begin_mark = buf.create_mark(None, insert, True) -- self.end_mark = buf.create_mark(None, insert, False) -- -- # Now parse the contents of this snippet, create Placeholders -- # and insert the placholder marks in the marks array of plugin_data -- self._parse(plugin_data) -- -- # So now all of the snippet is in the buffer, we have all our -- # placeholders right here, what's next, put all marks in the -- # plugin_data.marks -- k = self.placeholders.keys() -- k.sort(reverse=True) -- -- plugin_data.placeholders.insert(last_index, self.placeholders[0]) -- last_iter = self.placeholders[0].end_iter() -- -- for tabstop in k: -- if tabstop != -1 and tabstop != 0: -- placeholder = self.placeholders[tabstop] -- end_iter = placeholder.end_iter() -- -- if last_iter.compare(end_iter) < 0: -- last_iter = end_iter -- -- # Inserting placeholder -- plugin_data.placeholders.insert(last_index, placeholder) -- -- # Move end mark to last placeholder -- buf.move_mark(self.end_mark, last_iter) -- -- return self -- -- def deactivate(self): -- buf = self.begin_mark.get_buffer() -- -- buf.delete_mark(self.begin_mark) -- buf.delete_mark(self.end_mark) -- -- self.placeholders = {} -- -- def begin_iter(self): -- return self.begin_mark.get_buffer().get_iter_at_mark(self.begin_mark) -- -- def end_iter(self): -- return self.end_mark.get_buffer().get_iter_at_mark(self.end_mark) --# ex:ts=8:et: -+ def __init__(self, data): -+ self.data = data -+ -+ def __getitem__(self, prop): -+ return self.data[prop] -+ -+ def __setitem__(self, prop, value): -+ self.data[prop] = value -+ -+ def accelerator_display(self): -+ accel = self['accelerator'] -+ -+ if accel: -+ keyval, mod = Gtk.accelerator_parse(accel) -+ accel = Gtk.accelerator_get_label(keyval, mod) -+ -+ return accel or '' -+ -+ def display(self): -+ nm = markup_escape(self['description']) -+ -+ tag = self['tag'] -+ accel = self.accelerator_display() -+ detail = [] -+ -+ if tag and tag != '': -+ detail.append(tag) -+ -+ if accel and accel != '': -+ detail.append(accel) -+ -+ if not detail: -+ return nm -+ else: -+ return nm + ' (' + markup_escape(str.join(', ', detail)) + \ -+ ')' -+ -+ def _add_placeholder(self, placeholder): -+ if placeholder.tabstop in self.placeholders: -+ if placeholder.tabstop == -1: -+ self.placeholders[-1].append(placeholder) -+ self.plugin_data.ordered_placeholders.append(placeholder) -+ elif placeholder.tabstop == -1: -+ self.placeholders[-1] = [placeholder] -+ self.plugin_data.ordered_placeholders.append(placeholder) -+ else: -+ self.placeholders[placeholder.tabstop] = placeholder -+ self.plugin_data.ordered_placeholders.append(placeholder) -+ -+ def _insert_text(self, text): -+ # Insert text keeping indentation in mind -+ indented = unicode.join('\n' + unicode(self._indent), spaces_instead_of_tabs(self._view, text).split('\n')) -+ self._view.get_buffer().insert(self._insert_iter(), indented) -+ -+ def _insert_iter(self): -+ return self._view.get_buffer().get_iter_at_mark(self._insert_mark) -+ -+ def _create_environment(self, data): -+ val = ((data in os.environ) and os.environ[data]) or '' -+ -+ # Get all the current indentation -+ all_indent = compute_indentation(self._view, self._insert_iter()) -+ -+ # Substract initial indentation to get the snippet indentation -+ indent = all_indent[len(self._indent):] -+ -+ # Keep indentation -+ return unicode.join('\n' + unicode(indent), val.split('\n')) -+ -+ def _create_placeholder(self, data): -+ tabstop = data['tabstop'] -+ begin = self._insert_iter() -+ -+ if tabstop == 0: -+ # End placeholder -+ return PlaceholderEnd(self._view, begin, data['default']) -+ elif tabstop in self.placeholders: -+ # Mirror placeholder -+ return PlaceholderMirror(self._view, tabstop, begin) -+ else: -+ # Default placeholder -+ return Placeholder(self._view, tabstop, data['default'], begin) -+ -+ def _create_shell(self, data): -+ begin = self._insert_iter() -+ return PlaceholderShell(self._view, data['tabstop'], begin, data['contents']) -+ -+ def _create_eval(self, data): -+ begin = self._insert_iter() -+ return PlaceholderEval(self._view, data['tabstop'], data['dependencies'], begin, data['contents'], self._utils.namespace) -+ -+ def _create_regex(self, data): -+ begin = self._insert_iter() -+ return PlaceholderRegex(self._view, data['tabstop'], begin, data['input'], data['pattern'], data['substitution'], data['modifiers']) -+ -+ def _create_text(self, data): -+ return data -+ -+ def _invalid_placeholder(self, placeholder, remove): -+ buf = self._view.get_buffer() -+ -+ # Remove the text because this placeholder is invalid -+ if placeholder.default and remove: -+ buf.delete(placeholder.begin_iter(), placeholder.end_iter()) -+ -+ placeholder.remove() -+ -+ if placeholder.tabstop == -1: -+ index = self.placeholders[-1].index(placeholder) -+ del self.placeholders[-1][index] -+ else: -+ del self.placeholders[placeholder.tabstop] -+ -+ self.plugin_data.ordered_placeholders.remove(placeholder) -+ -+ def _parse(self, plugin_data): -+ # Initialize current variables -+ self._view = plugin_data.view -+ self._indent = compute_indentation(self._view, self._view.get_buffer().get_iter_at_mark(self.begin_mark)) -+ self._utils = EvalUtilities(self._view) -+ self.placeholders = {} -+ self._insert_mark = self.end_mark -+ self.plugin_data = plugin_data -+ -+ # Create parser -+ parser = Parser(data=self['text']) -+ -+ # Parse tokens -+ while (True): -+ token = parser.token() -+ -+ if not token: -+ break -+ -+ try: -+ val = {'environment': self._create_environment, -+ 'placeholder': self._create_placeholder, -+ 'shell': self._create_shell, -+ 'eval': self._create_eval, -+ 'regex': self._create_regex, -+ 'text': self._create_text}[token.klass](token.data) -+ except: -+ sys.stderr.write('Token class not supported: %s\n' % token.klass) -+ continue -+ -+ if isinstance(val, basestring): -+ # Insert text -+ self._insert_text(val) -+ else: -+ # Insert placeholder -+ self._add_placeholder(val) -+ -+ # Create end placeholder if there isn't one yet -+ if 0 not in self.placeholders: -+ self.placeholders[0] = PlaceholderEnd(self._view, self.end_iter(), None) -+ self.plugin_data.ordered_placeholders.append(self.placeholders[0]) -+ -+ # Make sure run_last is ran for all placeholders and remove any -+ # non `ok` placeholders -+ for tabstop in self.placeholders.copy(): -+ ph = (tabstop == -1 and list(self.placeholders[-1])) or [self.placeholders[tabstop]] -+ -+ for placeholder in ph: -+ placeholder.run_last(self.placeholders) -+ -+ if not placeholder.ok or placeholder.done: -+ self._invalid_placeholder(placeholder, not placeholder.ok) -+ -+ # Remove all the Expand placeholders which have a tabstop because -+ # they can be used to mirror, but they shouldn't be real tabstops -+ # (if they have mirrors installed). This is problably a bit of -+ # a dirty hack :) -+ if -1 not in self.placeholders: -+ self.placeholders[-1] = [] -+ -+ for tabstop in self.placeholders.copy(): -+ placeholder = self.placeholders[tabstop] -+ -+ if tabstop != -1: -+ if isinstance(placeholder, PlaceholderExpand) and \ -+ placeholder.has_references: -+ # Add to anonymous placeholders -+ self.placeholders[-1].append(placeholder) -+ -+ # Remove placeholder -+ del self.placeholders[tabstop] -+ -+ self.plugin_data = None -+ -+ def insert_into(self, plugin_data, insert): -+ buf = plugin_data.view.get_buffer() -+ last_index = 0 -+ -+ # Find closest mark at current insertion, so that we may insert -+ # our marks in the correct order -+ (current, next) = plugin_data.next_placeholder() -+ -+ if current: -+ # Insert AFTER current -+ last_index = plugin_data.placeholders.index(current) + 1 -+ elif next: -+ # Insert BEFORE next -+ last_index = plugin_data.placeholders.index(next) -+ else: -+ # Insert at first position -+ last_index = 0 -+ -+ # lastIndex now contains the position of the last mark -+ # Create snippet bounding marks -+ self.begin_mark = buf.create_mark(None, insert, True) -+ self.end_mark = buf.create_mark(None, insert, False) -+ -+ # Now parse the contents of this snippet, create Placeholders -+ # and insert the placholder marks in the marks array of plugin_data -+ self._parse(plugin_data) -+ -+ # So now all of the snippet is in the buffer, we have all our -+ # placeholders right here, what's next, put all marks in the -+ # plugin_data.marks -+ k = self.placeholders.keys() -+ k.sort(reverse=True) -+ -+ plugin_data.placeholders.insert(last_index, self.placeholders[0]) -+ last_iter = self.placeholders[0].end_iter() -+ -+ for tabstop in k: -+ if tabstop != -1 and tabstop != 0: -+ placeholder = self.placeholders[tabstop] -+ end_iter = placeholder.end_iter() -+ -+ if last_iter.compare(end_iter) < 0: -+ last_iter = end_iter -+ -+ # Inserting placeholder -+ plugin_data.placeholders.insert(last_index, placeholder) -+ -+ # Move end mark to last placeholder -+ buf.move_mark(self.end_mark, last_iter) -+ -+ return self -+ -+ def deactivate(self): -+ buf = self.begin_mark.get_buffer() -+ -+ buf.delete_mark(self.begin_mark) -+ buf.delete_mark(self.end_mark) -+ -+ self.placeholders = {} -+ -+ def begin_iter(self): -+ return self.begin_mark.get_buffer().get_iter_at_mark(self.begin_mark) -+ -+ def end_iter(self): -+ return self.end_mark.get_buffer().get_iter_at_mark(self.end_mark) -+ -+# ex:ts=4:et: -diff --git a/plugins/snippets/snippets/SubstitutionParser.py b/plugins/snippets/snippets/SubstitutionParser.py -old mode 100755 -new mode 100644 -index a41f5a6..246f4da ---- a/plugins/snippets/snippets/SubstitutionParser.py -+++ b/plugins/snippets/snippets/SubstitutionParser.py -@@ -18,185 +18,185 @@ - import re - - class ParseError(Exception): -- def __str__(self): -- return 'Parse error, resume next' -+ def __str__(self): -+ return 'Parse error, resume next' - - class Modifiers: -- def _first_char(s): -- first = (s != '' and s[0]) or '' -- rest = (len(s) > 1 and s[1:]) or '' -- -- return first, rest -- -- def upper_first(s): -- first, rest = Modifiers._first_char(s) -- -- return '%s%s' % (first.upper(), rest) -- -- def upper(s): -- return s.upper() -- -- def lower_first(s): -- first, rest = Modifiers._first_char(s) -- -- return '%s%s' % (first.lower(), rest) -- -- def lower(s): -- return s.lower() -- -- def title(s): -- return s.title() -- -- upper_first = staticmethod(upper_first) -- upper = staticmethod(upper) -- lower_first = staticmethod(lower_first) -- lower = staticmethod(lower) -- title = staticmethod(title) -- _first_char = staticmethod(_first_char) -+ def _first_char(s): -+ first = (s != '' and s[0]) or '' -+ rest = (len(s) > 1 and s[1:]) or '' -+ -+ return first, rest -+ -+ def upper_first(s): -+ first, rest = Modifiers._first_char(s) -+ -+ return '%s%s' % (first.upper(), rest) -+ -+ def upper(s): -+ return s.upper() -+ -+ def lower_first(s): -+ first, rest = Modifiers._first_char(s) -+ -+ return '%s%s' % (first.lower(), rest) -+ -+ def lower(s): -+ return s.lower() -+ -+ def title(s): -+ return s.title() -+ -+ upper_first = staticmethod(upper_first) -+ upper = staticmethod(upper) -+ lower_first = staticmethod(lower_first) -+ lower = staticmethod(lower) -+ title = staticmethod(title) -+ _first_char = staticmethod(_first_char) - - class SubstitutionParser: -- REG_ID = '[0-9]+' -- REG_NAME = '[a-zA-Z_]+' -- REG_MOD = '[a-zA-Z]+' -- REG_ESCAPE = '\\\\|\\(\\?|,|\\)' -- -- def __init__(self, pattern, groups = {}, modifiers = {}): -- self.pattern = pattern -- self.groups = groups -- -- self.REG_GROUP = '(?:(%s)|<(%s|%s)(?:,(%s))?>)' % (self.REG_ID, self.REG_ID, self.REG_NAME, self.REG_MOD) -- self.modifiers = {'u': Modifiers.upper_first, -- 'U': Modifiers.upper, -- 'l': Modifiers.lower_first, -- 'L': Modifiers.lower, -- 't': Modifiers.title} -- -- for k, v in modifiers.items(): -- self.modifiers[k] = v -- -- def parse(self): -- result, tokens = self._parse(self.pattern, None) -- -- return result -- -- def _parse(self, tokens, terminator): -- result = '' -- -- while tokens != '': -- if self._peek(tokens) == '' or self._peek(tokens) == terminator: -- tokens = self._remains(tokens) -- break -- -- try: -- res, tokens = self._expr(tokens, terminator) -- except ParseError: -- res, tokens = self._text(tokens) -- -- result += res -- -- return result, tokens -- -- def _peek(self, tokens, num = 0): -- return (num < len(tokens) and tokens[num]) -- -- def _token(self, tokens): -- if tokens == '': -- return '', ''; -- -- return tokens[0], (len(tokens) > 1 and tokens[1:]) or '' -- -- def _remains(self, tokens, num = 1): -- return (num < len(tokens) and tokens[num:]) or '' -- -- def _expr(self, tokens, terminator): -- if tokens == '': -- return '' -- -- try: -- return {'\\': self._escape, -- '(': self._condition}[self._peek(tokens)](tokens, terminator) -- except KeyError: -- raise ParseError -- -- def _text(self, tokens): -- return self._token(tokens) -- -- def _substitute(self, group, modifiers = ''): -- result = (self.groups.has_key(group) and self.groups[group]) or '' -- -- for modifier in modifiers: -- if self.modifiers.has_key(modifier): -- result = self.modifiers[modifier](result) -- -- return result -- -- def _match_group(self, tokens): -- match = re.match('\\\\%s' % self.REG_GROUP, tokens) -- -- if not match: -- return None, tokens -- -- return self._substitute(match.group(1) or match.group(2), match.group(3) or ''), tokens[match.end():] -- -- def _escape(self, tokens, terminator): -- # Try to match a group -- result, tokens = self._match_group(tokens) -- -- if result != None: -- return result, tokens -- -- s = self.REG_GROUP -- -- if terminator: -- s += '|%s' % re.escape(terminator) -- -- match = re.match('\\\\(\\\\%s|%s)' % (s, self.REG_ESCAPE), tokens) -- -- if not match: -- raise ParseError -- -- return match.group(1), tokens[match.end():] -- -- def _condition_value(self, tokens): -- match = re.match('\\\\?%s\s*' % self.REG_GROUP, tokens) -- -- if not match: -- return None, tokens -- -- groups = match.groups() -- name = groups[0] or groups[1] -- -- return self.groups.has_key(name) and self.groups[name] != None, tokens[match.end():] -- -- def _condition(self, tokens, terminator): -- # Match ? after ( -- if self._peek(tokens, 1) != '?': -- raise ParseError -- -- # Remove initial (? token -- tokens = self._remains(tokens, 2) -- condition, tokens = self._condition_value(tokens) -- -- if condition == None or self._peek(tokens) != ',': -- raise ParseError -- -- truepart, tokens = self._parse(self._remains(tokens), ',') -- -- if truepart == None: -- raise ParseError -- -- falsepart, tokens = self._parse(tokens, ')') -- -- if falsepart == None: -- raise ParseError -- -- if condition: -- return truepart, tokens -- else: -- return falsepart, tokens -- -- def escape_substitution(substitution): -- return re.sub('(%s|%s)' % (self.REG_GROUP, self.REG_ESCAPE), '\\\\\\1', substitution) -- -- escapesubstitution = staticmethod(escape_substitution) --# ex:ts=8:et: -+ REG_ID = '[0-9]+' -+ REG_NAME = '[a-zA-Z_]+' -+ REG_MOD = '[a-zA-Z]+' -+ REG_ESCAPE = '\\\\|\\(\\?|,|\\)' -+ -+ def __init__(self, pattern, groups = {}, modifiers = {}): -+ self.pattern = pattern -+ self.groups = groups -+ -+ self.REG_GROUP = '(?:(%s)|<(%s|%s)(?:,(%s))?>)' % (self.REG_ID, self.REG_ID, self.REG_NAME, self.REG_MOD) -+ self.modifiers = {'u': Modifiers.upper_first, -+ 'U': Modifiers.upper, -+ 'l': Modifiers.lower_first, -+ 'L': Modifiers.lower, -+ 't': Modifiers.title} -+ -+ for k, v in modifiers.items(): -+ self.modifiers[k] = v -+ -+ def parse(self): -+ result, tokens = self._parse(self.pattern, None) -+ -+ return result -+ -+ def _parse(self, tokens, terminator): -+ result = '' -+ -+ while tokens != '': -+ if self._peek(tokens) == '' or self._peek(tokens) == terminator: -+ tokens = self._remains(tokens) -+ break -+ -+ try: -+ res, tokens = self._expr(tokens, terminator) -+ except ParseError: -+ res, tokens = self._text(tokens) -+ -+ result += res -+ -+ return result, tokens -+ -+ def _peek(self, tokens, num = 0): -+ return (num < len(tokens) and tokens[num]) -+ -+ def _token(self, tokens): -+ if tokens == '': -+ return '', ''; -+ -+ return tokens[0], (len(tokens) > 1 and tokens[1:]) or '' -+ -+ def _remains(self, tokens, num = 1): -+ return (num < len(tokens) and tokens[num:]) or '' -+ -+ def _expr(self, tokens, terminator): -+ if tokens == '': -+ return '' -+ -+ try: -+ return {'\\': self._escape, -+ '(': self._condition}[self._peek(tokens)](tokens, terminator) -+ except KeyError: -+ raise ParseError -+ -+ def _text(self, tokens): -+ return self._token(tokens) -+ -+ def _substitute(self, group, modifiers = ''): -+ result = (self.groups.has_key(group) and self.groups[group]) or '' -+ -+ for modifier in modifiers: -+ if self.modifiers.has_key(modifier): -+ result = self.modifiers[modifier](result) -+ -+ return result -+ -+ def _match_group(self, tokens): -+ match = re.match('\\\\%s' % self.REG_GROUP, tokens) -+ -+ if not match: -+ return None, tokens -+ -+ return self._substitute(match.group(1) or match.group(2), match.group(3) or ''), tokens[match.end():] -+ -+ def _escape(self, tokens, terminator): -+ # Try to match a group -+ result, tokens = self._match_group(tokens) -+ -+ if result != None: -+ return result, tokens -+ -+ s = self.REG_GROUP -+ -+ if terminator: -+ s += '|%s' % re.escape(terminator) -+ -+ match = re.match('\\\\(\\\\%s|%s)' % (s, self.REG_ESCAPE), tokens) -+ -+ if not match: -+ raise ParseError -+ -+ return match.group(1), tokens[match.end():] -+ -+ def _condition_value(self, tokens): -+ match = re.match('\\\\?%s\s*' % self.REG_GROUP, tokens) -+ -+ if not match: -+ return None, tokens -+ -+ groups = match.groups() -+ name = groups[0] or groups[1] -+ -+ return self.groups.has_key(name) and self.groups[name] != None, tokens[match.end():] -+ -+ def _condition(self, tokens, terminator): -+ # Match ? after ( -+ if self._peek(tokens, 1) != '?': -+ raise ParseError -+ -+ # Remove initial (? token -+ tokens = self._remains(tokens, 2) -+ condition, tokens = self._condition_value(tokens) -+ -+ if condition == None or self._peek(tokens) != ',': -+ raise ParseError -+ -+ truepart, tokens = self._parse(self._remains(tokens), ',') -+ -+ if truepart == None: -+ raise ParseError -+ -+ falsepart, tokens = self._parse(tokens, ')') -+ -+ if falsepart == None: -+ raise ParseError -+ -+ if condition: -+ return truepart, tokens -+ else: -+ return falsepart, tokens -+ -+ def escape_substitution(substitution): -+ return re.sub('(%s|%s)' % (self.REG_GROUP, self.REG_ESCAPE), '\\\\\\1', substitution) -+ -+ escapesubstitution = staticmethod(escape_substitution) -+# ex:ts=4:et: -diff --git a/plugins/snippets/snippets/WindowHelper.py b/plugins/snippets/snippets/WindowHelper.py -old mode 100755 -new mode 100644 -index 3b08d12..296ff03 ---- a/plugins/snippets/snippets/WindowHelper.py -+++ b/plugins/snippets/snippets/WindowHelper.py -@@ -25,127 +25,127 @@ from Document import Document - from Library import Library - - class WindowHelper: -- def __init__(self, plugin): -- self.plugin = plugin -- self.current_controller = None -- self.current_language = None -- self.signal_ids = {} -- -- def run(self, window): -- self.window = window -- -- self.insert_menu() -- -- self.accel_group = Library().get_accel_group(None) -- -- window.add_accel_group(self.accel_group) -- window.connect('tab-added', self.on_tab_added) -- -- # Add controllers to all the current views -- for view in self.window.get_views(): -- if isinstance(view, Pluma.View) and not self.has_controller(view): -- view._snippet_controller = Document(self, view) -- -- self.update() -- -- def stop(self): -- self.window.remove_accel_group(self.accel_group) -- self.accel_group = None -- -- self.remove_menu() -- -- # Iterate over all the tabs and remove every controller -- for view in self.window.get_views(): -- if isinstance(view, Pluma.View) and self.has_controller(view): -- view._snippet_controller.stop() -- view._snippet_controller = None -- -- self.window = None -- self.plugin = None -- -- def insert_menu(self): -- manager = self.window.get_ui_manager() -- -- self.action_group = Gtk.ActionGroup("PlumaSnippetPluginActions") -- self.action_group.set_translation_domain('pluma') -- self.action_group.add_actions([('ManageSnippets', None, -- _('Manage _Snippets...'), \ -- None, _('Manage snippets'), \ -- self.on_action_snippets_activate)]) -- -- self.merge_id = manager.new_merge_id() -- manager.insert_action_group(self.action_group, -1) -- manager.add_ui(self.merge_id, '/MenuBar/ToolsMenu/ToolsOps_5', \ -- 'ManageSnippets', 'ManageSnippets', Gtk.UIManagerItemType.MENUITEM, False) -- -- def remove_menu(self): -- manager = self.window.get_ui_manager() -- manager.remove_ui(self.merge_id) -- manager.remove_action_group(self.action_group) -- self.action_group = None -- -- def find_snippet(self, snippets, tag): -- result = [] -- -- for snippet in snippets: -- if Snippet(snippet)['tag'] == tag: -- result.append(snippet) -- -- return result -- -- def has_controller(self, view): -- return hasattr(view, '_snippet_controller') and view._snippet_controller -- -- def update_language(self): -- if not self.window: -- return -- -- if self.current_language: -- accel_group = Library().get_accel_group( \ -- self.current_language) -- self.window.remove_accel_group(accel_group) -- -- if self.current_controller: -- self.current_language = self.current_controller.language_id -- -- if self.current_language != None: -- accel_group = Library().get_accel_group( \ -- self.current_language) -- self.window.add_accel_group(accel_group) -- else: -- self.current_language = None -- -- def language_changed(self, controller): -- if controller == self.current_controller: -- self.update_language() -- -- def update(self): -- view = self.window.get_active_view() -- -- if not view or not self.has_controller(view): -- return -- -- controller = view._snippet_controller -- -- if controller != self.current_controller: -- self.current_controller = controller -- self.update_language() -- -- # Callbacks -- -- def on_tab_added(self, window, tab): -- # Create a new controller for this tab if it has a standard pluma view -- view = tab.get_view() -- -- if isinstance(view, Pluma.View) and not self.has_controller(view): -- view._snippet_controller = Document(self, view) -- -- self.update() -- -- def on_action_snippets_activate(self, item): -- self.plugin.create_configure_dialog() -- -- def accelerator_activated(self, keyval, mod): -- return self.current_controller.accelerator_activate(keyval, mod) -- --# ex:ts=8:et: -+ def __init__(self, plugin): -+ self.plugin = plugin -+ self.current_controller = None -+ self.current_language = None -+ self.signal_ids = {} -+ -+ def run(self, window): -+ self.window = window -+ -+ self.insert_menu() -+ -+ self.accel_group = Library().get_accel_group(None) -+ -+ window.add_accel_group(self.accel_group) -+ window.connect('tab-added', self.on_tab_added) -+ -+ # Add controllers to all the current views -+ for view in self.window.get_views(): -+ if isinstance(view, Pluma.View) and not self.has_controller(view): -+ view._snippet_controller = Document(self, view) -+ -+ self.update() -+ -+ def stop(self): -+ self.window.remove_accel_group(self.accel_group) -+ self.accel_group = None -+ -+ self.remove_menu() -+ -+ # Iterate over all the tabs and remove every controller -+ for view in self.window.get_views(): -+ if isinstance(view, Pluma.View) and self.has_controller(view): -+ view._snippet_controller.stop() -+ view._snippet_controller = None -+ -+ self.window = None -+ self.plugin = None -+ -+ def insert_menu(self): -+ manager = self.window.get_ui_manager() -+ -+ self.action_group = Gtk.ActionGroup("PlumaSnippetPluginActions") -+ self.action_group.set_translation_domain('pluma') -+ self.action_group.add_actions([('ManageSnippets', None, -+ _('Manage _Snippets...'), \ -+ None, _('Manage snippets'), \ -+ self.on_action_snippets_activate)]) -+ -+ self.merge_id = manager.new_merge_id() -+ manager.insert_action_group(self.action_group, -1) -+ manager.add_ui(self.merge_id, '/MenuBar/ToolsMenu/ToolsOps_5', \ -+ 'ManageSnippets', 'ManageSnippets', Gtk.UIManagerItemType.MENUITEM, False) -+ -+ def remove_menu(self): -+ manager = self.window.get_ui_manager() -+ manager.remove_ui(self.merge_id) -+ manager.remove_action_group(self.action_group) -+ self.action_group = None -+ -+ def find_snippet(self, snippets, tag): -+ result = [] -+ -+ for snippet in snippets: -+ if Snippet(snippet)['tag'] == tag: -+ result.append(snippet) -+ -+ return result -+ -+ def has_controller(self, view): -+ return hasattr(view, '_snippet_controller') and view._snippet_controller -+ -+ def update_language(self): -+ if not self.window: -+ return -+ -+ if self.current_language: -+ accel_group = Library().get_accel_group( \ -+ self.current_language) -+ self.window.remove_accel_group(accel_group) -+ -+ if self.current_controller: -+ self.current_language = self.current_controller.language_id -+ -+ if self.current_language != None: -+ accel_group = Library().get_accel_group( \ -+ self.current_language) -+ self.window.add_accel_group(accel_group) -+ else: -+ self.current_language = None -+ -+ def language_changed(self, controller): -+ if controller == self.current_controller: -+ self.update_language() -+ -+ def update(self): -+ view = self.window.get_active_view() -+ -+ if not view or not self.has_controller(view): -+ return -+ -+ controller = view._snippet_controller -+ -+ if controller != self.current_controller: -+ self.current_controller = controller -+ self.update_language() -+ -+ # Callbacks -+ -+ def on_tab_added(self, window, tab): -+ # Create a new controller for this tab if it has a standard pluma view -+ view = tab.get_view() -+ -+ if isinstance(view, Pluma.View) and not self.has_controller(view): -+ view._snippet_controller = Document(self, view) -+ -+ self.update() -+ -+ def on_action_snippets_activate(self, item): -+ self.plugin.create_configure_dialog() -+ -+ def accelerator_activated(self, keyval, mod): -+ return self.current_controller.accelerator_activate(keyval, mod) -+ -+# ex:ts=4:et: -diff --git a/plugins/snippets/snippets/__init__.py b/plugins/snippets/snippets/__init__.py -old mode 100755 -new mode 100644 -index d005e89..8642406 ---- a/plugins/snippets/snippets/__init__.py -+++ b/plugins/snippets/snippets/__init__.py -@@ -23,71 +23,73 @@ from Library import Library - from Manager import Manager - - class SnippetsPlugin(GObject.Object, Peas.Activatable): -- __gtype_name__ = "SnippetsPlugin" -+ __gtype_name__ = "SnippetsPlugin" - -- object = GObject.Property(type=GObject.Object) -+ object = GObject.Property(type=GObject.Object) - -- def __init__(self): -- GObject.Object.__init__(self) -+ def __init__(self): -+ GObject.Object.__init__(self) - -- self.dlg = None -+ self.dlg = None - -- def system_dirs(self): -- if 'XDG_DATA_DIRS' in os.environ: -- datadirs = os.environ['XDG_DATA_DIRS'] -- else: -- datadirs = '/usr/local/share' + os.pathsep + '/usr/share' -+ def system_dirs(self): -+ if 'XDG_DATA_DIRS' in os.environ: -+ datadirs = os.environ['XDG_DATA_DIRS'] -+ else: -+ datadirs = '/usr/local/share' + os.pathsep + '/usr/share' - -- dirs = [] -+ dirs = [] - -- for d in datadirs.split(os.pathsep): -- d = os.path.join(d, 'pluma', 'plugins', 'snippets') -+ for d in datadirs.split(os.pathsep): -+ d = os.path.join(d, 'pluma', 'plugins', 'snippets') - -- if os.path.isdir(d): -- dirs.append(d) -+ if os.path.isdir(d): -+ dirs.append(d) - -- dirs.append(self.plugin_info.get_data_dir()) -- return dirs -+ dirs.append(self.plugin_info.get_data_dir()) -+ return dirs - -- def do_activate(self): -- library = Library() -- library.add_accelerator_callback(self.accelerator_activated) -+ def do_activate(self): -+ library = Library() -+ library.add_accelerator_callback(self.accelerator_activated) - -- snippetsdir = os.path.join(GLib.get_user_config_dir(), '/pluma/snippets') -- library.set_dirs(snippetsdir, self.system_dirs()) -+ snippetsdir = os.path.join(GLib.get_user_config_dir(), '/pluma/snippets') -+ library.set_dirs(snippetsdir, self.system_dirs()) - -- self._helper = WindowHelper(self) -+ self._helper = WindowHelper(self) - -- window = self.object -- self._helper.run(window) -+ window = self.object -+ self._helper.run(window) - -- def do_deactivate(self): -- library = Library() -- library.remove_accelerator_callback(self.accelerator_activated) -+ def do_deactivate(self): -+ library = Library() -+ library.remove_accelerator_callback(self.accelerator_activated) - -- self._helper.stop() -- self._helper = None -+ self._helper.stop() -+ self._helper = None - -- def do_update_state(self): -- self._helper.update() -+ def do_update_state(self): -+ self._helper.update() - -- def create_configure_dialog(self): -- if not self.dlg: -- self.dlg = Manager(self.plugin_info.get_data_dir()) -- else: -- self.dlg.run() -+ def create_configure_dialog(self): -+ if not self.dlg: -+ self.dlg = Manager(self.plugin_info.get_data_dir()) -+ else: -+ self.dlg.run() - -- window = Pluma.App.get_default().get_active_window() -+ window = Pluma.App.get_default().get_active_window() - -- if window: -- self.dlg.dlg.set_transient_for(window) -+ if window: -+ self.dlg.dlg.set_transient_for(window) - -- return self.dlg.dlg -+ return self.dlg.dlg - -- def accelerator_activated(self, group, obj, keyval, mod): -- ret = False -+ def accelerator_activated(self, group, obj, keyval, mod): -+ ret = False - -- if self._helper: -- ret = self._helper.accelerator_activated(keyval, mod) -+ if self._helper: -+ ret = self._helper.accelerator_activated(keyval, mod) - -- return ret -+ return ret -+ -+# ex:ts=4:et: --- -2.21.0 - diff --git a/pluma_0001-Switch-to-Python-3.patch b/pluma_0001-Switch-to-Python-3.patch deleted file mode 100644 index ca74728f37e5c37533da24b6c344d9835d250587..0000000000000000000000000000000000000000 --- a/pluma_0001-Switch-to-Python-3.patch +++ /dev/null @@ -1,87 +0,0 @@ -From d76c3e37d1212eaa3cac2f293d539bddf9a250f9 Mon Sep 17 00:00:00 2001 -From: Patrick Monnerat -Date: Mon, 27 May 2019 15:56:31 +0200 -Subject: [PATCH 1/2] Switch to Python 3 - ---- - configure.ac | 2 +- - plugins/externaltools/externaltools.plugin.desktop.in | 2 +- - plugins/pythonconsole/pythonconsole.plugin.desktop.in | 2 +- - plugins/quickopen/quickopen.plugin.desktop.in | 2 +- - plugins/snippets/snippets.plugin.desktop.in | 2 +- - pluma/pluma-plugins-engine.c | 2 +- - 6 files changed, 6 insertions(+), 6 deletions(-) - -diff --git a/configure.ac b/configure.ac -index ceb9a5c..c381583 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -189,7 +189,7 @@ else - have_introspection=no - fi - --AM_PATH_PYTHON([2.7]) -+AM_PATH_PYTHON([3.0]) - - dnl ================================================================ - dnl GSettings related settings -diff --git a/plugins/externaltools/externaltools.plugin.desktop.in b/plugins/externaltools/externaltools.plugin.desktop.in -index b3261e7..b1a7f8b 100644 ---- a/plugins/externaltools/externaltools.plugin.desktop.in -+++ b/plugins/externaltools/externaltools.plugin.desktop.in -@@ -1,5 +1,5 @@ - [Plugin] --Loader=python -+Loader=python3 - Module=externaltools - IAge=2 - _Name=External Tools -diff --git a/plugins/pythonconsole/pythonconsole.plugin.desktop.in b/plugins/pythonconsole/pythonconsole.plugin.desktop.in -index 50d2a7a..4ecbd30 100644 ---- a/plugins/pythonconsole/pythonconsole.plugin.desktop.in -+++ b/plugins/pythonconsole/pythonconsole.plugin.desktop.in -@@ -1,5 +1,5 @@ - [Plugin] --Loader=python -+Loader=python3 - Module=pythonconsole - IAge=2 - _Name=Python Console -diff --git a/plugins/quickopen/quickopen.plugin.desktop.in b/plugins/quickopen/quickopen.plugin.desktop.in -index 891a0c3..547e430 100644 ---- a/plugins/quickopen/quickopen.plugin.desktop.in -+++ b/plugins/quickopen/quickopen.plugin.desktop.in -@@ -1,5 +1,5 @@ - [Plugin] --Loader=python -+Loader=python3 - Module=quickopen - IAge=2 - _Name=Quick Open -diff --git a/plugins/snippets/snippets.plugin.desktop.in b/plugins/snippets/snippets.plugin.desktop.in -index ea960f3..ba1ecbc 100644 ---- a/plugins/snippets/snippets.plugin.desktop.in -+++ b/plugins/snippets/snippets.plugin.desktop.in -@@ -1,5 +1,5 @@ - [Plugin] --Loader=python -+Loader=python3 - Module=snippets - IAge=2 - _Name=Snippets -diff --git a/pluma/pluma-plugins-engine.c b/pluma/pluma-plugins-engine.c -index e765b0f..c670e62 100644 ---- a/pluma/pluma-plugins-engine.c -+++ b/pluma/pluma-plugins-engine.c -@@ -60,7 +60,7 @@ pluma_plugins_engine_init (PlumaPluginsEngine *engine) - - pluma_debug (DEBUG_PLUGINS); - -- peas_engine_enable_loader (PEAS_ENGINE (engine), "python"); -+ peas_engine_enable_loader (PEAS_ENGINE (engine), "python3"); - - engine->priv = G_TYPE_INSTANCE_GET_PRIVATE (engine, - PLUMA_TYPE_PLUGINS_ENGINE, --- -2.21.0 - diff --git a/pluma_0001-externaltools-plugin-change-code-for-Python-2-3-comp.patch b/pluma_0001-externaltools-plugin-change-code-for-Python-2-3-comp.patch deleted file mode 100644 index 4e355f4c1b6ec04d5d3b7604ba287b3e03966339..0000000000000000000000000000000000000000 --- a/pluma_0001-externaltools-plugin-change-code-for-Python-2-3-comp.patch +++ /dev/null @@ -1,1780 +0,0 @@ -From a53f0408ea3fb6d8a19ba75f93f6315375fbf978 Mon Sep 17 00:00:00 2001 -From: Patrick Monnerat -Date: Thu, 23 May 2019 20:42:23 +0200 -Subject: [PATCH] externaltools plugin: change code for Python 2 & 3 - compatibility. - ---- - plugins/externaltools/tools/__init__.py | 13 +- - plugins/externaltools/tools/capture.py | 21 +- - plugins/externaltools/tools/functions.py | 6 +- - plugins/externaltools/tools/library.py | 78 ++- - plugins/externaltools/tools/manager.py | 209 ++---- - plugins/externaltools/tools/outputpanel.py | 9 +- - plugins/externaltools/tools/outputpanel.ui | 22 +- - plugins/externaltools/tools/tools.ui | 706 ++++++++++----------- - 8 files changed, 469 insertions(+), 595 deletions(-) - -diff --git a/plugins/externaltools/tools/__init__.py b/plugins/externaltools/tools/__init__.py -index 153d6c6..b0b67dc 100755 ---- a/plugins/externaltools/tools/__init__.py -+++ b/plugins/externaltools/tools/__init__.py -@@ -16,14 +16,13 @@ - # 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', 'Manager', 'OutputPanel', 'Capture', 'UniqueById') -+__all__ = ('ExternalToolsPlugin', ) - - from gi.repository import GObject, Gtk, Peas, Pluma --from manager import Manager --from library import ToolLibrary --from outputpanel import OutputPanel --from capture import Capture --from functions import * -+from .manager import Manager -+from .library import ToolLibrary -+from .outputpanel import OutputPanel -+from .functions import * - - class ToolMenu(object): - def __init__(self, library, window, panel, plugin, menupath): -@@ -214,7 +213,7 @@ class ExternalToolsPlugin(GObject.Object, Peas.Activatable): - bottom = window.get_bottom_panel() - bottom.add_item_with_icon(self._output_buffer.panel, - _("Shell Output"), -- Gtk.STOCK_EXECUTE) -+ "system-run") - - def do_deactivate(self): - window = self.object -diff --git a/plugins/externaltools/tools/capture.py b/plugins/externaltools/tools/capture.py -index 73ce270..67d12bf 100755 ---- a/plugins/externaltools/tools/capture.py -+++ b/plugins/externaltools/tools/capture.py -@@ -18,7 +18,9 @@ - - __all__ = ('Capture', ) - --import os, sys, signal -+import os -+import sys -+import signal - import locale - import subprocess - import fcntl -@@ -39,7 +41,7 @@ class Capture(GObject.Object): - 'end-execute' : (GObject.SignalFlags.RUN_LAST, GObject.TYPE_NONE, (GObject.TYPE_INT,)) - } - -- def __init__(self, command, cwd = None, env = {}): -+ def __init__(self, command, cwd=None, env={}): - GObject.GObject.__init__(self) - self.pipe = None - self.env = env -@@ -58,6 +60,8 @@ class Capture(GObject.Object): - self.flags = flags - - def set_input(self, text): -+ if text and not isinstance(text, bytes): -+ text = text.encode("utf-8") - self.input_text = text - - def set_cwd(self, cwd): -@@ -87,7 +91,7 @@ class Capture(GObject.Object): - - try: - self.pipe = subprocess.Popen(self.command, **popen_args) -- except OSError, e: -+ except OSError as e: - self.pipe = None - self.emit('stderr-line', _('Could not execute command: %s') % (e, )) - return -@@ -116,7 +120,7 @@ class Capture(GObject.Object): - # IO - if self.input_text is not None: - # Write async, in chunks of something -- self.write_buffer = str(self.input_text) -+ self.write_buffer = self.input_text - - if self.idle_write_chunk(): - self.idle_write_id = GLib.idle_add(self.idle_write_chunk) -@@ -136,7 +140,7 @@ class Capture(GObject.Object): - self.pipe.stdin.write(self.write_buffer[:m]) - - if m == l: -- self.write_buffer = '' -+ self.write_buffer = b'' - self.pipe.stdin.close() - - self.idle_write_id = 0 -@@ -157,11 +161,10 @@ class Capture(GObject.Object): - - if len(line) > 0: - try: -- line = unicode(line, 'utf-8') -+ line = line.decode('utf-8') - except: -- line = unicode(line, -- locale.getdefaultlocale()[1], -- 'replace') -+ line = line.decode(locale.getdefaultlocale()[1], -+ 'replace') - - self.read_buffer += line - lines = self.read_buffer.splitlines(True) -diff --git a/plugins/externaltools/tools/functions.py b/plugins/externaltools/tools/functions.py -index dd4f82b..e76689b 100755 ---- a/plugins/externaltools/tools/functions.py -+++ b/plugins/externaltools/tools/functions.py -@@ -18,8 +18,8 @@ - - import os - from gi.repository import Gio, Gdk, Gtk, GtkSource, Pluma --from outputpanel import OutputPanel --from capture import * -+from .outputpanel import OutputPanel -+from .capture import * - - def default(val, d): - if val is not None: -@@ -131,8 +131,6 @@ def run_external_tool(window, panel, node): - 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() -diff --git a/plugins/externaltools/tools/library.py b/plugins/externaltools/tools/library.py -index 186c33f..ff3fa9b 100755 ---- a/plugins/externaltools/tools/library.py -+++ b/plugins/externaltools/tools/library.py -@@ -19,19 +19,21 @@ - import os - import re - import locale -+import codecs - from gi.repository import GLib - -+ - 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 = super(Singleton, cls).__new__(cls, *args, **kwargs) - cls._instance.__init_once__() - - return cls._instance - -+ - class ToolLibrary(Singleton): - def __init_once__(self): - self.locations = [] -@@ -46,7 +48,7 @@ class ToolLibrary(Singleton): - - # self.locations[0] is where we save the custom scripts - toolsdir = os.path.join(GLib.get_user_config_dir(), 'pluma/tools') -- self.locations.insert(0, toolsdir); -+ self.locations.insert(0, toolsdir) - - if not os.path.isdir(self.locations[0]): - os.makedirs(self.locations[0]) -@@ -74,7 +76,7 @@ class ToolLibrary(Singleton): - if not os.path.isfile(filename): - return - -- print "External tools: importing old tools into the new store..." -+ print("External tools: importing old tools into the new store...") - - xtree = et.parse(filename) - xroot = xtree.getroot() -@@ -97,7 +99,7 @@ class ToolLibrary(Singleton): - - tool.save_with_script(xtool.text) - -- def get_full_path(self, path, mode='r', system = True, local = True): -+ def get_full_path(self, path, mode='r', system=True, local=True): - assert (system or local) - if path is None: - return None -@@ -123,6 +125,7 @@ class ToolLibrary(Singleton): - os.mkdir(dirname) - return path - -+ - class ToolDirectory(object): - def __init__(self, parent, dirname): - super(ToolDirectory, self).__init__() -@@ -145,8 +148,7 @@ class ToolDirectory(object): - continue - for i in os.listdir(d): - elements[i] = None -- keys = elements.keys() -- keys.sort() -+ keys = sorted(elements.keys()) - return keys - - def _load(self): -@@ -196,7 +198,7 @@ class ToolDirectory(object): - class Tool(object): - RE_KEY = re.compile('^([a-zA-Z_][a-zA-Z0-9_.\-]*)(\[([a-zA-Z_@]+)\])?$') - -- def __init__(self, parent, filename = None): -+ def __init__(self, parent, filename=None): - super(Tool, self).__init__() - self.parent = parent - self.library = parent.library -@@ -212,7 +214,7 @@ class Tool(object): - if value.strip() == '': - return [] - else: -- return map(lambda x: x.strip(), value.split(',')) -+ return [x.strip() for x in value.split(',')] - - def _from_list(self, value): - return ','.join(value) -@@ -231,7 +233,7 @@ class Tool(object): - if filename is None: - return - -- fp = file(filename, 'r', 1) -+ fp = codecs.open(filename, 'r', encoding='utf-8') - in_block = False - lang = locale.getlocale(locale.LC_MESSAGES)[0] - -@@ -239,8 +241,10 @@ class Tool(object): - if not in_block: - in_block = line.startswith('# [Pluma Tool]') - continue -- if line.startswith('##') or line.startswith('# #'): continue -- if not line.startswith('# '): break -+ if line.startswith('##') or line.startswith('# #'): -+ continue -+ if not line.startswith('# '): -+ break - - try: - (key, value) = [i.strip() for i in line[2:].split('=', 1)] -@@ -266,9 +270,6 @@ class Tool(object): - 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) -@@ -284,7 +285,8 @@ class Tool(object): - - def get_applicability(self): - applicability = self._properties.get('Applicability') -- if applicability: return applicability -+ if applicability: -+ return applicability - return 'all' - - def set_applicability(self, value): -@@ -294,7 +296,8 @@ class Tool(object): - - def get_name(self): - name = self._properties.get('Name') -- if name: return name -+ if name: -+ return name - return os.path.basename(self.filename) - - def set_name(self, value): -@@ -304,7 +307,8 @@ class Tool(object): - - def get_shortcut(self): - shortcut = self._properties.get('Shortcut') -- if shortcut: return shortcut -+ if shortcut: -+ return shortcut - return None - - def set_shortcut(self, value): -@@ -314,7 +318,8 @@ class Tool(object): - - def get_comment(self): - comment = self._properties.get('Comment') -- if comment: return comment -+ if comment: -+ return comment - return self.filename - - def set_comment(self, value): -@@ -324,7 +329,8 @@ class Tool(object): - - def get_input(self): - input = self._properties.get('Input') -- if input: return input -+ if input: -+ return input - return 'nothing' - - def set_input(self, value): -@@ -334,7 +340,8 @@ class Tool(object): - - def get_output(self): - output = self._properties.get('Output') -- if output: return output -+ if output: -+ return output - return 'output-panel' - - def set_output(self, value): -@@ -344,7 +351,8 @@ class Tool(object): - - def get_save_files(self): - save_files = self._properties.get('Save-files') -- if save_files: return save_files -+ if save_files: -+ return save_files - return 'nothing' - - def set_save_files(self, value): -@@ -354,7 +362,8 @@ class Tool(object): - - def get_languages(self): - languages = self._properties.get('Languages') -- if languages: return languages -+ if languages: -+ return languages - return [] - - def set_languages(self, value): -@@ -370,7 +379,7 @@ class Tool(object): - if filename is None: - return True - -- fp = open(filename, 'r', 1) -+ fp = codecs.open(filename, 'r', encoding='utf-8') - for line in fp: - if line.strip() == '': - continue -@@ -386,7 +395,7 @@ class Tool(object): - if filename is None: - return ["#!/bin/sh\n"] - -- fp = open(filename, 'r', 1) -+ fp = codecs.open(filename, 'r', encoding='utf-8') - lines = list() - - # before entering the data block -@@ -396,7 +405,8 @@ class Tool(object): - lines.append(line) - # in the block: - for line in fp: -- if line.startswith('##'): continue -+ if line.startswith('##'): -+ continue - if not (line.startswith('# ') and '=' in line): - # after the block: strip one emtpy line (if present) - if line.strip() != '': -@@ -410,7 +420,7 @@ class Tool(object): - - def _dump_properties(self): - lines = ['# [Pluma Tool]'] -- for item in self._properties.iteritems(): -+ for item in self._properties.items(): - 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: -@@ -419,7 +429,7 @@ class Tool(object): - - def save_with_script(self, script): - filename = self.library.get_full_path(self.filename, 'w') -- fp = open(filename, 'w', 1) -+ fp = codecs.open(filename, 'w', encoding='utf-8') - - # Make sure to first print header (shebang, modeline), then - # properties, and then actual content -@@ -430,7 +440,6 @@ class Tool(object): - # Parse - for line in script: - line = line.rstrip("\n") -- - if not inheader: - content.append(line) - elif line.startswith('#!'): -@@ -453,7 +462,7 @@ class Tool(object): - fp.write(line + "\n") - - fp.close() -- os.chmod(filename, 0750) -+ os.chmod(filename, 0o750) - self.changed = False - - def save(self): -@@ -478,16 +487,17 @@ class Tool(object): - - if __name__ == '__main__': - library = ToolLibrary() -+ library.set_locations(os.path.expanduser("~/.config/pluma/tools")) - - def print_tool(t, indent): -- print indent * " " + "%s: %s" % (t.filename, t.name) -+ print(indent * " " + "%s: %s" % (t.filename, t.name)) - - def print_dir(d, indent): -- print indent * " " + d.dirname + '/' -+ print(indent * " " + d.dirname + '/') - for i in d.subdirs: -- print_dir(i, indent+1) -+ print_dir(i, indent + 1) - for i in d.tools: -- print_tool(i, indent+1) -+ print_tool(i, indent + 1) - - print_dir(library.tree, 0) - -diff --git a/plugins/externaltools/tools/manager.py b/plugins/externaltools/tools/manager.py -index 4da0deb..b8e143b 100755 ---- a/plugins/externaltools/tools/manager.py -+++ b/plugins/externaltools/tools/manager.py -@@ -19,71 +19,41 @@ - __all__ = ('Manager', ) - - import os.path --from library import * --from functions import * -+import re -+from .library import * -+from .functions import * - import hashlib - from xml.sax import saxutils - from gi.repository import GObject, Gio, Gdk, Gtk, GtkSource, Pluma - --class LanguagesPopup(Gtk.Window): -- __gtype_name__ = "LanguagePopup" -+class LanguagesPopup(Gtk.Popover): -+ __gtype_name__ = "LanguagesPopup" - - COLUMN_NAME = 0 - COLUMN_ID = 1 - COLUMN_ENABLED = 2 - -- def __init__(self, languages): -- Gtk.Window.__init__(self, type=Gtk.WindowType.POPUP) -+ def __init__(self, widget, languages): -+ Gtk.Popover.__init__(self, relative_to=widget) - -- self.set_default_size(200, 200) - self.props.can_focus = True - - self.build() - self.init_languages(languages) - -- self.show() -- -- self.grab_add() -- -- self.keyboard = None -- device_manager = Gdk.Display.get_device_manager(self.get_window().get_display()) -- for device in device_manager.list_devices(Gdk.DeviceType.MASTER): -- if device.get_source() == Gdk.InputSource.KEYBOARD: -- self.keyboard = device -- break -- -- self.pointer = device_manager.get_client_pointer() -- -- if self.keyboard is not None: -- self.keyboard.grab(self.get_window(), -- Gdk.GrabOwnership.WINDOW, False, -- Gdk.EventMask.KEY_PRESS_MASK | -- Gdk.EventMask.KEY_RELEASE_MASK, -- None, Gdk.CURRENT_TIME) -- self.pointer.grab(self.get_window(), -- Gdk.GrabOwnership.WINDOW, False, -- Gdk.EventMask.BUTTON_PRESS_MASK | -- Gdk.EventMask.BUTTON_RELEASE_MASK | -- Gdk.EventMask.POINTER_MOTION_MASK | -- Gdk.EventMask.ENTER_NOTIFY_MASK | -- Gdk.EventMask.LEAVE_NOTIFY_MASK | -- Gdk.EventMask.PROXIMITY_IN_MASK | -- Gdk.EventMask.PROXIMITY_OUT_MASK | -- Gdk.EventMask.SCROLL_MASK, -- None, Gdk.CURRENT_TIME) -- - self.view.get_selection().select_path((0,)) - - def build(self): - self.model = Gtk.ListStore(str, str, bool) - - self.sw = Gtk.ScrolledWindow() -+ self.sw.set_size_request(-1, 200) - self.sw.show() - -- self.sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) -+ self.sw.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) - self.sw.set_shadow_type(Gtk.ShadowType.ETCHED_IN) - -- self.view = Gtk.TreeView(self.model) -+ self.view = Gtk.TreeView(model=self.model) - self.view.show() - - self.view.set_headers_visible(False) -@@ -92,16 +62,16 @@ class LanguagesPopup(Gtk.Window): - - renderer = Gtk.CellRendererToggle() - column.pack_start(renderer, False) -- column.set_attributes(renderer, active=self.COLUMN_ENABLED) -+ column.add_attribute(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) -+ column.add_attribute(renderer, 'text', self.COLUMN_NAME) - - self.view.append_column(column) -- self.view.set_row_separator_func(self.on_separator) -+ self.view.set_row_separator_func(self.on_separator, None) - - self.sw.add(self.view) - -@@ -124,7 +94,7 @@ class LanguagesPopup(Gtk.Window): - self.model.foreach(self.enabled_languages, ret) - return ret - -- def on_separator(self, model, piter): -+ def on_separator(self, model, piter, user_data=None): - val = model.get_value(piter, self.COLUMN_NAME) - return val == '-' - -@@ -142,7 +112,7 @@ class LanguagesPopup(Gtk.Window): - 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,): -+ if path.get_indices()[0] == 0: - return False - - model.set_value(piter, self.COLUMN_ENABLED, enabled) -@@ -158,113 +128,6 @@ class LanguagesPopup(Gtk.Window): - else: - self.model.set_value(self.model.get_iter_first(), self.COLUMN_ENABLED, False) - -- def do_key_press_event(self, event): -- if event.keyval == Gdk.KEY_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.get_window() -- -- geometry = window.get_geometry() -- origin = window.get_origin() -- -- return event.x_root >= origin[1] and \ -- event.x_root <= origin[1] + geometry[2] and \ -- event.y_root >= origin[2] and \ -- event.y_root <= origin[2] + geometry[3] -- -- def do_destroy(self): -- if self.keyboard: -- self.keyboard.ungrab(Gdk.CURRENT_TIME) -- self.pointer.ungrab(Gdk.CURRENT_TIME) -- -- 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[1] - to[1] -- event.y += fr[2] - to[2] -- -- 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, reverse=True): -- allwidgets = self.resolve_widgets(self.get_child()) -- -- if reverse: -- allwidgets.reverse() -- -- for widget in allwidgets: -- windows = self.resolve_windows(widget.get_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, False) -- -- 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) - - class Manager(GObject.Object): - TOOL_COLUMN = 0 # For Tree -@@ -450,7 +313,9 @@ class Manager(GObject.Object): - n1 = t1.name - n2 = t2.name - -- return cmp(n1.lower(), n2.lower()) -+ n1 = n1.lower() -+ n2 = n2.lower() -+ return (n1 > n2) - (n1 < n2) - - def __init_tools_view(self): - # Tools column -@@ -499,8 +364,8 @@ class Manager(GObject.Object): - else: - return None, None - -- def compute_hash(self, string): -- return hashlib.md5(string).hexdigest() -+ def compute_hash(self, stringofbytes): -+ return hashlib.md5(stringofbytes).hexdigest() - - def save_current_tool(self): - if self.current_node is None: -@@ -521,7 +386,10 @@ class Manager(GObject.Object): - buf = self['commands'].get_buffer() - (start, end) = buf.get_bounds() - script = buf.get_text(start, end, False) -- h = self.compute_hash(script) -+ scriptbytes = script -+ if not isinstance(scriptbytes, bytes): -+ scriptbytes = scriptbytes.encode('utf-8') -+ h = self.compute_hash(scriptbytes) - if h != self.script_hash: - # script has changed -> save it - self.current_node.save_with_script([line + "\n" for line in script.splitlines()]) -@@ -573,6 +441,9 @@ class Manager(GObject.Object): - buf.set_text(script) - buf.end_not_undoable_action() - -+ if not isinstance(script, bytes): -+ script = script.encode('utf-8') -+ - self.script_hash = self.compute_hash(script) - contenttype, uncertain = Gio.content_type_guess(None, script) - lmanager = GtkSource.LanguageManager.get_default() -@@ -703,7 +574,7 @@ class Manager(GObject.Object): - if language in node.languages: - node.languages.remove(language) - -- self._tool_rows[node] = filter(lambda x: x.valid(), self._tool_rows[node]) -+ self._tool_rows[node] = [x for x in self._tool_rows[node] if x.valid()] - - if not self._tool_rows[node]: - del self._tool_rows[node] -@@ -714,7 +585,9 @@ class Manager(GObject.Object): - 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.set_cursor(self.model.get_path(piter), -+ self.view.get_column(self.TOOL_COLUMN), -+ False) - - self.view.grab_focus() - -@@ -799,19 +672,19 @@ class Manager(GObject.Object): - - def on_accelerator_key_press(self, entry, event): - mask = event.state & Gtk.accelerator_get_default_mod_mask() -+ keyname = Gdk.keyval_name(event.keyval) - -- if event.keyval == Gdk.KEY_Escape: -+ if keyname == 'Escape': - entry.set_text(default(self.current_node.shortcut, '')) - self['commands'].grab_focus() - return True -- elif event.keyval == Gdk.KEY_Delete \ -- or event.keyval == Gdk.KEY_BackSpace: -+ elif keyname == 'Delete' or keyname == '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(Gdk.KEY_F1, Gdk.KEY_F12 + 1): -+ elif re.match('^F(:1[012]?|[2-9])$', keyname): - # New accelerator - if self.set_accelerator(event.keyval, mask): - entry.set_text(default(self.current_node.shortcut, '')) -@@ -911,7 +784,7 @@ class Manager(GObject.Object): - ret = None - - if node: -- ref = Gtk.TreeRowReference(self.model, self.model.get_path(piter)) -+ ref = Gtk.TreeRowReference.new(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) -@@ -966,12 +839,8 @@ class Manager(GObject.Object): - 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.get_window().get_origin() -- popup.move(origin[1], origin[2] - popup.get_allocation().height) -- -- popup.connect('destroy', self.update_languages) -+ popup = LanguagesPopup(button, self.current_node.languages) -+ popup.show() -+ popup.connect('closed', self.update_languages) - - # ex:et:ts=4: -diff --git a/plugins/externaltools/tools/outputpanel.py b/plugins/externaltools/tools/outputpanel.py -index e063eb2..39fd99a 100755 ---- a/plugins/externaltools/tools/outputpanel.py -+++ b/plugins/externaltools/tools/outputpanel.py -@@ -20,11 +20,12 @@ - __all__ = ('OutputPanel', 'UniqueById') - - import os --from weakref import WeakKeyDictionary --from capture import * - import re --import linkparsing --import filelookup -+ -+from weakref import WeakKeyDictionary -+from .capture import * -+from . import linkparsing -+from . import filelookup - from gi.repository import GLib, Gdk, Gtk, Pango, Pluma - - class UniqueById: -diff --git a/plugins/externaltools/tools/outputpanel.ui b/plugins/externaltools/tools/outputpanel.ui -index 01904a6..30f2e33 100644 ---- a/plugins/externaltools/tools/outputpanel.ui -+++ b/plugins/externaltools/tools/outputpanel.ui -@@ -1,5 +1,5 @@ - -- - - -- -+ - True - False -+ True -+ True - - - True - False - True -+ True - in - - -@@ -33,9 +36,8 @@ Version: 2.91.3 - - - -- False -- True -- 0 -+ 0 -+ 0 - - - -@@ -43,7 +45,6 @@ Version: 2.91.3 - True - False - 6 -- vertical - 6 - end - -@@ -57,16 +58,15 @@ Version: 2.91.3 - - - -- True -- True -+ False -+ False - 0 - - - - -- False -- True -- 1 -+ 1 -+ 0 - - - -diff --git a/plugins/externaltools/tools/tools.ui b/plugins/externaltools/tools/tools.ui -index afdd3f9..21de842 100644 ---- a/plugins/externaltools/tools/tools.ui -+++ b/plugins/externaltools/tools/tools.ui -@@ -1,30 +1,45 @@ -- -+ -+ - -- -+ -+ -+ True -+ -+ - - - -- -+ - - - - -- Nothing -- nothing -+ All documents -+ all - - -- Current document -- document -+ All documents except untitled ones -+ titled - - -- All documents -- all -+ Local files only -+ local -+ -+ -+ Remote files only -+ remote -+ -+ -+ Untitled documents only -+ untitled - - - - - -+ - -+ - - - -@@ -62,7 +77,7 @@ - - - -- -+ - Nothing - nothing - -@@ -92,7 +107,7 @@ - - - -- -+ - - - -@@ -101,72 +116,105 @@ - - - -- All documents -- all -- -- -- All documents except untitled ones -- titled -- -- -- Local files only -- local -+ Nothing -+ nothing - - -- Remote files only -- remote -+ Current document -+ document - - -- Untitled documents only -- untitled -+ All documents -+ all - - - -- -- True -- - -+ False - External Tools Manager - 750 - 500 - dialog - True -- -- -- -+ -+ -+ -+ -+ -+ - -- -+ - True -+ False -+ vertical -+ -+ -+ True -+ False -+ end -+ -+ -+ gtk-help -+ True -+ True -+ True -+ False -+ True -+ -+ -+ False -+ False -+ 0 -+ -+ -+ -+ -+ gtk-close -+ True -+ True -+ True -+ False -+ True -+ -+ -+ False -+ False -+ 1 -+ -+ -+ -+ -+ False -+ False -+ end -+ 0 -+ -+ - -- -+ - True - True -- 6 -+ True -+ True - 275 - -- -+ - True -- 6 -- -- -- True -- 0 -- _Tools: -- True -- view -- -- -- False -- False -- 0 -- -- -+ False -+ 6 -+ 6 -+ 6 -+ 6 -+ True -+ True -+ vertical -+ 6 - - - True - False -- automatic -- automatic -+ True -+ True - in - - -@@ -174,29 +222,49 @@ - True - False - True -+ -+ -+ - - - - -- 1 -+ 0 -+ 1 -+ -+ -+ -+ -+ True -+ False -+ _Tools: -+ True -+ view -+ 0 -+ -+ -+ 0 -+ 0 - - - -- -+ - True -- 6 -+ False -+ start - - - True - False - True - False -- -+ start -+ - - - True -+ False - gtk-new -- 4 - - - -@@ -204,344 +272,311 @@ - False - False - 0 -+ True - - - -- -+ -+ True - False -+ True - False -- -+ start -+ - -- -+ - True -- gtk-revert-to-saved -- 4 -+ False -+ gtk-delete - - - - - False - False -- end -- 2 -+ 1 -+ True - - - -- -- True -+ - False -- True - False -- -+ start -+ - -- -+ - True -- gtk-delete -- 4 -+ False -+ gtk-revert-to-saved - - - - - False - False -- end -- 1 -+ 2 -+ True - - - - -- False -- False -- 2 -+ 0 -+ 2 - - - - -- False -- False -+ True -+ True - - - -- -+ - True -- 6 -+ False -+ True -+ True -+ vertical -+ 6 - -- -- True -- 0 -- 0.5 -- _Edit: -- commands -- True -- -- -- False -- False -- 0 -- -- -- -- -+ - True -+ False -+ True -+ True -+ vertical -+ 6 -+ 6 - -- -+ - True -- -+ False -+ _Applicability: -+ True -+ 0 - - -- False -- False -- 0 -+ 0 -+ 5 -+ -+ -+ -+ -+ True -+ False -+ _Output: -+ True -+ 0 -+ -+ -+ 0 -+ 4 -+ -+ -+ -+ -+ True -+ False -+ _Input: -+ True -+ 0 -+ -+ -+ 0 -+ 3 - - - -- -+ - True -- 6 -- 2 -- 6 -- 6 -+ False -+ _Save: -+ True -+ 0 -+ -+ -+ 0 -+ 2 -+ -+ -+ -+ -+ True -+ False -+ _Shortcut Key: -+ True -+ 0 -+ -+ -+ 0 -+ 1 -+ -+ -+ -+ -+ True -+ False -+ model_output - -- -- True -- True -- -- -- -- -- -- 1 -- 2 -- 1 -- 2 -- GTK_EXPAND | GTK_SHRINK | GTK_FILL -- GTK_FILL -- -+ -+ -+ 0 -+ - -+ -+ -+ 1 -+ 4 -+ -+ -+ -+ -+ True -+ False -+ model_input - -- -- True -- -- -- True -- model_applicability -- -- -- -- 0 -- -- -- -- -- 0 -- False -- True -- -- -- -- -- True -- True -- -- -- True -- -- -- -- True -- All Languages -- 0 -- 0.5 -- PANGO_ELLIPSIZE_MIDDLE -- -- -- -- -- -- -- 1 -- True -- True -- -- -- -- -- 1 -- 2 -- 5 -- 6 -- GTK_EXPAND | GTK_SHRINK | GTK_FILL -- GTK_FILL -- -+ -+ -+ 0 -+ - -+ -+ -+ 1 -+ 3 -+ -+ -+ -+ -+ True -+ False -+ model_save_files - -- -- True -- model_output -- -- -- -- 0 -- -- -- -- -- 1 -- 2 -- 4 -- 5 -- GTK_EXPAND | GTK_SHRINK | GTK_FILL -- GTK_FILL -- -+ -+ -+ 0 -+ - -+ -+ -+ 1 -+ 2 -+ -+ -+ -+ -+ True -+ True -+ -+ -+ -+ -+ -+ 1 -+ 1 -+ -+ -+ -+ -+ True -+ True -+ True -+ True -+ in -+ -+ -+ commands_buffer -+ True -+ True -+ False -+ GTK_SOURCE_SMART_HOME_END_AFTER -+ 2 -+ True -+ False -+ True -+ -+ -+ -+ -+ 0 -+ 0 -+ 2 -+ -+ -+ -+ -+ True -+ False - -- -+ - True -- model_input -+ False - -- -- -- 0 -- -+ -+ True -+ False -+ False -+ -+ -+ -+ True -+ False -+ All Languages -+ middle -+ 0 -+ 0.5 -+ -+ -+ - - - - 1 -- 2 -- 3 -- 4 -- GTK_EXPAND | GTK_SHRINK | GTK_FILL -- GTK_FILL -+ 0 - - - -- -- model_save_files -+ - True -+ False -+ model_applicability - -- -+ - - 0 - - - - -- 1 -- 2 -- 2 -- 3 -- GTK_EXPAND | GTK_SHRINK | GTK_FILL -- GTK_FILL -- -- -- -- -- True -- 0 -- _Applicability: -- True -- applicability -- -- -- 5 -- 6 -- GTK_FILL -- -- -- -- -- -- True -- 0 -- _Output: -- True -- output -- -- -- 4 -- 5 -- GTK_FILL -- -- -- -- -- -- True -- 0 -- _Input: -- True -- input -- -- -- 3 -- 4 -- GTK_FILL -- -- -- -- -- -- 0 -- _Save: -- True -- save-files -- True -- -- -- 2 -- 3 -- GTK_FILL -- -- -- -- -- -- True -- 0 -- _Shortcut Key: -- True -- accelerator -- -- -- 1 -- 2 -- GTK_FILL -- -- -- -- -- -- True -- True -- automatic -- automatic -- in -- -- -- commands_buffer -- True -- True -- False -- GTK_SOURCE_SMART_HOME_END_AFTER -- 2 -- True -- False -- True -- -- -- -- -- 2 -+ 0 -+ 0 - - - - -- 1 -+ 1 -+ 5 - - - - -- 1 -+ 0 -+ 1 -+ -+ -+ -+ -+ True -+ False -+ _Edit: -+ True -+ 0 -+ 0.5 -+ -+ -+ 0 -+ 0 - - - -@@ -557,47 +592,6 @@ - 1 - - -- -- -- True -- end -- -- -- gtk-help -- True -- True -- True -- False -- True -- -- -- False -- False -- 0 -- -- -- -- -- gtk-close -- True -- True -- True -- False -- True -- -- -- False -- False -- 1 -- -- -- -- -- False -- end -- 0 -- -- - - - --- -2.21.0 - diff --git a/pluma_0001-pythonconsole-plugin-change-source-code-for-Python-2.patch b/pluma_0001-pythonconsole-plugin-change-source-code-for-Python-2.patch deleted file mode 100644 index f65310d3d508521d794b39c070dacb73555383af..0000000000000000000000000000000000000000 --- a/pluma_0001-pythonconsole-plugin-change-source-code-for-Python-2.patch +++ /dev/null @@ -1,798 +0,0 @@ -From a54a8e7bf8e86f87316b5026e6e85d6d3fdab226 Mon Sep 17 00:00:00 2001 -From: Patrick Monnerat -Date: Wed, 22 May 2019 18:02:59 +0200 -Subject: [PATCH] pythonconsole plugin: change source code for Python 2 & 3 - compatibility. - -Also drop mateconf and use gsettings for preferences. -Preferences are now triggered as a PeasGtk.Configurable. ---- - configure.ac | 1 + - plugins/pythonconsole/Makefile.am | 19 +- - ...pluma.plugins.pythonconsole.gschema.xml.in | 30 +++ - .../pythonconsole/pythonconsole/__init__.py | 37 +-- - plugins/pythonconsole/pythonconsole/config.py | 158 ++++++------ - plugins/pythonconsole/pythonconsole/config.ui | 224 +++++++++--------- - .../pythonconsole/pythonconsole/console.py | 68 ++++-- - 7 files changed, 304 insertions(+), 233 deletions(-) - create mode 100644 plugins/pythonconsole/org.mate.pluma.plugins.pythonconsole.gschema.xml.in - -diff --git a/configure.ac b/configure.ac -index 1cdbb7e..ceb9a5c 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -250,6 +250,7 @@ plugins/filebrowser/org.mate.pluma.plugins.filebrowser.gschema.xml - plugins/modelines/Makefile - plugins/pythonconsole/Makefile - plugins/pythonconsole/pythonconsole/Makefile -+plugins/pythonconsole/org.mate.pluma.plugins.pythonconsole.gschema.xml - plugins/quickopen/Makefile - plugins/quickopen/quickopen/Makefile - plugins/snippets/data/lang/Makefile -diff --git a/plugins/pythonconsole/Makefile.am b/plugins/pythonconsole/Makefile.am -index 0a9ff96..51a2e4a 100644 ---- a/plugins/pythonconsole/Makefile.am -+++ b/plugins/pythonconsole/Makefile.am -@@ -7,9 +7,22 @@ plugin_in_files = pythonconsole.plugin.desktop.in - - plugin_DATA = $(plugin_in_files:.plugin.desktop.in=.plugin) - --EXTRA_DIST = $(plugin_in_files) -+pythonconsole_gschema_in = org.mate.pluma.plugins.pythonconsole.gschema.xml.in -+gsettings_SCHEMAS = $(pythonconsole_gschema_in:.xml.in=.xml) -+@GSETTINGS_RULES@ - --CLEANFILES = $(plugin_DATA) --DISTCLEANFILES = $(plugin_DATA) -+EXTRA_DIST = \ -+ $(plugin_in_files) \ -+ $(pythonconsole_gschema_in) -+ -+CLEANFILES = \ -+ $(plugin_DATA) \ -+ $(gsettings_SCHEMAS_in) \ -+ $(gsettings_SCHEMAS) -+ -+DISTCLEANFILES = \ -+ $(plugin_DATA) \ -+ $(gsettings_SCHEMAS_in) \ -+ $(gsettings_SCHEMAS) - - -include $(top_srcdir)/git.mk -diff --git a/plugins/pythonconsole/org.mate.pluma.plugins.pythonconsole.gschema.xml.in b/plugins/pythonconsole/org.mate.pluma.plugins.pythonconsole.gschema.xml.in -new file mode 100644 -index 0000000..7897fa9 ---- /dev/null -+++ b/plugins/pythonconsole/org.mate.pluma.plugins.pythonconsole.gschema.xml.in -@@ -0,0 +1,30 @@ -+ -+ -+ -+ '#314e6c' -+ Command Color Text -+ The command color text -+ -+ -+ '#990000' -+ Error Color Text -+ The error color text -+ -+ -+ true -+ Whether to use the system font -+ -+ If true, the terminal will use the desktop-global standard -+ font if it’s monospace (and the most similar font it can -+ come up with otherwise). -+ -+ -+ -+ 'Monospace 10' -+ Font -+ -+ A Pango font name. Examples are “Sans 12” or “Monospace Bold 14”. -+ -+ -+ -+ -diff --git a/plugins/pythonconsole/pythonconsole/__init__.py b/plugins/pythonconsole/pythonconsole/__init__.py -index 07d13c7..171b367 100755 ---- a/plugins/pythonconsole/pythonconsole/__init__.py -+++ b/plugins/pythonconsole/pythonconsole/__init__.py -@@ -24,22 +24,22 @@ - # Bits from pluma Python Console Plugin - # Copyrignt (C), 2005 Raphaël Slinckx - --from gi.repository import GObject, Gtk, Peas, Pluma -+from gi.repository import GObject, Gtk, Peas, PeasGtk, Pluma - --from console import PythonConsole --from config import PythonConsoleConfigDialog --from config import PythonConsoleConfig -+from .console import PythonConsole -+from .config import PythonConsoleConfigWidget -+from .config import PythonConsoleConfig - - PYTHON_ICON = 'text-x-python' - --class PythonConsolePlugin(GObject.Object, Peas.Activatable): -+class PythonConsolePlugin(GObject.Object, Peas.Activatable, PeasGtk.Configurable): - __gtype_name__ = "PythonConsolePlugin" - - object = GObject.Property(type=GObject.Object) - - def __init__(self): - GObject.Object.__init__(self) -- self.dlg = None -+ self.config_widget = None - - def do_activate(self): - window = self.object -@@ -47,8 +47,8 @@ class PythonConsolePlugin(GObject.Object, Peas.Activatable): - self._console = PythonConsole(namespace = {'__builtins__' : __builtins__, - 'pluma' : Pluma, - 'window' : window}) -- self._console.eval('print "You can access the main window through ' \ -- '\'window\' :\\n%s" % window', False) -+ self._console.eval('print("You can access the main window through ' \ -+ '\'window\' :\\n%s" % window)', False) - bottom = window.get_bottom_panel() - image = Gtk.Image() - image.set_from_icon_name(PYTHON_ICON, Gtk.IconSize.MENU) -@@ -61,22 +61,9 @@ class PythonConsolePlugin(GObject.Object, Peas.Activatable): - bottom = window.get_bottom_panel() - bottom.remove_item(self._console) - --def create_configure_dialog(self): -- -- if not self.dlg: -- self.dlg = PythonConsoleConfigDialog(self.get_data_dir()) -- -- dialog = self.dlg.dialog() -- window = pluma.app_get_default().get_active_window() -- if window: -- dialog.set_transient_for(window) -- -- return dialog -- --# Here we dynamically insert create_configure_dialog based on if configuration --# is enabled. This has to be done like this because pluma checks if a plugin --# is configurable solely on the fact that it has this member defined or not --if PythonConsoleConfig.enabled(): -- PythonConsolePlugin.create_configure_dialog = create_configure_dialog -+ def do_create_configure_widget(self): -+ if not self.config_widget: -+ self.config_widget = PythonConsoleConfigWidget(self.plugin_info.get_data_dir()) -+ return self.config_widget.configure_widget() - - # ex:et:ts=4: -diff --git a/plugins/pythonconsole/pythonconsole/config.py b/plugins/pythonconsole/pythonconsole/config.py -index fce0c9d..f973e38 100755 ---- a/plugins/pythonconsole/pythonconsole/config.py -+++ b/plugins/pythonconsole/pythonconsole/config.py -@@ -25,110 +25,126 @@ - # Copyrignt (C), 2005 Raphaël Slinckx - - import os --import gtk -+from gi.repository import Gio, Gtk, Gdk - --__all__ = ('PythonConsoleConfig', 'PythonConsoleConfigDialog') -- --MATECONF_KEY_BASE = '/apps/pluma/plugins/pythonconsole' --MATECONF_KEY_COMMAND_COLOR = MATECONF_KEY_BASE + '/command-color' --MATECONF_KEY_ERROR_COLOR = MATECONF_KEY_BASE + '/error-color' -- --DEFAULT_COMMAND_COLOR = '#314e6c' # Blue Shadow --DEFAULT_ERROR_COLOR = '#990000' # Accent Red Dark -+__all__ = ('PythonConsoleConfig', 'PythonConsoleConfigWidget') - - class PythonConsoleConfig(object): -- try: -- import mateconf -- except ImportError: -- mateconf = None -- -- def __init__(self): -- pass - -- @staticmethod -- def enabled(): -- return PythonConsoleConfig.mateconf != None -+ CONSOLE_KEY_BASE = 'org.mate.pluma.plugins.pythonconsole' -+ CONSOLE_KEY_COMMAND_COLOR = 'command-color' -+ CONSOLE_KEY_ERROR_COLOR = 'error-color' -+ CONSOLE_KEY_USE_SYSTEM_FONT = 'use-system-font' -+ CONSOLE_KEY_FONT = 'font' - -- @staticmethod -- def add_handler(handler): -- if PythonConsoleConfig.mateconf: -- PythonConsoleConfig.mateconf.client_get_default().notify_add(MATECONF_KEY_BASE, handler) -+ INTERFACE_KEY_BASE = 'org.mate.interface' -+ INTERFACE_KEY_MONOSPACE_FONT_NAME = 'monospace-font-name' - - color_command = property( -- lambda self: self.mateconf_get_str(MATECONF_KEY_COMMAND_COLOR, DEFAULT_COMMAND_COLOR), -- lambda self, value: self.mateconf_set_str(MATECONF_KEY_COMMAND_COLOR, value)) -+ lambda self: self.console_settings.get_string(self.CONSOLE_KEY_COMMAND_COLOR), -+ lambda self, value: self.console_settings.set_string(self.CONSOLE_KEY_COMMAND_COLOR, value) -+ ) - - color_error = property( -- lambda self: self.mateconf_get_str(MATECONF_KEY_ERROR_COLOR, DEFAULT_ERROR_COLOR), -- lambda self, value: self.mateconf_set_str(MATECONF_KEY_ERROR_COLOR, value)) -+ lambda self: self.console_settings.get_string(self.CONSOLE_KEY_ERROR_COLOR), -+ lambda self, value: self.console_settings.set_string(self.CONSOLE_KEY_ERROR_COLOR, value) -+ ) - -- @staticmethod -- def mateconf_get_str(key, default=''): -- if not PythonConsoleConfig.mateconf: -- return default -+ use_system_font = property( -+ lambda self: self.console_settings.get_boolean(self.CONSOLE_KEY_USE_SYSTEM_FONT), -+ lambda self, value: self.console_settings.set_boolean(self.CONSOLE_KEY_USE_SYSTEM_FONT, value) -+ ) - -- val = PythonConsoleConfig.mateconf.client_get_default().get(key) -- if val is not None and val.type == mateconf.VALUE_STRING: -- return val.get_string() -- else: -- return default -+ font = property( -+ lambda self: self.console_settings.get_string(self.CONSOLE_KEY_FONT), -+ lambda self, value: self.console_settings.set_string(self.CONSOLE_KEY_FONT, value) -+ ) - -- @staticmethod -- def mateconf_set_str(key, value): -- if not PythonConsoleConfig.mateconf: -- return -+ monospace_font_name = property( -+ lambda self: self.interface_settings.get_string(self.INTERFACE_KEY_MONOSPACE_FONT_NAME) -+ ) -+ -+ console_settings = Gio.Settings.new(CONSOLE_KEY_BASE) -+ interface_settings = Gio.Settings.new(INTERFACE_KEY_BASE) -+ -+ def __init__(self): -+ object.__init__(self) -+ -+ @classmethod -+ def enabled(self): -+ return self.console_settings != None -+ -+ @classmethod -+ def add_handler(self, handler): -+ self.console_settings.connect("changed", handler) -+ self.interface_settings.connect("changed", handler) - -- v = PythonConsoleConfig.mateconf.Value(mateconf.VALUE_STRING) -- v.set_string(value) -- PythonConsoleConfig.mateconf.client_get_default().set(key, v) - --class PythonConsoleConfigDialog(object): -+class PythonConsoleConfigWidget(object): -+ -+ CONSOLE_KEY_BASE = 'org.mate.pluma.plugins.pythonconsole' -+ CONSOLE_KEY_COMMAND_COLOR = 'command-color' -+ CONSOLE_KEY_ERROR_COLOR = 'error-color' - - def __init__(self, datadir): - object.__init__(self) -- self._dialog = None -+ self._widget = None - self._ui_path = os.path.join(datadir, 'ui', 'config.ui') -- self.config = PythonConsoleConfig() -+ self._config = PythonConsoleConfig() -+ self._ui = Gtk.Builder() - -- def dialog(self): -- if self._dialog is None: -- self._ui = gtk.Builder() -+ def configure_widget(self): -+ if self._widget is None: - self._ui.add_from_file(self._ui_path) - - self.set_colorbutton_color(self._ui.get_object('colorbutton-command'), -- self.config.color_command) -+ self._config.color_command) - self.set_colorbutton_color(self._ui.get_object('colorbutton-error'), -- self.config.color_error) -- -+ self._config.color_error) -+ checkbox = self._ui.get_object('checkbox-system-font') -+ checkbox.set_active(self._config.use_system_font) -+ self._fontbutton = self._ui.get_object('fontbutton-font') -+ self._fontbutton.set_font_name(self._config.font) -+ self.on_checkbox_system_font_toggled(checkbox) - self._ui.connect_signals(self) - -- self._dialog = self._ui.get_object('dialog-config') -- self._dialog.show_all() -- else: -- self._dialog.present() -+ self._widget = self._ui.get_object('widget-config') -+ self._widget.show_all() - -- return self._dialog -+ return self._widget - - @staticmethod - def set_colorbutton_color(colorbutton, value): -- try: -- color = gtk.gdk.color_parse(value) -- except ValueError: -- pass # Default color in config.ui used -- else: -- colorbutton.set_color(color) -+ rgba = Gdk.RGBA() -+ parsed = rgba.parse(value) - -- def on_dialog_config_response(self, dialog, response_id): -- self._dialog.destroy() -- -- def on_dialog_config_destroy(self, dialog): -- self._dialog = None -- self._ui = None -+ if parsed: -+ colorbutton.set_rgba(rgba) - - def on_colorbutton_command_color_set(self, colorbutton): -- self.config.color_command = colorbutton.get_color().to_string() -+ self._config.color_command = colorbutton.get_color().to_string() - - def on_colorbutton_error_color_set(self, colorbutton): -- self.config.color_error = colorbutton.get_color().to_string() -+ self._config.color_error = colorbutton.get_color().to_string() -+ -+ def on_checkbox_system_font_toggled(self, checkbox): -+ val = checkbox.get_active() -+ self._config.use_system_font = val -+ self._fontbutton.set_sensitive(not val) -+ -+ def on_fontbutton_font_set(self, fontbutton): -+ self._config.font = fontbutton.get_font_name() -+ -+ def on_widget_config_parent_set(self, widget, oldparent): -+ # Set icon in dialog close button. -+ try: -+ actionarea = widget.get_toplevel().get_action_area() -+ image = Gtk.Image.new_from_icon_name("window-close", -+ Gtk.IconSize.BUTTON) -+ for button in actionarea.get_children(): -+ button.set_image(image) -+ button.set_property("always-show-image", True) -+ except: -+ pass - - # ex:et:ts=4: -diff --git a/plugins/pythonconsole/pythonconsole/config.ui b/plugins/pythonconsole/pythonconsole/config.ui -index 8d337d6..392be7d 100644 ---- a/plugins/pythonconsole/pythonconsole/config.ui -+++ b/plugins/pythonconsole/pythonconsole/config.ui -@@ -1,126 +1,124 @@ - -- -+ - - -- -+ - True - False -- window-close -- -- -- False -- center-on-parent -- True -- dialog -- -- -- -- -+ 6 -+ 6 -+ 6 -+ 6 -+ vertical -+ 6 -+ 6 -+ -+ -+ -+ True -+ False -+ _Error color: -+ True -+ 0 -+ -+ -+ 0 -+ 1 -+ -+ -+ -+ -+ True -+ True -+ True -+ rgb(153,0,0) -+ -+ -+ -+ 1 -+ 1 -+ -+ -+ -+ -+ True -+ True -+ True -+ rgb(49,78,108) -+ -+ -+ -+ 1 -+ 0 -+ -+ -+ -+ - True - False -- -- -- True -- False -- end -- -- -- _Close -- True -- True -- True -- image1 -- True -- -- -- True -- True -- 0 -- -- -- -- -- False -- False -- end -- 0 -- -- -- -- -- True -- False -- 6 -- 2 -- 2 -- 6 -- 6 -- -- -- True -- False -- C_ommand color: -- True -- colorbutton-command -- 0 -- -- -- -- -- True -- False -- _Error color: -- True -- colorbutton-error -- 0 -- -- -- 1 -- 2 -- -- -- -- -- True -- True -- True -- #31314e4e6c6c -- -- -- -- 1 -- 2 -- -- -- -- -- True -- True -- True -- #999900000000 -- -- -- -- 1 -- 2 -- 1 -- 2 -- -- -- -- -- False -- True -- 1 -- -- -+ C_ommand color: -+ True -+ 0 - -+ -+ 0 -+ 0 -+ - -- -- button1 -- - -- -+ -+ True -+ False -+ True -+ -+ -+ 0 -+ 2 -+ 2 -+ -+ -+ -+ -+ Use system fixed width font -+ True -+ True -+ False -+ start -+ True -+ True -+ -+ -+ -+ 0 -+ 3 -+ 2 -+ -+ -+ -+ -+ True -+ False -+ start -+ Font: -+ -+ -+ 0 -+ 4 -+ -+ -+ -+ -+ True -+ True -+ True -+ Sans 12 -+ -+ -+ -+ -+ 1 -+ 4 -+ - - - -diff --git a/plugins/pythonconsole/pythonconsole/console.py b/plugins/pythonconsole/pythonconsole/console.py -index 75f60e4..0fd9c7c 100755 ---- a/plugins/pythonconsole/pythonconsole/console.py -+++ b/plugins/pythonconsole/pythonconsole/console.py -@@ -30,7 +30,7 @@ import re - import traceback - from gi.repository import GObject, Gdk, Gtk, Pango - --from config import PythonConsoleConfig -+from .config import PythonConsoleConfig - - __all__ = ('PythonConsole', 'OutFile') - -@@ -40,13 +40,14 @@ class PythonConsole(Gtk.ScrolledWindow): - 'grab-focus' : 'override', - } - -+ DEFAULT_FONT = "Monospace 10" -+ - def __init__(self, namespace = {}): - Gtk.ScrolledWindow.__init__(self) - - self.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) - self.set_shadow_type(Gtk.ShadowType.IN) - self.view = Gtk.TextView() -- self.view.modify_font(Pango.font_description_from_string('Monospace')) - self.view.set_editable(True) - self.view.set_wrap_mode(Gtk.WrapMode.WORD_CHAR) - self.add(self.view) -@@ -57,7 +58,8 @@ class PythonConsole(Gtk.ScrolledWindow): - self.error = buffer.create_tag("error") - self.command = buffer.create_tag("command") - -- PythonConsoleConfig.add_handler(self.apply_preferences) -+ self.config = PythonConsoleConfig() -+ self.config.add_handler(self.apply_preferences) - self.apply_preferences() - - self.__spaces_pattern = re.compile(r'^\s+') -@@ -88,9 +90,28 @@ class PythonConsole(Gtk.ScrolledWindow): - self.view.grab_focus() - - def apply_preferences(self, *args): -- config = PythonConsoleConfig() -- self.error.set_property("foreground", config.color_error) -- self.command.set_property("foreground", config.color_command) -+ self.error.set_property("foreground", self.config.color_error) -+ self.command.set_property("foreground", self.config.color_command) -+ -+ if self.config.use_system_font: -+ font_name = self.config.monospace_font_name -+ else: -+ font_name = self.config.font -+ -+ font_desc = None -+ try: -+ font_desc = Pango.FontDescription(font_name) -+ except: -+ try: -+ font_desc = Pango.FontDescription(self.config.monospace_font_name) -+ except: -+ try: -+ font_desc = Pango.FontDescription(self.DEFAULT_FONT) -+ except: -+ pass -+ -+ if font_desc: -+ self.view.modify_font(font_desc) - - def stop(self): - self.namespace = None -@@ -98,11 +119,13 @@ class PythonConsole(Gtk.ScrolledWindow): - def __key_press_event_cb(self, view, event): - modifier_mask = Gtk.accelerator_get_default_mod_mask() - event_state = event.state & modifier_mask -+ keyname = Gdk.keyval_name(event.keyval) - -- if event.keyval == Gdk.KEY_D and event_state == Gdk.ModifierType.CONTROL_MASK: -+ if keyname == "d" and event_state == Gdk.ModifierType.CONTROL_MASK: - self.destroy() - -- elif event.keyval == Gdk.KEY_Return and event_state == Gdk.ModifierType.CONTROL_MASK: -+ elif keyname == "Return" and \ -+ event_state == Gdk.ModifierType.CONTROL_MASK: - # Get the command - buffer = view.get_buffer() - inp_mark = buffer.get_mark("input") -@@ -128,7 +151,7 @@ class PythonConsole(Gtk.ScrolledWindow): - GObject.idle_add(self.scroll_to_end) - return True - -- elif event.keyval == Gdk.KEY_Return: -+ elif keyname == "Return": - # Get the marks - buffer = view.get_buffer() - lin_mark = buffer.get_mark("input-line") -@@ -172,22 +195,22 @@ class PythonConsole(Gtk.ScrolledWindow): - GObject.idle_add(self.scroll_to_end) - return True - -- elif event.keyval == Gdk.KEY_KP_Down or event.keyval == Gdk.KEY_Down: -+ elif keyname == "KP_Down" or keyname == "Down": - # Next entry from history - view.emit_stop_by_name("key_press_event") - self.history_down() - GObject.idle_add(self.scroll_to_end) - return True - -- elif event.keyval == Gdk.KEY_KP_Up or event.keyval == Gdk.KEY_Up: -+ elif keyname == "KP_Up" or keyname == "Up": - # Previous entry from history - view.emit_stop_by_name("key_press_event") - self.history_up() - GObject.idle_add(self.scroll_to_end) - return True - -- elif event.keyval == Gdk.KEY_KP_Left or event.keyval == Gdk.KEY_Left or \ -- event.keyval == Gdk.KEY_BackSpace: -+ elif keyname == "KP_Left" or keyname == "Left" or \ -+ keyname == "BackSpace": - buffer = view.get_buffer() - inp = buffer.get_iter_at_mark(buffer.get_mark("input")) - cur = buffer.get_iter_at_mark(buffer.get_insert()) -@@ -200,7 +223,7 @@ class PythonConsole(Gtk.ScrolledWindow): - # For the console we enable smart/home end behavior incoditionally - # since it is useful when editing python - -- elif (event.keyval == Gdk.KEY_KP_Home or event.keyval == Gdk.KEY_Home) and \ -+ elif (keyname == "KP_Home" or keyname == "Home") and \ - event_state == event_state & (Gdk.ModifierType.SHIFT_MASK|Gdk.ModifierType.CONTROL_MASK): - # Go to the begin of the command instead of the begin of the line - buffer = view.get_buffer() -@@ -219,7 +242,7 @@ class PythonConsole(Gtk.ScrolledWindow): - buffer.place_cursor(iter) - return True - -- elif (event.keyval == Gdk.KEY_KP_End or event.keyval == Gdk.KEY_End) and \ -+ elif (keyname == "KP_End" or keyname == "End") and \ - event_state == event_state & (Gdk.ModifierType.SHIFT_MASK|Gdk.ModifierType.CONTROL_MASK): - - buffer = view.get_buffer() -@@ -323,15 +346,18 @@ class PythonConsole(Gtk.ScrolledWindow): - # eval and exec are broken in how they deal with utf8-encoded - # strings so we have to explicitly decode the command before - # passing it along -- command = command.decode('utf8') -+ try: -+ command = command.decode('utf8') -+ except: -+ pass - - try: - try: - r = eval(command, self.namespace, self.namespace) - if r is not None: -- print `r` -+ print(repr(r)) - except SyntaxError: -- exec command in self.namespace -+ exec(command, self.namespace) - except: - if hasattr(sys, 'last_type') and sys.last_type == SystemExit: - self.destroy() -@@ -343,7 +369,7 @@ class PythonConsole(Gtk.ScrolledWindow): - - def destroy(self): - pass -- #gtk.ScrolledWindow.destroy(self) -+ #Gtk.ScrolledWindow.destroy(self) - - class OutFile: - """A fake output file object. It sends output to a TK test widget, -@@ -361,8 +387,8 @@ class OutFile: - def readlines(self): return [] - def write(self, s): self.console.write(s, self.tag) - def writelines(self, l): self.console.write(l, self.tag) -- def seek(self, a): raise IOError, (29, 'Illegal seek') -- def tell(self): raise IOError, (29, 'Illegal seek') -+ def seek(self, a): raise IOError(29, 'Illegal seek') -+ def tell(self): raise IOError(29, 'Illegal seek') - truncate = tell - - # ex:et:ts=4: --- -2.21.0 - diff --git a/pluma_0001-quickopen-plugin-change-code-for-Python-2-3-compatib.patch b/pluma_0001-quickopen-plugin-change-code-for-Python-2-3-compatib.patch deleted file mode 100644 index cf919d63713c9e4a5133bd1a60e8935059eadc09..0000000000000000000000000000000000000000 --- a/pluma_0001-quickopen-plugin-change-code-for-Python-2-3-compatib.patch +++ /dev/null @@ -1,427 +0,0 @@ -From bcb21731aacc344cdcd73631346633b6c50d4ec0 Mon Sep 17 00:00:00 2001 -From: Patrick Monnerat -Date: Thu, 23 May 2019 20:20:13 +0200 -Subject: [PATCH] quickopen plugin: change code for Python 2 & 3 compatibility. - ---- - plugins/quickopen/quickopen/__init__.py | 2 +- - plugins/quickopen/quickopen/popup.py | 124 ++++++++++++-------- - plugins/quickopen/quickopen/virtualdirs.py | 8 +- - plugins/quickopen/quickopen/windowhelper.py | 28 +++-- - 4 files changed, 94 insertions(+), 68 deletions(-) - -diff --git a/plugins/quickopen/quickopen/__init__.py b/plugins/quickopen/quickopen/__init__.py -index 3ae72a4..17ccdf7 100644 ---- a/plugins/quickopen/quickopen/__init__.py -+++ b/plugins/quickopen/quickopen/__init__.py -@@ -18,7 +18,7 @@ - # Boston, MA 02110-1301, USA. - - from gi.repository import GObject, Peas --from windowhelper import WindowHelper -+from .windowhelper import WindowHelper - - class QuickOpenPlugin(GObject.Object, Peas.Activatable): - __gtype_name__ = "QuickOpenPlugin" -diff --git a/plugins/quickopen/quickopen/popup.py b/plugins/quickopen/quickopen/popup.py -index c6cc801..be40509 100644 ---- a/plugins/quickopen/quickopen/popup.py -+++ b/plugins/quickopen/quickopen/popup.py -@@ -18,10 +18,11 @@ - # Boston, MA 02110-1301, USA. - - import os -+import sys - import fnmatch - import xml.sax.saxutils - from gi.repository import GObject, Gio, GLib, Gdk, Gtk, Pango, Pluma --from virtualdirs import VirtualDirectory -+from .virtualdirs import VirtualDirectory - - class Popup(Gtk.Dialog): - __gtype_name__ = "QuickOpenPopup" -@@ -30,11 +31,12 @@ class Popup(Gtk.Dialog): - Gtk.Dialog.__init__(self, - title=_('Quick Open'), - parent=window, -- flags=Gtk.DialogFlags.DESTROY_WITH_PARENT | Gtk.DialogFlags.MODAL, -- buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)) -- -- self._open_button = self.add_button(Gtk.STOCK_OPEN, Gtk.ResponseType.ACCEPT) -+ flags=Gtk.DialogFlags.DESTROY_WITH_PARENT | Gtk.DialogFlags.MODAL) - -+ self._add_button(_("_Cancel"), Gtk.ResponseType.CANCEL, "process-stop") -+ self._open_button = self._add_button(_("_Open"), -+ Gtk.ResponseType.ACCEPT, -+ "document-open") - self._handler = handler - self._build_ui() - -@@ -48,7 +50,10 @@ class Popup(Gtk.Dialog): - self._busy_cursor = Gdk.Cursor(Gdk.CursorType.WATCH) - - accel_group = Gtk.AccelGroup() -- accel_group.connect(Gdk.KEY_l, Gdk.ModifierType.CONTROL_MASK, 0, self.on_focus_entry) -+ accel_group.connect(Gdk.keyval_from_name('l'), -+ Gdk.ModifierType.CONTROL_MASK, -+ 0, -+ self.on_focus_entry) - - self.add_accel_group(accel_group) - -@@ -63,10 +68,11 @@ class Popup(Gtk.Dialog): - return self._size - - def _build_ui(self): -+ self.set_border_width(5) - vbox = self.get_content_area() - vbox.set_spacing(3) - -- self._entry = Gtk.Entry() -+ self._entry = Gtk.SearchEntry() - - self._entry.connect('changed', self.on_changed) - self._entry.connect('key-press-event', self.on_key_press_event) -@@ -78,7 +84,10 @@ class Popup(Gtk.Dialog): - tv = Gtk.TreeView() - tv.set_headers_visible(False) - -- self._store = Gtk.ListStore(Gio.Icon, str, GObject.Object, Gio.FileType) -+ self._store = Gtk.ListStore(Gio.Icon, -+ str, -+ GObject.Object, -+ Gio.FileType) - tv.set_model(self._store) - - self._treeview = tv -@@ -106,7 +115,7 @@ class Popup(Gtk.Dialog): - vbox.pack_start(sw, True, True, 0) - - lbl = Gtk.Label() -- lbl.set_alignment(0, 0.5) -+ lbl.set_halign(Gtk.Align.START) - lbl.set_ellipsize(Pango.EllipsizeMode.MIDDLE) - self._info_label = lbl - -@@ -129,19 +138,22 @@ class Popup(Gtk.Dialog): - cell.set_property('cell-background-set', False) - cell.set_property('style-set', False) - -- def _icon_from_stock(self, stock): -- theme = Gtk.icon_theme_get_default() -- size = Gtk.icon_size_lookup(Gtk.IconSize.MENU) -- pixbuf = theme.load_icon(stock, size[0], Gtk.IconLookupFlags.USE_BUILTIN) -- -- return pixbuf -+ def _add_button(self, label, response, icon=None): -+ button = self.add_button(label, response) -+ if icon: -+ image = Gtk.Image.new_from_icon_name(icon, Gtk.IconSize.BUTTON) -+ button.set_image(image) -+ button.set_property("always-show-image", True) -+ return button - - def _list_dir(self, gfile): - entries = [] - - try: -- ret = gfile.enumerate_children("standard::*", Gio.FileQueryInfoFlags.NONE, None) -- except GLib.GError: -+ ret = gfile.enumerate_children("standard::*", -+ Gio.FileQueryInfoFlags.NONE, -+ None) -+ except GLib.Error: - pass - - if isinstance(ret, Gio.FileEnumerator): -@@ -151,27 +163,23 @@ class Popup(Gtk.Dialog): - if not entry: - break - -- entries.append((gfile.get_child(entry.get_name()), entry)) -+ if not entry.get_is_backup(): -+ entries.append((gfile.get_child(entry.get_name()), entry)) - else: - entries = ret - - children = [] - - for entry in entries: -- children.append((entry[0], entry[1].get_name(), entry[1].get_file_type(), entry[1].get_icon())) -+ children.append((entry[0], -+ entry[1].get_name(), -+ entry[1].get_file_type(), -+ entry[1].get_icon())) - - return children - -- def _compare_entries(self, a, b, lpart): -- if lpart in a: -- if lpart in b: -- return cmp(a.index(lpart), b.index(lpart)) -- else: -- return -1 -- elif lpart in b: -- return 1 -- else: -- return 0 -+ def _key_entries(self, pos): -+ return pos if pos >= 0 else sys.maxsize - - def _match_glob(self, s, glob): - if glob: -@@ -185,8 +193,7 @@ class Popup(Gtk.Dialog): - - if not d in self._cache: - entries = self._list_dir(d) -- entries.sort(lambda x, y: cmp(x[1].lower(), y[1].lower())) -- -+ entries.sort(key=lambda x: x[1].lower()) - self._cache[d] = entries - else: - entries = self._cache[d] -@@ -212,7 +219,7 @@ class Popup(Gtk.Dialog): - (not lpart or len(parts) == 1): - found.append(entry) - -- found.sort(lambda a, b: self._compare_entries(a[1].lower(), b[1].lower(), lpart)) -+ found.sort(key=lambda x: self._key_entries(x[1].lower().find(lpart))) - - if lpart == '..': - newdirs.append(d.get_parent()) -@@ -251,7 +258,9 @@ class Popup(Gtk.Dialog): - return os.sep.join(out) - - def _get_icon(self, f): -- query = f.query_info(Gio.FILE_ATTRIBUTE_STANDARD_ICON, Gio.FileQueryInfoFlags.NONE, None) -+ query = f.query_info(Gio.FILE_ATTRIBUTE_STANDARD_ICON, -+ Gio.FileQueryInfoFlags.NONE, -+ None) - - if not query: - return None -@@ -304,7 +313,10 @@ class Popup(Gtk.Dialog): - for d in self._dirs: - if isinstance(d, VirtualDirectory): - for entry in d.enumerate_children("standard::*", 0, None): -- self._append_to_store((entry[1].get_icon(), xml.sax.saxutils.escape(entry[1].get_name()), entry[0], entry[1].get_file_type())) -+ self._append_to_store((entry[1].get_icon(), -+ xml.sax.saxutils.escape(entry[1].get_name()), -+ entry[0], -+ entry[1].get_file_type())) - - def _set_busy(self, busy): - if busy: -@@ -337,14 +349,17 @@ class Popup(Gtk.Dialog): - for d in self._dirs: - for entry in self.do_search_dir(parts, d): - pathparts = self._make_parts(d, entry[0], parts) -- self._append_to_store((entry[3], self.make_markup(parts, pathparts), entry[0], entry[2])) -+ self._append_to_store((entry[3], -+ self.make_markup(parts, pathparts), -+ entry[0], -+ entry[2])) - - piter = self._store.get_iter_first() - - if piter: -- self._treeview.get_selection().select_path(self._store.get_path(piter)) -+ path = self._store.get_path(piter) -+ self._treeview.get_selection().select_path(path) - -- self.get_window().set_cursor(None) - self._set_busy(False) - - def do_show(self): -@@ -366,7 +381,7 @@ class Popup(Gtk.Dialog): - model, rows = selection.get_selected_rows() - start = rows[0] - -- self._shift_start = Gtk.TreeRowReference(self._store, start) -+ self._shift_start = Gtk.TreeRowReference.new(self._store, start) - else: - start = self._shift_start.get_path() - -@@ -419,7 +434,7 @@ class Popup(Gtk.Dialog): - else: - self._select_index(num - 1, hasctrl, hasshift) - else: -- idx = path[0] -+ idx = path.get_indices()[0] - - if idx + howmany < 0: - self._select_index(0, hasctrl, hasshift) -@@ -461,19 +476,22 @@ class Popup(Gtk.Dialog): - if text[i] == os.sep: - break - -- self._entry.set_text(os.path.join(text[:i], os.path.basename(info[0].get_uri())) + os.sep) -+ self._entry.set_text(os.path.join(text[:i], -+ os.path.basename(info[0].get_uri())) + os.sep) - self._entry.set_position(-1) - self._entry.grab_focus() - return True - - if rows and ret: -- self.destroy() -+ # We destroy the popup in an idle callback to work around a crash that happens with -+ # GTK_IM_MODULE=xim. See https://bugzilla.gnome.org/show_bug.cgi?id=737711 . -+ GLib.idle_add(self.destroy) - - if not rows: - gfile = self._direct_file() - - if gfile and self._handler(gfile): -- self.destroy() -+ GLib.idle_add(self.destroy) - else: - ret = False - else: -@@ -495,20 +513,24 @@ class Popup(Gtk.Dialog): - - def on_key_press_event(self, widget, event): - move_mapping = { -- Gdk.KEY_Down: 1, -- Gdk.KEY_Up: -1, -- Gdk.KEY_Page_Down: 5, -- Gdk.KEY_Page_Up: -5 -+ "Down": 1, -+ "Up": -1, -+ "Page_Down": 5, -+ "Page_Up": -5 - } - -- if event.keyval == Gdk.KEY_Escape: -+ keyname = Gdk.keyval_name(event.keyval) -+ -+ if keyname == "Escape": - self.destroy() - return True -- elif event.keyval in move_mapping: -- return self._move_selection(move_mapping[event.keyval], event.state & Gdk.ModifierType.CONTROL_MASK, event.state & Gdk.ModifierType.SHIFT_MASK) -- elif event.keyval in [Gdk.KEY_Return, Gdk.KEY_KP_Enter, Gdk.KEY_Tab, Gdk.KEY_ISO_Left_Tab]: -+ elif keyname in move_mapping: -+ return self._move_selection(move_mapping[keyname], -+ event.state & Gdk.ModifierType.CONTROL_MASK, -+ event.state & Gdk.ModifierType.SHIFT_MASK) -+ elif keyname in ["Return", "KP_Enter", "Tab", "ISO_Left_Tab"]: - return self._activate() -- elif event.keyval == Gdk.KEY_space and event.state & Gdk.ModifierType.CONTROL_MASK: -+ elif keyname == "space" and event.state & Gdk.ModifierType.CONTROL_MASK: - self.toggle_cursor() - - return False -diff --git a/plugins/quickopen/quickopen/virtualdirs.py b/plugins/quickopen/quickopen/virtualdirs.py -index 53d716a..7bf66b8 100644 ---- a/plugins/quickopen/quickopen/virtualdirs.py -+++ b/plugins/quickopen/quickopen/virtualdirs.py -@@ -38,7 +38,9 @@ class VirtualDirectory(object): - return - - try: -- info = child.query_info("standard::*", Gio.FileQueryInfoFlags.NONE, None) -+ info = child.query_info("standard::*", -+ Gio.FileQueryInfoFlags.NONE, -+ None) - - if info: - self._children.append((child, info)) -@@ -46,7 +48,7 @@ class VirtualDirectory(object): - pass - - class RecentDocumentsDirectory(VirtualDirectory): -- def __init__(self, maxitems=10): -+ def __init__(self, maxitems=200): - VirtualDirectory.__init__(self, 'recent') - - self._maxitems = maxitems -@@ -56,7 +58,7 @@ class RecentDocumentsDirectory(VirtualDirectory): - manager = Gtk.RecentManager.get_default() - - items = manager.get_items() -- items.sort(lambda a, b: cmp(b.get_visited(), a.get_visited())) -+ items.sort(key=lambda a: a.get_visited(), reverse=True) - - added = 0 - -diff --git a/plugins/quickopen/quickopen/windowhelper.py b/plugins/quickopen/quickopen/windowhelper.py -index 19e44cb..7f9ab12 100644 ---- a/plugins/quickopen/quickopen/windowhelper.py -+++ b/plugins/quickopen/quickopen/windowhelper.py -@@ -17,11 +17,12 @@ - # Foundation, Inc., 51 Franklin St, Fifth Floor, - # Boston, MA 02110-1301, USA. - --from popup import Popup - import os -+import codecs - from gi.repository import Gio, GLib, Gtk, Pluma --from virtualdirs import RecentDocumentsDirectory --from virtualdirs import CurrentDocumentsDirectory -+from .popup import Popup -+from .virtualdirs import RecentDocumentsDirectory -+from .virtualdirs import CurrentDocumentsDirectory - - ui_str = """ - -@@ -60,12 +61,13 @@ class WindowHelper: - - def _install_menu(self): - manager = self._window.get_ui_manager() -+ action = Gtk.Action.new("QuickOpen", -+ _("Quick open"), -+ _("Quickly open documents")) -+ action.set_icon_name("document-open") -+ action.connect("activate", self.on_quick_open_activate) - self._action_group = Gtk.ActionGroup("PlumaQuickOpenPluginActions") -- self._action_group.add_actions([ -- ("QuickOpen", Gtk.STOCK_OPEN, _("Quick open"), -- 'O', _("Quickly open documents"), -- self.on_quick_open_activate) -- ]) -+ self._action_group.add_action_with_accel(action, "O") - - manager.insert_action_group(self._action_group, -1) - self._ui_id = manager.add_ui_from_string(ui_str) -@@ -95,10 +97,10 @@ class WindowHelper: - if uri: - gfile = Gio.file_new_for_uri(uri) - -- if gfile.is_native(): -+ if gfile and gfile.is_native(): - paths.append(gfile) - -- except StandardError: -+ except Exception: - pass - - # Recent documents -@@ -128,14 +130,14 @@ class WindowHelper: - self._popup.connect('destroy', self.on_popup_destroy) - - def _local_bookmarks(self): -- filename = os.path.expanduser('~/.gtk-bookmarks') -+ filename = os.path.expanduser('~/.config/gtk-3.0/bookmarks') - - if not os.path.isfile(filename): - return [] - - paths = [] - -- for line in file(filename, 'r').xreadlines(): -+ for line in codecs.open(filename, 'r', encoding='utf-8'): - uri = line.strip().split(" ")[0] - f = Gio.file_new_for_uri(uri) - -@@ -160,7 +162,7 @@ class WindowHelper: - desktopdir = None - - if os.path.isfile(config): -- for line in file(config, 'r').xreadlines(): -+ for line in codecs.open(config, 'r', encoding='utf-8'): - line = line.strip() - - if line.startswith('XDG_DESKTOP_DIR'): --- -2.21.0 - diff --git a/pluma_0001-snippets-plugin-change-code-for-Python-2-3-compatibi.patch b/pluma_0001-snippets-plugin-change-code-for-Python-2-3-compatibi.patch deleted file mode 100644 index 3d497e7c6f08eaf799de53a9cd2ce335b798ddd6..0000000000000000000000000000000000000000 --- a/pluma_0001-snippets-plugin-change-code-for-Python-2-3-compatibi.patch +++ /dev/null @@ -1,2182 +0,0 @@ -From 2056b6f2fcc6f0fd5a190f68c4310129535a7bf5 Mon Sep 17 00:00:00 2001 -From: Patrick Monnerat -Date: Mon, 27 May 2019 18:16:18 +0200 -Subject: [PATCH] snippets plugin: change code for Python 2 & 3 compatibility - ---- - plugins/snippets/snippets/Completion.py | 23 +- - plugins/snippets/snippets/Document.py | 151 ++--- - plugins/snippets/snippets/Exporter.py | 4 +- - plugins/snippets/snippets/Helper.py | 32 +- - plugins/snippets/snippets/Importer.py | 2 +- - plugins/snippets/snippets/LanguageManager.py | 3 +- - plugins/snippets/snippets/Library.py | 43 +- - plugins/snippets/snippets/Manager.py | 76 ++- - plugins/snippets/snippets/Parser.py | 2 +- - plugins/snippets/snippets/Placeholder.py | 85 ++- - plugins/snippets/snippets/Snippet.py | 46 +- - plugins/snippets/snippets/WindowHelper.py | 4 +- - plugins/snippets/snippets/__init__.py | 19 +- - plugins/snippets/snippets/snippets.ui | 679 +++++++------------ - 14 files changed, 528 insertions(+), 641 deletions(-) - -diff --git a/plugins/snippets/snippets/Completion.py b/plugins/snippets/snippets/Completion.py -index a860d21..ad0ad75 100644 ---- a/plugins/snippets/snippets/Completion.py -+++ b/plugins/snippets/snippets/Completion.py -@@ -1,14 +1,14 @@ - from gi.repository import GObject, Gtk, GtkSource, Pluma - --from Library import Library --from LanguageManager import get_language_manager --from Snippet import Snippet -+from .Library import Library -+from .LanguageManager import get_language_manager -+from .Snippet import Snippet - - class Proposal(GObject.Object, GtkSource.CompletionProposal): - __gtype_name__ = "PlumaSnippetsProposal" - - def __init__(self, snippet): -- GObject.Object.__init__(self) -+ super(Proposal, self).__init__() - self._snippet = Snippet(snippet) - - def snippet(self): -@@ -25,7 +25,7 @@ class Provider(GObject.Object, GtkSource.CompletionProvider): - __gtype_name__ = "PlumaSnippetsProvider" - - def __init__(self, name, language_id, handler): -- GObject.Object.__init__(self) -+ super(Provider, self).__init__() - - self.name = name - self.info_widget = None -@@ -38,7 +38,10 @@ class Provider(GObject.Object, GtkSource.CompletionProvider): - theme = Gtk.IconTheme.get_default() - f, w, h = Gtk.icon_size_lookup(Gtk.IconSize.MENU) - -- self.icon = theme.load_icon(Gtk.STOCK_JUSTIFY_LEFT, w, 0) -+ try: -+ self.icon = theme.load_icon("format-justify-left", w, 0) -+ except: -+ self.icon = None - - def __del__(self): - if self.mark: -@@ -89,9 +92,9 @@ class Provider(GObject.Object, GtkSource.CompletionProvider): - - # Filter based on the current word - if word: -- proposals = filter(lambda x: x['tag'].startswith(word), proposals) -+ proposals = (x for x in proposals if x['tag'].startswith(word)) - -- return map(lambda x: Proposal(x), proposals) -+ return [Proposal(x) for x in proposals] - - def do_populate(self, context): - proposals = self.get_proposals(self.get_word(context)) -@@ -113,6 +116,10 @@ class Provider(GObject.Object, GtkSource.CompletionProvider): - - sw = Gtk.ScrolledWindow() - sw.add(view) -+ sw.show_all() -+ -+ # Fixed size -+ sw.set_size_request(300, 200) - - self.info_view = view - self.info_widget = sw -diff --git a/plugins/snippets/snippets/Document.py b/plugins/snippets/snippets/Document.py -index f1cc065..f40186b 100644 ---- a/plugins/snippets/snippets/Document.py -+++ b/plugins/snippets/snippets/Document.py -@@ -18,12 +18,12 @@ - import os - import re - --from gi.repository import GLib, Gio, Gdk, Gtk, GtkSource, Pluma -+from gi.repository import GLib, Gio, Gdk, Gtk, Pluma - --from Library import Library --from Snippet import Snippet --from Placeholder import * --import Completion -+from .Library import Library -+from .Snippet import Snippet -+from .Placeholder import * -+from . import Completion - - class DynamicSnippet(dict): - def __init__(self, text): -@@ -31,9 +31,7 @@ class DynamicSnippet(dict): - self.valid = True - - class Document: -- TAB_KEY_VAL = (Gdk.KEY_Tab, \ -- Gdk.KEY_ISO_Left_Tab) -- SPACE_KEY_VAL = (Gdk.KEY_space,) -+ TAB_KEY_VAL = ('Tab', 'ISO_Left_Tab') - - def __init__(self, instance, view): - self.view = None -@@ -109,7 +107,7 @@ class Document: - self.deactivate_snippet(snippet, True) - - completion = self.view.get_completion() -- if completion: -+ if completion and self.provider in completion.get_providers(): - completion.remove_provider(self.provider) - - self.view = view -@@ -182,8 +180,6 @@ class Document: - snippets = Library().from_accelerator(accelerator, \ - self.language_id) - -- snippets_debug('Accel!') -- - if len(snippets) == 0: - return False - elif len(snippets) == 1: -@@ -193,6 +189,7 @@ class Document: - provider = Completion.Provider(_('Snippets'), self.language_id, self.on_proposal_activated) - provider.set_proposals(snippets) - -+ cm = self.view.get_completion() - cm.show([provider], cm.create_context(None)) - - return True -@@ -266,7 +263,6 @@ class Document: - - # Find the nearest placeholder - if nearest(piter, begin, end, found): -- foundIndex = index - found = placeholder - - # Find the current placeholder -@@ -276,7 +272,7 @@ class Document: - currentIndex = index - current = placeholder - -- if current and current != found and \ -+ if current and found and current != found and \ - (current.begin_iter().compare(found.begin_iter()) == 0 or \ - current.end_iter().compare(found.begin_iter()) == 0) and \ - self.active_placeholder and \ -@@ -363,57 +359,60 @@ class Document: - - def env_get_current_word(self, buf): - start, end = buffer_word_boundary(buf) -- - return buf.get_text(start, end, False) - - def env_get_current_line(self, buf): - start, end = buffer_line_boundary(buf) -- - return buf.get_text(start, end, False) - - def env_get_current_line_number(self, buf): - start, end = buffer_line_boundary(buf) - return str(start.get_line() + 1) - -- def env_get_document_uri(self, buf): -- location = buf.get_location() -- -+ def location_uri_for_env(self, location): - if location: - return location.get_uri() -- else: -- return '' -- -- def env_get_document_name(self, buf): -- location = buf.get_location() -+ return '' - -+ def location_name_for_env(self, location): - if location: - return location.get_basename() -- else: -- return '' -- -- def env_get_document_scheme(self, buf): -- location = buf.get_location() -+ return '' - -+ def location_scheme_for_env(self, location): - if location: - return location.get_uri_scheme() -- else: -- return '' -- -- def env_get_document_path(self, buf): -- location = buf.get_location() -+ return '' - -+ def location_path_for_env(self, location): - if location: - return location.get_path() -- else: -- return '' -- -- def env_get_document_dir(self, buf): -- location = buf.get_location() -+ return '' - -+ def location_dir_for_env(self, location): - if location: -- return location.get_parent().get_path() or '' -- else: -- return '' -+ parent = location.get_parent() -+ -+ if parent and parent.has_uri_scheme('file'): -+ return parent.get_path() or '' -+ -+ return '' -+ -+ def env_add_for_location(self, environ, location, prefix): -+ parts = { -+ 'URI': self.location_uri_for_env, -+ 'NAME': self.location_name_for_env, -+ 'SCHEME': self.location_scheme_for_env, -+ 'PATH': self.location_path_for_env, -+ 'DIR': self.location_dir_for_env, -+ } -+ -+ for k in parts: -+ v = parts[k](location) -+ key = prefix + '_' + k -+ environ[key] = str(v) -+ -+ return environ - - def env_get_document_type(self, buf): - typ = buf.get_mime_type() -@@ -451,25 +450,30 @@ class Document: - - return ' '.join(documents_path) - -- def update_environment(self): -+ def get_environment(self): - buf = self.view.get_buffer() -+ environ = {} -+ -+ for k in os.environ: -+ # Get the original environment -+ v = os.environ[k] -+ environ[k] = v - -- variables = {'PLUMA_SELECTED_TEXT': self.env_get_selected_text, -+ variables = { -+ 'PLUMA_SELECTED_TEXT': self.env_get_selected_text, - 'PLUMA_CURRENT_WORD': self.env_get_current_word, - 'PLUMA_CURRENT_LINE': self.env_get_current_line, - 'PLUMA_CURRENT_LINE_NUMBER': self.env_get_current_line_number, -- 'PLUMA_CURRENT_DOCUMENT_URI': self.env_get_document_uri, -- 'PLUMA_CURRENT_DOCUMENT_NAME': self.env_get_document_name, -- 'PLUMA_CURRENT_DOCUMENT_SCHEME': self.env_get_document_scheme, -- 'PLUMA_CURRENT_DOCUMENT_PATH': self.env_get_document_path, -- 'PLUMA_CURRENT_DOCUMENT_DIR': self.env_get_document_dir, - 'PLUMA_CURRENT_DOCUMENT_TYPE': self.env_get_document_type, - 'PLUMA_DOCUMENTS_URI': self.env_get_documents_uri, - 'PLUMA_DOCUMENTS_PATH': self.env_get_documents_path, - } - - for var in variables: -- os.environ[var] = variables[var](buf) -+ environ[var] = variables[var](buf) -+ -+ self.env_add_for_location(environ, buf.get_location(), 'PLUMA_CURRENT_DOCUMENT') -+ return environ - - def uses_current_word(self, snippet): - matches = re.findall('(\\\\*)\\$PLUMA_CURRENT_WORD', snippet['text']) -@@ -489,12 +493,19 @@ class Document: - - return False - -- def apply_snippet(self, snippet, start = None, end = None): -+ def apply_snippet(self, snippet, start = None, end = None, environ = {}): - if not snippet.valid: - return False - -+ # Set environmental variables -+ env = self.get_environment() -+ -+ if environ: -+ for k in environ: -+ env[k] = environ[k] -+ - buf = self.view.get_buffer() -- s = Snippet(snippet) -+ s = Snippet(snippet, env) - - if not start: - start = buf.get_iter_at_mark(buf.get_insert()) -@@ -513,9 +524,6 @@ class Document: - # it will be removed - start, end = buffer_line_boundary(buf) - -- # Set environmental variables -- self.update_environment() -- - # You know, we could be in an end placeholder - (current, next) = self.next_placeholder() - if current and current.__class__ == PlaceholderEnd: -@@ -527,8 +535,6 @@ class Document: - buf.delete(start, end) - - # Insert the snippet -- holders = len(self.placeholders) -- - if len(self.active_snippets) == 0: - self.first_snippet_inserted() - -@@ -536,7 +542,7 @@ class Document: - self.active_snippets.append(sn) - - # Put cursor at first tab placeholder -- keys = filter(lambda x: x > 0, sn.placeholders.keys()) -+ keys = [x for x in sn.placeholders.keys() if x > 0] - - if len(keys) == 0: - if 0 in sn.placeholders: -@@ -637,7 +643,6 @@ class Document: - return True - - def deactivate_snippet(self, snippet, force = False): -- buf = self.view.get_buffer() - remove = [] - ordered_remove = [] - -@@ -792,10 +797,11 @@ class Document: - library = Library() - - state = event.get_state() -+ keyname = Gdk.keyval_name(event.keyval) - - if not (state & Gdk.ModifierType.CONTROL_MASK) and \ - not (state & Gdk.ModifierType.MOD1_MASK) and \ -- event.keyval in self.TAB_KEY_VAL: -+ keyname in self.TAB_KEY_VAL: - if not state & Gdk.ModifierType.SHIFT_MASK: - return self.run_snippet() - else: -@@ -868,20 +874,9 @@ class Document: - pathname = '' - dirname = '' - ruri = '' -+ environ = {'PLUMA_DROP_DOCUMENT_TYPE': mime} - -- if Pluma.utils_uri_has_file_scheme(uri): -- pathname = gfile.get_path() -- dirname = gfile.get_parent().get_path() -- -- name = os.path.basename(uri) -- scheme = gfile.get_uri_scheme() -- -- os.environ['PLUMA_DROP_DOCUMENT_URI'] = uri -- os.environ['PLUMA_DROP_DOCUMENT_NAME'] = name -- os.environ['PLUMA_DROP_DOCUMENT_SCHEME'] = scheme -- os.environ['PLUMA_DROP_DOCUMENT_PATH'] = pathname -- os.environ['PLUMA_DROP_DOCUMENT_DIR'] = dirname -- os.environ['PLUMA_DROP_DOCUMENT_TYPE'] = mime -+ self.env_add_for_location(environ, gfile, 'PLUMA_DROP_DOCUMENT') - - buf = self.view.get_buffer() - location = buf.get_location() -@@ -890,7 +885,7 @@ class Document: - - relpath = self.relative_path(ruri, uri, mime) - -- os.environ['PLUMA_DROP_DOCUMENT_RELATIVE_PATH'] = relpath -+ environ['PLUMA_DROP_DOCUMENT_RELATIVE_PATH'] = relpath - - mark = buf.get_mark('gtk_drag_target') - -@@ -898,7 +893,7 @@ class Document: - mark = buf.get_insert() - - piter = buf.get_iter_at_mark(mark) -- self.apply_snippet(snippet, piter, piter) -+ self.apply_snippet(snippet, piter, piter, environ) - - def in_bounds(self, x, y): - rect = self.view.get_visible_rect() -@@ -907,6 +902,9 @@ class Document: - return not (x < rect.x or x > rect.x + rect.width or y < rect.y or y > rect.y + rect.height) - - def on_drag_data_received(self, view, context, x, y, data, info, timestamp): -+ if not self.view.get_editable(): -+ return -+ - uris = drop_get_uris(data) - if not uris: - return -@@ -944,6 +942,9 @@ class Document: - return self.view.drag_dest_find_target(context, lst) - - def on_proposal_activated(self, proposal, piter): -+ if not self.view.get_editable(): -+ return False -+ - buf = self.view.get_buffer() - bounds = buf.get_selection_bounds() - -@@ -1048,8 +1049,6 @@ class Document: - if isinstance(placeholder, PlaceholderEnd): - return - -- buf = self.view.get_buffer() -- - col = self.view.get_style_context().get_color(Gtk.StateFlags.INSENSITIVE) - col.alpha = 0.5 - Gdk.cairo_set_source_rgba(ctx, col) -diff --git a/plugins/snippets/snippets/Exporter.py b/plugins/snippets/snippets/Exporter.py -index 850c3a4..713077f 100644 ---- a/plugins/snippets/snippets/Exporter.py -+++ b/plugins/snippets/snippets/Exporter.py -@@ -3,9 +3,9 @@ import tempfile - import sys - import shutil - --from Library import * -+from .Library import * - import xml.etree.ElementTree as et --from Helper import * -+from .Helper import * - - class Exporter: - def __init__(self, filename, snippets): -diff --git a/plugins/snippets/snippets/Helper.py b/plugins/snippets/snippets/Helper.py -index 6d440d0..c1f1c35 100644 ---- a/plugins/snippets/snippets/Helper.py -+++ b/plugins/snippets/snippets/Helper.py -@@ -15,10 +15,10 @@ - # along with this program; if not, write to the Free Software - # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - --import string - from xml.sax import saxutils --from xml.etree.ElementTree import * -+import xml.etree.ElementTree as et - import re -+import codecs - - from gi.repository import Gtk - -@@ -93,7 +93,7 @@ def write_xml(node, f, cdata_nodes=()): - assert node is not None - - if not hasattr(f, "write"): -- f = open(f, "wb") -+ f = codecs.open(f, "wb", encoding="utf-8") - - # Encoding - f.write("\n") -@@ -107,27 +107,27 @@ def _write_node(node, file, cdata_nodes=(), indent=0): - # write XML to file - tag = node.tag - -- if node is Comment: -- _write_indent(file, "\n" % saxutils.escape(node.text.encode('utf-8')), indent) -- elif node is ProcessingInstruction: -- _write_indent(file, "\n" % saxutils.escape(node.text.encode('utf-8')), indent) -+ if node is et.Comment: -+ _write_indent(file, "\n" % saxutils.escape(node.text), indent) -+ elif node is et.ProcessingInstruction: -+ _write_indent(file, "\n" % saxutils.escape(node.text), indent) - else: - items = node.items() - - if items or node.text or len(node): -- _write_indent(file, "<" + tag.encode('utf-8'), indent) -+ _write_indent(file, "<" + tag, indent) - - if items: - items.sort() # lexical order - for k, v in items: -- file.write(" %s=%s" % (k.encode('utf-8'), saxutils.quoteattr(v.encode('utf-8')))) -+ file.write(" %s=%s" % (k, saxutils.quoteattr(v))) - if node.text or len(node): - file.write(">") - if node.text and node.text.strip() != "": - if tag in cdata_nodes: - file.write(_cdata(node.text)) - else: -- file.write(saxutils.escape(node.text.encode('utf-8'))) -+ file.write(saxutils.escape(node.text)) - else: - file.write("\n") - -@@ -135,19 +135,17 @@ def _write_node(node, file, cdata_nodes=(), indent=0): - _write_node(n, file, cdata_nodes, indent + 1) - - if not len(node): -- file.write("\n") -+ file.write("\n") - else: -- _write_indent(file, "\n", \ -- indent) -+ _write_indent(file, "\n", indent) - else: - file.write(" />\n") - - if node.tail and node.tail.strip() != "": -- file.write(saxutils.escape(node.tail.encode('utf-8'))) -+ file.write(saxutils.escape(node.tail)) - --def _cdata(text, replace=string.replace): -- text = text.encode('utf-8') -- return '', ']]]]>') + ']]>' -+def _cdata(text): -+ return '', ']]]]>') + ']]>' - - def buffer_word_boundary(buf): - iter = buf.get_iter_at_mark(buf.get_insert()) -diff --git a/plugins/snippets/snippets/Importer.py b/plugins/snippets/snippets/Importer.py -index c1d211e..83c520c 100644 ---- a/plugins/snippets/snippets/Importer.py -+++ b/plugins/snippets/snippets/Importer.py -@@ -3,7 +3,7 @@ import tempfile - import sys - import shutil - --from Library import * -+from .Library import * - - class Importer: - def __init__(self, filename): -diff --git a/plugins/snippets/snippets/LanguageManager.py b/plugins/snippets/snippets/LanguageManager.py -index e738333..d962dcf 100644 ---- a/plugins/snippets/snippets/LanguageManager.py -+++ b/plugins/snippets/snippets/LanguageManager.py -@@ -1,7 +1,7 @@ - import os - from gi.repository import GtkSource - --from Library import Library -+from .Library import Library - - global manager - manager = None -@@ -19,4 +19,5 @@ def get_language_manager(): - manager.set_search_path(dirs + manager.get_search_path()) - - return manager -+ - # ex:ts=4:et: -diff --git a/plugins/snippets/snippets/Library.py b/plugins/snippets/snippets/Library.py -index f152082..d8ae219 100644 ---- a/plugins/snippets/snippets/Library.py -+++ b/plugins/snippets/snippets/Library.py -@@ -20,11 +20,12 @@ import weakref - import sys - import tempfile - import re -+import codecs - - from gi.repository import Gdk, Gtk - - import xml.etree.ElementTree as et --from Helper import * -+from .Helper import * - - class NamespacedId: - def __init__(self, namespace, id): -@@ -453,28 +454,38 @@ class SnippetsSystemFile: - lambda node: elements.append((node, True)), \ - lambda node: elements.append((node, False))) - -- parser = et.XMLTreeBuilder(target=builder) -+ self.ok = True -+ parser = et.XMLParser(target=builder) - self.insnippet = False - - try: -- f = open(self.path, "r") -+ f = codecs.open(self.path, "r", encoding='utf-8') -+ except IOError: -+ self.ok = False -+ return - -- while True: -+ while self.ok: -+ try: - data = f.read(readsize) -+ except IOError: -+ self.ok = False -+ break - -- if not data: -- break -+ if not data: -+ break - -+ try: - parser.feed(data) -+ except Exception: -+ self.ok = False -+ break - -- for element in elements: -- yield element -+ for element in elements: -+ yield element - -- del elements[:] -+ del elements[:] - -- f.close() -- except IOError: -- self.ok = False -+ f.close() - - def load(self): - if not self.ok: -@@ -531,6 +542,8 @@ class SnippetsUserFile(SnippetsSystemFile): - SnippetsSystemFile.__init__(self, path) - self.tainted = False - self.need_id = False -+ self.modifier = False -+ self.root = None - - def _set_root(self, element): - SnippetsSystemFile._set_root(self, element) -@@ -611,7 +624,7 @@ class SnippetsUserFile(SnippetsSystemFile): - - try: - if not os.path.isdir(path): -- os.makedirs(path, 0755) -+ os.makedirs(path, 0o755) - except OSError: - # TODO: this is bad... - sys.stderr.write("Error in making dirs\n") -@@ -929,8 +942,8 @@ class Library(Singleton): - def valid_accelerator(self, keyval, mod): - mod &= Gtk.accelerator_get_default_mod_mask() - -- return (mod and (Gdk.keyval_to_unicode(keyval) or \ -- keyval in range(Gdk.KEY_F1, Gdk.KEY_F12 + 1))) -+ return mod and (Gdk.keyval_to_unicode(keyval) or \ -+ re.match('^F(?:1[012]?|[2-9])$', Gdk.keyval_name(keyval))) - - def valid_tab_trigger(self, trigger): - if not trigger: -diff --git a/plugins/snippets/snippets/Manager.py b/plugins/snippets/snippets/Manager.py -index 9760fa7..71ada38 100644 ---- a/plugins/snippets/snippets/Manager.py -+++ b/plugins/snippets/snippets/Manager.py -@@ -21,13 +21,13 @@ import shutil - - from gi.repository import GObject, Gio, Gdk, Gtk, GtkSource, Pluma - --from Snippet import Snippet --from Helper import * --from Library import * --from Importer import * --from Exporter import * --from Document import Document --from LanguageManager import get_language_manager -+from .Snippet import Snippet -+from .Helper import * -+from .Library import * -+from .Importer import * -+from .Exporter import * -+from .Document import Document -+from .LanguageManager import get_language_manager - - class Manager: - NAME_COLUMN = 0 -@@ -42,7 +42,7 @@ class Manager: - dragging = False - dnd_target_list = [Gtk.TargetEntry.new('text/uri-list', 0, TARGET_URI)] - -- def __init__(self, datadir): -+ def __init__(self, datadir, window=None): - self.datadir = datadir - self.snippet = None - self.dlg = None -@@ -52,7 +52,7 @@ class Manager: - self.default_size = None - - self.key_press_id = 0 -- self.run() -+ self.run(window) - - def get_language_snippets(self, path, name = None): - library = Library() -@@ -159,9 +159,9 @@ class Manager: - snippet = model.get_value(iter, self.SNIPPET_COLUMN) - - if snippet and not snippet.valid: -- cell.set_property('stock-id', Gtk.STOCK_DIALOG_ERROR) -+ cell.set_property('icon-name', 'dialog-error') - else: -- cell.set_property('stock-id', None) -+ cell.set_property('icon-name', None) - - cell.set_property('xalign', 1.0) - -@@ -300,9 +300,6 @@ class Manager: - self.build_tree_view() - self.build_model() - -- image = self['image_remove'] -- image.set_from_stock(Gtk.STOCK_REMOVE, Gtk.IconSize.SMALL_TOOLBAR) -- - source_view = self['source_view_snippet'] - manager = get_language_manager() - lang = manager.get_language('snippets') -@@ -391,15 +388,15 @@ class Manager: - - if not (override ^ remove) or system: - button_remove.set_sensitive(False) -- image_remove.set_from_stock(Gtk.STOCK_DELETE, Gtk.IconSize.BUTTON) -+ image_remove.set_from_icon_name("edit-delete", Gtk.IconSize.BUTTON) - else: - button_remove.set_sensitive(True) - - if override: -- image_remove.set_from_stock(Gtk.STOCK_UNDO, Gtk.IconSize.BUTTON) -+ image_remove.set_from_icon_name("edit-undo", Gtk.IconSize.BUTTON) - tooltip = _('Revert selected snippet') - else: -- image_remove.set_from_stock(Gtk.STOCK_DELETE, Gtk.IconSize.BUTTON) -+ image_remove.set_from_icon_name("edit-delete", Gtk.IconSize.BUTTON) - tooltip = _('Delete selected snippet') - - button_remove.set_tooltip_text(tooltip) -@@ -427,12 +424,14 @@ class Manager: - - return self.snippet_changed(piter) - -- def run(self): -+ def run(self, window=None): - if not self.dlg: - self.build() -+ self.dlg.set_transient_for(window) - self.dlg.show() - else: - self.build_model() -+ self.dlg.set_transient_for(window) - self.dlg.present() - - def snippet_from_iter(self, model, piter): -@@ -611,7 +610,7 @@ class Manager: - self.default_size = [alloc.width, alloc.height] - - if resp == Gtk.ResponseType.HELP: -- Pluma.help_display(self, 'pluma', 'pluma-snippets-plugin') -+ Pluma.help_display(self.dlg, 'pluma', 'pluma-snippets-plugin') - return - - self.dlg.destroy() -@@ -668,7 +667,7 @@ class Manager: - - if text and not Library().valid_tab_trigger(text): - img = self['image_tab_trigger'] -- img.set_from_stock(Gtk.STOCK_DIALOG_ERROR, Gtk.IconSize.BUTTON) -+ img.set_from_icon_name("dialog-error", Gtk.IconSize.BUTTON) - img.show() - - #self['hbox_tab_trigger'].set_spacing(3) -@@ -790,10 +789,11 @@ class Manager: - self.import_snippets(f) - - def on_button_import_snippets_clicked(self, button): -- dlg = Gtk.FileChooserDialog(parent=self.dlg, title=_("Import snippets"), -- action=Gtk.FileChooserAction.OPEN, -- buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, -- Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) -+ dlg = Gtk.FileChooserDialog(title=_("Import snippets"), -+ parent=self.dlg, -+ action=Gtk.FileChooserAction.OPEN) -+ self._add_button(dlg, _('_Cancel'), Gtk.ResponseType.CANCEL, "process-stop") -+ self._add_button(dlg, _("_Open"), Gtk.ResponseType.OK, "document-open") - - dlg.add_filter(self.file_filter(_('All supported archives'), ('*.gz','*.bz2','*.tar', '*.xml'))) - dlg.add_filter(self.file_filter(_('Gzip compressed archive'), ('*.tar.gz',))) -@@ -875,10 +875,11 @@ class Manager: - return False - - if not filename: -- dlg = Gtk.FileChooserDialog(parent=self.dlg, title=_('Export snippets'), -- action=Gtk.FileChooserAction.SAVE, -- buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, -- Gtk.STOCK_SAVE, Gtk.ResponseType.OK)) -+ dlg = Gtk.FileChooserDialog(title=_('Export snippets'), -+ parent=self.dlg, -+ action=Gtk.FileChooserAction.SAVE) -+ self._add_button(dlg, _('_Cancel'), Gtk.ResponseType.CANCEL, "process-stop") -+ self._add_button(dlg, _("_Save"), Gtk.ResponseType.OK, "document-save") - - dlg._export_snippets = export_snippets - dlg.add_filter(self.file_filter(_('All supported archives'), ('*.gz','*.bz2','*.tar'))) -@@ -913,10 +914,11 @@ class Manager: - else: - systemsnippets.append(snippet) - -- dlg = Gtk.FileChooserDialog(parent=self.dlg, title=_('Export snippets'), -- action=Gtk.FileChooserAction.SAVE, -- buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, -- Gtk.STOCK_SAVE, Gtk.ResponseType.OK)) -+ dlg = Gtk.FileChooserDialog(title=_('Export snippets'), -+ parent=self.dlg, -+ action=Gtk.FileChooserAction.SAVE) -+ self._add_button(dlg, _('_Cancel'), Gtk.ResponseType.CANCEL, "process-stop") -+ self._add_button(dlg, _("_Save"), Gtk.ResponseType.OK, "document-save") - - dlg._export_snippets = snippets - -@@ -1145,4 +1147,14 @@ class Manager: - context.finish(True, False, timestamp) - - entry.stop_emission('drag_data_received') -+ -+ @staticmethod -+ def _add_button(dialog, label, response, icon=None): -+ button = dialog.add_button(label, response) -+ if icon: -+ image = Gtk.Image.new_from_icon_name(icon, Gtk.IconSize.BUTTON) -+ button.set_image(image) -+ button.set_property("always-show-image", True) -+ return button -+ - # ex:ts=4:et: -diff --git a/plugins/snippets/snippets/Parser.py b/plugins/snippets/snippets/Parser.py -index 280ce0c..f200043 100644 ---- a/plugins/snippets/snippets/Parser.py -+++ b/plugins/snippets/snippets/Parser.py -@@ -18,7 +18,7 @@ - import os - import re - import sys --from SubstitutionParser import SubstitutionParser -+from .SubstitutionParser import SubstitutionParser - - class Token: - def __init__(self, klass, data): -diff --git a/plugins/snippets/snippets/Placeholder.py b/plugins/snippets/snippets/Placeholder.py -index 9edf099..050fda6 100644 ---- a/plugins/snippets/snippets/Placeholder.py -+++ b/plugins/snippets/snippets/Placeholder.py -@@ -24,12 +24,12 @@ import locale - import subprocess - from gi.repository import GObject, GLib, Gtk - --from SubstitutionParser import SubstitutionParser --from Helper import * -+from .SubstitutionParser import SubstitutionParser -+from .Helper import * - - # These are places in a view where the cursor can go and do things - class Placeholder: -- def __init__(self, view, tabstop, defaults, begin): -+ def __init__(self, view, tabstop, environ, defaults, begin): - self.ok = True - self.done = False - self.buf = view.get_buffer() -@@ -38,6 +38,7 @@ class Placeholder: - self.mirrors = [] - self.leave_mirrors = [] - self.tabstop = tabstop -+ self.environ = environ - self.set_default(defaults) - self.prev_contents = self.default - self.set_mark_gravity() -@@ -49,6 +50,9 @@ class Placeholder: - - self.end = None - -+ def get_environ(self): -+ return self.environ -+ - def __str__(self): - return '%s (%s)' % (str(self.__class__), str(self.default)) - -@@ -81,10 +85,12 @@ class Placeholder: - return s - - def re_environment(self, m): -- if m.group(1) or not m.group(2) in os.environ: -+ env = self.get_environ() -+ -+ if m.group(1) or not m.group(2) in env: - return '$' + m.group(2) - else: -- return self.format_environment(os.environ[m.group(2)]) -+ return self.format_environment(env[m.group(2)]) - - def expand_environment(self, text): - if not text: -@@ -214,8 +220,8 @@ class Placeholder: - - # This is an placeholder which inserts a mirror of another Placeholder - class PlaceholderMirror(Placeholder): -- def __init__(self, view, tabstop, begin): -- Placeholder.__init__(self, view, -1, None, begin) -+ def __init__(self, view, tabstop, environ, begin): -+ Placeholder.__init__(self, view, -1, environ, None, begin) - self.mirror_stop = tabstop - - def update(self, mirror): -@@ -237,8 +243,8 @@ class PlaceholderMirror(Placeholder): - - # This placeholder indicates the end of a snippet - class PlaceholderEnd(Placeholder): -- def __init__(self, view, begin, default): -- Placeholder.__init__(self, view, 0, default, begin) -+ def __init__(self, view, environ, begin, default): -+ Placeholder.__init__(self, view, 0, environ, default, begin) - - def run_last(self, placeholders): - Placeholder.run_last(self, placeholders) -@@ -264,8 +270,8 @@ class PlaceholderEnd(Placeholder): - - # This placeholder is used to expand a command with embedded mirrors - class PlaceholderExpand(Placeholder): -- def __init__(self, view, tabstop, begin, s): -- Placeholder.__init__(self, view, tabstop, None, begin) -+ def __init__(self, view, tabstop, environ, begin, s): -+ Placeholder.__init__(self, view, tabstop, environ, None, begin) - - self.mirror_text = {0: ''} - self.timeout_id = None -@@ -359,8 +365,6 @@ class PlaceholderExpand(Placeholder): - return ret - - def update(self, mirror): -- text = None -- - if mirror: - self.mirror_text[mirror.tabstop] = mirror.get_text() - -@@ -379,8 +383,8 @@ class PlaceholderExpand(Placeholder): - - # The shell placeholder executes commands in a subshell - class PlaceholderShell(PlaceholderExpand): -- def __init__(self, view, tabstop, begin, s): -- PlaceholderExpand.__init__(self, view, tabstop, begin, s) -+ def __init__(self, view, tabstop, environ, begin, s): -+ PlaceholderExpand.__init__(self, view, tabstop, environ, begin, s) - - self.shell = None - self.remove_me = False -@@ -412,7 +416,7 @@ class PlaceholderShell(PlaceholderExpand): - self.close_shell() - self.remove_timeout() - -- self.set_text(str.join('', self.shell_output).rstrip('\n')) -+ self.set_text(''.join(self.shell_output).rstrip('\n')) - - if self.default == None: - self.default = self.get_text() -@@ -423,19 +427,24 @@ class PlaceholderShell(PlaceholderExpand): - - def process_cb(self, source, condition): - if condition & GObject.IO_IN: -- line = source.readline() -+ while True: -+ line = source.readline() - -- if len(line) > 0: -- try: -- line = unicode(line, 'utf-8') -- except: -- line = unicode(line, locale.getdefaultlocale()[1], -- 'replace') -+ if len(line) <= 0: -+ break - -- self.shell_output += line -- self.install_timeout() -+ if isinstance(line, bytes): -+ try: -+ line = line.decode('utf-8') -+ except UnicodeDecodeError: -+ line = line.decode(locale.getdefaultlocale()[1], -+ errors='replace') - -- return True -+ self.shell_output += line -+ self.install_timeout() -+ -+ if not (condition & GObject.IO_HUP): -+ return True - - self.process_close() - return False -@@ -456,7 +465,7 @@ class PlaceholderShell(PlaceholderExpand): - popen_args = { - 'cwd' : None, - 'shell': True, -- 'env' : os.environ, -+ 'env': self.get_environ(), - 'stdout': subprocess.PIPE - } - -@@ -491,8 +500,8 @@ class TimeoutError(Exception): - - # The python placeholder evaluates commands in python - class PlaceholderEval(PlaceholderExpand): -- def __init__(self, view, tabstop, refs, begin, s, namespace): -- PlaceholderExpand.__init__(self, view, tabstop, begin, s) -+ def __init__(self, view, tabstop, environ, refs, begin, s, namespace): -+ PlaceholderExpand.__init__(self, view, tabstop, environ, begin, s) - - self.fdread = 0 - self.remove_me = False -@@ -529,7 +538,7 @@ class PlaceholderEval(PlaceholderExpand): - return hasattr(signal, 'SIGALRM') - - def timeout_cb(self, signum = 0, frame = 0): -- raise TimeoutError, "Operation timed out (>2 seconds)" -+ raise TimeoutError("Operation timed out (>2 seconds)") - - def install_timeout(self): - if not self.timeout_supported(): -@@ -568,7 +577,7 @@ class PlaceholderEval(PlaceholderExpand): - del self.namespace['process_snippet'] - - try: -- exec text in self.namespace -+ exec(text, self.namespace) - except: - traceback.print_exc() - -@@ -593,7 +602,7 @@ class PlaceholderEval(PlaceholderExpand): - 'time, execution aborted.') % self.command) - - return False -- except Exception, detail: -+ except Exception as detail: - self.remove_timeout() - - message_dialog(None, Gtk.MessageType.ERROR, -@@ -612,8 +621,8 @@ class PlaceholderEval(PlaceholderExpand): - - # Regular expression placeholder - class PlaceholderRegex(PlaceholderExpand): -- def __init__(self, view, tabstop, begin, inp, pattern, substitution, modifiers): -- PlaceholderExpand.__init__(self, view, tabstop, begin, '') -+ def __init__(self, view, tabstop, environ, begin, inp, pattern, substitution, modifiers): -+ PlaceholderExpand.__init__(self, view, tabstop, environ, begin, '') - - self.instant_update = True - self.inp = inp -@@ -652,10 +661,12 @@ class PlaceholderRegex(PlaceholderExpand): - return re.escape(s) - - def get_input(self): -+ env = self.get_environ() -+ - if isinstance(self.inp, int): - return self.mirror_text[self.inp] -- elif self.inp in os.environ: -- return os.environ[self.inp] -+ elif self.inp in env: -+ return env[self.inp] - else: - return '' - -@@ -672,7 +683,7 @@ class PlaceholderRegex(PlaceholderExpand): - # Try to compile pattern - try: - regex = re.compile(pattern, self.modifiers) -- except re.error, message: -+ except re.error as message: - sys.stderr.write('Could not compile regular expression: %s\n%s\n' % (pattern, message)) - return False - -diff --git a/plugins/snippets/snippets/Snippet.py b/plugins/snippets/snippets/Snippet.py -index 192b036..91e6380 100644 ---- a/plugins/snippets/snippets/Snippet.py -+++ b/plugins/snippets/snippets/Snippet.py -@@ -16,11 +16,12 @@ - # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - - import os -+import six - from gi.repository import Gio, Gtk - --from Placeholder import * --from Parser import Parser, Token --from Helper import * -+from .Placeholder import * -+from .Parser import Parser, Token -+from .Helper import * - - class EvalUtilities: - def __init__(self, view=None): -@@ -87,8 +88,8 @@ class EvalUtilities: - for col in range(0, len(items[row]) - 1): - item = items[row][col] - -- result += item + ("\t" * ((maxlen[col] - \ -- self._real_len(item, tablen)) / tablen)) -+ result += item + ("\t" * int(((maxlen[col] - \ -+ self._real_len(item, tablen)) / tablen))) - - result += items[row][len(items[row]) - 1] - -@@ -98,8 +99,9 @@ class EvalUtilities: - return result - - class Snippet: -- def __init__(self, data): -+ def __init__(self, data, environ = {}): - self.data = data -+ self.environ = environ - - def __getitem__(self, prop): - return self.data[prop] -@@ -132,7 +134,7 @@ class Snippet: - if not detail: - return nm - else: -- return nm + ' (' + markup_escape(str.join(', ', detail)) + \ -+ return nm + ' (' + markup_escape(", ".join(detail)) + \ - ')' - - def _add_placeholder(self, placeholder): -@@ -149,14 +151,17 @@ class Snippet: - - def _insert_text(self, text): - # Insert text keeping indentation in mind -- indented = unicode.join('\n' + unicode(self._indent), spaces_instead_of_tabs(self._view, text).split('\n')) -+ indented = (six.u('\n') + self._indent).join(spaces_instead_of_tabs(self._view, text).split('\n')) - self._view.get_buffer().insert(self._insert_iter(), indented) - - def _insert_iter(self): - return self._view.get_buffer().get_iter_at_mark(self._insert_mark) - - def _create_environment(self, data): -- val = ((data in os.environ) and os.environ[data]) or '' -+ if data in self.environ: -+ val = self.environ[data] -+ else: -+ val = '' - - # Get all the current indentation - all_indent = compute_indentation(self._view, self._insert_iter()) -@@ -165,7 +170,7 @@ class Snippet: - indent = all_indent[len(self._indent):] - - # Keep indentation -- return unicode.join('\n' + unicode(indent), val.split('\n')) -+ return (six.u('\n') + indent).join(val.split('\n')) - - def _create_placeholder(self, data): - tabstop = data['tabstop'] -@@ -173,25 +178,25 @@ class Snippet: - - if tabstop == 0: - # End placeholder -- return PlaceholderEnd(self._view, begin, data['default']) -+ return PlaceholderEnd(self._view, self.environ, begin, data['default']) - elif tabstop in self.placeholders: - # Mirror placeholder -- return PlaceholderMirror(self._view, tabstop, begin) -+ return PlaceholderMirror(self._view, tabstop, self.environ, begin) - else: - # Default placeholder -- return Placeholder(self._view, tabstop, data['default'], begin) -+ return Placeholder(self._view, tabstop, self.environ, data['default'], begin) - - def _create_shell(self, data): - begin = self._insert_iter() -- return PlaceholderShell(self._view, data['tabstop'], begin, data['contents']) -+ return PlaceholderShell(self._view, data['tabstop'], self.environ, begin, data['contents']) - - def _create_eval(self, data): - begin = self._insert_iter() -- return PlaceholderEval(self._view, data['tabstop'], data['dependencies'], begin, data['contents'], self._utils.namespace) -+ return PlaceholderEval(self._view, data['tabstop'], self.environ, data['dependencies'], begin, data['contents'], self._utils.namespace) - - def _create_regex(self, data): - begin = self._insert_iter() -- return PlaceholderRegex(self._view, data['tabstop'], begin, data['input'], data['pattern'], data['substitution'], data['modifiers']) -+ return PlaceholderRegex(self._view, data['tabstop'], self.environ, begin, data['input'], data['pattern'], data['substitution'], data['modifiers']) - - def _create_text(self, data): - return data -@@ -239,11 +244,11 @@ class Snippet: - 'eval': self._create_eval, - 'regex': self._create_regex, - 'text': self._create_text}[token.klass](token.data) -- except: -+ except KeyError: - sys.stderr.write('Token class not supported: %s\n' % token.klass) - continue - -- if isinstance(val, basestring): -+ if isinstance(val, six.string_types): - # Insert text - self._insert_text(val) - else: -@@ -252,7 +257,7 @@ class Snippet: - - # Create end placeholder if there isn't one yet - if 0 not in self.placeholders: -- self.placeholders[0] = PlaceholderEnd(self._view, self.end_iter(), None) -+ self.placeholders[0] = PlaceholderEnd(self._view, self.environ, self.end_iter(), None) - self.plugin_data.ordered_placeholders.append(self.placeholders[0]) - - # Make sure run_last is ran for all placeholders and remove any -@@ -317,8 +322,7 @@ class Snippet: - # So now all of the snippet is in the buffer, we have all our - # placeholders right here, what's next, put all marks in the - # plugin_data.marks -- k = self.placeholders.keys() -- k.sort(reverse=True) -+ k = sorted(self.placeholders.keys(), reverse=True) - - plugin_data.placeholders.insert(last_index, self.placeholders[0]) - last_iter = self.placeholders[0].end_iter() -diff --git a/plugins/snippets/snippets/WindowHelper.py b/plugins/snippets/snippets/WindowHelper.py -index 296ff03..44ac558 100644 ---- a/plugins/snippets/snippets/WindowHelper.py -+++ b/plugins/snippets/snippets/WindowHelper.py -@@ -21,8 +21,8 @@ import gettext - - from gi.repository import GObject, Gtk, Pluma - --from Document import Document --from Library import Library -+from .Document import Document -+from .Library import Library - - class WindowHelper: - def __init__(self, plugin): -diff --git a/plugins/snippets/snippets/__init__.py b/plugins/snippets/snippets/__init__.py -index 8642406..ada586c 100644 ---- a/plugins/snippets/snippets/__init__.py -+++ b/plugins/snippets/snippets/__init__.py -@@ -18,9 +18,9 @@ - import os - from gi.repository import GObject, GLib, Gtk, Peas, Pluma - --from WindowHelper import WindowHelper --from Library import Library --from Manager import Manager -+from .WindowHelper import WindowHelper -+from .Library import Library -+from .Manager import Manager - - class SnippetsPlugin(GObject.Object, Peas.Activatable): - __gtype_name__ = "SnippetsPlugin" -@@ -53,7 +53,7 @@ class SnippetsPlugin(GObject.Object, Peas.Activatable): - library = Library() - library.add_accelerator_callback(self.accelerator_activated) - -- snippetsdir = os.path.join(GLib.get_user_config_dir(), '/pluma/snippets') -+ snippetsdir = os.path.join(GLib.get_user_config_dir(), 'pluma/snippets') - library.set_dirs(snippetsdir, self.system_dirs()) - - self._helper = WindowHelper(self) -@@ -72,15 +72,12 @@ class SnippetsPlugin(GObject.Object, Peas.Activatable): - self._helper.update() - - def create_configure_dialog(self): -- if not self.dlg: -- self.dlg = Manager(self.plugin_info.get_data_dir()) -- else: -- self.dlg.run() -- - window = Pluma.App.get_default().get_active_window() - -- if window: -- self.dlg.dlg.set_transient_for(window) -+ if not self.dlg: -+ self.dlg = Manager(self.plugin_info.get_data_dir(), window) -+ else: -+ self.dlg.run(window) - - return self.dlg.dlg - -diff --git a/plugins/snippets/snippets/snippets.ui b/plugins/snippets/snippets/snippets.ui -index 6fcaf85..833aa02 100644 ---- a/plugins/snippets/snippets/snippets.ui -+++ b/plugins/snippets/snippets/snippets.ui -@@ -1,8 +1,11 @@ -- -+ -+ - - -+ - - -+ - - - -@@ -32,303 +35,249 @@ - - - -- -- True -- - -+ False - Snippets Manager -- GTK_WINDOW_TOPLEVEL -- GTK_WIN_POS_NONE -- False - 750 - 500 -- True - True -- True -+ dialog - True -- False -- GDK_WINDOW_TYPE_HINT_DIALOG -- GDK_GRAVITY_NORTH_WEST -- True -- False -- -- -+ -+ -+ -+ -+ - -- -+ - True -- False -- 0 -+ False -+ vertical - -- -+ - True -- GTK_BUTTONBOX_END -+ False -+ True -+ end - -- -+ -+ gtk-help - True -- True - True -- gtk-close -+ True -+ False - True -- GTK_RELIEF_NORMAL -- True - -+ -+ True -+ True -+ 0 -+ - - -- -+ -+ gtk-close - True -- True - True -- gtk-help -+ True -+ False - True -- GTK_RELIEF_NORMAL -- True - -+ -+ True -+ True -+ end -+ 1 -+ - - - -- 0 - False - True -- GTK_PACK_END -+ end -+ 0 - - - -- -- 6 -+ - True - True - 275 - -- -- 230 -+ - True -- False -- 6 -- -- -- True -- _Snippets: -- True -- False -- GTK_JUSTIFY_LEFT -- False -- False -- 0 -- 0.5 -- 0 -- 0 -- tree_view_snippets -- PANGO_ELLIPSIZE_NONE -- -1 -- False -- 0 -- -- -- 0 -- False -- False -- -- -+ False -+ True -+ True -+ vertical -+ 6 - - - True - True -- GTK_POLICY_AUTOMATIC -- GTK_POLICY_AUTOMATIC -- GTK_SHADOW_IN -- GTK_CORNER_TOP_LEFT -+ True -+ True -+ in - - - True - True - False -- False -- False -- True -- False -- False -- False -- -- -+ -+ -+ -+ -+ - - - - -- 0 -- True -- True -+ 0 -+ 1 - - - -- -+ - True -- False -- 6 -+ False -+ start -+ _Snippets: -+ True -+ tree_view_snippets -+ -+ -+ 0 -+ 0 -+ -+ -+ -+ -+ True -+ False -+ True -+ 6 - - - True -- Create new snippet -- True - True -- GTK_RELIEF_NORMAL -- True -- -+ True -+ False -+ Create new snippet -+ - - - True -- gtk-new -- 4 -- 0.5 -- 0.5 -- 0 -- 0 -+ False -+ document-new - - - - -- 0 -- False -- False -+ 0 -+ 0 - - - - - True -- Import snippets -- True - True -- GTK_RELIEF_NORMAL -- True -- -+ True -+ False -+ Import snippets -+ - - - True -- gtk-open -- 4 -- 0.5 -- 0.5 -- 0 -- 0 -+ False -+ document-open - - - - -- 0 -- False -- False -+ 1 -+ 0 - - - - - True -- Export selected snippets -- True - True -- GTK_RELIEF_NORMAL -- True -- -+ True -+ False -+ Export selected snippets -+ - - - True -- gtk-save -- 4 -- 0.5 -- 0.5 -- 0 -- 0 -+ False -+ document-save - - - - -- 0 -- False -- False -+ 2 -+ 0 - - - - - True - False -- Delete selected snippet -- True - True -- GTK_RELIEF_NORMAL -- True -- -+ True -+ False -+ Delete selected snippet -+ end -+ True -+ - - - True -- gtk-delete -- 4 -- 0.5 -- 0.5 -- 0 -- 0 -+ False -+ edit-delete - - - - -- 0 -- False -- False -- GTK_PACK_END -+ 3 -+ 0 - - - - -- 0 -- False -- False -+ 0 -+ 2 - - - - -- False - False -+ True - - - -- -+ - True -- False -- 12 -+ False -+ True -+ True -+ vertical -+ 12 - -- -+ - True -- False -- 6 -- -- -- True -- _Edit: -- True -- False -- GTK_JUSTIFY_LEFT -- False -- False -- 0 -- 0.5 -- 0 -- 0 -- PANGO_ELLIPSIZE_NONE -- -1 -- False -- 0 -- -- -- 0 -- False -- False -- -- -+ False -+ True -+ True -+ vertical -+ 6 - - - True - True -- GTK_POLICY_AUTOMATIC -- GTK_POLICY_AUTOMATIC -- GTK_SHADOW_IN -- GTK_CORNER_TOP_LEFT -+ True -+ True -+ in - - - source_buffer -@@ -346,302 +295,198 @@ - - - -- 0 -- True -- True -+ 0 -+ 1 -+ -+ -+ -+ -+ True -+ False -+ start -+ True -+ _Edit: -+ True -+ -+ -+ 0 -+ 0 - - - - -- 0 -- True -- True -+ 0 -+ 0 - - - -- -+ - True -- False -- 6 -+ False -+ True -+ vertical -+ 6 - - - True -+ False -+ start -+ True - Activation -- False - True -- GTK_JUSTIFY_LEFT -- False -- False -- 0 -- 0.5 -- 0 -- 0 -- PANGO_ELLIPSIZE_NONE -- -1 -- False -- 0 - -- -+ - - - -- 0 -- False -- False -+ 0 -+ 0 - - - -- -+ - True -- False -- 0 -+ False -+ 12 -+ True -+ 6 -+ 6 - -- -+ - True -- -- False -- False -- GTK_JUSTIFY_LEFT -- False -- False -- 0.5 -- 0.5 -- 0 -- 0 -- PANGO_ELLIPSIZE_NONE -- -1 -- False -- 0 -+ False -+ True -+ model1 -+ True -+ 0 -+ -+ -+ True -+ -+ - - -- 0 -- False -- False -+ 1 -+ 2 -+ 2 - - - -- -+ - True -- 3 -- 2 -- False -- 6 -- 6 -- -- -- True -- _Tab trigger: -- True -- False -- GTK_JUSTIFY_LEFT -- False -- False -- 0 -- 0.5 -- 0 -- 0 -- entry_tab_trigger -- PANGO_ELLIPSIZE_NONE -- -1 -- False -- 0 -- -- -- 0 -- 1 -- 0 -- 1 -- fill -- -- -- -- -- -- True -- -- -- True -- False -- Single word the snippet is activated with after pressing Tab -- True -- True -- True -- 0 -- -- True -- * -- False -- -- -- -- -- True -- 0 -- -- -- -- -- False -- -- -- False -- 1 -- 3 -- -- -- -- -- 1 -- 2 -- 0 -- 1 -- -- -- -- -- -- True -- False -- Shortcut key with which the snippet is activated -- True -- False -- True -- 0 -- -- True -- * -- False -- -- -- -- -- -- 1 -- 2 -- 1 -- 2 -- -- -- -- -- -- True -- S_hortcut key: -- True -- False -- GTK_JUSTIFY_LEFT -- False -- False -- 0 -- 0.5 -- 0 -- 0 -- entry_accelerator -- PANGO_ELLIPSIZE_NONE -- -1 -- False -- 0 -- -- -- 0 -- 1 -- 1 -- 2 -- fill -- -- -- -- -- -- True -- _Drop targets: -- True -- False -- GTK_JUSTIFY_LEFT -- False -- False -- 0 -- 0.5 -- 0 -- 0 -- entry_accelerator -- PANGO_ELLIPSIZE_NONE -- -1 -- False -- 0 -- -- -- 0 -- 1 -- 2 -- 3 -- fill -- -- -- -- -- -- True -- False -- True -- True -- True -- model1 -- -- -- -- 0 -- -- -- -- -- 1 -- 2 -- 2 -- 3 -- fill -- fill -- -- -+ False -+ start -+ _Drop targets: -+ True -+ -+ -+ 0 -+ 2 -+ -+ -+ -+ -+ True -+ False -+ True -+ Shortcut key with which the snippet is activated -+ True -+ False -+ * -+ -+ -+ - - -- 0 -- True -- True -+ 1 -+ 1 -+ 2 -+ -+ -+ -+ -+ True -+ False -+ start -+ S_hortcut key: -+ True -+ -+ -+ 0 -+ 1 -+ -+ -+ -+ -+ True -+ False -+ start -+ _Tab trigger: -+ True -+ -+ -+ 0 -+ 0 -+ -+ -+ -+ -+ False -+ -+ -+ 2 -+ 0 -+ -+ -+ -+ -+ True -+ False -+ True -+ Single word the snippet is activated with after pressing Tab -+ True -+ * -+ -+ -+ -+ -+ 1 -+ 0 - - - - -- 0 -- True -- True -+ 0 -+ 1 - - - - -- 0 -- False -- False -+ 0 -+ 1 - - - - -- True - True -+ True - - - - -- 0 - True - True -+ 1 - - - - - -- closebutton1 - button1 -+ closebutton1 - - -+ -+ True -+ - --- -2.21.0 -