[gedit-plugins] Implemented support for accelerators
- From: Jesse van den Kieboom <jessevdk src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gedit-plugins] Implemented support for accelerators
- Date: Sun, 21 Mar 2010 21:54:17 +0000 (UTC)
commit c6ae7a42f845a603b2d4454e7de92864f43b3b1a
Author: Jesse van den Kieboom <jesse icecrew nl>
Date: Sun Mar 21 22:53:16 2010 +0100
Implemented support for accelerators
plugins/commander/commander/commands/Makefile.am | 1 +
plugins/commander/commander/commands/__init__.py | 70 +++++++++++-
.../commander/commander/commands/accel_group.py | 104 ++++++++++++++++++
plugins/commander/commander/commands/method.py | 13 ++-
plugins/commander/commander/entry.py | 111 +++++++++++++------
5 files changed, 256 insertions(+), 43 deletions(-)
---
diff --git a/plugins/commander/commander/commands/Makefile.am b/plugins/commander/commander/commands/Makefile.am
index 00018e2..dd8830a 100644
--- a/plugins/commander/commander/commands/Makefile.am
+++ b/plugins/commander/commander/commands/Makefile.am
@@ -3,6 +3,7 @@
plugindir = $(GEDIT_PLUGINS_LIBS_DIR)/commander/commands
plugin_PYTHON = \
+ accel_group.py \
completion.py \
exceptions.py \
__init__.py \
diff --git a/plugins/commander/commander/commands/__init__.py b/plugins/commander/commander/commands/__init__.py
index cb7ee04..33e65df 100644
--- a/plugins/commander/commander/commands/__init__.py
+++ b/plugins/commander/commander/commands/__init__.py
@@ -13,7 +13,10 @@ import method
import result
import exceptions
-__all__ = ['is_commander_module', 'Commands']
+from accel_group import AccelGroup
+from accel_group import Accelerator
+
+__all__ = ['is_commander_module', 'Commands', 'Accelerator']
def attrs(**kwargs):
def generator(f):
@@ -34,6 +37,9 @@ def autocomplete(d={}, **kwargs):
return attrs(autocomplete=ret)
+def accelerator(*args, **kwargs):
+ return attrs(accelerator=Accelerator(args, kwargs))
+
def is_commander_module(mod):
if type(mod) == types.ModuleType:
return mod and ('__commander_module__' in mod.__dict__)
@@ -107,6 +113,7 @@ class Commands(Singleton):
self._modules = None
self._dirs = []
self._monitors = []
+ self._accel_group = None
self._timeouts = {}
@@ -126,7 +133,35 @@ class Commands(Singleton):
glib.source_remove(self._timeouts[k])
self._timeouts = {}
-
+
+ def accelerator_activated(self, accel, mod, state, entry):
+ self.run(state, mod.execute('', [], entry, 0, accel.arguments))
+
+ def scan_accelerators(self, modules=None):
+ if modules == None:
+ self._accel_group = AccelGroup()
+ modules = self.modules()
+
+ recurse_mods = []
+
+ for mod in modules:
+ if type(mod) == types.ModuleType:
+ recurse_mods.append(mod)
+ else:
+ accel = mod.accelerator()
+
+ if accel != None:
+ self._accel_group.add(accel, self.accelerator_activated, mod)
+
+ for mod in recurse_mods:
+ self.scan_accelerators(mod.commands())
+
+ def accelerator_group(self):
+ if not self._accel_group:
+ self.scan_accelerators()
+
+ return self._accel_group
+
def modules(self):
self.ensure()
return list(self._modules)
@@ -303,11 +338,31 @@ class Commands(Singleton):
return mod
- def remove_module_root(self, mod):
+ def remove_module_accelerators(self, modules):
+ recurse_mods = []
+
+ for mod in modules:
+ if type(mod) == types.ModuleType:
+ recurse_mods.append(mod)
+ else:
+ accel = mod.accelerator()
+
+ if accel != None:
+ self._accel_group.remove(accel)
+
+ for mod in recurse_mods:
+ self.remove_module_accelerators(mod.commands())
+
+ def remove_module(self, mod):
+ # Remove roots
for r in mod.roots():
if r in self._modules:
self._modules.remove(r)
-
+
+ # Remove accelerators
+ if self._accel_group:
+ self.remove_module_accelerators([mod])
+
def reload_module(self, mod):
if isinstance(mod, basestring):
mod = self.resolve_module(mod)
@@ -316,7 +371,7 @@ class Commands(Singleton):
return
# Remove roots
- self.remove_module_root(mod)
+ self.remove_module(mod)
# Now, try to reload the module
try:
@@ -332,13 +387,16 @@ class Commands(Singleton):
for r in mod.roots():
bisect.insort(self._modules, r)
+ if self._accel_group:
+ self.scan_accelerators([mod])
+
def on_timeout_delete(self, path, mod):
if not path in self._timeouts:
return False
# Remove the module
mod.unload()
- self.remove_module_root(mod)
+ self.remove_module(mod)
self._modules.remove(mod)
return False
diff --git a/plugins/commander/commander/commands/accel_group.py b/plugins/commander/commander/commands/accel_group.py
new file mode 100644
index 0000000..934f9dc
--- /dev/null
+++ b/plugins/commander/commander/commands/accel_group.py
@@ -0,0 +1,104 @@
+import gtk
+
+class Accelerator:
+ def __init__(self, accelerators, arguments={}):
+ if not hasattr(accelerators, '__iter__'):
+ accelerators = [accelerators]
+
+ self.accelerators = accelerators
+ self.arguments = arguments
+
+class AccelCallback:
+ def __init__(self, accel, callback, data):
+ self.accelerator = accel
+ self.callback = callback
+ self.data = data
+
+ def activate(self, state, entry):
+ self.callback(self.accelerator, self.data, state, entry)
+
+class AccelGroup:
+ def __init__(self, parent=None, name='', accelerators={}):
+ self.accelerators = dict(accelerators)
+ self.parent = parent
+ self.name = name
+
+ def add(self, accel, callback, data=None):
+ num = len(accel.accelerators)
+ mapping = self.accelerators
+
+ for i in range(num):
+ parsed = gtk.accelerator_parse(accel.accelerators[i])
+
+ if not gtk.accelerator_valid(*parsed):
+ return
+
+ named = gtk.accelerator_name(*parsed)
+ inmap = named in mapping
+
+ if i == num - 1 and inmap:
+ # Last one cannot be in the map
+ return
+ elif inmap and isinstance(mapping[named], AccelCallback):
+ # It's already mapped...
+ return
+ else:
+ if not inmap:
+ mapping[named] = {}
+
+ if i == num - 1:
+ mapping[named] = AccelCallback(accel, callback, data)
+
+ mapping = mapping[named]
+
+ def remove_real(self, accelerators, accels):
+ if not accels:
+ return
+
+ parsed = gtk.accelerator_parse(accels[0])
+
+ if not gtk.accelerator_valid(*parsed):
+ return
+
+ named = gtk.accelerator_name(*parsed)
+
+ if not named in accelerators:
+ return
+
+ if len(accels) == 1:
+ del accelerators[named]
+ else:
+ self.remove_real(accelerators[named], accels[1:])
+
+ if not accelerators[named]:
+ del accelerators[named]
+
+ def remove(self, accel):
+ self.remove_real(self.accelerators, accel.accelerators)
+
+ def activate(self, key, mod):
+ named = gtk.accelerator_name(key, mod)
+
+ if not named in self.accelerators:
+ return None
+
+ accel = self.accelerators[named]
+
+ if isinstance(accel, AccelCallback):
+ return accel
+ else:
+ return AccelGroup(self, named, accel)
+
+ def full_name(self):
+ name = ''
+
+ if self.parent:
+ name = self.parent.full_name()
+
+ if self.name:
+ if name:
+ name += ', '
+
+ name += self.name
+
+ return name
diff --git a/plugins/commander/commander/commands/method.py b/plugins/commander/commander/commands/method.py
index cc8ef58..dffab2e 100644
--- a/plugins/commander/commander/commands/method.py
+++ b/plugins/commander/commander/commands/method.py
@@ -19,7 +19,13 @@ class Method:
return getattr(self.method, 'autocomplete')
return None
-
+
+ def accelerator(self):
+ if hasattr(self.method, 'accelerator'):
+ return getattr(self.method, 'accelerator')
+
+ return None
+
def args(self):
fp = self.func_props()
@@ -52,7 +58,7 @@ class Method:
def oneline_doc(self):
return self.doc().split("\n")[0]
- def execute(self, argstr, words, entry, modifier):
+ def execute(self, argstr, words, entry, modifier, kk = {}):
fp = self.func_props()
kwargs = {'argstr': argstr, 'args': words, 'entry': entry, 'view': entry.view(), 'modifier': modifier, 'window': entry.view().get_toplevel()}
@@ -85,6 +91,9 @@ class Method:
if not fp.keywords:
kwargs = {}
+ for k in kk:
+ kwargs[k] = kk[k]
+
return self.method(*args, **kwargs)
def __cmp__(self, other):
diff --git a/plugins/commander/commander/entry.py b/plugins/commander/commander/entry.py
index 928d38e..7c01931 100644
--- a/plugins/commander/commander/entry.py
+++ b/plugins/commander/commander/entry.py
@@ -11,6 +11,7 @@ import commands.completion
import commands.module
import commands.method
import commands.exceptions
+import commands.accel_group
import commander.utils as utils
@@ -58,6 +59,8 @@ class Entry(gtk.EventBox):
self._history = History(os.path.expanduser('~/.gnome2/gedit/commander/history'))
self._prompt = None
+
+ self._accel_group = None
hbox.pack_start(self._prompt_label, False, False, 0)
hbox.pack_start(self._entry, True, True, 0)
@@ -71,7 +74,7 @@ class Entry(gtk.EventBox):
self.connect('destroy', self.on_destroy)
- self._history_prefix = None
+ self._history_prefix = None
self._suspended = None
self._handlers = [
[0, gtk.keysyms.Up, self.on_history_move, -1],
@@ -147,23 +150,29 @@ class Entry(gtk.EventBox):
def on_entry_focus_out(self, widget, evnt):
if self._entry.flags() & gtk.SENSITIVE:
self.destroy()
-
+
def on_entry_key_press(self, widget, evnt):
state = evnt.state & gtk.accelerator_get_default_mod_mask()
text = self._entry.get_text()
- if evnt.keyval == gtk.keysyms.Escape and self._info_window:
- if self._suspended:
- self._suspended.resume()
-
+ if evnt.keyval == gtk.keysyms.Escape:
if self._info_window:
- self._info_window.destroy()
+ if self._suspended:
+ self._suspended.resume()
- self._entry.set_sensitive(True)
- return True
+ if self._info_window:
+ self._info_window.destroy()
- if evnt.keyval == gtk.keysyms.Escape:
- if text:
+ self._entry.set_sensitive(True)
+ elif self._accel_group:
+ self._accel_group = self._accel_group.parent
+
+ if not self._accel_group or not self._accel_group.parent:
+ self._entry.set_editable(True)
+ self._accel_group = None
+
+ self.prompt()
+ elif text:
self._entry.set_text('')
elif self._command_state:
self._command_state.clear()
@@ -174,6 +183,30 @@ class Entry(gtk.EventBox):
return True
+ if state or self._accel_group:
+ # Check if it should be handled by the accel group
+ group = self._accel_group
+
+ if not self._accel_group:
+ group = commands.Commands().accelerator_group()
+
+ accel = group.activate(evnt.keyval, state)
+
+ if isinstance(accel, commands.accel_group.AccelGroup):
+ self._accel_group = accel
+ self._entry.set_text('')
+ self._entry.set_editable(False)
+ self.prompt()
+
+ return True
+ elif isinstance(accel, commands.accel_group.AccelCallback):
+ self._entry.set_editable(True)
+ self.run_command(lambda: accel.activate(self._command_state, self))
+ return True
+
+ if not self._entry.get_editable():
+ return True
+
for handler in self._handlers:
if (handler[0] == None or handler[0] == state) and evnt.keyval == handler[1] and handler[2](handler[3], state):
return True
@@ -211,13 +244,16 @@ class Entry(gtk.EventBox):
def prompt(self, pr=''):
self._prompt = pr
+ if self._accel_group != None:
+ pr = '<i>%s</i>' % (saxutils.escape(self._accel_group.full_name()),)
+
if not pr:
pr = ''
else:
pr = ' ' + pr
-
+
self._prompt_label.set_markup('<b>>>></b>%s' % pr)
-
+
def make_info(self):
if self._info_window == None:
self._info_window = Info(self)
@@ -256,7 +292,7 @@ class Entry(gtk.EventBox):
if self._info_window and self._info_window.empty():
self._info_window.destroy()
self._entry.grab_focus()
- self._entry.set_sensitive(True)
+ self._entry.set_sensitive(True)
def _show_wait_cancel(self):
self._cancel_button = self.info_add_action(gtk.STOCK_STOP, self.on_wait_cancel)
@@ -298,39 +334,24 @@ class Entry(gtk.EventBox):
self.hide()
gtk.EventBox.destroy(self)
- def on_execute(self, dummy, modifier):
- if self._info_window and not self._suspended:
- self._info_window.destroy()
-
- text = self._entry.get_text().strip()
- words = list(self._re_complete.finditer(text))
- wordsstr = []
-
- for word in words:
- spec = self._complete_word_match(word)
- wordsstr.append(spec[0])
-
- if not wordsstr and not self._command_state:
- self._entry.set_text('')
- return
-
+ def run_command(self, cb):
self._suspended = None
try:
- ret = commands.Commands().execute(self._command_state, text, words, wordsstr, self, modifier)
+ ret = cb()
except Exception, e:
self.command_history_done()
self._command_state.clear()
-
+
self.prompt()
-
+
# Show error in info
self.info_show('<b><span color="#f66">Error:</span></b> ' + saxutils.escape(str(e)), True)
if not isinstance(e, commands.exceptions.Execute):
self.info_show(traceback.format_exc(), False)
- return True
+ return None
if ret == commands.result.Result.SUSPEND:
# Wait for it...
@@ -342,7 +363,7 @@ class Entry(gtk.EventBox):
else:
self.command_history_done()
self.prompt('')
-
+
if ret == commands.result.Result.PROMPT:
self.prompt(ret.prompt)
elif (ret == None or ret == commands.result.HIDE) and not self._prompt and (not self._info_window or self._info_window.empty()):
@@ -352,6 +373,26 @@ class Entry(gtk.EventBox):
else:
self._entry.grab_focus()
+ return ret
+
+ def on_execute(self, dummy, modifier):
+ if self._info_window and not self._suspended:
+ self._info_window.destroy()
+
+ text = self._entry.get_text().strip()
+ words = list(self._re_complete.finditer(text))
+ wordsstr = []
+
+ for word in words:
+ spec = self._complete_word_match(word)
+ wordsstr.append(spec[0])
+
+ if not wordsstr and not self._command_state:
+ self._entry.set_text('')
+ return
+
+ self.run_command(lambda: commands.Commands().execute(self._command_state, text, words, wordsstr, self, modifier))
+
return True
def on_complete(self, dummy, modifier):
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]