diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 17:42:51 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 17:42:51 +0000 |
commit | ba429d344132c088177e853cce8ff7181570b221 (patch) | |
tree | 87ebf15269b4301737abd1735baabba71be93622 /plugins/snippets/snippets/manager.py | |
parent | Initial commit. (diff) | |
download | gedit-upstream.tar.xz gedit-upstream.zip |
Adding upstream version 44.2.upstream/44.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'plugins/snippets/snippets/manager.py')
-rw-r--r-- | plugins/snippets/snippets/manager.py | 1143 |
1 files changed, 1143 insertions, 0 deletions
diff --git a/plugins/snippets/snippets/manager.py b/plugins/snippets/snippets/manager.py new file mode 100644 index 0000000..ca64f18 --- /dev/null +++ b/plugins/snippets/snippets/manager.py @@ -0,0 +1,1143 @@ +# Gedit snippets plugin +# Copyright (C) 2005-2006 Jesse van den Kieboom <jesse@icecrew.nl> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import os +import tempfile +import shutil + +from gi.repository import Gtk, Gio, Gdk, GObject + +from .snippet import Snippet +from . import helper +from .library import Library +from .importer import Importer +from .exporter import Exporter +from .languagemanager import get_language_manager + +try: + import gettext + gettext.bindtextdomain('gedit') + gettext.textdomain('gedit') + _ = gettext.gettext +except: + _ = lambda s: s + +class Manager(Gtk.Window, Gtk.Buildable): + NAME_COLUMN = 0 + SORT_COLUMN = 1 + LANG_COLUMN = 2 + SNIPPET_COLUMN = 3 + TARGET_URI = 105 + + __gtype_name__ = "GeditSnippetsManager" + + model = None + drag_icons = ('gnome-mime-application-x-tarz', 'gnome-package', 'package') + default_export_name = _('Snippets archive') + '.tar.gz' + dragging = False + + def __init__(self): + self.snippet = None + self._temp_export = None + self._size = (0, 0) + + self.key_press_id = 0 + self.dnd_target_list = Gtk.TargetList.new([]) + self.dnd_target_list.add(Gdk.atom_intern("text/uri-list", True), 0, self.TARGET_URI) + + def get_final_size(self): + return self._size + + 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, ('<i>' + _('Add a new snippet…') + \ + '</i>', '', 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 = Gio.Application.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() + 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('icon-name', 'dialog-error') + else: + cell.set_property('icon-name', 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() + + files = [Gio.file_new_for_uri(u) for u in uris] + + self.import_snippets(files) + + 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, [], Gdk.DragAction.DEFAULT | Gdk.DragAction.COPY) + tv.drag_source_set_target_list(self.dnd_target_list) + + # Set it as a drag destination for importing snippets + tv.drag_dest_set(Gtk.DestDefaults.HIGHLIGHT | Gtk.DestDefaults.DROP, + [], Gdk.DragAction.DEFAULT | Gdk.DragAction.COPY) + + tv.drag_dest_set_target_list(self.dnd_target_list) + + 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 do_parser_finished(self, builder): + self.builder = builder + + handlers_dic = { + 'on_add_snippet_button_clicked': self.on_add_snippet_button_clicked, + 'on_remove_snippet_button_clicked': self.on_remove_snippet_button_clicked, + 'on_import_snippets_button_clicked': self.on_import_snippets_button_clicked, + 'on_export_snippets_button_clicked': self.on_export_snippets_button_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() + + # join treeview and toolbar + context = self['scrolled_window_snippets'].get_style_context() + context.set_junction_sides(Gtk.JunctionSides.BOTTOM) + context = self['toolbar'].get_style_context() + context.set_junction_sides(Gtk.JunctionSides.TOP) + context.set_junction_sides(Gtk.JunctionSides.BOTTOM) + + 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) + + 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) + + 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 __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): + sn = self.model.get_value(piter, self.SNIPPET_COLUMN) + + if sn == 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_toolbar_buttons(self): + button_add = self['add_snippet_button'] + button_remove = self['remove_snippet_button'] + + button_add.set_sensitive(self.language_path != None) + override, remove, system = self.selected_snippets_state() + + if not (override ^ remove) or system: + button_remove.set_sensitive(False) + button_remove.set_icon_name('list-remove-symbolic') + else: + button_remove.set_sensitive(True) + + if override: + button_remove.set_icon_name('edit-undo-symbolic') + tooltip = _('Revert selected snippet') + else: + button_remove.set_icon_name('list-remove-symbolic') + 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_toolbar_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 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: + 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 + + 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_toolbar_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 do_destroy(self): + Gtk.Dialog.do_destroy(self) + + if not self.model: + return + + # Remove temporary drag export + if self._temp_export: + shutil.rmtree(os.path.dirname(self._temp_export)) + self._temp_export = None + + self.unref_languages() + self.snippet = None + self.model = None + + 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_icon_name('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 alphanumeric characters (or _, : and .) 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_add_snippet_button_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, files): + success = True + + for gfile in files: + if not gfile.has_uri_scheme('file'): + continue + + # Remove file:// + filename = gfile.get_path() + + importer = Importer(filename) + error = importer.run() + + if error: + message = _('The following error occurred while importing: %s') % error + success = False + helper.message_dialog(self.get_toplevel(), Gtk.MessageType.ERROR, message) + + self.build_model(True) + + if success: + message = _('Import successfully completed') + helper.message_dialog(self.get_toplevel(), 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_files() + dialog.destroy() + + self.import_snippets(f) + + def on_import_snippets_button_clicked(self, button): + dlg = Gtk.FileChooserDialog(parent=self.get_toplevel(), title=_("Import snippets"), + action=Gtk.FileChooserAction.OPEN, + buttons=(_("_Cancel"), Gtk.ResponseType.CANCEL, + _("_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: + helper.message_dialog(self.get_toplevel(), 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 <b>system</b> 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') + helper.message_dialog(self.get_toplevel(), Gtk.MessageType.QUESTION, message) + return False + + if not filename: + dlg = Gtk.FileChooserDialog(parent=self.get_toplevel(), title=_('Export snippets'), + action=Gtk.FileChooserAction.SAVE, + buttons=(_("_Cancel"), Gtk.ResponseType.CANCEL, + _("_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_export_snippets_button_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.get_toplevel(), title=_('Export snippets'), + action=Gtk.FileChooserAction.SAVE, + buttons=(_('_Cancel'), Gtk.ResponseType.CANCEL, + _('_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 <b>system</b> 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') + helper.message_dialog(self.get_toplevel(), Gtk.MessageType.QUESTION, 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_remove_snippet_button_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: + # FIXME: this should be fixed in pygobject or something + references.append(Gtk.TreeRowReference.new(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): + 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.get_state()): + # New accelerator + self.set_accelerator(event.keyval, \ + event.get_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_remove_snippet_button_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 = helper.drop_get_uris(selection_data) + if not uris: + return + + text = entry.get_text() + + if text: + mimes = [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: |