This adds the neccessary keyboard handling, grabbing the input, switching to
the new activity, or canceling the operation. It is also responsible for
showing the frame during the tabbing operation.
---
 src/view/keyhandler.py |   96 +++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 94 insertions(+), 2 deletions(-)
diff --git a/src/view/keyhandler.py b/src/view/keyhandler.py
index 306bb21..8dee50c 100644
--- a/src/view/keyhandler.py
+++ b/src/view/keyhandler.py
@@ -32,6 +32,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',
@@ -77,10 +81,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)
@@ -131,15 +138,75 @@ class KeyHandler(object):
             self._get_speech_proxy().SayText(text, reply_handler=lambda: None, \
                 error_handler=self._on_speech_err)
 
+    def _window_tabbing(self, direction):
+        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
+
+        if self._tabbing_windows:
+            # We have the grab, so show the frame.
+            shell = view.Shell.get_instance()
+            frame = shell.get_frame()
+            frame.show(frame.MODE_NON_INTERACTIVE)
+
+            if direction == 1:
+                shell.tabbing_next_activity()
+            else:
+                shell.tabbing_previous_activity()
+
+        return self._tabbing_windows
+
+    def _stop_window_tabbing(self, cancel=True):
+        # 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()
+        frame = shell.get_frame()
+
+        if not cancel:
+            shell.tabbing_activate_current()
+        else:
+            shell.tabbing_cancel()
+
+        frame.hide()
+
+        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()
@@ -218,9 +285,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(True)
+                    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(True)
+                return True
 
         return False
+
+    def _key_released_cb(self, grabber, keycode, state):
+        if self._tabbing_windows:
+            # We stop tabbing and switch to the new window as soon as the
+            # modifier key is raised again.
+            if grabber.is_specific_modifier(keycode, _TABBING_MODIFIER):
+                self._stop_window_tabbing(False)
+
+            return True
+        return False
+
_______________________________________________
Sugar mailing list
Sugar@lists.laptop.org
http://lists.laptop.org/listinfo/sugar

Reply via email to