[sugar] [PATCH 3/6] Handle the keyboard event handling for tabbing.

Benjamin Berg benjamin at sipsolutions.net
Thu Jun 19 15:09:37 EDT 2008


---

 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:
+            # 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()
+
+            return True
+        return False
+



More information about the Sugar mailing list