Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Berg <benjamin@sipsolutions.net>2008-06-20 15:01:56 (GMT)
committer Benjamin Berg <benjamin@sipsolutions.net>2008-06-20 15:01:56 (GMT)
commit7333ec948549328e696ea37a587ea8ec20831ea1 (patch)
treee900960a43ce8cffa712ce965e4bcc684918c88e
parente7e12131b4b21cb2b5f96a5541f034c11efad471 (diff)
parentd901f7c3c745c937202084f8965c0d30207a9c72 (diff)
Merge branch 'tabbing'
Conflicts: src/view/keyhandler.py
-rw-r--r--src/model/homemodel.py13
-rw-r--r--src/view/Makefile.am1
-rw-r--r--src/view/frame/activitiestray.py8
-rw-r--r--src/view/keyhandler.py34
-rw-r--r--src/view/tabbinghandler.py144
5 files changed, 197 insertions, 3 deletions
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 <benjamin@sipsolutions.net>
+#
+# 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
+