From cd1d858d3ce02af27fd2711f5ca108fee2f539fb Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Wed, 14 Oct 2009 13:05:07 +0100 Subject: [PATCH] Start making keybindings work in Terminal(). They don't work yet --- terminatorlib/keybindings.py | 192 ++++++++++++++++----------------- terminatorlib/newterminator.py | 8 ++ terminatorlib/terminal.py | 44 +++++++- 3 files changed, 147 insertions(+), 97 deletions(-) diff --git a/terminatorlib/keybindings.py b/terminatorlib/keybindings.py index 76af7997..6702264d 100644 --- a/terminatorlib/keybindings.py +++ b/terminatorlib/keybindings.py @@ -1,6 +1,6 @@ #!/usr/bin/python -# Terminator - multiple gnome terminals in one window -# Copyright (C) 2006-2008 cmsj@tenshu.net +# Terminator - multiple gnome terminals in one window +# Copyright (C) 2006-2008 cmsj@tenshu.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 @@ -24,112 +24,112 @@ keyboard shortcuts. import re import gtk -from terminatorlib.config import err +from util import err class KeymapError(Exception): - """Custom exception for errors in keybinding configurations""" - def __init__(self, value): - Exception.__init__(self, value) - self.value = value - self.action = 'unknown' + """Custom exception for errors in keybinding configurations""" + def __init__(self, value): + Exception.__init__(self, value) + self.value = value + self.action = 'unknown' - def __str__(self): - return "Keybinding '%s' invalid: %s" % (self.action, self.value) + def __str__(self): + return "Keybinding '%s' invalid: %s" % (self.action, self.value) MODIFIER = re.compile('<([^<]+)>') -class TerminatorKeybindings: - """Class to handle loading and lookup of Terminator keybindings""" +class Keybindings: + """Class to handle loading and lookup of Terminator keybindings""" - modifiers = { - 'ctrl': gtk.gdk.CONTROL_MASK, - 'control': gtk.gdk.CONTROL_MASK, - 'shift': gtk.gdk.SHIFT_MASK, - 'alt': gtk.gdk.MOD1_MASK, - 'super': gtk.gdk.SUPER_MASK, - } + modifiers = { + 'ctrl': gtk.gdk.CONTROL_MASK, + 'control': gtk.gdk.CONTROL_MASK, + 'shift': gtk.gdk.SHIFT_MASK, + 'alt': gtk.gdk.MOD1_MASK, + 'super': gtk.gdk.SUPER_MASK, + } - empty = {} - keys = None - _masks = None - _lookup = None + empty = {} + keys = None + _masks = None + _lookup = None - def __init__(self): - self.keymap = gtk.gdk.keymap_get_default() - self.configure({}) + def __init__(self): + self.keymap = gtk.gdk.keymap_get_default() + self.configure({}) - def configure(self, bindings): - """Accept new bindings and reconfigure with them""" - self.keys = bindings - self.reload() + def configure(self, bindings): + """Accept new bindings and reconfigure with them""" + self.keys = bindings + self.reload() - def reload(self): - """Parse bindings and mangle into an appropriate form""" - self._lookup = {} - self._masks = 0 - for action, bindings in self.keys.items(): - if not isinstance(bindings, tuple): - bindings = (bindings,) + def reload(self): + """Parse bindings and mangle into an appropriate form""" + self._lookup = {} + self._masks = 0 + for action, bindings in self.keys.items(): + if not isinstance(bindings, tuple): + bindings = (bindings,) - for binding in bindings: - if binding is None or binding == "None": - continue + for binding in bindings: + if binding is None or binding == "None": + continue + try: + keyval, mask = self._parsebinding(binding) + # Does much the same, but with poorer error handling. + #keyval, mask = gtk.accelerator_parse(binding) + except KeymapError, ex: + ex.action = action + raise ex + else: + if mask & gtk.gdk.SHIFT_MASK: + if keyval == gtk.keysyms.Tab: + keyval = gtk.keysyms.ISO_Left_Tab + mask &= ~gtk.gdk.SHIFT_MASK + else: + keyvals = gtk.gdk.keyval_convert_case(keyval) + if keyvals[0] != keyvals[1]: + keyval = keyvals[1] + mask &= ~gtk.gdk.SHIFT_MASK + else: + keyval = gtk.gdk.keyval_to_lower(keyval) + self._lookup.setdefault(mask, {}) + self._lookup[mask][keyval] = action + self._masks |= mask + + def _parsebinding(self, binding): + """Parse an individual binding using gtk's binding function""" + mask = 0 + modifiers = re.findall(MODIFIER, binding) + if modifiers: + for modifier in modifiers: + mask |= self._lookup_modifier(modifier) + key = re.sub(MODIFIER, '', binding) + if key == '': + raise KeymapError('No key found') + keyval = gtk.gdk.keyval_from_name(key) + if keyval == 0: + raise KeymapError("Key '%s' is unrecognised" % key) + return (keyval, mask) + + def _lookup_modifier(self, modifier): + """Map modifier names to gtk values""" try: - keyval, mask = self._parsebinding(binding) - # Does much the same, but with poorer error handling. - #keyval, mask = gtk.accelerator_parse(binding) - except KeymapError, ex: - ex.action = action - raise ex - else: - if mask & gtk.gdk.SHIFT_MASK: - if keyval == gtk.keysyms.Tab: - keyval = gtk.keysyms.ISO_Left_Tab - mask &= ~gtk.gdk.SHIFT_MASK - else: - keyvals = gtk.gdk.keyval_convert_case(keyval) - if keyvals[0] != keyvals[1]: - keyval = keyvals[1] - mask &= ~gtk.gdk.SHIFT_MASK - else: - keyval = gtk.gdk.keyval_to_lower(keyval) - self._lookup.setdefault(mask, {}) - self._lookup[mask][keyval] = action - self._masks |= mask + return self.modifiers[modifier.lower()] + except KeyError: + raise KeymapError("Unhandled modifier '<%s>'" % modifier) - def _parsebinding(self, binding): - """Parse an individual binding using gtk's binding function""" - mask = 0 - modifiers = re.findall(MODIFIER, binding) - if modifiers: - for modifier in modifiers: - mask |= self._lookup_modifier(modifier) - key = re.sub(MODIFIER, '', binding) - if key == '': - raise KeymapError('No key found') - keyval = gtk.gdk.keyval_from_name(key) - if keyval == 0: - raise KeymapError("Key '%s' is unrecognised" % key) - return (keyval, mask) - - def _lookup_modifier(self, modifier): - """Map modifier names to gtk values""" - try: - return self.modifiers[modifier.lower()] - except KeyError: - raise KeymapError("Unhandled modifier '<%s>'" % modifier) - - def lookup(self, event): - """Translate a keyboard event into a mapped key""" - try: - keyval, egroup, level, consumed = self.keymap.translate_keyboard_state( - event.hardware_keycode, - event.state & ~gtk.gdk.LOCK_MASK, - event.group) - except TypeError: - err ("keybindings.lookup failed to translate keyboard event: %s" % - dir(event)) - return None - mask = (event.state & ~consumed) & self._masks - return self._lookup.get(mask, self.empty).get(keyval, None) + def lookup(self, event): + """Translate a keyboard event into a mapped key""" + try: + keyval, egroup, level, consumed = self.keymap.translate_keyboard_state( + event.hardware_keycode, + event.state & ~gtk.gdk.LOCK_MASK, + event.group) + except TypeError: + err ("keybindings.lookup failed to translate keyboard event: %s" % + dir(event)) + return None + mask = (event.state & ~consumed) & self._masks + return self._lookup.get(mask, self.empty).get(keyval, None) diff --git a/terminatorlib/newterminator.py b/terminatorlib/newterminator.py index fbd83917..7df9d86f 100755 --- a/terminatorlib/newterminator.py +++ b/terminatorlib/newterminator.py @@ -4,6 +4,8 @@ """terminator.py - class for the master Terminator singleton""" from borg import Borg +from config import Config +from keybindings import Keybindings class Terminator(Borg): """master object for the application""" @@ -13,6 +15,7 @@ class Terminator(Borg): terminals = None groups = None config = None + keybindings = None splittogroup = None autocleangroups = None @@ -38,6 +41,11 @@ class Terminator(Borg): self.splittogroup = False if not self.autocleangroups: self.autocleangroups = True + if not self.config: + self.config = Config() + if not self.keybindings: + self.keybindings = Keybindings() + self.keybindings.configure(self.config['keybindings']) def register_terminal(self, terminal): """Register a new terminal widget""" diff --git a/terminatorlib/terminal.py b/terminatorlib/terminal.py index a83eaa43..aa4af704 100755 --- a/terminatorlib/terminal.py +++ b/terminatorlib/terminal.py @@ -376,6 +376,7 @@ class Terminal(gtk.VBox): def create_group(self, item): """Create a new group""" + #FIXME: Make this work pass def set_groupsend(self, widget, value): @@ -395,6 +396,7 @@ class Terminal(gtk.VBox): def reconfigure(self, widget=None): """Reconfigure our settings""" + # FIXME: actually reconfigure our settings pass def get_window_title(self): @@ -409,7 +411,39 @@ class Terminal(gtk.VBox): def on_keypress(self, widget, event): """Handler for keyboard events""" - pass + if not event: + dbg('Terminal::on_keypress: Called on %s with no event' % widget) + return(False) + + # FIXME: Does keybindings really want to live in Terminator()? + mapping = self.terminator.keybindings.lookup(event) + + if mapping == "hide_window": + return(False) + + if mapping and mapping not in ['close_window', 'full_screen']: + dbg('Terminal::on_keypress: lookup found: %r' % mapping) + # handle the case where user has re-bound copy to ctrl+ + # we only copy if there is a selection otherwise let it fall through + # to ^ + if (mapping == "copy" and event.state & gtk.gdk.CONTROL_MASK): + if self._vte.get_has_selection (): + getattr(self, "key_" + mapping)() + return(True) + else: + getattr(self, "key_" + mapping)() + return(True) + + # FIXME: This is all clearly wrong. We should be doing this better + # FIXMS: maybe we can emit the key event and let Terminator() care? + if self.terminator.groupsend != 0 and self.vte.is_focus(): + if self.group and self.terminator.groupsend == 1: + self.terminator.group_emit(self, self.group, 'key-press-event', + event) + if self.terminator.groupsend == 2: + self.terminator.all_emit(self, 'key-press-event', event) + + return(False) def on_buttonpress(self, widget, event): """Handler for mouse events""" @@ -460,23 +494,29 @@ class Terminal(gtk.VBox): self.vte.set_encoding(encoding) def on_drag_begin(self, widget, drag_context, data): + # FIXME: Implement this pass def on_drag_data_get(self, widget, drag_context, selection_data, info, time, data): + # FIXME: Implement this pass def on_drag_motion(self, widget, drag_context, x, y, time, data): + # FIXME: Implement this pass def on_drag_data_received(self, widget, drag_context, x, y, selection_data, info, time, data): + # FIXME: Implement this pass def on_vte_focus(self, widget): + # FIXME: Implement this pass def on_vte_focus_out(self, widget, event): + # FIXME: Implement this pass def on_vte_focus_in(self, widget, event): @@ -488,6 +528,7 @@ class Terminal(gtk.VBox): self.vte.grab_focus() def on_resize_window(self): + # FIXME: Implement this pass def on_vte_size_allocate(self, widget, allocation): @@ -496,6 +537,7 @@ class Terminal(gtk.VBox): pass def on_vte_notify_enter(self, term, event): + # FIXME: Implement this pass def hide_titlebar(self):