Start making keybindings work in Terminal(). They don't work yet
This commit is contained in:
parent
fdcd1c89f9
commit
cd1d858d3c
|
@ -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)
|
|
||||||
|
|
||||||
|
|
|
@ -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"""
|
||||||
|
|
|
@ -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):
|
||||||
|
|
Loading…
Reference in New Issue