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