---
src/view/keyhandler.py | 91 +++-
1 files changed, 89 insertions(+), 2 deletions(-)
diff --git a/src/view/keyhandler.py b/src/view/keyhandler.py
index 16f5a43..b42b93c 100644
--- a/src/view/keyhandler.py
+++ b/src/view/keyhandler.py
@@ -33,6 +33,10 @@ _BRIGHTNESS_STEP = 2
_VOLUME_STEP = 10
_BRIGHTNESS_MAX = 15
_VOLUME_MAX = 100
+# The modifier used for tabbing. Should the shortcuts ever be made user
+# configurable, then some code to figure out the apropriate modifier is
+# needed instead of hardcoding it.
+_TABBING_MODIFIER = gtk.gdk.MOD1_MASK
_actions_table = {
'F1' : 'zoom_mesh',
@@ -78,10 +82,13 @@ class KeyHandler(object):
self._keycode_pressed = 0
self._keystate_pressed = 0
self._speech_proxy = None
+self._tabbing_windows = False
self._key_grabber = KeyGrabber()
self._key_grabber.connect('key-pressed',
self._key_pressed_cb)
+self._key_grabber.connect('key-released',
+ self._key_released_cb)
for key in _actions_table.keys():
self._key_grabber.grab(key)
@@ -132,15 +139,70 @@ class KeyHandler(object):
self._get_speech_proxy().SayText(text, reply_handler=lambda: None,
\
error_handler=self._on_speech_err)
+def _window_tabbing(self, direction):
+shell = view.Shell.get_instance()
+if not self._tabbing_windows:
+logging.debug('Grabing the input.')
+
+screen = gtk.gdk.screen_get_default()
+window = screen.get_root_window()
+keyboard_grab_result = gtk.gdk.keyboard_grab(window)
+pointer_grab_result = gtk.gdk.pointer_grab(window)
+
+self._tabbing_windows = (keyboard_grab_result ==
gtk.gdk.GRAB_SUCCESS and
+ pointer_grab_result ==
gtk.gdk.GRAB_SUCCESS)
+
+# Now test that the modifier is still active to prevent race
+# conditions. We also test if one of the grabs failed.
+mask = window.get_pointer()[2]
+if not self._tabbing_windows or not (mask _TABBING_MODIFIER):
+logging.debug('Releasing grabs again.')
+
+if keyboard_grab_result != gtk.gdk.GRAB_SUCCESS:
+gtk.gdk.keyboard_ungrab()
+if pointer_grab_result != gtk.gdk.GRAB_SUCCESS:
+gtk.gdk.pointer_ungrab()
+self._tabbing_windows = False
+else:
+shell.tabbing_start()
+
+first_switch = True
+else:
+first_switch = False
+
+if self._tabbing_windows:
+if direction == 1:
+shell.tabbing_next_activity(first_switch)
+else:
+shell.tabbing_previous_activity(first_switch)
+
+return self._tabbing_windows
+
+def _stop_window_tabbing(self):
+# Some useless key was pressed, or Alt released.
+if not self._tabbing_windows:
+return
+
+logging.debug('Releasing grabs again.')
+gtk.gdk.keyboard_ungrab()
+gtk.gdk.pointer_ungrab()
+
+shell = view.Shell.get_instance()
+shell.tabbing_stop()
+
+self._tabbing_windows = False
+
def handle_say_text(self):
clipboard = gtk.clipboard_get(selection=PRIMARY)
clipboard.request_text(self._primary_selection_cb)
def handle_previous_window(self):
-view.Shell.get_instance().activate_previous_activity()
+if not self._window_tabbing(-1):
+view.Shell.get_instance().activate_previous_activity()
def handle_next_window(self):
-view.Shell.get_instance().activate_next_activity()
+if not self._window_tabbing(1):
+view.Shell.get_instance().activate_next_activity()
def handle_close_window(self):
view.Shell.get_instance().close_current_activity()
@@ -252,9 +314,34 @@ class KeyHandler(object):
self._keystate_pressed = state
action = _actions_table[key]
+if self._tabbing_windows:
+# Only accept window tabbing events, everything else
+# cancels the tabbing operation.
+if not action in [next_window, previous_window]:
+self._stop_window_tabbing()
+return True
+
method = getattr(self, 'handle_' + action)
method()
return True
+else:
+# If this is not a registered key, then cancel any active
+# tabbing.
+if self._tabbing_windows:
+if not grabber.is_modifier(keycode):
+self._stop_window_tabbing()
+return True
return False
+
+def _key_released_cb(self, grabber, keycode, state):
+if self._tabbing_windows:
+