From 7333ec948549328e696ea37a587ea8ec20831ea1 Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Fri, 20 Jun 2008 15:01:56 +0000 Subject: Merge branch 'tabbing' Conflicts: src/view/keyhandler.py --- diff --git a/src/model/homemodel.py b/src/model/homemodel.py index a75adcf..06dffa9 100644 --- a/src/model/homemodel.py +++ b/src/model/homemodel.py @@ -64,6 +64,7 @@ class HomeModel(gobject.GObject): self._activities = [] self._active_activity = None + self._tabbing = False screen = wnck.screen_get_default() screen.connect('window-opened', self._window_opened_cb) @@ -102,6 +103,13 @@ class HomeModel(gobject.GObject): """Returns the activity that the user is currently working in""" return self._active_activity + def tabbing_set_activity(self, activity): + if activity: + self._tabbing = True + self._set_active_activity(activity) + else: + self._tabbing = False + def _set_active_activity(self, home_activity): if self._active_activity == home_activity: return @@ -185,6 +193,11 @@ class HomeModel(gobject.GObject): logging.error("set_active() failed: %s" % err) def _active_window_changed_cb(self, screen, previous_window=None): + if self._tabbing: + # Ignore any window changes when tabbing, as these are comming + # in delayed. + return + window = screen.get_active_window() if window is None: return diff --git a/src/view/Makefile.am b/src/view/Makefile.am index b09d965..a710bfe 100644 --- a/src/view/Makefile.am +++ b/src/view/Makefile.am @@ -10,6 +10,7 @@ sugar_PYTHON = \ clipboardmenu.py \ keyhandler.py \ pulsingicon.py \ + tabbinghandler.py \ OverlayWindow.py \ palettes.py \ Shell.py diff --git a/src/view/frame/activitiestray.py b/src/view/frame/activitiestray.py index 0c5b3f8..c34c119 100644 --- a/src/view/frame/activitiestray.py +++ b/src/view/frame/activitiestray.py @@ -302,6 +302,7 @@ class ActivitiesTray(HTray): self._buttons = {} self._invite_to_item = {} + self._freeze_button_clicks = False self._home_model = shellmodel.get_instance().get_home() self._home_model.connect('activity-added', self.__activity_added_cb) @@ -336,11 +337,16 @@ class ActivitiesTray(HTray): def __activity_changed_cb(self, home_model, home_activity): logging.debug('__activity_changed_cb: %r' % home_activity) + button = self._buttons[home_activity.get_activity_id()] + self._freeze_button_clicks = True button.props.active = True + self._freeze_button_clicks = True + + self.scroll_to_item(button) def __activity_clicked_cb(self, button, home_activity): - if button.props.active: + if not self._freeze_button_clicks and button.props.active: logging.debug('ActivitiesTray.__activity_clicked_cb') window = home_activity.get_window() if window: diff --git a/src/view/keyhandler.py b/src/view/keyhandler.py index 3684299..c7d3001 100644 --- a/src/view/keyhandler.py +++ b/src/view/keyhandler.py @@ -27,12 +27,14 @@ from sugar._sugarext import KeyGrabber from hardware import hardwaremanager import view.Shell +from view.tabbinghandler import TabbingHandler from model.shellmodel import ShellModel _BRIGHTNESS_STEP = 2 _VOLUME_STEP = 10 _BRIGHTNESS_MAX = 15 _VOLUME_MAX = 100 +_TABBING_MODIFIER = gtk.gdk.MOD1_MASK _actions_table = { 'F1' : 'zoom_mesh', @@ -81,6 +83,10 @@ class KeyHandler(object): self._key_grabber = KeyGrabber() self._key_grabber.connect('key-pressed', self._key_pressed_cb) + self._key_grabber.connect('key-released', + self._key_released_cb) + + self._tabbing_handler = TabbingHandler(_TABBING_MODIFIER) for key in _actions_table.keys(): self._key_grabber.grab(key) @@ -136,10 +142,10 @@ class KeyHandler(object): clipboard.request_text(self._primary_selection_cb) def handle_previous_window(self): - view.Shell.get_instance().activate_previous_activity() + self._tabbing_handler.previous_activity() def handle_next_window(self): - view.Shell.get_instance().activate_next_activity() + self._tabbing_handler.next_activity() def handle_close_window(self): view.Shell.get_instance().close_current_activity() @@ -251,9 +257,33 @@ class KeyHandler(object): self._keystate_pressed = state action = _actions_table[key] + if self._tabbing_handler.is_tabbing(): + # Only accept window tabbing events, everything else + # cancels the tabbing operation. + if not action in ["next_window", "previous_window"]: + self._tabbing_handler.stop() + return True + method = getattr(self, 'handle_' + action) method() return True + else: + # If this is not a registered key, then cancel tabbing. + if self._tabbing_handler.is_tabbing(): + if not grabber.is_modifier(keycode): + self._tabbing_handler.stop() + return True + + return False + + def _key_released_cb(self, grabber, keycode, state): + if self._tabbing_handler.is_tabbing(): + # We stop tabbing and switch to the new window as soon as the + # modifier key is raised again. + if grabber.is_modifier(keycode, mask=_TABBING_MODIFIER): + self._tabbing_handler.stop() + return True return False + diff --git a/src/view/tabbinghandler.py b/src/view/tabbinghandler.py new file mode 100644 index 0000000..dd57e6e --- /dev/null +++ b/src/view/tabbinghandler.py @@ -0,0 +1,144 @@ +# Copyright (C) 2008, Benjamin Berg +# +# 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 +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import logging +import gtk +import gobject + +import view.Shell +from view.frame import frame +from model import shellmodel + +_RAISE_DELAY = 250 + +class TabbingHandler(object): + def __init__(self, modifier): + self._tabbing = False + self._modifier = modifier + self._timeout = None + self._frame = frame.get_instance() + + def _start_tabbing(self): + if not self._tabbing: + 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 = (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 or not (mask & self._modifier): + logging.debug('Releasing grabs again.') + + # ungrab keyboard/pointer if the grab was successfull. + 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 = False + else: + shell = view.Shell.get_instance() + shell.take_activity_screenshot() + + self._frame.show(self._frame.MODE_NON_INTERACTIVE) + + def __timeout_cb(self): + self._activate_current() + self._timeout = None + return False + + def _start_timeout(self): + self._cancel_timeout() + self._timeout = gobject.timeout_add(_RAISE_DELAY, self.__timeout_cb) + + def _cancel_timeout(self): + if self._timeout: + gobject.source_remove(self._timeout) + self._timeout = None + + def _activate_current(self): + shell_model = shellmodel.get_instance() + home_model = shell_model.get_home() + activity = home_model.get_active_activity() + if activity and activity.get_window(): + activity.get_window().activate(1) + + def next_activity(self): + if not self._tabbing: + first_switch = True + self._start_tabbing() + else: + first_switch = False + + if self._tabbing: + shell_model = shellmodel.get_instance() + home_model = shell_model.get_home() + zoom_level = shell_model.get_zoom_level() + zoom_activity = (zoom_level == shellmodel.ShellModel.ZOOM_ACTIVITY) + + if not zoom_activity and first_switch: + activity = home_model.get_active_activity() + else: + activity = home_model.get_next_activity() + + home_model.tabbing_set_activity(activity) + self._start_timeout() + else: + view.Shell.get_instance().activate_next_activity() + + def previous_activity(self): + if not self._tabbing: + first_switch = True + self._start_tabbing() + else: + first_switch = False + + if self._tabbing: + shell_model = shellmodel.get_instance() + home_model = shell_model.get_home() + zoom_level = shell_model.get_zoom_level() + zoom_activity = (zoom_level == shellmodel.ShellModel.ZOOM_ACTIVITY) + + if not zoom_activity and first_switch: + activity = home_model.get_active_activity() + else: + activity = home_model.get_previous_activity() + + home_model.tabbing_set_activity(activity) + self._start_timeout() + else: + view.Shell.get_instance().activate_next_activity() + + def stop(self): + gtk.gdk.keyboard_ungrab() + gtk.gdk.pointer_ungrab() + self._tabbing = False + + self._frame.hide() + + self._cancel_timeout() + self._activate_current() + + def is_tabbing(self): + return self._tabbing + -- cgit v0.9.1