Start making keybindings work in Terminal(). They don't work yet

This commit is contained in:
Chris Jones 2009-10-14 13:05:07 +01:00
parent fdcd1c89f9
commit cd1d858d3c
3 changed files with 147 additions and 97 deletions

View File

@ -1,6 +1,6 @@
#!/usr/bin/python #!/usr/bin/python
# Terminator - multiple gnome terminals in one window # Terminator - multiple gnome terminals in one window
# Copyright (C) 2006-2008 cmsj@tenshu.net # Copyright (C) 2006-2008 cmsj@tenshu.net
# #
# This program is free software; you can redistribute it and/or modify # 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 # it under the terms of the GNU General Public License as published by
@ -24,112 +24,112 @@ keyboard shortcuts.
import re import re
import gtk import gtk
from terminatorlib.config import err from util import err
class KeymapError(Exception): class KeymapError(Exception):
"""Custom exception for errors in keybinding configurations""" """Custom exception for errors in keybinding configurations"""
def __init__(self, value): def __init__(self, value):
Exception.__init__(self, value) Exception.__init__(self, value)
self.value = value self.value = value
self.action = 'unknown' self.action = 'unknown'
def __str__(self): def __str__(self):
return "Keybinding '%s' invalid: %s" % (self.action, self.value) return "Keybinding '%s' invalid: %s" % (self.action, self.value)
MODIFIER = re.compile('<([^<]+)>') MODIFIER = re.compile('<([^<]+)>')
class TerminatorKeybindings: class Keybindings:
"""Class to handle loading and lookup of Terminator keybindings""" """Class to handle loading and lookup of Terminator keybindings"""
modifiers = { modifiers = {
'ctrl': gtk.gdk.CONTROL_MASK, 'ctrl': gtk.gdk.CONTROL_MASK,
'control': gtk.gdk.CONTROL_MASK, 'control': gtk.gdk.CONTROL_MASK,
'shift': gtk.gdk.SHIFT_MASK, 'shift': gtk.gdk.SHIFT_MASK,
'alt': gtk.gdk.MOD1_MASK, 'alt': gtk.gdk.MOD1_MASK,
'super': gtk.gdk.SUPER_MASK, 'super': gtk.gdk.SUPER_MASK,
} }
empty = {} empty = {}
keys = None keys = None
_masks = None _masks = None
_lookup = None _lookup = None
def __init__(self): def __init__(self):
self.keymap = gtk.gdk.keymap_get_default() self.keymap = gtk.gdk.keymap_get_default()
self.configure({}) self.configure({})
def configure(self, bindings): def configure(self, bindings):
"""Accept new bindings and reconfigure with them""" """Accept new bindings and reconfigure with them"""
self.keys = bindings self.keys = bindings
self.reload() self.reload()
def reload(self): def reload(self):
"""Parse bindings and mangle into an appropriate form""" """Parse bindings and mangle into an appropriate form"""
self._lookup = {} self._lookup = {}
self._masks = 0 self._masks = 0
for action, bindings in self.keys.items(): for action, bindings in self.keys.items():
if not isinstance(bindings, tuple): if not isinstance(bindings, tuple):
bindings = (bindings,) bindings = (bindings,)
for binding in bindings: for binding in bindings:
if binding is None or binding == "None": if binding is None or binding == "None":
continue 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: try:
keyval, mask = self._parsebinding(binding) return self.modifiers[modifier.lower()]
# Does much the same, but with poorer error handling. except KeyError:
#keyval, mask = gtk.accelerator_parse(binding) raise KeymapError("Unhandled modifier '<%s>'" % modifier)
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): def lookup(self, event):
"""Parse an individual binding using gtk's binding function""" """Translate a keyboard event into a mapped key"""
mask = 0 try:
modifiers = re.findall(MODIFIER, binding) keyval, egroup, level, consumed = self.keymap.translate_keyboard_state(
if modifiers: event.hardware_keycode,
for modifier in modifiers: event.state & ~gtk.gdk.LOCK_MASK,
mask |= self._lookup_modifier(modifier) event.group)
key = re.sub(MODIFIER, '', binding) except TypeError:
if key == '': err ("keybindings.lookup failed to translate keyboard event: %s" %
raise KeymapError('No key found') dir(event))
keyval = gtk.gdk.keyval_from_name(key) return None
if keyval == 0: mask = (event.state & ~consumed) & self._masks
raise KeymapError("Key '%s' is unrecognised" % key) return self._lookup.get(mask, self.empty).get(keyval, None)
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)

View File

@ -4,6 +4,8 @@
"""terminator.py - class for the master Terminator singleton""" """terminator.py - class for the master Terminator singleton"""
from borg import Borg from borg import Borg
from config import Config
from keybindings import Keybindings
class Terminator(Borg): class Terminator(Borg):
"""master object for the application""" """master object for the application"""
@ -13,6 +15,7 @@ class Terminator(Borg):
terminals = None terminals = None
groups = None groups = None
config = None config = None
keybindings = None
splittogroup = None splittogroup = None
autocleangroups = None autocleangroups = None
@ -38,6 +41,11 @@ class Terminator(Borg):
self.splittogroup = False self.splittogroup = False
if not self.autocleangroups: if not self.autocleangroups:
self.autocleangroups = True 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): def register_terminal(self, terminal):
"""Register a new terminal widget""" """Register a new terminal widget"""

View File

@ -376,6 +376,7 @@ class Terminal(gtk.VBox):
def create_group(self, item): def create_group(self, item):
"""Create a new group""" """Create a new group"""
#FIXME: Make this work
pass pass
def set_groupsend(self, widget, value): def set_groupsend(self, widget, value):
@ -395,6 +396,7 @@ class Terminal(gtk.VBox):
def reconfigure(self, widget=None): def reconfigure(self, widget=None):
"""Reconfigure our settings""" """Reconfigure our settings"""
# FIXME: actually reconfigure our settings
pass pass
def get_window_title(self): def get_window_title(self):
@ -409,7 +411,39 @@ class Terminal(gtk.VBox):
def on_keypress(self, widget, event): def on_keypress(self, widget, event):
"""Handler for keyboard events""" """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+<key>
# we only copy if there is a selection otherwise let it fall through
# to ^<key>
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): def on_buttonpress(self, widget, event):
"""Handler for mouse events""" """Handler for mouse events"""
@ -460,23 +494,29 @@ class Terminal(gtk.VBox):
self.vte.set_encoding(encoding) self.vte.set_encoding(encoding)
def on_drag_begin(self, widget, drag_context, data): def on_drag_begin(self, widget, drag_context, data):
# FIXME: Implement this
pass pass
def on_drag_data_get(self, widget, drag_context, selection_data, info, time, def on_drag_data_get(self, widget, drag_context, selection_data, info, time,
data): data):
# FIXME: Implement this
pass pass
def on_drag_motion(self, widget, drag_context, x, y, time, data): def on_drag_motion(self, widget, drag_context, x, y, time, data):
# FIXME: Implement this
pass pass
def on_drag_data_received(self, widget, drag_context, x, y, selection_data, def on_drag_data_received(self, widget, drag_context, x, y, selection_data,
info, time, data): info, time, data):
# FIXME: Implement this
pass pass
def on_vte_focus(self, widget): def on_vte_focus(self, widget):
# FIXME: Implement this
pass pass
def on_vte_focus_out(self, widget, event): def on_vte_focus_out(self, widget, event):
# FIXME: Implement this
pass pass
def on_vte_focus_in(self, widget, event): def on_vte_focus_in(self, widget, event):
@ -488,6 +528,7 @@ class Terminal(gtk.VBox):
self.vte.grab_focus() self.vte.grab_focus()
def on_resize_window(self): def on_resize_window(self):
# FIXME: Implement this
pass pass
def on_vte_size_allocate(self, widget, allocation): def on_vte_size_allocate(self, widget, allocation):
@ -496,6 +537,7 @@ class Terminal(gtk.VBox):
pass pass
def on_vte_notify_enter(self, term, event): def on_vte_notify_enter(self, term, event):
# FIXME: Implement this
pass pass
def hide_titlebar(self): def hide_titlebar(self):