Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--addons/bubblemessage.py16
-rw-r--r--data/ui/creator.glade49
-rwxr-xr-xsetup.py22
-rwxr-xr-xsrc/extensions/tutoriusremote.py74
-rw-r--r--src/tutorius/TProbe.py (renamed from tutorius/TProbe.py)69
-rw-r--r--src/tutorius/__init__.py (renamed from tutorius/__init__.py)0
-rw-r--r--src/tutorius/actions.py78
-rw-r--r--src/tutorius/addon.py (renamed from tutorius/addon.py)0
-rw-r--r--src/tutorius/constraints.py (renamed from tutorius/constraints.py)0
-rw-r--r--src/tutorius/core.py (renamed from tutorius/core.py)0
-rw-r--r--src/tutorius/creator.py (renamed from tutorius/creator.py)192
-rw-r--r--src/tutorius/dbustools.py (renamed from tutorius/dbustools.py)0
-rw-r--r--src/tutorius/dialog.py (renamed from tutorius/dialog.py)0
-rw-r--r--src/tutorius/editor.py (renamed from tutorius/editor.py)0
-rw-r--r--src/tutorius/engine.py (renamed from tutorius/engine.py)0
-rw-r--r--src/tutorius/filters.py (renamed from tutorius/filters.py)0
-rw-r--r--src/tutorius/gtkutils.py (renamed from tutorius/gtkutils.py)0
-rw-r--r--src/tutorius/linear_creator.py (renamed from tutorius/linear_creator.py)0
-rw-r--r--src/tutorius/overlayer.py (renamed from tutorius/overlayer.py)300
-rw-r--r--src/tutorius/properties.py (renamed from tutorius/properties.py)0
-rw-r--r--src/tutorius/service.py (renamed from tutorius/service.py)0
-rw-r--r--src/tutorius/services.py (renamed from tutorius/services.py)0
-rw-r--r--src/tutorius/store.py (renamed from tutorius/store.py)0
-rw-r--r--src/tutorius/testwin.py (renamed from tutorius/testwin.py)0
-rw-r--r--src/tutorius/textbubble.py (renamed from tutorius/textbubble.py)0
-rw-r--r--src/tutorius/uam/__init__.py (renamed from tutorius/uam/__init__.py)0
-rw-r--r--src/tutorius/uam/gobjectparser.py (renamed from tutorius/uam/gobjectparser.py)0
-rw-r--r--src/tutorius/uam/gtkparser.py (renamed from tutorius/uam/gtkparser.py)0
-rw-r--r--src/tutorius/vault.py (renamed from tutorius/vault.py)0
-rw-r--r--src/tutorius/viewer.py (renamed from tutorius/viewer.py)4
-rw-r--r--tutorius/actions.py178
31 files changed, 594 insertions, 388 deletions
diff --git a/addons/bubblemessage.py b/addons/bubblemessage.py
index 6572a6a..d76dc1c 100644
--- a/addons/bubblemessage.py
+++ b/addons/bubblemessage.py
@@ -13,7 +13,7 @@
# 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
-from ..actions import Action, DragWrapper
+from ..actions import Action
from ..properties import TStringProperty, TArrayProperty
from .. import overlayer
from ..services import ObjectStore
@@ -86,21 +86,17 @@ class BubbleMessage(Action):
"""
if not self.overlay:
self.overlay = ObjectStore().activity._overlayer
- assert not self._drag, "bubble action set to editmode twice"
x, y = self.position
self._bubble = overlayer.TextBubble(text=self.message,
tailpos=self.tail_pos)
- self.overlay.put(self._bubble, x, y)
+ self._handle = self.overlay.put(
+ self._bubble,
+ x, y,
+ draggable=True,
+ position_cb=self._update_position)
self._bubble.show()
- self._drag = DragWrapper(self._bubble, self.position, True)
-
def exit_editmode(self, *args):
- x,y = self._drag.position
- self.position = (int(x), int(y))
- if self._drag:
- self._drag.draggable = False
- self._drag = None
if self._bubble:
self.overlay.remove(self._bubble)
self._bubble = None
diff --git a/data/ui/creator.glade b/data/ui/creator.glade
index 1c9669d..35214df 100644
--- a/data/ui/creator.glade
+++ b/data/ui/creator.glade
@@ -1,16 +1,18 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--Generated with glade3 3.4.5 on Sun Nov 1 16:39:50 2009 -->
<glade-interface>
- <!-- interface-requires gtk+ 2.16 -->
- <!-- interface-naming-policy project-wide -->
<widget class="GtkWindow" id="mainwindow">
<property name="width_request">300</property>
<property name="height_request">500</property>
+ <property name="type">GTK_WINDOW_POPUP</property>
<property name="title" translatable="yes">Toolbox</property>
<property name="resizable">False</property>
- <property name="window_position">center-on-parent</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
<property name="default_width">200</property>
<property name="default_height">500</property>
<property name="destroy_with_parent">True</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_UTILITY</property>
<property name="skip_taskbar_hint">True</property>
<property name="skip_pager_hint">True</property>
<property name="focus_on_map">False</property>
@@ -19,35 +21,37 @@
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
- <property name="orientation">vertical</property>
+ <property name="orientation">GTK_ORIENTATION_VERTICAL</property>
<property name="spacing">5</property>
+ <property name="orientation">GTK_ORIENTATION_VERTICAL</property>
<child>
<widget class="GtkHButtonBox" id="hbuttonbox1">
<property name="visible">True</property>
<property name="spacing">5</property>
- <property name="layout_style">start</property>
+ <property name="layout_style">GTK_BUTTONBOX_START</property>
<child>
<widget class="GtkButton" id="button2">
- <property name="label">gtk-save</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
+ <property name="label">gtk-save</property>
<property name="use_stock">True</property>
+ <property name="response_id">0</property>
<signal name="clicked" handler="on_save_clicked"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
- <property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="button4">
- <property name="label">gtk-quit</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
+ <property name="label">gtk-quit</property>
<property name="use_stock">True</property>
+ <property name="response_id">0</property>
<signal name="clicked" handler="on_quit_clicked"/>
</widget>
<packing>
@@ -60,24 +64,24 @@
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
- <property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow1">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="hscrollbar_policy">never</property>
- <property name="vscrollbar_policy">automatic</property>
- <property name="shadow_type">in</property>
+ <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
<child>
<widget class="GtkViewport" id="viewport1">
<property name="visible">True</property>
- <property name="resize_mode">queue</property>
+ <property name="resize_mode">GTK_RESIZE_QUEUE</property>
<child>
<widget class="GtkVBox" id="vbox2">
<property name="visible">True</property>
- <property name="orientation">vertical</property>
+ <property name="orientation">GTK_ORIENTATION_VERTICAL</property>
+ <property name="orientation">GTK_ORIENTATION_VERTICAL</property>
<child>
<widget class="GtkExpander" id="expander1">
<property name="visible">True</property>
@@ -90,7 +94,6 @@
<property name="columns">2</property>
<property name="row_spacing">0</property>
<property name="column_spacing">0</property>
- <property name="item_padding">0</property>
<signal name="item_activated" handler="on_action_activate"/>
</widget>
</child>
@@ -106,7 +109,6 @@
</widget>
<packing>
<property name="expand">False</property>
- <property name="position">0</property>
</packing>
</child>
<child>
@@ -121,7 +123,6 @@
<property name="columns">2</property>
<property name="row_spacing">0</property>
<property name="column_spacing">0</property>
- <property name="item_padding">0</property>
<signal name="item_activated" handler="on_event_activate"/>
</widget>
</child>
@@ -153,8 +154,9 @@
<child>
<widget class="GtkVBox" id="propbox">
<property name="visible">True</property>
- <property name="orientation">vertical</property>
+ <property name="orientation">GTK_ORIENTATION_VERTICAL</property>
<property name="spacing">10</property>
+ <property name="orientation">GTK_ORIENTATION_VERTICAL</property>
<child>
<placeholder/>
</child>
@@ -169,26 +171,27 @@
<widget class="GtkHButtonBox" id="hbuttonbox2">
<property name="visible">True</property>
<property name="spacing">5</property>
- <property name="layout_style">start</property>
+ <property name="layout_style">GTK_BUTTONBOX_START</property>
<child>
<widget class="GtkButton" id="button1">
- <property name="label">gtk-media-record</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
+ <property name="label">gtk-media-record</property>
<property name="use_stock">True</property>
+ <property name="response_id">0</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
- <property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="button3">
- <property name="label">gtk-media-stop</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
+ <property name="label">gtk-media-stop</property>
<property name="use_stock">True</property>
+ <property name="response_id">0</property>
</widget>
<packing>
<property name="expand">False</property>
diff --git a/setup.py b/setup.py
index a994937..b46c08f 100755
--- a/setup.py
+++ b/setup.py
@@ -74,7 +74,7 @@ class TestCommand(Command):
sources)
coverage.report(sources)
coverage.erase()
-
+
def _listsources(self, arg, dirname, fnames):
fnames = filter(lambda x:x.endswith('.py'), fnames)
for name in fnames:
@@ -83,25 +83,27 @@ class TestCommand(Command):
setup(name='Tutorius',
version='0.0',
description='Interactive tutor and Tutorial creator',
- maintainer='Simon Poirier',
+ maintainer='Simon Poirier',
maintainer_email='simpoir@gmail.com',
author='Tutorius team',
author_email='sugar-narratives@googlegroups.com',
url='http://tutorius.org',
license='GPLv3',
packages=[
- 'sugar.tutorius',
- 'sugar.tutorius.uam',
- 'sugar.tutorius.addons',
- ],
+ 'sugar.tutorius',
+ 'sugar.tutorius.uam',
+ 'sugar.tutorius.addons',
+ ],
package_dir={
- 'sugar.tutorius': 'tutorius',
- 'sugar.tutorius.addons': 'addons',
- },
+ 'sugar.tutorius': 'src/tutorius',
+ 'sugar.tutorius.addons': 'addons',
+ },
cmdclass = {'test': TestCommand},
data_files=[('share/icons/sugar/scalable/actions', glob.glob('data/icons/*.svg')),
+ ('share/icons/sugar/scalable/device', ['data/icons/tutortool.svg']),
('share/tutorius/ui', glob.glob('data/ui/*.glade')),
+ ('share/sugar/extensions/deviceicon', glob.glob('src/extensions/*')),
]
)
-# vim: set et sw=4 sts=4 ts=4:
+# vim: set et sw=4 sts=4 ts=4:
diff --git a/src/extensions/tutoriusremote.py b/src/extensions/tutoriusremote.py
new file mode 100755
index 0000000..4ae8ac8
--- /dev/null
+++ b/src/extensions/tutoriusremote.py
@@ -0,0 +1,74 @@
+# Copyright (C) 2009, Tutorius.org
+# Copyright (C) 2009, Simon Poirier <simpoir@gmail.com>
+#
+#
+# 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
+"""
+This modules regroups the UI elements that drives the tutorial and tutorial
+creator from the Sugar frame.
+"""
+
+import gtk
+from gettext import gettext as _
+import gconf
+import dbus
+
+from sugar.graphics.tray import TrayIcon
+from sugar.graphics.palette import Palette
+from sugar.graphics.xocolor import XoColor
+
+from jarabe.frame.frameinvoker import FrameWidgetInvoker
+
+from sugar.tutorius.creator import Creator
+
+_ICON_NAME = 'tutortool'
+
+class TutoriusRemote(TrayIcon):
+
+ FRAME_POSITION_RELATIVE = 102
+
+ def __init__(self, creator):
+ self._creator = creator
+
+ client = gconf.client_get_default()
+ self._color = XoColor(client.get_string('/desktop/sugar/user/color'))
+
+ super(TutoriusRemote, self).__init__(icon_name=_ICON_NAME,
+ xo_color=self._color)
+
+ self.set_palette_invoker(FrameWidgetInvoker(self))
+
+ self.palette = TPalette(_('Tutorius'))
+ self.palette.set_group_id('frame')
+
+
+class TPalette(Palette):
+ def __init__(self, primary_text):
+ super(TPalette, self).__init__(primary_text)
+
+ self._creator_item = gtk.MenuItem(_('Create a tutorial'))
+ self._creator_item.connect('activate', self._start_creator)
+ self._creator_item.show()
+ self.menu.append(self._creator_item)
+
+ self.set_content(None)
+
+ def _start_creator(self, widget):
+ Creator().start_authoring(tutorial=None)
+
+
+
+def setup(tray):
+ tray.add_device(TutoriusRemote(Creator()))
diff --git a/tutorius/TProbe.py b/src/tutorius/TProbe.py
index f55547c..1e94894 100644
--- a/tutorius/TProbe.py
+++ b/src/tutorius/TProbe.py
@@ -93,11 +93,12 @@ class TProbe(dbus.service.Object):
# ------------------ Action handling --------------------------------------
@dbus.service.method("org.tutorius.ProbeInterface",
- in_signature='s', out_signature='s')
- def install(self, pickled_action):
+ in_signature='ss', out_signature='s')
+ def install(self, pickled_action, is_editing):
"""
Install an action on the Activity
@param pickled_action string pickled action
+ @param is_editing whether this action comes from the editor
@return string address of installed action
"""
loaded_action = pickle.loads(str(pickled_action))
@@ -110,17 +111,21 @@ class TProbe(dbus.service.Object):
if action._props:
action._props.update(loaded_action._props)
- action.do()
-
+ if not pickle.loads(str(is_editing)):
+ action.do()
+ else:
+ action.enter_editmode()
+
return address
@dbus.service.method("org.tutorius.ProbeInterface",
- in_signature='ss', out_signature='')
- def update(self, address, action_props):
+ in_signature='sss', out_signature='')
+ def update(self, address, action_props, is_editing):
"""
Update an already registered action
@param address string address returned by install()
@param action_props pickled action properties
+ @param is_editing whether this action comes from the editor
@return None
"""
action = self._installedActions[address]
@@ -128,20 +133,28 @@ class TProbe(dbus.service.Object):
if action._props:
props = pickle.loads(str(action_props))
action._props.update(props)
- action.undo()
- action.do()
+ if not pickle.loads(str(is_editing)):
+ action.undo()
+ action.do()
+ else:
+ action.exit_editmode()
+ action.enter_editmode()
@dbus.service.method("org.tutorius.ProbeInterface",
- in_signature='s', out_signature='')
- def uninstall(self, address):
+ in_signature='ss', out_signature='')
+ def uninstall(self, address, is_editing):
"""
Uninstall an action
@param address string address returned by install()
+ @param is_editing whether this action comes from the editor
@return None
"""
if self._installedActions.has_key(address):
action = self._installedActions[address]
- action.undo()
+ if not pickle.loads(str(is_editing)):
+ action.undo()
+ else:
+ action.exit_editmode()
self._installedActions.pop(address)
@@ -283,39 +296,46 @@ class ProbeProxy:
def __clear_action(self, action):
self._actions.pop(action, None)
- def install(self, action, block=False):
+ def install(self, action, block=False, is_editing=False):
"""
Install an action on the TProbe's activity
@param action Action to install
@param block Force a synchroneous dbus call if True
+ @param is_editing whether this action comes from the editor
@return None
"""
- return remote_call(self._probe.install, (pickle.dumps(action),),
+ return remote_call(self._probe.install, (pickle.dumps(action),
+ pickle.dumps(is_editing)),
save_args(self.__update_action, action),
block=block)
- def update(self, action, newaction, block=False):
+ def update(self, action, newaction, block=False, is_editing=False):
"""
Update an already installed action's properties and run it again
@param action Action to update
@param newaction Action to update it with
@param block Force a synchroneous dbus call if True
+ @param is_editing whether this action comes from the editor
@return None
"""
#TODO review how to make this work well
if not action in self._actions:
raise RuntimeWarning("Action not installed")
#TODO Check error handling
- return remote_call(self._probe.update, (self._actions[action], pickle.dumps(newaction._props)), block=block)
+ return remote_call(self._probe.update, (self._actions[action],
+ pickle.dumps(newaction._props),
+ pickle.dumps(is_editing)),
+ block=block)
- def uninstall(self, action, block=False):
+ def uninstall(self, action, block=False, is_editing=False):
"""
Uninstall an installed action
@param action Action to uninstall
@param block Force a synchroneous dbus call if True
+ @param is_editing whether this action comes from the editor
"""
if action in self._actions:
- remote_call(self._probe.uninstall,(self._actions.pop(action),), block=block)
+ remote_call(self._probe.uninstall,(self._actions.pop(action), is_editing), block=block)
def __update_event(self, event, callback, address):
LOGGER.debug("ProbeProxy :: Registered event %s with address %s", str(hash(event)), str(address))
@@ -455,39 +475,42 @@ class ProbeManager(object):
if self._current_activity == activity_id:
self._current_activity = None
- def install(self, action, block=False):
+ def install(self, action, block=False, is_editing=False):
"""
Install an action on the current activity
@param action Action to install
@param block Force a synchroneous dbus call if True
+ @param is_editing whether this action comes from the editor
@return None
"""
if self.currentActivity:
- return self._probes[self.currentActivity].install(action, block)
+ return self._probes[self.currentActivity].install(action, block, is_editing)
else:
raise RuntimeWarning("No activity attached")
- def update(self, action, newaction, block=False):
+ def update(self, action, newaction, block=False, is_editing=False):
"""
Update an already installed action's properties and run it again
@param action Action to update
@param newaction Action to update it with
@param block Force a synchroneous dbus call if True
+ @param is_editing whether this action comes from the editor
@return None
"""
if self.currentActivity:
- return self._probes[self.currentActivity].update(action, newaction, block)
+ return self._probes[self.currentActivity].update(action, newaction, block, is_editing)
else:
raise RuntimeWarning("No activity attached")
- def uninstall(self, action, block=False):
+ def uninstall(self, action, block=False, is_editing=False):
"""
Uninstall an installed action
@param action Action to uninstall
@param block Force a synchroneous dbus call if True
+ @param is_editing whether this action comes from the editor
"""
if self.currentActivity:
- return self._probes[self.currentActivity].uninstall(action, block)
+ return self._probes[self.currentActivity].uninstall(action, block, is_editing)
else:
raise RuntimeWarning("No activity attached")
diff --git a/tutorius/__init__.py b/src/tutorius/__init__.py
index e69de29..e69de29 100644
--- a/tutorius/__init__.py
+++ b/src/tutorius/__init__.py
diff --git a/src/tutorius/actions.py b/src/tutorius/actions.py
new file mode 100644
index 0000000..b52886d
--- /dev/null
+++ b/src/tutorius/actions.py
@@ -0,0 +1,78 @@
+# Copyright (C) 2009, Tutorius.org
+#
+# 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
+"""
+This module defines Actions that can be done and undone on a state
+"""
+import gtk
+
+from gettext import gettext as _
+
+from sugar.graphics import icon
+
+from . import addon
+from .services import ObjectStore
+from .properties import *
+
+class Action(TPropContainer):
+ """Base class for Actions"""
+ def __init__(self):
+ TPropContainer.__init__(self)
+ self.position = (0,0)
+
+ def do(self, **kwargs):
+ """
+ Perform the action
+ """
+ raise NotImplementedError("Not implemented")
+
+ def undo(self):
+ """
+ Revert anything the action has changed
+ """
+ pass #Should raise NotImplemented?
+
+ def enter_editmode(self, **kwargs):
+ """
+ Enters edit mode. The action should display itself in some way,
+ without affecting the currently running application. The default is
+ a small box with the action icon.
+ """
+ meta = addon.get_addon_meta(type(self).__name__)
+
+ self.__edit_img = icon.Icon(icon_name=meta['icon'],
+ icon_size=gtk.ICON_SIZE_LARGE_TOOLBAR)
+ ## Eventbox create a visible window for the icon, so it clips correctly
+ #self.__edit_img = gtk.EventBox()
+ #self.__edit_img.set_visible_window(True)
+ #self.__edit_img.add(actionicon)
+
+ x, y = self.position
+
+ self._handle = ObjectStore().activity._overlayer.put(
+ self.__edit_img,
+ x, y,
+ draggable=True,
+ position_cb=self._update_position)
+
+ self.__edit_img.show_all()
+
+ def _update_position(self, x, y):
+ self.position = [int(x), int(y)]
+
+ def exit_editmode(self, **kwargs):
+ ObjectStore().activity._overlayer.remove(self._handle)
+ self.__edit_img.destroy()
+
diff --git a/tutorius/addon.py b/src/tutorius/addon.py
index 7ac68f7..7ac68f7 100644
--- a/tutorius/addon.py
+++ b/src/tutorius/addon.py
diff --git a/tutorius/constraints.py b/src/tutorius/constraints.py
index 519bce8..519bce8 100644
--- a/tutorius/constraints.py
+++ b/src/tutorius/constraints.py
diff --git a/tutorius/core.py b/src/tutorius/core.py
index bfbe07b..bfbe07b 100644
--- a/tutorius/core.py
+++ b/src/tutorius/core.py
diff --git a/tutorius/creator.py b/src/tutorius/creator.py
index c477056..c6934b7 100644
--- a/tutorius/creator.py
+++ b/src/tutorius/creator.py
@@ -27,28 +27,55 @@ import gobject
from gettext import gettext as T
import os
-from sugar.graphics import icon
-import copy
+from sugar.graphics import icon, style
+import jarabe.frame
from . import overlayer, gtkutils, actions, vault, properties, addon
from . import filters
from .services import ObjectStore
from .core import Tutorial, FiniteStateMachine, State
from . import viewer
+from . import TProbe
class Creator(object):
"""
Class acting as a bridge between the creator, serialization and core
classes. This contains most of the UI part of the editor.
"""
- def __init__(self, activity, tutorial=None):
+
+ _instance = None
+
+ def __new__(typ):
+ """
+ This class is a singleton. There can never be more than one creator
+ at a time. This method returns a new instance of typ only if none
+ already exists.
+ """
+ return typ._instance or object.__new__(typ)
+
+ def __init__(self):
+ if Creator._instance:
+ return
+
+ super(Creator, self).__init__()
+ self.tuto = None
+ self.is_authoring = False
+ Creator._instance = self
+ self._probe_mgr = TProbe.ProbeManager()
+
+ def start_authoring(self, tutorial=None):
"""
- Instanciate a tutorial creator for the activity.
+ Start authoring a tutorial.
- @param activity to bind the creator to
- @param tutorial an existing tutorial to edit, or None to create one
+ @type tutorial: str or None
+ @param tutorial: the unique identifier to an existing tutorial to
+ modify, or None to create a new one.
"""
- self._activity = activity
+ if self.is_authoring:
+ raise Exception("Already authoring")
+
+ self.is_authoring = True
+
if not tutorial:
self._tutorial = FiniteStateMachine('Untitled')
self._state = State(name='INIT')
@@ -60,25 +87,14 @@ class Creator(object):
self._action_panel = None
self._current_filter = None
- self._intro_mask = None
- self._intro_handle = None
- allocation = self._activity.get_allocation()
- self._width = allocation.width
- self._height = allocation.height
self._selected_widget = None
self._eventmenu = None
self.tuto = None
self._guid = None
- self._hlmask = overlayer.Rectangle(None, (1.0, 0.0, 0.0, 0.5))
- self._activity._overlayer.put(self._hlmask, 0, 0)
-
- dlg_width = 300
- dlg_height = 70
- sw = gtk.gdk.screen_width()
- sh = gtk.gdk.screen_height()
+ frame = jarabe.frame.get_view()
- self._propedit = ToolBox(self._activity)
+ self._propedit = ToolBox(None)
self._propedit.tree.signal_autoconnect({
'on_quit_clicked': self._cleanup_cb,
'on_save_clicked': self.save,
@@ -86,18 +102,39 @@ class Creator(object):
'on_event_activate': self._add_event_cb,
})
self._propedit.window.move(
- gtk.gdk.screen_width()-self._propedit.window.get_allocation().width,
- 100)
-
+ gtk.gdk.screen_width()-self._propedit.window.get_allocation().width\
+ -style.GRID_CELL_SIZE,
+ style.GRID_CELL_SIZE)
+ self._propedit.window.connect('enter-notify-event',
+ frame._enter_notify_cb)
+ self._propedit.window.connect('leave-notify-event',
+ frame._leave_notify_cb)
self._overview = viewer.Viewer(self._tutorial, self)
- self._overview.win.set_transient_for(self._activity)
+ self._overview.win.set_transient_for(frame._bottom_panel)
+ self._overview.win.connect('enter-notify-event',
+ frame._enter_notify_cb)
+ self._overview.win.connect('leave-notify-event',
+ frame._leave_notify_cb)
- self._overview.win.move(0, gtk.gdk.screen_height()- \
- self._overview.win.get_allocation().height)
+ self._overview.win.move(style.GRID_CELL_SIZE,
+ gtk.gdk.screen_height()-style.GRID_CELL_SIZE \
+ -self._overview.win.get_allocation().height)
self._transitions = dict()
+ # FIXME : remove when probemgr completed
+ self._probe_mgr.attach('org.laptop.Calculate')
+ self._probe_mgr._current_activity = 'org.laptop.Calculate'
+
+ def _tool_enter_notify_cb(self, window, event):
+ frame = jarabe.frame.get_view()
+ frame._bottom_panel.hover = True
+
+ def _tool_leave_notify_cb(self, window, event):
+ frame = jarabe.frame.get_view()
+ frame._bottom_panel.hover = False
+
def _update_next_state(self, state, event, next_state):
self._transitions[event] = next_state
@@ -121,7 +158,8 @@ class Creator(object):
if fsm_action is action:
state.clear_actions()
if state is self._state:
- fsm_action.exit_editmode()
+ self._probe_mgr.uninstall(fsmaction, is_editing=True)
+ #fsm_action.exit_editmode()
state_actions.remove(fsm_action)
self.set_insertion_point(state.name)
for keep_action in state_actions:
@@ -182,14 +220,16 @@ class Creator(object):
def set_insertion_point(self, state_name):
for action in self._state.get_action_list():
- action.exit_editmode()
+ self._probe_mgr.uninstall(action, is_editing=True)
+ #action.exit_editmode()
self._state = self._tutorial.get_state_by_name(state_name)
self._overview.win.queue_draw()
state_actions = self._state.get_action_list()
for action in state_actions:
- action.enter_editmode()
- action._drag._eventbox.connect_after(
- "button-release-event", self._action_refresh_cb, action)
+ self._probe_mgr.install(action, is_editing=True)
+ #action.enter_editmode()
+ #action._drag._eventbox.connect_after(
+ # "button-release-event", self._action_refresh_cb, action)
if state_actions:
self._propedit.action = state_actions[0]
@@ -205,54 +245,21 @@ class Creator(object):
"""
# undo actions so they don't persist through step editing
for action in self._state.get_action_list():
- action.exit_editmode()
- self._hlmask.covered = None
+ self._probe_mgr.uninstall(action, is_editing=True)
+ #action.exit_editmode()
self._propedit.action = None
self._activity.queue_draw()
- def _intro_cb(self, widget, evt):
- """
- Callback for capture of widget events, when in introspect mode.
- """
- if evt.type == gtk.gdk.BUTTON_PRESS:
- # widget has focus, let's hilight it
- win = gtk.gdk.display_get_default().get_window_at_pointer()
- click_wdg = win[0].get_user_data()
- if not click_wdg.is_ancestor(self._activity._overlayer):
- # as popups are not (yet) supported, it would break
- # badly if we were to play with a widget not in the
- # hierarchy.
- return
- for hole in self._intro_mask.pass_thru:
- self._intro_mask.mask(hole)
- self._intro_mask.unmask(click_wdg)
- self._selected_widget = gtkutils.raddr_lookup(click_wdg)
-
- if self._eventmenu:
- self._eventmenu.destroy()
- self._eventmenu = gtk.Menu()
- menuitem = gtk.MenuItem(label=type(click_wdg).__name__)
- menuitem.set_sensitive(False)
- self._eventmenu.append(menuitem)
- self._eventmenu.append(gtk.MenuItem())
-
- for item in gobject.signal_list_names(click_wdg):
- menuitem = gtk.MenuItem(label=item)
- menuitem.connect("activate", self._evfilt_cb, item)
- self._eventmenu.append(menuitem)
- self._eventmenu.show_all()
- self._eventmenu.popup(None, None, None, evt.button, evt.time)
- self._activity.queue_draw()
-
def _add_action_cb(self, widget, path):
"""Callback for the action creation toolbar tool"""
action_type = self._propedit.actions_list[path][ToolBox.ICON_NAME]
action = addon.create(action_type)
- action.enter_editmode()
+ self._probe_mgr.install(action, is_editing=True)
+ #action.enter_editmode()
self._state.add_action(action)
# FIXME: replace following with event catching
- action._drag._eventbox.connect_after(
- "button-release-event", self._action_refresh_cb, action)
+ #action._drag._eventbox.connect_after(
+ # "button-release-event", self._action_refresh_cb, action)
self._overview.win.queue_draw()
def _add_event_cb(self, widget, path):
@@ -314,12 +321,14 @@ class Creator(object):
Callback for refreshing properties values and notifying the
property dialog of the new values.
"""
- action.exit_editmode()
- action.enter_editmode()
+ self._probe_mgr.uninstall(action, is_editing=True)
+ #action.exit_editmode()
+ self._probe_mgr.install(action, is_editing=True)
+ #action.enter_editmode()
self._activity.queue_draw()
# TODO: replace following with event catching
- action._drag._eventbox.connect_after(
- "button-release-event", self._action_refresh_cb, action)
+ #action._drag._eventbox.connect_after(
+ # "button-release-event", self._action_refresh_cb, action)
self._propedit.action = action
self._overview.win.queue_draw()
@@ -330,10 +339,10 @@ class Creator(object):
"""
# undo actions so they don't persist through step editing
for action in self._state.get_action_list():
- action.exit_editmode()
+ self._probe_mgr.uninstall(action, is_editing=True)
+ #action.exit_editmode()
dialog = gtk.MessageDialog(
- parent=self._activity,
flags=gtk.DIALOG_MODAL,
type=gtk.MESSAGE_QUESTION,
buttons=gtk.BUTTONS_YES_NO,
@@ -344,19 +353,14 @@ class Creator(object):
self.save()
# remove UI remains
- self._hlmask.covered = None
- self._activity._overlayer.remove(self._hlmask)
- self._hlmask.destroy()
- self._hlmask = None
self._propedit.destroy()
self._overview.destroy()
self._activity.queue_draw()
- del self._activity._creator
+ self.is_authoring = False
def save(self, widget=None):
if not self.tuto:
- dlg = TextInputDialog(self._activity,
- text=T("Enter a tutorial title."),
+ dlg = TextInputDialog(text=T("Enter a tutorial title."),
field=T("Title"))
tutorialName = ""
while not tutorialName: tutorialName = dlg.pop()
@@ -369,16 +373,11 @@ class Creator(object):
bundle.write_metadata_file(self.tuto)
bundle.write_fsm(self._tutorial)
-
- def launch(*args, **kwargs):
- """
- Launch and attach a creator to the currently running activity.
- """
- activity = ObjectStore().activity
- if not hasattr(activity, "_creator"):
- activity._creator = Creator(activity)
+ def launch(self, *args):
+ assert False, "REMOVE THIS CALL!!!"
launch = staticmethod(launch)
+
class ToolBox(object):
ICON_LABEL = 0
ICON_IMAGE = 1
@@ -392,6 +391,8 @@ class ToolBox(object):
'ui', 'creator.glade')
self.tree = gtk.glade.XML(glade_file)
self.window = self.tree.get_widget('mainwindow')
+ self.window.modify_bg(gtk.STATE_NORMAL,
+ style.COLOR_TOOLBAR_GREY.get_gdk_color())
self._propbox = self.tree.get_widget('propbox')
self.window.set_transient_for(parent)
@@ -521,19 +522,19 @@ class ToolBox(object):
setattr(action, propname, tuple(attr))
except ValueError:
widget.set_text(str(getattr(action, propname)[idx]))
- self.__parent._creator._action_refresh_cb(None, None, action)
+ Creator()._action_refresh_cb(None, None, action)
def _uam_prop_changed(self, widget, action, propname):
selector = WidgetSelector(self.__parent)
selection = selector.select()
setattr(action, propname, selection)
- self.__parent._creator._action_refresh_cb(None, None, action)
+ Creator()._action_refresh_cb(None, None, action)
def _str_prop_changed(self, widget, evt, action, propname):
buf = widget.get_buffer()
setattr(action, propname, buf.get_text(buf.get_start_iter(), buf.get_end_iter()))
- self.__parent._creator._action_refresh_cb(None, None, action)
+ Creator()._action_refresh_cb(None, None, action)
def _int_prop_changed(self, widget, evt, action, prop):
setattr(action, propname, widget.get_value_as_int())
- self.__parent._creator._action_refresh_cb(None, None, action)
+ Creator()._action_refresh_cb(None, None, action)
class WidgetSelector(object):
@@ -695,6 +696,7 @@ class TextInputDialog(gtk.MessageDialog):
def _dialog_done_cb(self, entry, response):
self.response(response)
+
# The purpose of this function is to reformat text, as current IconView
# implentation does not insert carriage returns on long lines.
# To preserve layout, this call reformat text to fit in small space under an
diff --git a/tutorius/dbustools.py b/src/tutorius/dbustools.py
index 5d70d7b..5d70d7b 100644
--- a/tutorius/dbustools.py
+++ b/src/tutorius/dbustools.py
diff --git a/tutorius/dialog.py b/src/tutorius/dialog.py
index be51a0e..be51a0e 100644
--- a/tutorius/dialog.py
+++ b/src/tutorius/dialog.py
diff --git a/tutorius/editor.py b/src/tutorius/editor.py
index 9d2effe..9d2effe 100644
--- a/tutorius/editor.py
+++ b/src/tutorius/editor.py
diff --git a/tutorius/engine.py b/src/tutorius/engine.py
index e77a018..e77a018 100644
--- a/tutorius/engine.py
+++ b/src/tutorius/engine.py
diff --git a/tutorius/filters.py b/src/tutorius/filters.py
index 38cf86b..38cf86b 100644
--- a/tutorius/filters.py
+++ b/src/tutorius/filters.py
diff --git a/tutorius/gtkutils.py b/src/tutorius/gtkutils.py
index 1a9cb0f..1a9cb0f 100644
--- a/tutorius/gtkutils.py
+++ b/src/tutorius/gtkutils.py
diff --git a/tutorius/linear_creator.py b/src/tutorius/linear_creator.py
index f664c49..f664c49 100644
--- a/tutorius/linear_creator.py
+++ b/src/tutorius/linear_creator.py
diff --git a/tutorius/overlayer.py b/src/tutorius/overlayer.py
index b967739..f0d251e 100644
--- a/tutorius/overlayer.py
+++ b/src/tutorius/overlayer.py
@@ -18,6 +18,9 @@ drawing management (Overlayer) and basic overlayable widgets are defined here.
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+import pygtk
+pygtk.require('2.0')
+
import gobject
import gtk
import cairo
@@ -57,20 +60,72 @@ class Overlayer(gtk.Layout):
@param overlayed widget to be overlayed. Will be resized to full size.
"""
+
+ _instance = None
+ def __new__(klass, *args):
+ """
+ As the overlay runs in a separate window, bound to the activity's main
+ window, it doesn't make sense to have multiple instances.
+ For this reason, __new__ will enforce the singleton pattern and
+ return the same instance.
+
+ @param klass: the type to instanciate
+ @returns: the instance of Overlayer for this process.
+ """
+ return Overlayer._instance or gtk.Layout.__new__(klass)
+
def __init__(self, overlayed=None):
+ if Overlayer._instance:
+ return
+ Overlayer._instance = self
super(Overlayer, self).__init__()
- self._overlayed = overlayed
- if overlayed:
- self.put(overlayed, 0, 0)
+ _set_wm_compositing(True)
- self.__realizer = self.connect_after("realize", self.__init_realized)
- self.connect("size-allocate", self.__size_allocate)
- self.show()
+ self._overlayed = overlayed
+ self._win = gtk.Window(gtk.WINDOW_POPUP)
+ self._win.set_property('skip-taskbar-hint', True)
+ self._win.set_property('skip-pager-hint', True)
+ self._win.set_accept_focus(False)
+ self._win.set_decorated(False)
+ self._win.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_NOTIFICATION)
+ self._win.set_keep_above(True)
+ self._win.set_app_paintable(True)
+
+ self._win.connect("screen-changed", self.__init_realized)
+ self._win.connect("expose-event", self.__expose_overlay)
+ self.__visible = True
+ gobject.timeout_add_seconds(5, self.__keep_visible)
+
+ # ensure colormap is set before realization, so the window gets
+ # realized with transparency enabled.
+ self.__init_realized(self._win)
+ self._win.show_all()
self.__render_handle = None
- def put(self, child, x, y):
+ def __keep_visible(self, *args):
+ """
+ This is a hack to ensure the overlay window is always visible.
+ Ideally, it would be bound to the window, manager raising itself
+ with its parent window. Right now, it still is in the same process
+ as the activity, with the side-effect of disappearing along with any
+ popup that pops down, including menus and tooltips.
+
+ set self.__visible to false to stop this behaviour.
+ """
+ # TODO: use Activity WM tracking to ensure overlayers don't "fight"
+ # for the focus. We should also evaluate the benefit of completely
+ # extracting the overlayer from the activities and putting it as part of
+ # the tutorius service
+ # standalone service.
+ if self.__visible:
+ self._win.present()
+ gobject.timeout_add_seconds(5, self.__keep_visible)
+
+
+
+ def put(self, child, x, y, draggable=False, position_cb=None):
"""
Adds a child widget to be overlayed. This can be, overlay widgets or
normal GTK widgets (though normal widgets will alwas appear under
@@ -79,15 +134,33 @@ class Overlayer(gtk.Layout):
@param child the child to add
@param x the horizontal coordinate for positionning
@param y the vertical coordinate for positionning
+ @param draggable: whether or not this widget's should have the
+ mouse-drag functionality enabled.
+ @param position_cb: a callback for obtaining position changes
+ if draggable was activated. Callbacks signature should be
+ cb(x, y).
+ @returns: a handle, used when calling Overlayer.remove(handle)
"""
if hasattr(child, "draw_with_context"):
# if the widget has the CanvasDrawable protocol, use it.
child.no_expose = True
- super(Overlayer, self).put(child, x, y)
+
+ wrapper = DragWrapper(child, draggable, position_cb)
+ super(Overlayer, self).put(wrapper.container, x, y)
+ self.show_all()
# be sure to redraw or the overlay may not show
self.queue_draw()
+ return wrapper
+
+ def remove(self, handle):
+ """
+ Remove the widget with handle from the overlay.
+
+ @param handle: the handle of the widget, as returned by put()
+ """
+ super(Overlayer, self).remove(handle.container)
def __init_realized(self, widget):
"""
@@ -96,61 +169,63 @@ class Overlayer(gtk.Layout):
callback for the first expose run. It should also be called after
beign reparented to ensure the window used for drawing is set up.
"""
- assert hasattr(self.window, "set_composited"), \
- "compositing not supported or widget not realized."
- self.disconnect(self.__realizer)
- del self.__realizer
+ screen = widget.get_screen()
+ colormap = screen.get_rgba_colormap()
+ if not colormap:
+ colormap = screen.get_rgb_colormap()
+
+ widget.set_colormap(colormap)
- self.parent.set_app_paintable(True)
+ self._win.set_size_request(screen.get_width(), screen.get_height())
- # the parent is composited, so we can access gtk's rendered buffer
- # and overlay over. If we don't composite, we won't be able to read
- # pixels and background will be black.
- self.window.set_composited(True)
- self.__render_handle = self.parent.connect_after("expose-event", \
- self.__expose_overlay)
+ return True
def __expose_overlay(self, widget, event):
"""expose event handler to draw the thing."""
- #get our child (in this case, the event box)
- child = widget.get_child()
+ screen = widget.get_screen()
+ colormap = screen.get_rgba_colormap()
+ widget.set_colormap(colormap)
+ width, height = widget.get_size()
- #create a cairo context to draw to the window
ctx = widget.window.cairo_create()
-
- #the source data is the (composited) event box
- ctx.set_source_pixmap(child.window,
- child.allocation.x,
- child.allocation.y)
-
- #draw no more than our expose event intersects our child
- region = gtk.gdk.region_rectangle(child.allocation)
- rect = gtk.gdk.region_rectangle(event.area)
- region.intersect(rect)
- ctx.region (region)
- ctx.clip()
+ # clear the window
+ ctx.set_source_rgba(1.0, 1.0, 1.0, 0.0)
+ ctx.set_operator(cairo.OPERATOR_SOURCE)
+ ctx.paint()
+
+ ctx.set_source_rgba(1.0, 0.2, 0.2, 0.7)
+ ctx.rectangle(10.0, 10.0, 200.0, 150.0)
+ ctx.fill()
+ ctx.move_to(20,20)
+ ctx.set_source_rgba(0.0, 0.2, 0.2, 0.7)
+ layout = ctx.create_layout()
+ layout.set_markup(self._overlayed.get_title())
+ ctx.show_layout(layout)
+
+
+ ##draw no more than our expose event intersects our child
+ #region = gtk.gdk.region_rectangle(child.allocation)
+ #rect = gtk.gdk.region_rectangle(event.area)
+ #region.intersect(rect)
+ #ctx.region (region)
+ #ctx.clip()
ctx.set_operator(cairo.OPERATOR_OVER)
- # has to be blended and a 1.0 alpha would not make it blend
- ctx.paint_with_alpha(0.99)
#draw overlay
- for drawn_child in self.get_children()[1:]:
+ for child in self.get_children():
+ drawn_child = child.get_children()[0]
if hasattr(drawn_child, "draw_with_context"):
drawn_child.draw_with_context(ctx)
- def __size_allocate(self, widget, allocation):
- """
- Set size allocation (actual gtk widget size) and propagate it to
- overlayed child
- """
- self.allocation = allocation
- # One may wonder why using size_request instead of size_allocate;
- # Since widget is laid out in a Layout box, the Layout will honor the
- # requested size. Using size_allocate could make a nasty nested loop in
- # some cases.
- self._overlayed.set_size_request(allocation.width, allocation.height)
+ # let events pass through
+ mask = gtk.gdk.Pixmap(None, width, height, 1)
+ mask_ctx = mask.cairo_create()
+ mask_ctx.set_operator(cairo.OPERATOR_CLEAR)
+ mask_ctx.paint()
+ self._win.input_shape_combine_mask(mask, 0, 0)
+ return False
class TextBubble(gtk.Widget):
"""
@@ -181,6 +256,7 @@ class TextBubble(gtk.Widget):
self._no_expose = False
self.__exposer = None
+ self._text_layout = None
def draw_with_context(self, context):
"""
@@ -499,5 +575,133 @@ class Mask(gtk.EventBox):
gobject.type_register(Mask)
+class DragWrapper(object):
+ """Wrapper to allow gtk widgets to be dragged around"""
+ def __init__(self, widget, draggable=False, callback=None):
+ """
+ Creates a wrapper to allow gtk widgets to be mouse dragged, if the
+ parent container supports the move() method, like a gtk.Layout.
+
+ Note: initial position attribute is none, until the widget is moved.
+
+ @param widget the widget to enhance with drag capability
+ @param draggable wether to enable the drag functionality now
+ @param callback: a callback for obtaining position changes
+ if draggable was activated. Callbacks signature should be
+ cb(x, y).
+ """
+ self._widget = widget
+ self.container = gtk.EventBox()
+ self.container.show()
+ self.container.set_visible_window(False)
+ self.container.add(widget)
+
+ self._drag_on = False # whether dragging is enabled
+ self._rel_pos = (0,0) # mouse pos relative to widget
+ self._handles = [] # event handlers
+ self._dragging = False # whether a drag is in progress
+ self.position = None # position of the widget
+ self._callback = callback
+
+ self.draggable = draggable
+
+ def _pressed_cb(self, widget, evt):
+ """Callback for start of drag event"""
+ self.container.grab_add()
+ self._dragging = True
+ self._rel_pos = evt.get_coords()
+
+ def _moved_cb(self, widget, evt):
+ """Callback for mouse drag events"""
+ if not self._dragging:
+ return
+
+ # Focus on a widget before dragging another would
+ # create addititonal move event, making the widget jump unexpectedly.
+ # Solution found was to process those focus events before dragging.
+ if gtk.events_pending():
+ return
+
+ xrel, yrel = self._rel_pos
+ xparent, yparent = evt.get_coords()
+ xparent, yparent = widget.translate_coordinates(widget.parent,
+ xparent, yparent)
+ self.position = (xparent-xrel, yparent-yrel)
+ self._widget.parent.move(self.container, *self.position)
+ self._widget.parent.queue_draw()
+
+ def _released_cb(self, *args):
+ """Callback for end of drag (mouse release)."""
+ self.container.grab_remove()
+ self._dragging = False
+ if self._callback:
+ callback(*self.position)
+
+ def _drag_end(self, *args):
+ """Callback for end of drag (stolen focus)."""
+ self._dragging = False
+
+ def set_draggable(self, value):
+ """Setter for the draggable property"""
+ if bool(value) ^ bool(self._drag_on):
+ if value:
+ size = self._widget.size_request()
+ self.container.set_size_request(*size)
+ self._handles.append(self.container.connect(
+ "button-press-event", self._pressed_cb))
+ self._handles.append(self.container.connect(
+ "button-release-event", self._released_cb))
+ self._handles.append(self.container.connect(
+ "motion-notify-event", self._moved_cb))
+ self._handles.append(self.container.connect(
+ "grab-broken-event", self._drag_end))
+ else:
+ while self._handles:
+ handle = self._handles.pop()
+ self.container.disconnect(handle)
+ self._drag_on = value
+
+ def get_draggable(self):
+ """Getter for the draggable property"""
+ return self._drag_on
+
+ draggable = property(fset=set_draggable, fget=get_draggable, \
+ doc="Property to enable the draggable behaviour of the widget")
+
+ def set_widget(self, widget):
+ """Setter for the widget property"""
+ if self._dragging or self._drag_on:
+ raise Exception("Can't change widget while dragging is enabled.")
+
+ assert hasattr(widget, "parent"), "wrapped widget should have a parent"
+ parent = widget.parent
+ assert hasattr(parent, "move"), "container of widget need move method"
+ self._widget = widget
+
+ def get_widget(self):
+ """Getter for the widget property"""
+ return self._widget
+
+ widget = property(fset=set_widget, fget=get_widget)
+
+
+GCONF_COMPOSITING_KEY = '/apps/metacity/general/compositing_manager'
+
+def _set_wm_compositing(compositing):
+ """
+ Utility method to reset metacity's compostiting.
+ Compositing allows windows to blend together and avoids flickers
+ when widgets redraw (like the frame animation).
+
+ @type compositing: bool
+ @param compositing: Whether to enable metacity compositing feature.
+ @returns: the new value for compositing
+ """
+ import gconf
+ client = gconf.client_get_default()
+ if not client.get_bool(GCONF_COMPOSITING_KEY):
+ client.set_bool(GCONF_COMPOSITING_KEY, compositing)
+ return client.get_bool(GCONF_COMPOSITING_KEY)
+
# vim:set ts=4 sts=4 sw=4 et:
diff --git a/tutorius/properties.py b/src/tutorius/properties.py
index 5422532..5422532 100644
--- a/tutorius/properties.py
+++ b/src/tutorius/properties.py
diff --git a/tutorius/service.py b/src/tutorius/service.py
index eb246a1..eb246a1 100644
--- a/tutorius/service.py
+++ b/src/tutorius/service.py
diff --git a/tutorius/services.py b/src/tutorius/services.py
index e7b17d8..e7b17d8 100644
--- a/tutorius/services.py
+++ b/src/tutorius/services.py
diff --git a/tutorius/store.py b/src/tutorius/store.py
index 9c8bdff..9c8bdff 100644
--- a/tutorius/store.py
+++ b/src/tutorius/store.py
diff --git a/tutorius/testwin.py b/src/tutorius/testwin.py
index ef92b7f..ef92b7f 100644
--- a/tutorius/testwin.py
+++ b/src/tutorius/testwin.py
diff --git a/tutorius/textbubble.py b/src/tutorius/textbubble.py
index e09b298..e09b298 100644
--- a/tutorius/textbubble.py
+++ b/src/tutorius/textbubble.py
diff --git a/tutorius/uam/__init__.py b/src/tutorius/uam/__init__.py
index bcd67e1..bcd67e1 100644
--- a/tutorius/uam/__init__.py
+++ b/src/tutorius/uam/__init__.py
diff --git a/tutorius/uam/gobjectparser.py b/src/tutorius/uam/gobjectparser.py
index c1fba3d..c1fba3d 100644
--- a/tutorius/uam/gobjectparser.py
+++ b/src/tutorius/uam/gobjectparser.py
diff --git a/tutorius/uam/gtkparser.py b/src/tutorius/uam/gtkparser.py
index ede2f03..ede2f03 100644
--- a/tutorius/uam/gtkparser.py
+++ b/src/tutorius/uam/gtkparser.py
diff --git a/tutorius/vault.py b/src/tutorius/vault.py
index b455a52..b455a52 100644
--- a/tutorius/vault.py
+++ b/src/tutorius/vault.py
diff --git a/tutorius/viewer.py b/src/tutorius/viewer.py
index 272558e..fc86d44 100644
--- a/tutorius/viewer.py
+++ b/src/tutorius/viewer.py
@@ -68,7 +68,8 @@ class Viewer(object):
self.drag_pos = None
self.selection = []
- self.win = gtk.Window(gtk.WINDOW_TOPLEVEL)
+ self.win = gtk.Window(gtk.WINDOW_POPUP)
+ self.win.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_UTILITY)
self.win.set_size_request(400, 200)
self.win.set_gravity(gtk.gdk.GRAVITY_SOUTH_WEST)
self.win.show()
@@ -378,6 +379,7 @@ class Viewer(object):
ctx.clip()
ctx.paint()
+ ctx.translate(20, 0)
ctx.translate(BLOCK_PADDING, BLOCK_PADDING)
painter = self._paint_state(ctx, states)
diff --git a/tutorius/actions.py b/tutorius/actions.py
deleted file mode 100644
index bb15459..0000000
--- a/tutorius/actions.py
+++ /dev/null
@@ -1,178 +0,0 @@
-# Copyright (C) 2009, Tutorius.org
-#
-# 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
-"""
-This module defines Actions that can be done and undone on a state
-"""
-import gtk
-
-from gettext import gettext as _
-
-from sugar.graphics import icon
-
-from . import addon
-from .services import ObjectStore
-from .properties import *
-
-class DragWrapper(object):
- """Wrapper to allow gtk widgets to be dragged around"""
- def __init__(self, widget, position, draggable=False):
- """
- Creates a wrapper to allow gtk widgets to be mouse dragged, if the
- parent container supports the move() method, like a gtk.Layout.
- @param widget the widget to enhance with drag capability
- @param position the widget's position. Will translate the widget if needed
- @param draggable wether to enable the drag functionality now
- """
- self._widget = widget
- self._eventbox = None
- self._drag_on = False # whether dragging is enabled
- self._rel_pos = (0,0) # mouse pos relative to widget
- self._handles = [] # event handlers
- self._dragging = False # whether a drag is in progress
- self.position = position # position of the widget
-
- self.draggable = draggable
-
- def _pressed_cb(self, widget, evt):
- """Callback for start of drag event"""
- self._eventbox.grab_add()
- self._dragging = True
- self._rel_pos = evt.get_coords()
-
- def _moved_cb(self, widget, evt):
- """Callback for mouse drag events"""
- if not self._dragging:
- return
-
- # Focus on a widget before dragging another would
- # create addititonal move event, making the widget jump unexpectedly.
- # Solution found was to process those focus events before dragging.
- if gtk.events_pending():
- return
-
- xrel, yrel = self._rel_pos
- xparent, yparent = evt.get_coords()
- xparent, yparent = widget.translate_coordinates(widget.parent,
- xparent, yparent)
- self.position = (xparent-xrel, yparent-yrel)
- self._widget.parent.move(self._eventbox, *self.position)
- self._widget.parent.move(self._widget, *self.position)
- self._widget.parent.queue_draw()
-
- def _released_cb(self, *args):
- """Callback for end of drag (mouse release)."""
- self._eventbox.grab_remove()
- self._dragging = False
-
- def _drag_end(self, *args):
- """Callback for end of drag (stolen focus)."""
- self._dragging = False
-
- def set_draggable(self, value):
- """Setter for the draggable property"""
- if bool(value) ^ bool(self._drag_on):
- if value:
- self._eventbox = gtk.EventBox()
- self._eventbox.show()
- self._eventbox.set_visible_window(False)
- size = self._widget.size_request()
- self._eventbox.set_size_request(*size)
- self._widget.parent.put(self._eventbox, *self.position)
- self._handles.append(self._eventbox.connect(
- "button-press-event", self._pressed_cb))
- self._handles.append(self._eventbox.connect(
- "button-release-event", self._released_cb))
- self._handles.append(self._eventbox.connect(
- "motion-notify-event", self._moved_cb))
- self._handles.append(self._eventbox.connect(
- "grab-broken-event", self._drag_end))
- else:
- while len(self._handles):
- handle = self._handles.pop()
- self._eventbox.disconnect(handle)
- self._eventbox.parent.remove(self._eventbox)
- self._eventbox.destroy()
- self._eventbox = None
- self._drag_on = value
-
- def get_draggable(self):
- """Getter for the draggable property"""
- return self._drag_on
-
- draggable = property(fset=set_draggable, fget=get_draggable, \
- doc="Property to enable the draggable behaviour of the widget")
-
- def set_widget(self, widget):
- """Setter for the widget property"""
- if self._dragging or self._drag_on:
- raise Exception("Can't change widget while dragging is enabled.")
-
- assert hasattr(widget, "parent"), "wrapped widget should have a parent"
- parent = widget.parent
- assert hasattr(parent, "move"), "container of widget need move method"
- self._widget = widget
-
- def get_widget(self):
- """Getter for the widget property"""
- return self._widget
-
- widget = property(fset=set_widget, fget=get_widget)
-
-class Action(TPropContainer):
- """Base class for Actions"""
- def __init__(self):
- TPropContainer.__init__(self)
- self.position = (0,0)
- self._drag = None
-
- def do(self, **kwargs):
- """
- Perform the action
- """
- raise NotImplementedError("Not implemented")
-
- def undo(self):
- """
- Revert anything the action has changed
- """
- pass #Should raise NotImplemented?
-
- def enter_editmode(self, **kwargs):
- """
- Enters edit mode. The action should display itself in some way,
- without affecting the currently running application. The default is
- a small box with the action icon.
- """
- meta = addon.get_addon_meta(type(self).__name__)
-
- actionicon = icon.Icon(icon_name=meta['icon'],
- icon_size=gtk.ICON_SIZE_LARGE_TOOLBAR)
- # Eventbox create a visible window for the icon, so it clips correctly
- self.__edit_img = gtk.EventBox()
- self.__edit_img.set_visible_window(True)
- self.__edit_img.add(actionicon)
-
- x, y = self.position
-
- ObjectStore().activity._overlayer.put(self.__edit_img, x, y)
- self.__edit_img.show_all()
- self._drag = DragWrapper(self.__edit_img, self.position, True)
-
- def exit_editmode(self, **kwargs):
- x, y = self._drag.position
- self.position = [int(x), int(y)]
- self.__edit_img.destroy()
-