# Gedit snippets plugin # Copyright (C) 2005-2006 Jesse van den Kieboom # # 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, ('' + _('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 = 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 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') 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 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') 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: