Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/src/sugar/tutorius
diff options
context:
space:
mode:
Diffstat (limited to 'src/sugar/tutorius')
-rw-r--r--src/sugar/tutorius/Makefile.am19
-rw-r--r--src/sugar/tutorius/__init__.py0
-rw-r--r--src/sugar/tutorius/actions.py324
-rw-r--r--src/sugar/tutorius/addon.py76
-rw-r--r--src/sugar/tutorius/addons/Makefile.am6
-rw-r--r--src/sugar/tutorius/addons/__init__.py0
-rw-r--r--src/sugar/tutorius/addons/bubblemessage.py113
-rw-r--r--src/sugar/tutorius/addons/dialogmessage.py66
-rw-r--r--src/sugar/tutorius/addons/gtkwidgeteventfilter.py69
-rw-r--r--src/sugar/tutorius/bundler.py556
-rw-r--r--src/sugar/tutorius/constraints.py207
-rw-r--r--src/sugar/tutorius/core.py528
-rw-r--r--src/sugar/tutorius/creator.py436
-rw-r--r--src/sugar/tutorius/dialog.py59
-rw-r--r--src/sugar/tutorius/editor.py318
-rw-r--r--src/sugar/tutorius/filters.py204
-rw-r--r--src/sugar/tutorius/gtkutils.py203
-rw-r--r--src/sugar/tutorius/linear_creator.py95
-rw-r--r--src/sugar/tutorius/overlayer.py503
-rw-r--r--src/sugar/tutorius/properties.py323
-rw-r--r--src/sugar/tutorius/services.py69
-rw-r--r--src/sugar/tutorius/temp.py1
-rw-r--r--src/sugar/tutorius/tests/actiontests.py206
-rw-r--r--src/sugar/tutorius/tests/bundlertests.py65
-rw-r--r--src/sugar/tutorius/tests/constraintstests.py233
-rw-r--r--src/sugar/tutorius/tests/coretests.py597
-rw-r--r--src/sugar/tutorius/tests/filterstests.py201
-rw-r--r--src/sugar/tutorius/tests/gtkutilstests.py211
-rw-r--r--src/sugar/tutorius/tests/linear_creatortests.py79
-rw-r--r--src/sugar/tutorius/tests/overlaytests.py119
-rw-r--r--src/sugar/tutorius/tests/propertiestests.py402
-rwxr-xr-xsrc/sugar/tutorius/tests/run-tests.py74
-rw-r--r--src/sugar/tutorius/tests/serializertests.py197
-rw-r--r--src/sugar/tutorius/tests/servicestests.py53
-rw-r--r--src/sugar/tutorius/tests/uamtests.py61
-rw-r--r--src/sugar/tutorius/testwin.py92
-rw-r--r--src/sugar/tutorius/textbubble.py109
-rw-r--r--src/sugar/tutorius/uam/Makefile.am5
-rw-r--r--src/sugar/tutorius/uam/__init__.py88
-rw-r--r--src/sugar/tutorius/uam/gobjectparser.py27
-rw-r--r--src/sugar/tutorius/uam/gtkparser.py44
41 files changed, 0 insertions, 7038 deletions
diff --git a/src/sugar/tutorius/Makefile.am b/src/sugar/tutorius/Makefile.am
deleted file mode 100644
index 072a119..0000000
--- a/src/sugar/tutorius/Makefile.am
+++ /dev/null
@@ -1,19 +0,0 @@
-SUBDIRS = uam addons
-
-sugardir = $(pythondir)/sugar/tutorius
-sugar_PYTHON = \
- __init__.py \
- core.py \
- dialog.py \
- actions.py \
- gtkutils.py \
- filters.py \
- services.py \
- overlayer.py \
- editor.py \
- constraints.py \
- properties.py \
- creator.py \
- bundler.py \
- linear_creator.py \
- addon.py
diff --git a/src/sugar/tutorius/__init__.py b/src/sugar/tutorius/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/src/sugar/tutorius/__init__.py
+++ /dev/null
diff --git a/src/sugar/tutorius/actions.py b/src/sugar/tutorius/actions.py
deleted file mode 100644
index 4269cd7..0000000
--- a/src/sugar/tutorius/actions.py
+++ /dev/null
@@ -1,324 +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
-"""
-from gettext import gettext as _
-
-from sugar.tutorius import gtkutils, addon
-from dialog import TutoriusDialog
-import overlayer
-from sugar.tutorius.editor import WidgetIdentifier
-from sugar.tutorius.services import ObjectStore
-from sugar.tutorius.properties import *
-from sugar.graphics import icon
-import gtk.gdk
-
-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()
-
-class OnceWrapper(Action):
- """
- Wraps a class to perform an action once only
-
- This ConcreteActions's do() method will only be called on the first do()
- and the undo() will be callable after do() has been called
- """
-
- _action = TAddonProperty()
-
- def __init__(self, action):
- Action.__init__(self)
- self._called = False
- self._need_undo = False
- self._action = action
-
- def do(self):
- """
- Do the action only on the first time
- """
- if not self._called:
- self._called = True
- self._action.do()
- self._need_undo = True
-
- def undo(self):
- """
- Undo the action if it's been done
- """
- if self._need_undo:
- self._action.undo()
- self._need_undo = False
-
-class WidgetIdentifyAction(Action):
- def __init__(self):
- Action.__init__(self)
- self.activity = None
- self._dialog = None
-
- def do(self):
- os = ObjectStore()
- if os.activity:
- self.activity = os.activity
-
- self._dialog = WidgetIdentifier(self.activity)
- self._dialog.show()
-
-
- def undo(self):
- if self._dialog:
- self._dialog.destroy()
-
-class ChainAction(Action):
- """Utility class to allow executing actions in a specific order"""
- def __init__(self, *actions):
- """ChainAction(action1, ... ) builds a chain of actions"""
- Action.__init__(self)
- self._actions = actions
-
- def do(self,**kwargs):
- """do() each action in the chain"""
- for act in self._actions:
- act.do(**kwargs)
-
- def undo(self):
- """undo() each action in the chain, starting with the last"""
- for act in reversed(self._actions):
- act.undo()
-
-class DisableWidgetAction(Action):
- def __init__(self, target):
- """Constructor
- @param target target treeish
- """
- Action.__init__(self)
- self._target = target
- self._widget = None
-
- def do(self):
- """Action do"""
- os = ObjectStore()
- if os.activity:
- self._widget = gtkutils.find_widget(os.activity, self._target)
- if self._widget:
- self._widget.set_sensitive(False)
-
- def undo(self):
- """Action undo"""
- if self._widget:
- self._widget.set_sensitive(True)
-
-
-class TypeTextAction(Action):
- """
- Simulate a user typing text in a widget
- Work on any widget that implements a insert_text method
-
- @param widget The treehish representation of the widget
- @param text the text that is typed
- """
- def __init__(self, widget, text):
- Action.__init__(self)
-
- self._widget = widget
- self._text = text
-
- def do(self, **kwargs):
- """
- Type the text
- """
- widget = gtkutils.find_widget(ObjectStore().activity, self._widget)
- if hasattr(widget, "insert_text"):
- widget.insert_text(self._text, -1)
-
- def undo(self):
- """
- no undo
- """
- pass
-
-class ClickAction(Action):
- """
- Action that simulate a click on a widget
- Work on any widget that implements a clicked() method
-
- @param widget The threehish representation of the widget
- """
- def __init__(self, widget):
- Action.__init__(self)
- self._widget = widget
-
- def do(self):
- """
- click the widget
- """
- widget = gtkutils.find_widget(ObjectStore().activity, self._widget)
- if hasattr(widget, "clicked"):
- widget.clicked()
-
- def undo(self):
- """
- No undo
- """
- pass
-
diff --git a/src/sugar/tutorius/addon.py b/src/sugar/tutorius/addon.py
deleted file mode 100644
index 51791d1..0000000
--- a/src/sugar/tutorius/addon.py
+++ /dev/null
@@ -1,76 +0,0 @@
-# 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 module manages the loading and listing of tutorius addons.
-Addons are modular actions and events that are package in such a way that they
-can be autodetected and can integrate with Tutorius components (the editor)
-without any configuration or explicit dependencies (python import).
-
-An action addon is expected to have a metadata dict such as this one:
-__action__ = {
- "name" : "HelloWorld",
- "display_name" : "Hello World!",
- "icon" : "hello",
- "class" : HelloAction,
- "mandatory_props" : ["text"],
-}
-"""
-
-import os
-import re
-import logging
-
-PREFIX = __name__+"s"
-PATH = re.sub("addon\\.py[c]$", "", __file__)+"addons"
-
-_cache = None
-
-def _reload_addons():
- global _cache
- _cache = {}
- for addon in filter(lambda x: x.endswith("py"), os.listdir(PATH)):
- mod = __import__(PREFIX+'.'+re.sub("\\.py$", "", addon), {}, {}, [""])
- if hasattr(mod, "__action__"):
- _cache[mod.__action__['name']] = mod.__action__
- continue
- if hasattr(mod, "__event__"):
- _cache[mod.__event__['name']] = mod.__event__
-
-def create(name, *args, **kwargs):
- global _cache
- if not _cache:
- _reload_addons()
- try:
- return _cache[name]['class'](*args, **kwargs)
- except KeyError:
- logging.error("Addon not found for class '%s'", name)
- return None
-
-def list_addons():
- global _cache
- if not _cache:
- _reload_addons()
- return _cache.keys()
-
-def get_addon_meta(name):
- global _cache
- if not _cache:
- _reload_addons()
- return _cache[name]
-
-# vim:set ts=4 sts=4 sw=4 et:
diff --git a/src/sugar/tutorius/addons/Makefile.am b/src/sugar/tutorius/addons/Makefile.am
deleted file mode 100644
index 3d1d18a..0000000
--- a/src/sugar/tutorius/addons/Makefile.am
+++ /dev/null
@@ -1,6 +0,0 @@
-sugardir = $(pythondir)/sugar/tutorius/addons
-sugar_PYTHON = \
- __init__.py \
- bubblemessage.py \
- gtkwidgeteventfilter.py \
- dialogmessage.py
diff --git a/src/sugar/tutorius/addons/__init__.py b/src/sugar/tutorius/addons/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/src/sugar/tutorius/addons/__init__.py
+++ /dev/null
diff --git a/src/sugar/tutorius/addons/bubblemessage.py b/src/sugar/tutorius/addons/bubblemessage.py
deleted file mode 100644
index a859ef8..0000000
--- a/src/sugar/tutorius/addons/bubblemessage.py
+++ /dev/null
@@ -1,113 +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
-from sugar.tutorius.actions import *
-
-class BubbleMessage(Action):
- message = TStringProperty("Message")
- # Create the position as an array of fixed-size 2
- position = TArrayProperty([0,0], 2, 2)
- # Do the same for the tail position
- tail_pos = TArrayProperty([0,0], 2, 2)
-
- def __init__(self, message=None, pos=None, speaker=None, tailpos=None):
- """
- Shows a dialog with a given text, at the given position on the screen.
-
- @param message A string to display to the user
- @param pos A list of the form [x, y]
- @param speaker treeish representation of the speaking widget
- @param tailpos The position of the tail of the bubble; useful to point to
- specific elements of the interface
- """
- Action.__init__(self)
-
- if pos:
- self.position = pos
- if tailpos:
- self.tail_pos = tailpos
- if message:
- self.message = message
-
- self.overlay = None
- self._bubble = None
- self._speaker = None
-
- def do(self):
- """
- Show the dialog
- """
- # get or inject overlayer
- self.overlay = ObjectStore().activity._overlayer
- # FIXME: subwindows, are left to overlap this. This behaviour is
- # undesirable. subwindows (i.e. child of top level windows) should be
- # handled either by rendering over them, or by finding different way to
- # draw the overlay.
-
- if not self.overlay:
- self.overlay = ObjectStore().activity._overlayer
- if not self._bubble:
- x, y = self.position
- # TODO: tails are relative to tailpos. They should be relative to
- # the speaking widget. Same of the bubble position.
- self._bubble = overlayer.TextBubble(text=self.message,
- tailpos=self.tail_pos)
- self._bubble.show()
- self.overlay.put(self._bubble, x, y)
- self.overlay.queue_draw()
-
- def undo(self):
- """
- Destroy the dialog
- """
- if self._bubble:
- self._bubble.destroy()
- self._bubble = None
-
- def enter_editmode(self, *args):
- """
- Enters edit mode. The action should display itself in some way,
- without affecting the currently running application.
- """
- 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._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
- self.overlay = None
-
-__action__ = {
- "name" : "BubbleMessage",
- "display_name" : "Message Bubble",
- "icon" : "message-bubble",
- "class" : BubbleMessage,
- "mandatory_props" : ["message"]
-}
-
diff --git a/src/sugar/tutorius/addons/dialogmessage.py b/src/sugar/tutorius/addons/dialogmessage.py
deleted file mode 100644
index 22a223b..0000000
--- a/src/sugar/tutorius/addons/dialogmessage.py
+++ /dev/null
@@ -1,66 +0,0 @@
-# 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
-
-from sugar.tutorius.actions import *
-
-class DialogMessage(Action):
- message = TStringProperty("Message")
- position = TArrayProperty([0, 0], 2, 2)
-
- def __init__(self, message=None, pos=None):
- """
- Shows a dialog with a given text, at the given position on the screen.
-
- @param message A string to display to the user
- @param pos A list of the form [x, y]
- """
- super(DialogMessage, self).__init__()
- self._dialog = None
-
- if message:
- self.message = message
- if pos: self.position = pos
-
- def do(self):
- """
- Show the dialog
- """
- self._dialog = TutoriusDialog(self.message)
- self._dialog.set_button_clicked_cb(self._dialog.close_self)
- self._dialog.set_modal(False)
- self._dialog.move(self.position[0], self.position[1])
- self._dialog.show()
-
- def undo(self):
- """
- Destroy the dialog
- """
- if self._dialog:
- self._dialog.destroy()
- self._dialog = None
-
-__action__ = {
- "name" : "DialogMessage",
- "display_name" : "Message Dialog",
- "icon" : "window_fullscreen",
- "class" : DialogMessage,
- "mandatory_props" : ["message"]
-}
-
-# vim:set ts=4 sts=4 sw=4 et:
-
diff --git a/src/sugar/tutorius/addons/gtkwidgeteventfilter.py b/src/sugar/tutorius/addons/gtkwidgeteventfilter.py
deleted file mode 100644
index cbfb00c..0000000
--- a/src/sugar/tutorius/addons/gtkwidgeteventfilter.py
+++ /dev/null
@@ -1,69 +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
-from sugar.tutorius.filters import *
-from sugar.tutorius.properties import *
-
-class GtkWidgetEventFilter(EventFilter):
- """
- Basic Event filter for Gtk widget events
- """
- object_id = TUAMProperty()
- event_name = TStringProperty("clicked")
-
- def __init__(self, next_state=None, object_id=None, event_name=None):
- """Constructor
- @param next_state default EventFilter param, passed on to EventFilter
- @param object_id object fqdn-style identifier
- @param event_name event to attach to
- """
- super(GtkWidgetEventFilter,self).__init__(next_state)
- self._callback = None
- self.object_id = object_id
- self.event_name = event_name
- self._widget = None
- self._handler_id = None
-
- def install_handlers(self, callback, **kwargs):
- """install handlers
- @param callback default EventFilter callback arg
- @param activity keyword argument activity must be present to install
- the event handler into the activity's widget hierarchy
- """
- super(GtkWidgetEventFilter, self).install_handlers(callback, **kwargs)
- if not "activity" in kwargs:
- raise TypeError("activity argument is Mandatory")
-
- #find the widget and connect to its event
- self._widget = find_widget(kwargs["activity"], self.object_id)
- self._handler_id = self._widget.connect( \
- self.event_name, self.do_callback )
-
- def remove_handlers(self):
- """remove handlers"""
- super(GtkWidgetEventFilter, self).remove_handlers()
- #if an event was connected, disconnect it
- if self._handler_id:
- self._widget.handler_disconnect(self._handler_id)
- self._handler_id=None
-
-__event__ = {
- "name" : "GtkWidgetEventFilter",
- "display_name" : "GTK Event catcher",
- "icon" : "player_play",
- "class" : GtkWidgetEventFilter,
- "mandatory_props" : ["object_id"]
-}
-
diff --git a/src/sugar/tutorius/bundler.py b/src/sugar/tutorius/bundler.py
deleted file mode 100644
index 8e7fc3d..0000000
--- a/src/sugar/tutorius/bundler.py
+++ /dev/null
@@ -1,556 +0,0 @@
-# Copyright (C) 2009, Tutorius.org
-# Copyright (C) 2009, Jean-Christophe Savard <savard.jean.christophe@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 module contains all the data handling class of Tutorius
-"""
-
-import logging
-import os
-import uuid
-import xml.dom.minidom
-
-from sugar.tutorius import addon
-from sugar.tutorius.core import Tutorial, State, FiniteStateMachine
-from sugar.tutorius.filters import *
-from sugar.tutorius.actions import *
-from ConfigParser import SafeConfigParser
-
-# this is where user installed/generated tutorials will go
-def _get_store_root():
- return os.path.join(os.getenv("HOME"),".sugar",os.getenv("SUGAR_PROFILE"),"tutorius","data")
-# this is where activity bundled tutorials should be, under the activity bundle
-def _get_bundle_root():
- return os.path.join(os.getenv("SUGAR_BUNDLE_PATH"),"data","tutorius","data")
-
-INI_ACTIVITY_SECTION = "RELATED_ACTIVITIES"
-INI_METADATA_SECTION = "GENERAL_METADATA"
-INI_GUID_PROPERTY = "GUID"
-INI_NAME_PROPERTY = "NAME"
-INI_XML_FSM_PROPERTY = "FSM_FILENAME"
-INI_FILENAME = "meta.ini"
-TUTORIAL_FILENAME = "tutorial.xml"
-NODE_COMPONENT = "Component"
-
-class TutorialStore(object):
-
- def list_available_tutorials(self, activity_name, activity_vers):
- """
- Generate the list of all tutorials present on disk for a
- given activity.
-
- @returns a map of tutorial {names : GUID}.
- """
- # check both under the activity data and user installed folders
- paths = [_get_store_root(), _get_bundle_root()]
-
- tutoGuidName = {}
-
- for repository in paths:
- # (our) convention dictates that tutorial folders are named
- # with their GUID (for unicity) but this is not enforced.
- try:
- for tuto in os.listdir(repository):
- parser = SafeConfigParser()
- parser.read(os.path.join(repository, tuto, INI_FILENAME))
- guid = parser.get(INI_METADATA_SECTION, INI_GUID_PROPERTY)
- name = parser.get(INI_METADATA_SECTION, INI_NAME_PROPERTY)
- activities = parser.options(INI_ACTIVITY_SECTION)
- # enforce matching activity name AND version, as UI changes
- # break tutorials. We may lower this requirement when the
- # UAM gets less dependent on the widget order.
- # Also note property names are always stored lowercase.
- if activity_name.lower() in activities:
- version = parser.get(INI_ACTIVITY_SECTION, activity_name)
- if activity_vers == version:
- tutoGuidName[guid] = name
- except OSError:
- # the repository may not exist. Continue scanning
- pass
-
- return tutoGuidName
-
- def load_tutorial(self, Guid):
- """
- Rebuilds a tutorial object from it's serialized state.
- Common storing paths will be scanned.
-
- @param Guid the generic identifier of the tutorial
- @returns a Tutorial object containing an FSM
- """
- bundle = TutorialBundler(Guid)
- bundle_path = bundle.get_tutorial_path()
- config = SafeConfigParser()
- config.read(os.path.join(bundle_path, INI_FILENAME))
-
- serializer = XMLSerializer()
-
- name = config.get(INI_METADATA_SECTION, INI_NAME_PROPERTY)
- fsm = serializer.load_fsm(Guid)
-
- tuto = Tutorial(name, fsm)
- return tuto
-
-
-class Serializer(object):
- """
- Interface that provide serializing and deserializing of the FSM
- used in the tutorials to/from disk. Must be inherited.
- """
-
- def save_fsm(self,fsm):
- """
- Save fsm to disk. If a GUID parameter is provided, the existing GUID is
- located in the .ini files in the store root and bundle root and
- the corresponding FSM is/are overwritten. If the GUId is not found, an
- exception occur. If no GUID is provided, FSM is written in a new file
- in the store root.
- """
- NotImplementedError
-
- def load_fsm(self):
- """
- Load fsm from disk.
- """
- NotImplementedError
-
-class XMLSerializer(Serializer):
- """
- Class that provide serializing and deserializing of the FSM
- used in the tutorials to/from a .xml file. Inherit from Serializer
- """
-
- def _create_state_dict_node(self, state_dict, doc):
- """
- Create and return a xml Node from a State dictionnary.
- """
- statesList = doc.createElement("States")
- for state_name, state in state_dict.items():
- stateNode = doc.createElement("State")
- statesList.appendChild(stateNode)
- stateNode.setAttribute("Name", state_name)
- actionsList = stateNode.appendChild(self._create_action_list_node(state.get_action_list(), doc))
- eventfiltersList = stateNode.appendChild(self._create_event_filters_node(state.get_event_filter_list(), doc))
- return statesList
-
- def _create_component_node(self, comp, doc):
- """
- Takes a single component (action or eventfilter) and transforms it
- into a xml node.
-
- @param comp A single component
- @param doc The XML document root (used to create nodes only
- @return A XML Node object with the component tag name
- """
- compNode = doc.createElement(NODE_COMPONENT)
-
- # Write down just the name of the Action class as the Class
- # property --
- compNode.setAttribute("Class",type(comp).__name__)
-
- # serialize all tutorius properties
- for propname in comp.get_properties():
- propval = getattr(comp, propname)
- if getattr(type(comp), propname).type == "addonlist":
- for subval in propval:
- compNode.appendChild(self._create_component_node(subval, doc))
- elif getattr(type(comp), propname).type == "addonlist":
- compNode.appendChild(self._create_component_node(subval, doc))
- else:
- # repr instead of str, as we want to be able to eval() it into a
- # valid object.
- compNode.setAttribute(propname, repr(propval))
-
- return compNode
-
- def _create_action_list_node(self, action_list, doc):
- """
- Create and return a xml Node from a Action list.
-
- @param action_list A list of actions
- @param doc The XML document root (used to create new nodes only)
- @return A XML Node object with the Actions tag name and a serie of
- Action children
- """
- actionsList = doc.createElement("Actions")
- for action in action_list:
- # Create the action node
- actionNode = self._create_component_node(action, doc)
- # Append it to the list
- actionsList.appendChild(actionNode)
-
- return actionsList
-
- def _create_event_filters_node(self, event_filters, doc):
- """
- Create and return a xml Node from a event filters.
- """
- eventFiltersList = doc.createElement("EventFiltersList")
- for event_f in event_filters:
- eventFilterNode = self._create_component_node(event_f, doc)
- eventFiltersList.appendChild(eventFilterNode)
-
- return eventFiltersList
-
- def save_fsm(self, fsm, xml_filename, path):
- """
- Save fsm to disk, in the xml file specified by "xml_filename", in the
- "path" folder. If the specified file doesn't exist, it will be created.
- """
- self.doc = doc = xml.dom.minidom.Document()
- fsm_element = doc.createElement("FSM")
- doc.appendChild(fsm_element)
- fsm_element.setAttribute("Name", fsm.name)
- fsm_element.setAttribute("StartStateName", fsm.start_state_name)
- statesDict = fsm_element.appendChild(self._create_state_dict_node(fsm._states, doc))
-
- fsm_actions_node = self._create_action_list_node(fsm.actions, doc)
- fsm_actions_node.tagName = "FSMActions"
- actionsList = fsm_element.appendChild(fsm_actions_node)
-
- file_object = open(os.path.join(path, xml_filename), "w")
- file_object.write(doc.toprettyxml())
- file_object.close()
-
-
- def _find_tutorial_dir_with_guid(self, guid):
- """
- Finds the tutorial with the associated GUID. If it is found, return
- the path to the tutorial's directory. If it doesn't exist, raise an
- IOError.
-
- A note : if there are two tutorials with this GUID in the folders,
- they will both be inspected and the one with the highest version
- number will be returned. If they have the same version number, the one
- from the global store will be returned.
-
- @param guid The GUID of the tutorial that is to be loaded.
- """
- # Attempt to find the tutorial's directory in the global directory
- global_dir = os.path.join(_get_store_root(), guid)
- # Then in the activty's bundle path
- activity_dir = os.path.join(_get_bundle_root(), guid)
-
- # If they both exist
- if os.path.isdir(global_dir) and os.path.isdir(activity_dir):
- # Inspect both metadata files
- global_meta = os.path.join(global_dir, "meta.ini")
- activity_meta = os.path.join(activity_dir, "meta.ini")
-
- # Open both config files
- global_parser = SafeConfigParser()
- global_parser.read(global_meta)
-
- activity_parser = SafeConfigParser()
- activity_parser.read(activity_meta)
-
- # Get the version number for each tutorial
- global_version = global_parser.get(INI_METADATA_SECTION, "version")
- activity_version = activity_parser.get(INI_METADATA_SECTION, "version")
-
- # If the global version is higher or equal, we'll take it
- if global_version >= activity_version:
- return global_dir
- else:
- return activity_dir
-
- # Do we just have the global directory?
- if os.path.isdir(global_dir):
- return global_dir
-
- # Or just the activity's bundle directory?
- if os.path.isdir(activity_dir):
- return activity_dir
-
- # Error : none of these directories contain the tutorial
- raise IOError(2, "Neither the global nor the bundle directory contained the tutorial with GUID %s"%guid)
-
- def _load_xml_properties(self, properties_elem):
- """
- Changes a list of properties into fully instanciated properties.
-
- @param properties_elem An XML element reprensenting a list of
- properties
- """
- return []
-
- def _load_xml_event_filters(self, filters_elem):
- """
- Loads up a list of Event Filters.
-
- @param filters_elem An XML Element representing a list of event filters
- """
- reformed_event_filters_list = []
- event_filter_element_list = filters_elem.getElementsByTagName(NODE_COMPONENT)
- new_event_filter = None
-
- for event_filter in event_filter_element_list:
- new_event_filter = self._load_xml_component(event_filter)
-
- if new_event_filter is not None:
- reformed_event_filters_list.append(new_event_filter)
-
- return reformed_event_filters_list
-
- def _load_xml_component(self, node):
- """
- Loads a single addon component instance from an Xml node.
-
- @param node The component XML Node to transform
- object
- @return The addon component object of the correct type according to the XML
- description
- """
- new_action = addon.create(node.getAttribute("Class"))
- if not new_action:
- return None
-
- for attrib in node.attributes.keys():
- if attrib == "Class": continue
- # security note: keep sandboxed
- setattr(new_action, attrib, eval(node.getAttribute(attrib), {}, {}))
-
- # recreate complex attributes
- for sub in node.childNodes:
- name = getattr(new_action, sub.nodeName)
- if name == "addon":
- setattr(new_action, sub.getAttribute("Name"), self._load_xml_action(sub))
-
- return new_action
-
- def _load_xml_actions(self, actions_elem):
- """
- Transforms an Actions element into a list of instanciated Action.
-
- @param actions_elem An XML Element representing a list of Actions
- """
- reformed_actions_list = []
- actions_element_list = actions_elem.getElementsByTagName(NODE_COMPONENT)
-
- for action in actions_element_list:
- new_action = self._load_xml_component(action)
-
- reformed_actions_list.append(new_action)
-
- return reformed_actions_list
-
- def _load_xml_states(self, states_elem):
- """
- Takes in a States element and fleshes out a complete list of State
- objects.
-
- @param states_elem An XML Element that represents a list of States
- """
- reformed_state_list = []
- # item(0) because there is always only one <States> tag in the xml file
- # so states_elem should always contain only one element
- states_element_list = states_elem.item(0).getElementsByTagName("State")
-
- for state in states_element_list:
- stateName = state.getAttribute("Name")
- # Using item 0 in the list because there is always only one
- # Actions and EventFilterList element per State node.
- actions_list = self._load_xml_actions(state.getElementsByTagName("Actions")[0])
- event_filters_list = self._load_xml_event_filters(state.getElementsByTagName("EventFiltersList")[0])
- reformed_state_list.append(State(stateName, actions_list, event_filters_list))
-
- return reformed_state_list
-
- def _load_xml_fsm(self, fsm_elem):
- """
- Takes in an XML element representing an FSM and returns the fully
- crafted FSM.
-
- @param fsm_elem The XML element that describes a FSM
- """
- # Load the FSM's name and start state's name
- fsm_name = fsm_elem.getAttribute("Name")
-
- fsm_start_state_name = None
- try:
- fsm_start_state_name = fsm_elem.getAttribute("StartStateName")
- except:
- pass
-
- fsm = FiniteStateMachine(fsm_name, start_state_name=fsm_start_state_name)
-
- # Load the states
- states = self._load_xml_states(fsm_elem.getElementsByTagName("States"))
- for state in states:
- fsm.add_state(state)
-
- # Load the actions on this FSM
- actions = self._load_xml_actions(fsm_elem.getElementsByTagName("FSMActions")[0])
- for action in actions:
- fsm.add_action(action)
-
- # Load the event filters
- events = self._load_xml_event_filters(fsm_elem.getElementsByTagName("EventFiltersList")[0])
- for event in events:
- fsm.add_event_filter(event)
-
- return fsm
-
-
- def load_fsm(self, guid):
- """
- Load fsm from xml file whose .ini file guid match argument guid.
- """
- # Fetch the directory (if any)
- tutorial_dir = self._find_tutorial_dir_with_guid(guid)
-
- # Open the XML file
- tutorial_file = os.path.join(tutorial_dir, TUTORIAL_FILENAME)
-
- xml_dom = xml.dom.minidom.parse(tutorial_file)
-
- fsm_elem = xml_dom.getElementsByTagName("FSM")[0]
-
- return self._load_xml_fsm(fsm_elem)
-
-
-class TutorialBundler(object):
- """
- This class provide the various data handling methods useable by the tutorial
- editor.
- """
-
- def __init__(self,generated_guid = None):
- """
- Tutorial_bundler constructor. If a GUID is given in the parameter, the
- Tutorial_bundler object will be associated with it. If no GUID is given,
- a new GUID will be generated,
- """
-
- self.Guid = generated_guid or str(uuid.uuid1())
-
- #Look for the file in the path if a uid is supplied
- if generated_guid:
- #General store
- store_path = os.path.join(_get_store_root(), generated_guid, INI_FILENAME)
- if os.path.isfile(store_path):
- self.Path = os.path.dirname(store_path)
- else:
- #Bundle store
- bundle_path = os.path.join(_get_bundle_root(), generated_guid, INI_FILENAME)
- if os.path.isfile(bundle_path):
- self.Path = os.path.dirname(bundle_path)
- else:
- raise IOError(2,"Unable to locate metadata file for guid '%s'" % generated_guid)
-
- else:
- #Create the folder, any failure will go through to the caller for now
- store_path = os.path.join(_get_store_root(), self.Guid)
- os.makedirs(store_path)
- self.Path = store_path
-
- def write_metadata_file(self, tutorial):
- """
- Write metadata to the property file.
- @param tutorial Tutorial for which to write metadata
- """
- #Create the Config Object and populate it
- cfg = SafeConfigParser()
- cfg.add_section(INI_METADATA_SECTION)
- cfg.set(INI_METADATA_SECTION, INI_GUID_PROPERTY, self.Guid)
- cfg.set(INI_METADATA_SECTION, INI_NAME_PROPERTY, tutorial.name)
- cfg.set(INI_METADATA_SECTION, INI_XML_FSM_PROPERTY, TUTORIAL_FILENAME)
- cfg.add_section(INI_ACTIVITY_SECTION)
- cfg.set(INI_ACTIVITY_SECTION, os.environ['SUGAR_BUNDLE_NAME'],
- os.environ['SUGAR_BUNDLE_VERSION'])
-
- #Write the ini file
- cfg.write( file( os.path.join(self.Path, INI_FILENAME), 'w' ) )
-
- def get_tutorial_path(self):
- """
- Return the path of the .ini file associated with the guiven guid set in
- the Guid property of the Tutorial_Bundler. If the guid is present in
- more than one path, the store_root is given priority.
- """
-
- store_root = _get_store_root()
- bundle_root = _get_bundle_root()
-
- config = SafeConfigParser()
- path = None
-
- logging.debug("************ Path of store_root folder of activity : " \
- + store_root)
-
- # iterate in each GUID subfolder
- for dir in os.listdir(store_root):
-
- # iterate for each .ini file in the store_root folder
-
- for file_name in os.listdir(os.path.join(store_root, dir)):
- if file_name.endswith(".ini"):
- logging.debug("******************* Found .ini file : " \
- + file_name)
- config.read(os.path.join(store_root, dir, file_name))
- if config.get(INI_METADATA_SECTION, INI_GUID_PROPERTY) == self.Guid:
- xml_filename = config.get(INI_METADATA_SECTION,
- INI_XML_FSM_PROPERTY)
-
- path = os.path.join(store_root, dir)
- return path
-
- logging.debug("************ Path of bundle_root folder of activity : " \
- + bundle_root)
-
-
- # iterate in each GUID subfolder
- for dir in os.listdir(bundle_root):
-
- # iterate for each .ini file in the bundle_root folder
- for file_name in os.listdir(os.path.join(bundle_root, dir)):
- if file_name.endswith(".ini"):
- logging.debug("******************* Found .ini file : " \
- + file_name)
- config.read(os.path.join(bundle_root, dir, file_name))
- if config.get(INI_METADATA_SECTION, INI_GUID_PROPERTY) == self.Guid:
- path = os.path.join(bundle_root, self.Guid)
- return path
-
- if path is None:
- logging.debug("**************** Error : GUID not found")
- raise KeyError
-
- def write_fsm(self, fsm):
-
- """
- Save fsm to disk. If a GUID parameter is provided, the existing GUID is
- located in the .ini files in the store root and bundle root and
- the corresponding FSM is/are created or overwritten. If the GUID is not
- found, an exception occur.
- """
-
- config = SafeConfigParser()
-
- serializer = XMLSerializer()
- path = os.path.join(self.Path, "meta.ini")
- config.read(path)
- xml_filename = config.get(INI_METADATA_SECTION, INI_XML_FSM_PROPERTY)
- serializer.save_fsm(fsm, xml_filename, self.Path)
-
-
- def add_resources(self, typename, file):
- """
- Add ressources to metadata.
- """
- raise NotImplementedError("add_resources not implemented")
diff --git a/src/sugar/tutorius/constraints.py b/src/sugar/tutorius/constraints.py
deleted file mode 100644
index 36abdfb..0000000
--- a/src/sugar/tutorius/constraints.py
+++ /dev/null
@@ -1,207 +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
-"""
-Constraints
-
-Defines a set of constraints with their related errors. These constraints are
-made to be used inside TutoriusProperties in order to limit the values that
-they might take. They can also be used to enforce a particular format or type
-for some properties.
-"""
-
-# For the File Constraint
-import os
-
-class Constraint():
- """
- Basic block for defining constraints on a TutoriusProperty. Every class
- inheriting from Constraint will have a validate function that will be
- executed when the property's value is to be changed.
- """
- def validate(self, value):
- """
- This function receives the value that is proposed as a new value for
- the property. It needs to raise an Error in the case where the value
- does not respect this constraint.
- """
- raise NotImplementedError("Unable to validate a base Constraint")
-
-class ValueConstraint(Constraint):
- """
- A value constraint contains a _limit member that can be used in a children
- class as a basic value. See UpperLimitConstraint for an exemple.
- """
- def __init__(self, limit):
- self.limit = limit
-
-class UpperLimitConstraintError(Exception):
- pass
-
-class UpperLimitConstraint(ValueConstraint):
- def validate(self, value):
- """
- Evaluates whether the given value is smaller than the limit.
-
- @raise UpperLimitConstraintError When the value is strictly higher than
- the limit.
- """
- if self.limit is not None:
- if self.limit >= value:
- return
- raise UpperLimitConstraintError()
- return
-
-class LowerLimitConstraintError(Exception):
- pass
-
-class LowerLimitConstraint(ValueConstraint):
- def validate(self, value):
- """
- If the value is lower than the limit, this function raises an error.
-
- @raise LowerLimitConstraintError When the value is strictly smaller
- than the limit.
- """
- if self.limit is not None:
- if value >= self.limit:
- return
- raise LowerLimitConstraintError()
- return
-
-class MaxSizeConstraintError(Exception):
- pass
-
-class MaxSizeConstraint(ValueConstraint):
- def validate(self, value):
- """
- Evaluate whether a given object is smaller than the given size when
- run through len(). Great for string, lists and the like. ;)
-
- @raise SizeConstraintError If the length of the value is strictly
- bigger than the limit.
- """
- if self.limit is not None:
- if self.limit >= len(value):
- return
- raise MaxSizeConstraintError("Setter : trying to set value of length %d while limit is %d"%(len(value), self.limit))
- return
-
-class MinSizeConstraintError(Exception):
- pass
-
-class MinSizeConstraint(ValueConstraint):
- def validate(self, value):
- """
- Evaluate whether a given object is smaller than the given size when
- run through len(). Great for string, lists and the like. ;)
-
- @raise SizeConstraintError If the length of the value is strictly
- bigger than the limit.
- """
- if self.limit is not None:
- if self.limit <= len(value):
- return
- raise MinSizeConstraintError("Setter : trying to set value of length %d while limit is %d"%(len(value), self.limit))
- return
-
-class ColorConstraintError(Exception):
- pass
-
-class ColorArraySizeError(ColorConstraintError):
- pass
-
-class ColorTypeError(ColorConstraintError):
- pass
-
-class ColorValueError(ColorConstraintError):
- pass
-
-class ColorConstraint(Constraint):
- """
- Validates that the value is an array of size 3 with three numbers between
- 0 and 255 (inclusively) in it.
-
- """
- def validate(self, value):
- if len(value) != 3:
- raise ColorArraySizeError("The value is not an array of size 3")
-
- if not (type(value[0]) == type(22) and type(value[1]) == type(22) and type(value[2]) == type(22)):
- raise ColorTypeError("Not all the elements of the array are integers")
-
- if value[0] > 255 or value[0] <0:
- raise ColorValueError("Red value is not between 0 and 255")
-
- if value[1] > 255 or value[1] <0:
- raise ColorValueError("Green value is not between 0 and 255")
-
- if value[2] > 255 or value[2] <0:
- raise ColorValueError("Blue value is not between 0 and 255")
-
- return
-
-class BooleanConstraintError(Exception):
- pass
-
-class BooleanConstraint(Constraint):
- """
- Validates that the value is either True or False.
- """
- def validate(self, value):
- if value == True or value == False:
- return
- raise BooleanConstraintError("Value is not True or False")
-
-class EnumConstraintError(Exception):
- pass
-
-class EnumConstraint(Constraint):
- """
- Validates that the value is part of a set of well-defined values.
- """
- def __init__(self, accepted_values):
- """
- Creates the constraint and stores the list of accepted values.
-
- @param correct_values A list that contains all the values that will
- be declared as satisfying the constraint
- """
- self._accepted_values = accepted_values
-
- def validate(self, value):
- """
- Ensures that the value that is passed is part of the list of accepted
- values.
- """
- if not value in self._accepted_values:
- raise EnumConstraintError("Value is not part of the enumeration")
- return
-
-class FileConstraintError(Exception):
- pass
-
-class FileConstraint(Constraint):
- """
- Ensures that the string given corresponds to an existing file on disk.
- """
- def validate(self, value):
- # TODO : Decide on the architecture for file retrieval on disk
- # Relative paths? From where? Support macros?
- #
- if not os.path.isfile(value):
- raise FileConstraintError("Non-existing file : %s"%value)
- return
-
diff --git a/src/sugar/tutorius/core.py b/src/sugar/tutorius/core.py
deleted file mode 100644
index dd2435e..0000000
--- a/src/sugar/tutorius/core.py
+++ /dev/null
@@ -1,528 +0,0 @@
-# Copyright (C) 2009, Tutorius.org
-# Copyright (C) 2009, Vincent Vinet <vince.vinet@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
-"""
-Core
-
-This module contains the core classes for tutorius
-
-"""
-
-import gtk
-import logging
-import copy
-import os
-
-from sugar.tutorius.dialog import TutoriusDialog
-from sugar.tutorius.gtkutils import find_widget
-from sugar.tutorius.services import ObjectStore
-
-logger = logging.getLogger("tutorius")
-
-class Tutorial (object):
- """
- Tutorial Class, used to run through the FSM.
- """
-
- def __init__(self, name, fsm,filename= None):
- """
- Creates an unattached tutorial.
- """
- object.__init__(self)
- self.name = name
- self.activity_init_state_filename = filename
-
- self.state_machine = fsm
- self.state_machine.set_tutorial(self)
-
- self.state = None
-
- self.handlers = []
- self.activity = None
- #Rest of initialisation happens when attached
-
- def attach(self, activity):
- """
- Attach to a running activity
-
- @param activity the activity to attach to
- """
- #For now, absolutely detach if a previous one!
- if self.activity:
- self.detach()
- self.activity = activity
- ObjectStore().activity = activity
- ObjectStore().tutorial = self
- self._prepare_activity()
- self.state_machine.set_state("INIT")
-
- def detach(self):
- """
- Detach from the current activity
- """
-
- # Uninstall the whole FSM
- self.state_machine.teardown()
-
- #FIXME There should be some amount of resetting done here...
- self.activity = None
-
-
- def set_state(self, name):
- """
- Switch to a new state
- """
- logger.debug("==== NEW STATE: %s ====" % name)
-
- self.state_machine.set_state(name)
-
-
- # Currently unused -- equivalent function is in each state
- def _eventfilter_state_done(self, eventfilter):
- """
- Callback handler for eventfilter to notify
- when we must go to the next state.
- """
- #XXX Tests should be run here normally
-
- #Swith to the next state pointed by the eventfilter
- self.set_state(eventfilter.get_next_state())
-
- def _prepare_activity(self):
- """
- Prepare the activity for the tutorial by loading the saved state and
- emitting gtk signals
- """
- #Load the saved activity if any
- if self.activity_init_state_filename is not None:
- #For now the file will be saved in the data folder
- #of the activity root directory
- filename = os.getenv("SUGAR_ACTIVITY_ROOT") + "/data/" +\
- self.activity_init_state_filename
- if os.path.exists(filename):
- self.activity.read_file(filename)
-
-
-class State(object):
- """
- This is a step in a tutorial. The state represents a collection of actions
- to undertake when entering the state, and a series of event filters
- with associated actions that point to a possible next state.
- """
-
- def __init__(self, name="", action_list=None, event_filter_list=None, tutorial=None):
- """
- Initializes the content of the state, like loading the actions
- that are required and building the correct tests.
-
- @param action_list The list of actions to execute when entering this
- state
- @param event_filter_list A list of tuples of the form
- (event_filter, next_state_name), that explains the outgoing links for
- this state
- @param tutorial The higher level container of the state
- """
- object.__init__(self)
-
- self.name = name
-
- self._actions = action_list or []
-
- # Unused for now
- #self.tests = []
-
- self._event_filters = event_filter_list or []
-
- self.tutorial = tutorial
-
- def set_tutorial(self, tutorial):
- """
- Associates this state with a tutorial. A tutorial must be set prior
- to executing anything in the state. The reason for this is that the
- states need to have access to the activity (via the tutorial) in order
- to properly register their callbacks on the activities' widgets.
-
- @param tutorial The tutorial that this state runs under.
- """
- if self.tutorial == None :
- self.tutorial = tutorial
- else:
- raise RuntimeWarning(\
- "The state %s was already associated with a tutorial." % self.name)
-
- def setup(self):
- """
- Install the state itself, by first registering the event filters
- and then triggering the actions.
- """
- for eventfilter in self._event_filters:
- eventfilter.install_handlers(self._event_filter_state_done_cb,
- activity=self.tutorial.activity)
-
- for action in self._actions:
- action.do()
-
- def teardown(self):
- """
- Uninstall all the event filters that were active in this state.
- Also undo every action that was installed for this state. This means
- removing dialogs that were displayed, removing highlights, etc...
- """
- # Remove the handlers for the all of the state's event filters
- for event_filter in self._event_filters:
- event_filter.remove_handlers()
-
- # Undo all the actions related to this state
- for action in self._actions:
- action.undo()
-
- def _event_filter_state_done_cb(self, event_filter):
- """
- Callback for event filters. This function needs to inform the
- tutorial that the state is over and tell it what is the next state.
-
- @param event_filter The event filter that was called
- """
- # Run the tests here, if need be
-
- # Warn the higher level that we wish to change state
- self.tutorial.set_state(event_filter.get_next_state())
-
- # Model manipulation
- # These functions are used to simplify the creation of states
- def add_action(self, new_action):
- """
- Adds an action to the state (only if it wasn't added before)
-
- @param new_action The new action to execute when in this state
- @return True if added, False otherwise
- """
- if new_action not in self._actions:
- self._actions.append(new_action)
- return True
- return False
-
- # remove_action - We did not define names for the action, hence they're
- # pretty hard to remove on a precise basis
-
- def get_action_list(self):
- """
- @return A list of actions that the state will execute
- """
- return self._actions
-
- def clear_actions(self):
- """
- Removes all the action associated with this state. A cleared state will
- not do anything when entered or exited.
- """
- self._actions = []
-
- def add_event_filter(self, event_filter):
- """
- Adds an event filter that will cause a transition from this state.
-
- The same event filter may not be added twice.
-
- @param event_filter The new event filter that will trigger a transition
- @return True if added, False otherwise
- """
- if event_filter not in self._event_filters:
- self._event_filters.append(event_filter)
- return True
- return False
-
- def get_event_filter_list(self):
- """
- @return The list of event filters associated with this state.
- """
- return self._event_filters
-
- def clear_event_filters(self):
- """
- Removes all the event filters associated with this state. A state that
- was just cleared will become a sink and will be the end of the
- tutorial.
- """
- self._event_filters = []
-
-class FiniteStateMachine(State):
- """
- This is a collection of states, with a start state and an end callback.
- It is used to simplify the development of the various tutorials by
- encapsulating a collection of states that represent a given learning
- process.
-
- For now, we will consider that there can only be states
- inserted in the FSM, and that there are no nested FSM inside.
- """
-
- def __init__(self, name, tutorial=None, state_dict=None, start_state_name="INIT", action_list=None):
- """
- The constructor for a FSM. Pass in the start state and the setup
- actions that need to be taken when the FSM itself start (which may be
- different from what is done in the first state of the machine).
-
- @param name A short descriptive name for this FSM
- @param tutorial The tutorial that will execute this FSM. If None is
- attached on creation, then one must absolutely be attached before
- executing the FSM with set_tutorial().
- @param state_dict A dictionary containing the state names as keys and
- the state themselves as entries.
- @param start_state_name The name of the starting state, if different
- from "INIT"
- @param action_list The actions to undertake when initializing the FSM
- """
- State.__init__(self, name)
-
- self.name = name
- self.tutorial = tutorial
-
- # Dictionnary of states contained in the FSM
- self._states = state_dict or {}
-
- self.start_state_name = start_state_name
- # Set the current state to None - we are not executing anything yet
- self.current_state = None
-
- # Register the actions for the FSM - They will be processed at the
- # FSM level, meaning that when the FSM will start, it will first
- # execute those actions. When the FSM closes, it will tear down the
- # inner actions of the state, then close its own actions
- self.actions = action_list or []
-
- # Flag to mention that the FSM was initialized
- self._fsm_setup_done = False
- # Flag that must be raised when the FSM is to be teared down
- self._fsm_teardown_done = False
- # Flag used to declare that the FSM has reached an end state
- self._fsm_has_finished = False
-
- def set_tutorial(self, tutorial):
- """
- This associates the FSM to the given tutorial. It MUST be associated
- either in the constructor or with this function prior to executing the
- FSM.
-
- @param tutorial The tutorial that will execute this FSM.
- """
- # If there was no tutorial associated
- if self.tutorial == None:
- # Associate it with this FSM and all the underlying states
- self.tutorial = tutorial
- for state in self._states.itervalues():
- state.set_tutorial(tutorial)
- else:
- raise RuntimeWarning(\
- "The FSM %s is already associated with a tutorial."%self.name)
-
- def setup(self):
- """
- This function initializes the FSM the first time it is called.
- Then, every time it is called, it initializes the current state.
- """
- # Are we associated with a tutorial?
- if self.tutorial == None:
- raise UnboundLocalError("No tutorial was associated with FSM %s" % self.name)
-
- # If we never initialized the FSM itself, then we need to run all the
- # actions associated with the FSM.
- if self._fsm_setup_done == False:
- # Remember the initial state - we might want to reset
- # or rewind the FSM at a later moment
- self.start_state = self._states[self.start_state_name]
- self.current_state = self.start_state
- # Flag the FSM level setup as done
- self._fsm_setup_done = True
- # Execute all the FSM level actions
- for action in self.actions:
- action.do()
-
- # Then, we need to run the setup of the current state
- self.current_state.setup()
-
- def set_state(self, new_state_name):
- """
- This functions changes the current state of the finite state machine.
-
- @param new_state The identifier of the state we need to go to
- """
- # TODO : Since we assume no nested FSMs, we don't set state on the
- # inner States / FSMs
-## # Pass in the name to the internal state - it might be a FSM and
-## # this name will apply to it
-## self.current_state.set_state(new_state_name)
-
- # Make sure the given state is owned by the FSM
- if not self._states.has_key(new_state_name):
- # If we did not recognize the name, then we do not possess any
- # state by that name - we must ignore this state change request as
- # it will be done elsewhere in the hierarchy (or it's just bogus).
- return
-
- if self.current_state != None:
- if new_state_name == self.current_state.name:
- # If we already are in this state, we do not need to change
- # anything in the current state - By design, a state may not point
- # to itself
- return
-
- new_state = self._states[new_state_name]
-
- # Undo the actions of the old state
- self.teardown()
-
- # Insert the new state
- self.current_state = new_state
-
- # Call the initial actions in the new state
- self.setup()
-
- def get_current_state_name(self):
- """
- Returns the name of the current state.
-
- @return A string representing the name of the current state
- """
- return self.current_state.name
-
- def teardown(self):
- """
- Revert any changes done by setup()
- """
- # Teardown the current state
- if self.current_state is not None:
- self.current_state.teardown()
-
- # If we just finished the whole FSM, we need to also call the teardown
- # on the FSM level actions
- if self._fsm_has_finished == True:
- # Flag the FSM teardown as not needed anymore
- self._fsm_teardown_done = True
- # Undo all the FSM level actions here
- for action in self.actions:
- action.undo()
-
- # TODO : It might be nice to have a start() and stop() method for the
- # FSM.
-
- # Data manipulation section
- # These functions are dedicated to the building and editing of a graph.
- def add_state(self, new_state):
- """
- Inserts a new state in the FSM.
-
- @param new_state The State object that will now be part of the FSM
- @raise KeyError In the case where a state with this name already exists
- """
- if self._states.has_key(new_state.name):
- raise KeyError("There is already a state by this name in the FSM")
-
- self._states[new_state.name] = new_state
-
- # Not such a great name for the state accessor... We already have a
- # set_state name, so get_state would conflict with the notion of current
- # state - I would recommend having a set_current_state instead.
- def get_state_by_name(self, state_name):
- """
- Fetches a state from the FSM, based on its name. If there is no
- such state, the method will throw a KeyError.
-
- @param state_name The name of the desired state
- @return The State object having the given name
- """
- return self._states[state_name]
-
- def remove_state(self, state_name):
- """
- Removes a state from the FSM. Raises a KeyError when the state is
- not existent.
-
- Warning : removing a state will also remove all the event filters that
- point to this given name, to preserve the FSM's integrity. If you only
- want to edit a state, you would be better off fetching this state with
- get_state_by_name().
-
- @param state_name A string being the name of the state to remove
- @raise KeyError When the state_name does not a represent a real state
- stored in the dictionary
- """
-
- state_to_remove = self._states[state_name]
-
- # Remove the state from the states' dictionnary
- for st in self._states.itervalues():
- # Iterate through the list of event filters and remove those
- # that point to the state that will be removed
-
- #TODO : Move this code inside the State itself - we're breaking
- # encap :P
- for event_filter in st._event_filters:
- if event_filter.get_next_state() == state_name:
- st._event_filters.remove(event_filter)
-
- # Remove the state from the dictionary
- del self._states[state_name]
-
- # Exploration methods - used to know more about a given state
- def get_following_states(self, state_name):
- """
- Returns a tuple of the names of the states that point to the given
- state. If there is no such state, the function raises a KeyError.
-
- @param state_name The name of the state to analyse
- @raise KeyError When there is no state by this name in the FSM
- """
- state = self._states[state_name]
-
- next_states = set()
-
- for event_filter in state._event_filters:
- next_states.add(event_filter.get_next_state())
-
- return tuple(next_states)
-
- def get_previous_states(self, state_name):
- """
- Returns a tuple of the names of the state that can transition to
- the given state. If there is no such state, the function raises a
- KeyError.
-
- @param state_name The name of the state that the returned states might
- transition to.
- """
- # This might seem a bit funny, but we don't verify if the given
- # state is present or not in the dictionary.
- # This is due to the fact that when building a graph, we might have a
- # prototypal state that has not been inserted yet. We could not know
- # which states are pointing to it until we insert it in the graph.
-
- states = []
- # Walk through the list of states
- for st in self._states.itervalues():
- for event_filter in st._event_filters:
- if event_filter.get_next_state() == state_name:
- states.append(event_filter.get_next_state())
- continue
-
- return tuple(states)
-
- # Convenience methods to see the content of a FSM
- def __str__(self):
- out_string = ""
- for st in self._states.itervalues():
- out_string += st.name + ", "
- return out_string
diff --git a/src/sugar/tutorius/creator.py b/src/sugar/tutorius/creator.py
deleted file mode 100644
index d6826dc..0000000
--- a/src/sugar/tutorius/creator.py
+++ /dev/null
@@ -1,436 +0,0 @@
-"""
-This package contains UI classes related to tutorial authoring.
-This includes visual display of tools to edit and create tutorials from within
-the activity itself.
-"""
-# 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 1 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 gtk.gdk
-import gobject
-from gettext import gettext as T
-
-from sugar.graphics.toolbutton import ToolButton
-
-from sugar.tutorius import overlayer, gtkutils, actions, bundler, properties, addon
-from sugar.tutorius import filters
-from sugar.tutorius.services import ObjectStore
-from sugar.tutorius.linear_creator import LinearCreator
-from sugar.tutorius.tutorial import Tutorial
-
-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):
- """
- Instanciate a tutorial creator for the activity.
-
- @param activity to bind the creator to
- @param tutorial an existing tutorial to edit, or None to create one
- """
- self._activity = activity
- if not tutorial:
- self._tutorial = LinearCreator()
- else:
- self._tutorial = tutorial
-
- self._action_panel = None
- self._current_filter = None
- self._intro_mask = None
- self._intro_handle = None
- self._state_bubble = overlayer.TextBubble(self._tutorial.state_name)
- allocation = self._activity.get_allocation()
- self._width = allocation.width
- self._height = allocation.height
- self._selected_widget = None
- self._eventmenu = None
-
- self._hlmask = overlayer.Rectangle(None, (1.0, 0.0, 0.0, 0.5))
- self._activity._overlayer.put(self._hlmask, 0, 0)
-
- self._activity._overlayer.put(self._state_bubble,
- self._width/2-self._state_bubble.allocation.width/2, 0)
-
- dlg_width = 300
- dlg_height = 70
- sw = gtk.gdk.screen_width()
- sh = gtk.gdk.screen_height()
- self._tooldialog = gtk.Window()
- self._tooldialog.set_title("Tutorius tools")
- self._tooldialog.set_transient_for(self._activity)
- self._tooldialog.set_decorated(True)
- self._tooldialog.set_resizable(False)
- self._tooldialog.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_UTILITY)
- self._tooldialog.set_destroy_with_parent(True)
- self._tooldialog.set_deletable(False)
- self._tooldialog.set_size_request(dlg_width, dlg_height)
-
- toolbar = gtk.Toolbar()
- for tool in addon.list_addons():
- meta = addon.get_addon_meta(tool)
- toolitem = ToolButton(meta['icon'])
- toolitem.set_tooltip(meta['display_name'])
- toolitem.connect("clicked", self._add_action_cb, tool)
- toolbar.insert(toolitem, -1)
- toolitem = ToolButton("go-next")
- toolitem.connect("clicked", self._add_step_cb)
- toolitem.set_tooltip("Add Step")
- toolbar.insert(toolitem, -1)
- toolitem = ToolButton("stop")
- toolitem.connect("clicked", self._cleanup_cb)
- toolitem.set_tooltip("End Tutorial")
- toolbar.insert(toolitem, -1)
- self._tooldialog.add(toolbar)
- self._tooldialog.show_all()
- # simpoir: I suspect the realized widget is a tiny bit larger than
- # it should be, thus the -10.
- self._tooldialog.move(sw-10-dlg_width, sh-dlg_height)
-
- self._propedit = EditToolBox(self._activity)
-
- def _evfilt_cb(self, menuitem, event_name, *args):
- """
- This will get called once the user has selected a menu item from the
- event filter popup menu. This should add the correct event filter
- to the FSM and increment states.
- """
- self.introspecting = False
- eventfilter = addon.create('GtkWidgetEventFilter',
- next_state=None,
- object_id=self._selected_widget,
- event_name=event_name)
- # undo actions so they don't persist through step editing
- for action in self._tutorial.current_actions:
- action.exit_editmode()
- self._tutorial.event(eventfilter)
- self._state_bubble.label = self._tutorial.state_name
- self._hlmask.covered = None
- 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 set_intropecting(self, value):
- """
- Set whether creator is in UI introspection mode. Setting this will
- connect necessary handlers.
- @param value True to setup introspection handlers.
- """
- if bool(value) ^ bool(self._intro_mask):
- if value:
- self._intro_mask = overlayer.Mask(catch_events=True)
- self._intro_handle = self._intro_mask.connect_after(
- "button-press-event", self._intro_cb)
- self._activity._overlayer.put(self._intro_mask, 0, 0)
- else:
- self._intro_mask.catch_events = False
- self._intro_mask.disconnect(self._intro_handle)
- self._intro_handle = None
- self._activity._overlayer.remove(self._intro_mask)
- self._intro_mask = None
-
- def get_introspecting(self):
- """
- Whether creator is in UI introspection (catch all event) mode.
- @return True if introspection handlers are connected, or False if not.
- """
- return bool(self._intro_mask)
-
- introspecting = property(fset=set_intropecting, fget=get_introspecting)
-
- def _add_action_cb(self, widget, actiontype):
- """Callback for the action creation toolbar tool"""
- action = addon.create(actiontype)
- if isinstance(action, actions.Action):
- action.enter_editmode()
- self._tutorial.action(action)
- # FIXME: replace following with event catching
- action._drag._eventbox.connect_after(
- "button-release-event", self._action_refresh_cb, action)
- else:
- addonname = type(action).__name__
- meta = addon.get_addon_meta(addonname)
- had_introspect = False
- for propname in meta['mandatory_props']:
- prop = getattr(type(action), propname)
- if isinstance(prop, properties.TUAMProperty):
- had_introspect = True
- self.introspecting = True
- elif isinstance(prop, properties.TStringProperty):
- dlg = TextInputDialog(title="Mandatory property",
- field=propname)
- setattr(action, propname, dlg.pop())
- else:
- raise NotImplementedError()
-
- # FIXME: hack to reuse previous introspection code
- if not had_introspect:
- self._tutorial.event(action)
-
-
- def _action_refresh_cb(self, widget, evt, action):
- """
- Callback for refreshing properties values and notifying the
- property dialog of the new values.
- """
- action.exit_editmode()
- 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)
- self._propedit.action = action
-
- def _add_step_cb(self, widget):
- """Callback for the "add step" tool"""
- self.introspecting = True
-
- def _cleanup_cb(self, *args):
- """
- Quit editing and cleanup interface artifacts.
- """
- self.introspecting = False
- eventfilter = filters.EventFilter(None)
- # undo actions so they don't persist through step editing
- for action in self._tutorial.current_actions:
- action.exit_editmode()
- self._tutorial.event(eventfilter)
-
- dlg = TextInputDialog(text=T("Enter a tutorial title."),
- field=T("Title"))
- tutorialName = ""
- while not tutorialName: tutorialName = dlg.pop()
- dlg.destroy()
-
- # prepare tutorial for serialization
- tuto = Tutorial(tutorialName, self._tutorial.fsm)
- bundle = bundler.TutorialBundler()
- bundle.write_metadata_file(tuto)
- bundle.write_fsm(self._tutorial.fsm)
-
- # remove UI remains
- self._hlmask.covered = None
- self._activity._overlayer.remove(self._hlmask)
- self._activity._overlayer.remove(self._state_bubble)
- self._hlmask.destroy()
- self._hlmask = None
- self._tooldialog.destroy()
- self._propedit.destroy()
- self._activity.queue_draw()
- del self._activity._creator
-
- 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)
- launch = staticmethod(launch)
-
-class EditToolBox(gtk.Window):
- """Helper toolbox class for managing action properties"""
- def __init__(self, parent, action=None):
- """
- Create the property edition toolbox and display it.
-
- @param parent the parent window of this toolbox, usually an activity
- @param action the action to introspect/edit
- """
- gtk.Window.__init__(self)
- self._action = None
- self.__parent = parent # private avoid gtk clash
-
- self.set_title("Action Properties")
- self.set_transient_for(parent)
- self.set_decorated(True)
- self.set_resizable(False)
- self.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_UTILITY)
- self.set_destroy_with_parent(True)
- self.set_deletable(False)
- self.set_size_request(200, 400)
-
- self._vbox = gtk.VBox()
- self.add(self._vbox)
- propwin = gtk.ScrolledWindow()
- propwin.props.hscrollbar_policy = gtk.POLICY_AUTOMATIC
- propwin.props.vscrollbar_policy = gtk.POLICY_AUTOMATIC
- self._vbox.pack_start(propwin)
- self._propbox = gtk.VBox(spacing=10)
- propwin.add(self._propbox)
-
- self.action = action
-
- sw = gtk.gdk.screen_width()
- sh = gtk.gdk.screen_height()
-
- self.show_all()
- self.move(sw-10-200, (sh-400)/2)
-
- def refresh(self):
- """Refresh property values from the selected action."""
- if self._action is None:
- return
- props = self._action._props.keys()
- for propnum in xrange(len(props)):
- row = self._propbox.get_children()[propnum]
- propname = props[propnum]
- prop = getattr(type(self._action), propname)
- propval = getattr(self._action, propname)
- if isinstance(prop, properties.TStringProperty):
- propwdg = row.get_children()[1]
- propwdg.get_buffer().set_text(propval)
- elif isinstance(prop, properties.TIntProperty):
- propwdg = row.get_children()[1]
- propwdg.set_value(propval)
- elif isinstance(prop, properties.TArrayProperty):
- propwdg = row.get_children()[1]
- for i in xrange(len(propval)):
- entry = propwdg.get_children()[i]
- entry.set_text(str(propval[i]))
- else:
- propwdg = row.get_children()[1]
- propwdg.set_text(str(propval))
-
- def set_action(self, action):
- """Setter for the action property."""
- if self._action is action:
- self.refresh()
- return
- parent = self._propbox.get_parent()
- parent.remove(self._propbox)
- self._propbox = gtk.VBox(spacing=10)
- parent.add(self._propbox)
-
- self._action = action
- if action is None:
- return
- for propname in action._props.keys():
- row = gtk.HBox()
- row.pack_start(gtk.Label(T(propname)), False, False, 10)
- prop = getattr(type(action), propname)
- propval = getattr(action, propname)
- if isinstance(prop, properties.TStringProperty):
- propwdg = gtk.TextView()
- propwdg.get_buffer().set_text(propval)
- propwdg.connect_after("focus-out-event", \
- self._str_prop_changed, action, propname)
- elif isinstance(prop, properties.TIntProperty):
- adjustment = gtk.Adjustment(value=propval,
- lower=prop.lower_limit.limit,
- upper=prop.upper_limit.limit,
- step_incr=1)
- propwdg = gtk.SpinButton(adjustment=adjustment)
- propwdg.connect_after("focus-out-event", \
- self._int_prop_changed, action, prop)
- elif isinstance(prop, properties.TArrayProperty):
- propwdg = gtk.HBox()
- for i in xrange(len(propval)):
- entry = gtk.Entry()
- propwdg.pack_start(entry)
- entry.connect_after("focus-out-event", \
- self._list_prop_changed, action, propname, i)
- else:
- propwdg = gtk.Entry()
- propwdg.set_text(str(propval))
- row.pack_end(propwdg)
- self._propbox.pack_start(row, expand=False)
- self._vbox.show_all()
- self.refresh()
-
- def get_action(self):
- """Getter for the action property"""
- return self._action
- action = property(fset=set_action, fget=get_action, doc=\
- "Action to be edited through introspection.")
-
- def _list_prop_changed(self, widget, evt, action, propname, idx):
- try:
- getattr(action, propname)[idx] = int(widget.get_text())
- except ValueError:
- widget.set_text(str(getattr(action, propname)[idx]))
- self.__parent._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)
- 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)
-
-class TextInputDialog(gtk.MessageDialog):
- def __init__(self, text, field):
- gtk.MessageDialog.__init__(self, None,
- gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
- gtk.MESSAGE_QUESTION,
- gtk.BUTTONS_OK,
- None)
- self.set_markup(text)
- self.entry = gtk.Entry()
- self.entry.connect("activate", self._dialog_done_cb, gtk.RESPONSE_OK)
- hbox = gtk.HBox()
- lbl = gtk.Label(field)
- hbox.pack_start(lbl, False)
- hbox.pack_end(self.entry)
- self.vbox.pack_end(hbox, True, True)
- self.show_all()
-
- def pop(self):
- self.run()
- self.hide()
- text = self.entry.get_text()
- return text
-
- def _dialog_done_cb(self, entry, response):
- self.response(response)
-
-# vim:set ts=4 sts=4 sw=4 et:
diff --git a/src/sugar/tutorius/dialog.py b/src/sugar/tutorius/dialog.py
deleted file mode 100644
index be51a0e..0000000
--- a/src/sugar/tutorius/dialog.py
+++ /dev/null
@@ -1,59 +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
-"""
-The Dialog module provides means of interacting with the user
-through the use of Dialogs.
-"""
-import gtk
-
-class TutoriusDialog(gtk.Dialog):
- """
- TutoriusDialog is a simple wrapper around gtk.Dialog.
-
- It allows creating and showing a dialog and connecting the response and
- button click events to callbacks.
- """
- def __init__(self, label="Hint", button_clicked_cb=None, response_cb=None):
- """
- Constructor.
-
- @param label text to be shown on the dialog
- @param button_clicked_cb callback for the button click
- @param response_cb callback for the dialog response
- """
- gtk.Dialog.__init__(self)
-
- self._button = gtk.Button(label)
-
- self.add_action_widget(self._button, 1)
-
- if not button_clicked_cb == None:
- self._button.connect("clicked", button_clicked_cb)
-
- self._button.show()
-
- if not response_cb == None:
- self.connect("response", response_cb)
-
- self.set_decorated(False)
-
- def set_button_clicked_cb(self, funct):
- """Setter for the button_clicked callback"""
- self._button.connect("clicked", funct)
-
- def close_self(self, arg=None):
- """Close the dialog"""
- self.destroy()
diff --git a/src/sugar/tutorius/editor.py b/src/sugar/tutorius/editor.py
deleted file mode 100644
index 42cc718..0000000
--- a/src/sugar/tutorius/editor.py
+++ /dev/null
@@ -1,318 +0,0 @@
-# Copyright (C) 2009, Tutorius.org
-# Greatly influenced by sugar/activity/namingalert.py
-#
-# 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
-""" Tutorial Editor Module
-"""
-
-import gtk
-import gobject
-#import hippo
-#import gconf
-
-from gettext import gettext as _
-
-from sugar.tutorius.gtkutils import register_signals_numbered, get_children
-
-class WidgetIdentifier(gtk.Window):
- """
- Tool that allows identifying widgets.
-
- """
- __gtype_name__ = 'TutoriusWidgetIdentifier'
-
- def __init__(self, activity):
- gtk.Window.__init__(self)
-
- self._activity = activity
- self._handlers = {}
- # dict of signals to register on the widgets.
- # key : signal name
- # value : initial checkbox status
- signals = {
- "focus":True,
- "button-press-event":True,
- "enter-notify-event":False,
- "leave-notify-event":False,
- "key-press-event":True,
- "text-selected":True,
- "clicked":True,
- }
-
- self.set_decorated(False)
- self.set_resizable(False)
- self.set_modal(False)
-
- self.connect('realize', self.__realize_cb)
-
- self._expander = gtk.Expander(_("Widget Identifier"))
- self._expander.set_expanded(True)
- self.add(self._expander)
- self._expander.connect("notify::expanded", self.__expander_cb)
-
- self._expander.show()
-
- nbk = gtk.Notebook()
- self._expander.add(nbk)
- nbk.show()
-
- ###############################
- # Event log viewer page
- ###############################
- self.logview = gtk.TextView()
- self.logview.set_editable(False)
- self.logview.set_cursor_visible(False)
- self.logview.set_wrap_mode(gtk.WRAP_NONE)
- self._textbuffer = self.logview.get_buffer()
-
- swd = gtk.ScrolledWindow()
- swd.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
- swd.add(self.logview)
- self.logview.show()
-
- nbk.append_page(swd, gtk.Label(_("Log")))
- swd.show()
-
- ###############################
- # Filters page
- ###############################
- filters = gtk.Table( (len(signals)+1)/2, 2)
-
- xpos, ypos = 0, 0
- for key, active in signals.items():
- cbtn = gtk.CheckButton(label=key)
- filters.attach(cbtn, xpos, xpos+1, ypos, ypos+1)
- cbtn.show()
- cbtn.set_active(active)
- if active:
- self._handlers[key] = register_signals_numbered( \
- self._activity, self._handle_events, events=(key,))
- else:
- self._handlers[key] = []
-
- cbtn.connect("toggled", self.__filter_toggle_cb, key)
-
- #Follow lines then columns
- xpos, ypos = (xpos+1)%2, ypos+(xpos%2)
-
- nbk.append_page(filters, gtk.Label(_("Events")))
- filters.show()
-
- ###############################
- # Explorer Page
- ###############################
- tree = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
- explorer = gtk.TreeView(tree)
-
- pathrendr = gtk.CellRendererText()
- pathrendr.set_properties(background="#ffffff", foreground="#000000")
- pathcol = gtk.TreeViewColumn(_("Path"), pathrendr, text=0, background=0, foreground=0)
- explorer.append_column(pathcol)
-
- typerendr = gtk.CellRendererText()
- typerendr.set_properties(background="#ffffff", foreground="#000000")
- typecol = gtk.TreeViewColumn(_("Widget"), typerendr, text=1, background=1, foreground=1)
- explorer.append_column(typecol)
-
- self.__populate_treestore(
- tree, #tree
- tree.append(None, ["0",self._activity.get_name()]), #parent
- self._activity, #widget
- "0" #path
- )
-
- explorer.set_expander_column(typecol)
-
- swd2 = gtk.ScrolledWindow()
- swd2.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
- swd2.add(explorer)
- explorer.show()
- nbk.append_page(swd2, gtk.Label(_("Explorer")))
- swd2.show()
-
- ###############################
- # GObject Explorer Page
- ###############################
- tree2 = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
- explorer2 = gtk.TreeView(tree2)
-
- pathrendr2 = gtk.CellRendererText()
- pathrendr2.set_properties(background="#ffffff", foreground="#000000")
- pathcol2 = gtk.TreeViewColumn(_("Path"), pathrendr2, text=0, background=0, foreground=0)
- explorer2.append_column(pathcol2)
-
- typerendr2 = gtk.CellRendererText()
- typerendr2.set_properties(background="#ffffff", foreground="#000000")
- typecol2 = gtk.TreeViewColumn(_("Widget"), typerendr2, text=1, background=1, foreground=1)
- explorer2.append_column(typecol2)
-
- self.__populate_gobject_treestore(
- tree2, #tree
- tree2.append(None, ["activity",self._activity.get_name()]), #parent
- self._activity, #widget
- "activity" #path
- )
-
- explorer2.set_expander_column(typecol2)
-
- swd3 = gtk.ScrolledWindow()
- swd3.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
- swd3.add(explorer2)
- explorer2.show()
- nbk.append_page(swd3, gtk.Label(_("GObject Explorer")))
- swd3.show()
-
- def __populate_treestore(self, tree, parent, widget, path):
- """Populates the treestore with the widget's children recursively
- @param tree gtk.TreeStore to populate
- @param parent gtk.TreeIter to append to
- @param widget gtk.Widget to check for children
- @param path treeish of the widget
- """
- #DEBUG: show parameters in log window gehehe
- #self._handle_events((path,str(type(widget))))
- children = get_children(widget)
- for i in xrange(len(children)):
- childpath = ".".join([path, str(i)])
- child = children[i]
- self.__populate_treestore(
- tree, #tree
- tree.append(parent, [childpath, child.get_name()]), #parent
- child, #widget
- childpath #path
- )
-
-
- def __populate_gobject_treestore(self, tree, parent, widget, path, listed=None):
- """Populates the treestore with the widget's children recursively
- @param tree gtk.TreeStore to populate
- @param parent gtk.TreeIter to append to
- @param widget gtk.Widget to check for children
- @param path treeish of the widget
- """
- listed = listed or []
- if widget in listed:
- return
- listed.append(widget)
- #DEBUG: show parameters in log window gehehe
- #self._handle_events((path,str(type(widget))))
- #Add a child node
- children = tree.append(parent, ["","children"])
- for i in dir(widget):
- #Add if a gobject
- try:
- child = getattr(widget, i)
- except:
- continue
- if isinstance(child,gobject.GObject):
- childpath = ".".join([path, i])
- child = getattr(widget, i)
- self.__populate_gobject_treestore(
- tree, #tree
- tree.append(children, [childpath, i]), #parent
- child, #widget
- path + "." + i, #path,
- listed
- )
- widgets = tree.append(parent, ["","widgets"])
- wchildren = get_children(widget)
- for i in xrange(len(wchildren)):
- childpath = ".".join([path, str(i)])
- child = wchildren[i]
- self.__populate_gobject_treestore(
- tree, #tree
- tree.append(widgets, [childpath, (hasattr(child,"get_name") and child.get_name()) or i]), #parent
- child, #widget
- childpath, #path,
- listed
- )
-
- #Add signals and attributes nodes
- signals = tree.append(parent, ["","signals"])
- for signame in gobject.signal_list_names(widget):
- tree.append(signals, ["",signame])
-
- attributes = tree.append(parent, ["","properties"])
- for prop in gobject.list_properties(widget):
- tree.append(attributes, ["",prop])
-
- def __filter_toggle_cb(self, btn, eventname):
- """Callback for signal name checkbuttons' toggling"""
- #Disconnect existing handlers on key
- self.__disconnect_handlers(eventname)
- if btn.get_active():
- #if checked, reconnect
- self._handlers[eventname] = register_signals_numbered( \
- self._activity, self._handle_events, events=(eventname,))
-
-
- def __expander_cb(self, *args):
- """Callback for the window expander toggling"""
- if self._expander.get_expanded():
- self.__move_expanded()
- else:
- self.__move_collapsed()
-
- def __move_expanded(self):
- """Move the window to it's expanded position"""
- width = 500
- height = 300
- swidth = gtk.gdk.screen_width()
- sheight = gtk.gdk.screen_height()
-
- self.set_size_request(width, height)
- self.move((swidth-width)/2, sheight-height)
-
- def __move_collapsed(self):
- """Move the window to it's collapsed position"""
- width = 150
- height = 40
- swidth = gtk.gdk.screen_width()
- sheight = gtk.gdk.screen_height()
-
- self.set_size_request(width, height)
- self.move((swidth-width)/2, sheight-height)
-
- def __realize_cb(self, widget):
- """Callback for realize"""
- self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
- self.window.set_accept_focus(True)
- self.__move_expanded()
-
- def _disconnect_handlers(self):
- """ Disconnect all event handlers """
- for key in self._handlers:
- self.__disconnect_handlers(key)
-
- def __disconnect_handlers(self, key):
- """ Disconnect event handlers associated to signal name "key" """
- if self._handlers.has_key(key):
- for widget, handlerid in self._handlers[key]:
- widget.handler_disconnect(handlerid)
- del self._handlers[key]
-
- def _handle_events(self, *args):
- """ Event handler for subscribed widget events.
- Accepts variable length argument list. Last must be
- a two-tuple containing (event name, widget name) """
- sig, name = args[-1]
- text = "\r\n".join(
- (["%s event received from %s" % (sig, name)] +
- self._textbuffer.get_text(*(self._textbuffer.get_bounds())
- ).split("\r\n"))[:80]
- )
- self._textbuffer.set_text(text)
-
-
diff --git a/src/sugar/tutorius/filters.py b/src/sugar/tutorius/filters.py
deleted file mode 100644
index aa8c997..0000000
--- a/src/sugar/tutorius/filters.py
+++ /dev/null
@@ -1,204 +0,0 @@
-# Copyright (C) 2009, Tutorius.org
-# Copyright (C) 2009, Vincent Vinet <vince.vinet@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
-
-import gobject
-import gtk
-import logging
-logger = logging.getLogger("filters")
-
-from sugar.tutorius.gtkutils import find_widget
-from sugar.tutorius.services import ObjectStore
-from sugar.tutorius import properties
-
-
-class EventFilter(properties.TPropContainer):
- """
- Base class for an event filter
- """
-
- next_state = properties.TStringProperty("None")
-
- def __init__(self, next_state=None):
- """
- Constructor.
- @param next_state name of the next state
- """
- super(EventFilter, self).__init__()
- if next_state:
- self.next_state = next_state
- self._callback = None
-
- def get_next_state(self):
- """
- Getter for the next state
- """
- return self.next_state
-
- def set_next_state(self, new_next_name):
- """
- Setter for the next state. Should only be used during construction of
- the event_fitler, not while the tutorial is running.
- """
- self.next_state = new_next_name
-
- def install_handlers(self, callback, **kwargs):
- """
- install_handlers is called for eventfilters to setup all
- necessary event handlers to be able to catch the desired
- event.
-
- @param callback the callback function that will be called
- with the event filter as an argument when the event is catched
- and validated.
- @param **kwargs unused by this handler for now, allows subclasses
- to receive information about the context when installing
-
- Subclasses must call this super method to setup the callback if they
- feel like cooperating
- """
- self._callback = callback
-
- def remove_handlers(self):
- """
- remove_handlers is called when a state is done so that all
- event filters can cleanup any handlers they have installed
-
- This function will also clear the callback function so that any
- leftover handler that is triggered will not be able to change the
- application state.
-
- subclasses must call this super method to cleanup the callback if they
- collaborate and use this classe's do_callback()
- """
- self._callback = None
-
- def do_callback(self, *args, **kwargs):
- """
- Default callback function that calls the event filter callback
- with the event filter as only argument.
- """
- if self._callback:
- self._callback(self)
-
-class TimerEvent(EventFilter):
- """
- TimerEvent is a special EventFilter that uses gobject
- timeouts to trigger a state change after a specified amount
- of time. It must be used inside a gobject main loop to work.
- """
- def __init__(self,next_state,timeout_s):
- """Constructor.
-
- @param next_state default EventFilter param, passed on to EventFilter
- @param timeout_s timeout in seconds
- """
- super(TimerEvent,self).__init__(next_state)
- self._timeout = timeout_s
- self._handler_id = None
-
- def install_handlers(self, callback, **kwargs):
- """install_handlers creates the timer and starts it"""
- super(TimerEvent,self).install_handlers(callback, **kwargs)
- #Create the timer
- self._handler_id = gobject.timeout_add_seconds(self._timeout, self._timeout_cb)
-
- def remove_handlers(self):
- """remove handler removes the timer"""
- super(TimerEvent,self).remove_handlers()
- if self._handler_id:
- try:
- #XXX What happens if this was already triggered?
- #remove the timer
- gobject.source_remove(self._handler_id)
- except:
- pass
-
- def _timeout_cb(self):
- """
- _timeout_cb triggers the eventfilter callback.
-
- It is necessary because gobject timers only stop if the callback they
- trigger returns False
- """
- self.do_callback()
- return False #Stops timeout
-
-class GtkWidgetTypeFilter(EventFilter):
- """
- Event Filter that listens for keystrokes on a widget
- """
- def __init__(self, next_state, object_id, text=None, strokes=None):
- """Constructor
- @param next_state default EventFilter param, passed on to EventFilter
- @param object_id object tree-ish identifier
- @param text resulting text expected
- @param strokes list of strokes expected
-
- At least one of text or strokes must be supplied
- """
- super(GtkWidgetTypeFilter, self).__init__(next_state)
- self._object_id = object_id
- self._text = text
- self._captext = ""
- self._strokes = strokes
- self._capstrokes = []
- self._widget = None
- self._handler_id = None
-
- def install_handlers(self, callback, **kwargs):
- """install handlers
- @param callback default EventFilter callback arg
- """
- super(GtkWidgetTypeFilter, self).install_handlers(callback, **kwargs)
- logger.debug("~~~GtkWidgetTypeFilter install")
- activity = ObjectStore().activity
- if activity is None:
- logger.error("No activity")
- raise RuntimeWarning("no activity in the objectstore")
-
- self._widget = find_widget(activity, self._object_id)
- if self._widget:
- self._handler_id= self._widget.connect("key-press-event",self.__keypress_cb)
- logger.debug("~~~Connected handler %d on %s" % (self._handler_id,self._object_id) )
-
- def remove_handlers(self):
- """remove handlers"""
- super(GtkWidgetTypeFilter, self).remove_handlers()
- #if an event was connected, disconnect it
- if self._handler_id:
- self._widget.handler_disconnect(self._handler_id)
- self._handler_id=None
-
- def __keypress_cb(self, widget, event, *args):
- """keypress callback"""
- logger.debug("~~~keypressed!")
- key = event.keyval
- keystr = event.string
- logger.debug("~~~Got key: " + str(key) + ":"+ keystr)
- self._capstrokes += [key]
- #TODO Treat other stuff, such as arrows
- if key == gtk.keysyms.BackSpace:
- self._captext = self._captext[:-1]
- else:
- self._captext = self._captext + keystr
-
- logger.debug("~~~Current state: " + str(self._capstrokes) + ":" + str(self._captext))
- if not self._strokes is None and self._strokes in self._capstrokes:
- self.do_callback()
- if not self._text is None and self._text in self._captext:
- self.do_callback()
-
diff --git a/src/sugar/tutorius/gtkutils.py b/src/sugar/tutorius/gtkutils.py
deleted file mode 100644
index 1a9cb0f..0000000
--- a/src/sugar/tutorius/gtkutils.py
+++ /dev/null
@@ -1,203 +0,0 @@
-# Copyright (C) 2009, Tutorius.org
-# Copyright (C) 2009, Vincent Vinet <vince.vinet@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
-"""
-Utility classes and functions that are gtk related
-"""
-import gtk
-
-def raddr_lookup(widget):
- name = []
- child = widget
- parent = widget.parent
- while parent:
- name.append(str(parent.get_children().index(child)))
- child = parent
- parent = child.parent
-
- name.append("0") # root object itself
- name.reverse()
- return ".".join(name)
-
-
-def find_widget(base, target_fqdn):
- """Find a widget by digging into a parent widget's children tree
- @param base the parent widget
- @param target_fqdn fqdn-style target object name
-
- @return widget found
-
- The object should normally be the activity widget, as it is the root
- widget for activities. The target_fqdn is a dot separated list of
- indexes used in widget.get_children and should start with a 0 which is
- the base widget itself,
-
- Example Usage:
- find_widget(activity,"0.0.0.1.0.0.2")
- """
- path = target_fqdn.split(".")
- #We select the first object and pop the first zero
- obj = base
- path.pop(0)
-
- while len(path) > 0:
- try:
- obj = get_children(obj)[int(path.pop(0))]
- except:
- break
-
- return obj
-
-EVENTS = (
- "focus",
- "button-press-event",
- "enter-notify-event",
- "leave-notify-event",
- "key-press-event",
- "text-selected",
- "clicked",
-)
-
-IGNORED_WIDGETS = (
- "GtkVBox",
- "GtkHBox",
- "GtkAlignment",
- "GtkNotebook",
- "GtkButton",
- "GtkToolItem",
- "GtkToolbar",
-)
-
-def register_signals_numbered(target, handler, prefix="0", max_depth=None, events=None):
- """
- Recursive function to register event handlers on an target
- and it's children. The event handler is called with an extra
- argument which is a two-tuple containing the signal name and
- the FQDN-style name of the target that triggered the event.
-
- This function registers all of the events listed in
- EVENTS
-
- Example arg tuple added:
- ("focus", "1.1.2")
- Side effects:
- -Handlers connected on the various targets
-
- @param target the target to recurse on
- @param handler the handler function to connect
- @param prefix name prepended to the target name to form a chain
- @param max_depth maximum recursion depth, None for infinity
-
- @returns list of (object, handler_id)
- """
- ret = []
- evts = events or EVENTS
- #Gtk Containers have a get_children() function
- children = get_children(target)
- for i in range(len(children)):
- child = children[i]
- if max_depth is None or max_depth > 0:
- #Recurse with a prefix on all children
- pre = ".".join( \
- [p for p in (prefix, str(i)) if not p is None]
- )
- if max_depth is None:
- dep = None
- else:
- dep = max_depth - 1
- ret+=register_signals_numbered(child, handler, pre, dep, evts)
- #register events on the target if a widget XXX necessary to check this?
- if isinstance(target, gtk.Widget):
- for sig in evts:
- try:
- ret.append( \
- (target, target.connect(sig, handler, (sig, prefix) ))\
- )
- except TypeError:
- pass
-
- return ret
-
-def register_signals(target, handler, prefix=None, max_depth=None, events=None):
- """
- Recursive function to register event handlers on an target
- and it's children. The event handler is called with an extra
- argument which is a two-tuple containing the signal name and
- the FQDN-style name of the target that triggered the event.
-
- This function registers all of the events listed in
- EVENTS and omits widgets with a name matching
- IGNORED_WIDGETS from the name hierarchy.
-
- Example arg tuple added:
- ("focus", "Activity.Toolbox.Bold")
- Side effects:
- -Handlers connected on the various targets
-
- @param target the target to recurse on
- @param handler the handler function to connect
- @param prefix name prepended to the target name to form a chain
- @param max_depth maximum recursion depth, None for infinity
-
- @returns list of (object, handler_id)
- """
- ret = []
- evts = events or EVENTS
- #Gtk Containers have a get_children() function
- for child in get_children(target):
- if max_depth is None or max_depth > 0:
- #Recurse with a prefix on all children
- pre = ".".join( \
- [p for p in (prefix, target.get_name()) \
- if not (p is None or p in IGNORED_WIDGETS)] \
- )
- if max_depth is None:
- dep = None
- else:
- dep = max_depth - 1
- ret += register_signals(child, handler, pre, dep, evts)
- name = ".".join( \
- [p for p in (prefix, target.get_name()) \
- if not (p is None or p in IGNORED_WIDGETS)] \
- )
- #register events on the target if a widget XXX necessary to check this?
- if isinstance(target, gtk.Widget):
- for sig in evts:
- try:
- ret.append( \
- (target, target.connect(sig, handler, (sig, name) )) \
- )
- except TypeError:
- pass
-
- return ret
-
-def get_children(widget):
- """Lists widget's children"""
- #widgets with multiple children
- try:
- return widget.get_children()
- except (AttributeError,TypeError):
- pass
-
- #widgets with a single child
- try:
- return [widget.get_child(),]
- except (AttributeError,TypeError):
- pass
-
- #otherwise return empty list
- return []
diff --git a/src/sugar/tutorius/linear_creator.py b/src/sugar/tutorius/linear_creator.py
deleted file mode 100644
index 91b11f4..0000000
--- a/src/sugar/tutorius/linear_creator.py
+++ /dev/null
@@ -1,95 +0,0 @@
-# Copyright (C) 2009, Tutorius.org
-# Greatly influenced by sugar/activity/namingalert.py
-#
-# 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
-
-from sugar.tutorius.core import *
-from sugar.tutorius.actions import *
-from sugar.tutorius.filters import *
-
-from copy import deepcopy
-
-class LinearCreator(object):
- """
- This class is used to create a FSM from a linear sequence of orders. The
- orders themselves are meant to be either an action or a transition.
- """
-
- def __init__(self):
- self.fsm = FiniteStateMachine("Sample Tutorial")
- self.current_actions = []
- self.nb_state = 0
- self.state_name = "INIT"
-
- def set_name(self, name):
- """
- Sets the name of the generated FSM.
- """
- self.fsm.name = name
-
- def action(self, action):
- """
- Adds an action to execute in the current state.
- """
- self.current_actions.append(action)
-
- def event(self, event_filter):
- """
- Adds a transition to another state. When executing this, all the actions
- previously called will be bundled in a single state, with the exit
- condition of this state being the transition just added.
-
- Whatever the name of the next state you inserted in the event, it will
- be replaced to point to the next event in the line.
- """
- if len(self.current_actions) != 0:
- # Set the next state name - there is no way the caller should have
- # to deal with that.
- next_state_name = "State %d" % (self.nb_state+1)
- event_filter.set_next_state(next_state_name)
- state = State(self.state_name, action_list=self.current_actions,
- event_filter_list=[event_filter])
- self.state_name = next_state_name
-
- self.nb_state += 1
- self.fsm.add_state(state)
-
- # Clear the actions from the list
- self.current_actions = []
-
- def generate_fsm(self):
- """
- Returns a finite state machine corresponding to the sequence of calls
- that were made from this point on.
- """
- # Copy the whole FSM that was generated yet
- new_fsm = deepcopy(self.fsm)
-
- # Generate the final state
- state = None
- if len(self.current_actions) != 0:
- state = State("State" + str(self.nb_state), action_list=self.current_actions)
- # Don't increment the nb_state here - we would break the linearity
- # because we might generate more stuff with this creator later.
- # Since we rely on linearity for continuity when generating the
- # next state's name on an event filter, we cannot increment here.
- else:
- state = State("State" + str(self.nb_state))
-
- # Insert the state in the copy of the FSM
- new_fsm.add_state(state)
-
- return new_fsm
-
diff --git a/src/sugar/tutorius/overlayer.py b/src/sugar/tutorius/overlayer.py
deleted file mode 100644
index 931949d..0000000
--- a/src/sugar/tutorius/overlayer.py
+++ /dev/null
@@ -1,503 +0,0 @@
-"""
-This module manages drawing of overlayed widgets. The class responsible for
-drawing management (Overlayer) and basic overlayable widgets are defined here.
-"""
-# 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
-
-import gobject
-import gtk
-import cairo
-import pangocairo
-from math import pi
-
-from sugar import profile
-
-# for easy profile access from cairo
-color = profile.get_color().get_stroke_color()
-xo_line_color = (int(color[1:3], 16)/255.0,
- int(color[3:5], 16)/255.0,
- int(color[5:7], 16)/255.0)
-color = profile.get_color().get_fill_color()
-xo_fill_color = (int(color[1:3], 16)/255.0,
- int(color[3:5], 16)/255.0,
- int(color[5:7], 16)/255.0)
-del color
-
-# This is the CanvasDrawable protocol. Any widget wishing to be drawn on the
-# overlay must implement it. See TextBubble for a sample implementation.
-#class CanvasDrawable(object):
-# """Defines the CanvasDrawable protocol"""
-# no_expose = None
-# def draw_with_context(self, context):
-# """
-# Draws the cairo widget with the passed cairo context.
-# This will be called if the widget is child of an overlayer.
-# """
-# pass
-
-class Overlayer(gtk.Layout):
- """
- This guy manages drawing of overlayed widgets. Those can be standard GTK
- widgets or special "cairoDrawable" widgets which support the defined
- interface (see the put method).
-
- @param overlayed widget to be overlayed. Will be resized to full size.
- """
- def __init__(self, overlayed=None):
- gtk.Layout.__init__(self)
-
- self._overlayed = overlayed
- if overlayed:
- self.put(overlayed, 0, 0)
-
- self.__realizer = self.connect("expose-event", self.__init_realized)
- self.connect("size-allocate", self.__size_allocate)
- self.show()
-
- self.__render_handle = None
-
- def put(self, child, x, y):
- """
- Adds a child widget to be overlayed. This can be, overlay widgets or
- normal GTK widgets (though normal widgets will alwas appear under
- cairo widgets due to the rendering chain).
-
- @param child the child to add
- @param x the horizontal coordinate for positionning
- @param y the vertical coordinate for positionning
- """
- if hasattr(child, "draw_with_context"):
- # if the widget has the CanvasDrawable protocol, use it.
- child.no_expose = True
- gtk.Layout.put(self, child, x, y)
-
- # be sure to redraw or the overlay may not show
- self.queue_draw()
-
-
- def __init_realized(self, widget, event):
- """
- Initializer to set once widget is realized.
- Since an expose event is signaled only to realized widgets, we set this
- 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
-
- self.parent.set_app_paintable(True)
-
- # 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)
-
- 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()
-
- #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()
-
- 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:]:
- 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)
-
-
-class TextBubble(gtk.Widget):
- """
- A CanvasDrawableWidget drawing a round textbox and a tail pointing
- to a specified widget.
- """
- def __init__(self, text, speaker=None, tailpos=[0,0]):
- """
- Creates a new cairo rendered text bubble.
-
- @param text the text to render in the bubble
- @param speaker the widget to compute the tail position from
- @param tailpos (optional) position relative to the bubble to use as
- the tail position, if no speaker
- """
- gtk.Widget.__init__(self)
-
- # FIXME: ensure previous call does not interfere with widget stacking,
- # as using a gtk.Layout and stacking widgets may reveal a screwed up
- # order with the cairo widget on top.
- self.__label = None
-
- self.label = text
- self.speaker = speaker
- self.tailpos = tailpos
- self.line_width = 5
- self.padding = 20
-
- self._no_expose = False
- self.__exposer = None
-
- def draw_with_context(self, context):
- """
- Draw using the passed cairo context instead of creating a new cairo
- context. This eases blending between multiple cairo-rendered
- widgets.
- """
- context.translate(self.allocation.x, self.allocation.y)
- width = self.allocation.width
- height = self.allocation.height
- xradius = width/2
- yradius = height/2
- width -= self.line_width
- height -= self.line_width
- #
- # TODO fetch speaker coordinates
-
- # draw bubble tail if present
- if self.tailpos != [0,0]:
- context.move_to(xradius-width/4, yradius)
- context.line_to(self.tailpos[0], self.tailpos[1])
- context.line_to(xradius+width/4, yradius)
- context.set_line_width(self.line_width)
- context.set_source_rgb(*xo_line_color)
- context.stroke_preserve()
-
- # bubble border
- context.move_to(width-self.padding, 0.0)
- context.line_to(self.padding, 0.0)
- context.arc_negative(self.padding, self.padding, self.padding,
- 3*pi/2, pi)
- context.line_to(0.0, height-self.padding)
- context.arc_negative(self.padding, height-self.padding, self.padding,
- pi, pi/2)
- context.line_to(width-self.padding, height)
- context.arc_negative(width-self.padding, height-self.padding,
- self.padding, pi/2, 0)
- context.line_to(width, self.padding)
- context.arc_negative(width-self.padding, self.padding, self.padding,
- 0.0, -pi/2)
- context.set_line_width(self.line_width)
- context.set_source_rgb(*xo_line_color)
- context.stroke_preserve()
- context.set_source_rgb(*xo_fill_color)
- context.fill()
-
- # bubble painting. Redrawing the inside after the tail will combine
- if self.tailpos != [0,0]:
- context.move_to(xradius-width/4, yradius)
- context.line_to(self.tailpos[0], self.tailpos[1])
- context.line_to(xradius+width/4, yradius)
- context.set_line_width(self.line_width)
- context.set_source_rgb(*xo_fill_color)
- context.fill()
-
- context.set_source_rgb(1.0, 1.0, 1.0)
- pangoctx = pangocairo.CairoContext(context)
- self._text_layout.set_markup(self.__label)
- text_size = self._text_layout.get_pixel_size()
- pangoctx.move_to(
- int((self.allocation.width-text_size[0])/2),
- int((self.allocation.height-text_size[1])/2))
- pangoctx.show_layout(self._text_layout)
-
- # work done. Be kind to next cairo widgets and reset matrix.
- context.identity_matrix()
-
- def do_realize(self):
- """ Setup gdk window creation. """
- self.set_flags(gtk.REALIZED | gtk.NO_WINDOW)
- self.window = self.get_parent_window()
- if not self._no_expose:
- self.__exposer = self.connect_after("expose-event", \
- self.__on_expose)
-
- def __on_expose(self, widget, event):
- """Redraw event callback."""
- ctx = self.window.cairo_create()
-
- self.draw_with_context(ctx)
-
- return True
-
- def _set_label(self, value):
- """Sets the label and flags the widget to be redrawn."""
- self.__label = "<b>%s</b>"%value
- if not self.parent:
- return
- ctx = self.parent.window.cairo_create()
- pangoctx = pangocairo.CairoContext(ctx)
- self._text_layout = pangoctx.create_layout()
- self._text_layout.set_markup(value)
- del pangoctx, ctx#, surf
-
- def do_size_request(self, requisition):
- """Fill requisition with size occupied by the widget."""
- ctx = self.parent.window.cairo_create()
- pangoctx = pangocairo.CairoContext(ctx)
- self._text_layout = pangoctx.create_layout()
- self._text_layout.set_markup(self.__label)
-
- width, height = self._text_layout.get_pixel_size()
- requisition.width = int(width+2*self.padding)
- requisition.height = int(height+2*self.padding)
-
- def do_size_allocate(self, allocation):
- """Save zone allocated to the widget."""
- self.allocation = allocation
-
- def _get_label(self):
- """Getter method for the label property"""
- return self.__label[3:-4]
-
- def _set_no_expose(self, value):
- """setter for no_expose property"""
- self._no_expose = value
- if not (self.flags() and gtk.REALIZED):
- return
-
- if self.__exposer and value:
- self.parent.disconnect(self.__exposer)
- self.__exposer = None
- elif (not self.__exposer) and (not value):
- self.__exposer = self.parent.connect_after("expose-event",
- self.__on_expose)
-
- def _get_no_expose(self):
- """getter for no_expose property"""
- return self._no_expose
-
- no_expose = property(fset=_set_no_expose, fget=_get_no_expose,
- doc="Whether the widget should handle exposition events or not.")
-
- label = property(fget=_get_label, fset=_set_label,
- doc="Text label which is to be painted on the top of the widget")
-
-gobject.type_register(TextBubble)
-
-class Rectangle(gtk.Widget):
- """
- A CanvasDrawableWidget drawing a rectangle over a specified widget.
- """
- def __init__(self, widget, color):
- """
- Creates a new Rectangle
-
- @param widget the widget to cover
- @param color the color of the rectangle, as a 4-tuple RGBA
- """
- gtk.Widget.__init__(self)
-
- self.covered = widget
- self.color = color
-
- self.__exposer = self.connect("expose-event", self.__on_expose)
-
- def draw_with_context(self, context):
- """
- Draw using the passed cairo context instead of creating a new cairo
- context. This eases blending between multiple cairo-rendered
- widgets.
- """
- if self.covered is None:
- # nothing to hide, no coordinates, no drawing
- return
- mask_alloc = self.covered.get_allocation()
- x, y = self.covered.translate_coordinates(self.parent, 0, 0)
-
- context.rectangle(x, y, mask_alloc.width, mask_alloc.height)
- context.set_source_rgba(*self.color)
- context.fill()
-
- def do_realize(self):
- """ Setup gdk window creation. """
- self.set_flags(gtk.REALIZED | gtk.NO_WINDOW)
-
- self.window = self.get_parent_window()
- if not isinstance(self.parent, Overlayer):
- assert False, "%s should not realize" % type(self).__name__
- print "Danger, Will Robinson! Rectangle parent is not Overlayer"
-
- def __on_expose(self, widget, event):
- """Redraw event callback."""
- assert False, "%s wasn't meant to be exposed by gtk" % \
- type(self).__name__
- ctx = self.window.cairo_create()
-
- self.draw_with_context(ctx)
-
- return True
-
- def do_size_request(self, requisition):
- """Fill requisition with size occupied by the masked widget."""
- # This is a bit pointless, as this will always ignore allocation and
- # be rendered directly on overlay, but for sanity, let's put some values
- # in there.
- if not self.covered:
- requisition.width = 0
- requisition.height = 0
- return
-
- masked_alloc = self.covered.get_allocation()
- requisition.width = masked_alloc.width
- requisition.height = masked_alloc.height
-
- def do_size_allocate(self, allocation):
- """Save zone allocated to the widget."""
- self.allocation = allocation
-
- def _set_no_expose(self, value):
- """setter for no_expose property"""
- if self.__exposer and value:
- self.disconnect(self.__exposer)
- self.__exposer = None
- elif (not self.__exposer) and (not value):
- self.__exposer = self.connect("expose-event", self.__on_expose)
-
- def _get_no_expose(self):
- """getter for no_expose property"""
- return not self.__exposer
-
- no_expose = property(fset=_set_no_expose, fget=_get_no_expose,
- doc="Whether the widget should handle exposition events or not.")
-gobject.type_register(Rectangle)
-
-class Mask(gtk.EventBox):
- """
- A CanvasDrawableWidget drawing a rectangle over a specified widget.
- """
- def __init__(self, catch_events=False, pass_thru=()):
- """
- Creates a new Rectangle
-
- @param catch_events whether the Mask should catch events
- @param pass_thru the widgets that "punch holes" through this Mask.
- Events will pass through to those widgets.
- """
- gtk.EventBox.__init__(self)
- self.no_expose = True # ignored
- self._catch_events = False
- self.catch_events = catch_events
- self.pass_thru = list(pass_thru)
-
- def __del__(self):
- for widget in self.pass_thru:
- widget.drag_unhighlight()
-
- def mask(self, widget):
- """
- Remove the widget from the unmasked list.
- @param widget a widget to remask
- """
- assert widget in self.pass_thru, \
- "trying to mask already masked widget"
- self.pass_thru.remove(widget)
- widget.drag_unhighlight()
-
- def unmask(self, widget):
- """
- Add to the unmasked list the widget passed.
- A hole will be punched through the mask at that widget's position.
- @param widget a widget to unmask
- """
- if widget not in self.pass_thru:
- widget.drag_highlight()
- self.pass_thru.append(widget)
-
-
- def set_catch_events(self, do_catch):
- """Sets whether the mask catches events of widgets under it"""
- if bool(self._catch_events) ^ bool(do_catch):
- if do_catch:
- self._catch_events = True
- self.grab_add()
- else:
- self.grab_remove()
- self._catch_events = False
-
- def get_catch_events(self):
- """Gets whether the mask catches events of widgets under it"""
- return bool(self._catch_handle)
-
- catch_events = property(fset=set_catch_events, fget=get_catch_events)
-
- def draw_with_context(self, context):
- """
- Draw using the passed cairo context instead of creating a new cairo
- context. This eases blending between multiple cairo-rendered
- widgets.
- """
- # Fill parent container
- mask_alloc = self.parent.get_allocation()
- oldrule = context.get_fill_rule()
- context.set_fill_rule(cairo.FILL_RULE_EVEN_ODD)
- x, y = self.translate_coordinates(self.parent, 0, 0)
-
- context.rectangle(x, y, mask_alloc.width, mask_alloc.height)
- for hole in self.pass_thru:
- alloc = hole.get_allocation()
- x, y = hole.translate_coordinates(self.parent, 0, 0)
- context.rectangle(x, y, alloc.width, alloc.height)
- context.set_source_rgba(0, 0, 0, 0.7)
- context.fill()
- context.set_fill_rule(oldrule)
-
- def do_size_request(self, requisition):
- """Fill requisition with size occupied by the masked widget."""
- # This is required for the event box to span across all the parent.
- alloc = self.parent.get_allocation()
- requisition.width = alloc.width
- requisition.height = alloc.height
-
- def do_size_allocate(self, allocation):
- """Save zone allocated to the widget."""
- self.allocation = allocation
-
-gobject.type_register(Mask)
-
-
-# vim:set ts=4 sts=4 sw=4 et:
diff --git a/src/sugar/tutorius/properties.py b/src/sugar/tutorius/properties.py
deleted file mode 100644
index 34b508a..0000000
--- a/src/sugar/tutorius/properties.py
+++ /dev/null
@@ -1,323 +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 contains properties class that can be included in other types.
-TutoriusProperties have the same behaviour as python properties (assuming you
-also use the TPropContainer), with the added benefit of having builtin dialog
-prompts and constraint validation.
-"""
-
-from sugar.tutorius.constraints import Constraint, \
- UpperLimitConstraint, LowerLimitConstraint, \
- MaxSizeConstraint, MinSizeConstraint, \
- ColorConstraint, FileConstraint, BooleanConstraint, EnumConstraint
-
-class TPropContainer(object):
- """
- A class containing properties. This does the attribute wrapping between
- the container instance and the property value. As properties are on the
- containing classes, they allow static introspection of those types
- at the cost of needing a mapping between container instances, and
- property values. This is what TPropContainer does.
- """
- def __init__(self):
- """
- Prepares the instance for property value storage. This is done at
- object initialization, thus allowing initial mapping of properties
- declared on the class. Properties won't work correctly without
- this call.
- """
- # create property value storage
- object.__setattr__(self, "_props", {})
- for attr_name in dir(type(self)):
- propinstance = object.__getattribute__(self, attr_name)
- if isinstance(propinstance, TutoriusProperty):
- # only care about TutoriusProperty instances
- propinstance.tname = attr_name
- self._props[attr_name] = propinstance.validate(
- propinstance.default)
-
- def __getattribute__(self, name):
- """
- Process the 'fake' read of properties in the appropriate instance
- container. Pass 'real' attributes as usual.
- """
- try:
- props = object.__getattribute__(self, "_props")
- except AttributeError:
- # necessary for deepcopy as order of init can't be guaranteed
- object.__setattr__(self, "_props", {})
- props = object.__getattribute__(self, "_props")
-
- try:
- # try gettin value from property storage
- # if it's not in the map, it's not a property or its default wasn't
- # set at initialization.
- return props[name]
- except KeyError:
- return object.__getattribute__(self, name)
-
- def __setattr__(self, name, value):
- """
- Process the 'fake' write of properties in the appropriate instance
- container. Pass 'real' attributes as usual.
-
- @param name the name of the property
- @param value the value to assign to name
- @return the setted value
- """
- props = object.__getattribute__(self, "_props")
- try:
- # We attempt to get the property object with __getattribute__
- # to work through inheritance and benefit of the MRO.
- return props.__setitem__(name,
- object.__getattribute__(self, name).validate(value))
- except AttributeError:
- return object.__setattr__(self, name, value)
-
- def get_properties(self):
- """
- Return the list of property names.
- """
- return object.__getattribute__(self, "_props").keys()
-
-class TutoriusProperty(object):
- """
- The base class for all actions' properties. The interface is the following :
-
- value : the value of the property
-
- type : the type of the property
-
- get_contraints() : the constraints inserted on this property. They define
- what is acceptable or not as values.
- """
- def __init__(self):
- super(TutoriusProperty, self).__init__()
- self.type = None
- self._constraints = None
- self.default = None
-
- def get_constraints(self):
- """
- Returns the list of constraints associated to this property.
- """
- if self._constraints is None:
- self._constraints = []
- for i in dir(self):
- typ = getattr(self, i)
- if isinstance(typ, Constraint):
- self._constraints.append(i)
- return self._constraints
-
- def validate(self, value):
- """
- Validates the value of the property. If the value does not respect
- the constraints on the property, this method will raise an exception.
-
- The exception should be of the type related to the constraint that
- failed. E.g. When a int is to be set with a value that
- """
- for constraint_name in self.get_constraints():
- constraint = getattr(self, constraint_name)
- constraint.validate(value)
- return value
-
-class TAddonListProperty(TutoriusProperty):
- """
- Stores an addon component list as a property.
- The purpose of this class is to allow correct mapping of properties
- through encapsulated hierarchies.
- """
- pass
-
-
- def get_constraints(self):
- """
- Returns the list of constraints associated to this property.
- """
- if self._constraints is None:
- self._constraints = []
- for i in dir(self):
- typ = getattr(self, i)
- if isinstance(typ, Constraint):
- self._constraints.append(i)
- return self._constraints
-
-class TIntProperty(TutoriusProperty):
- """
- Represents an integer. Can have an upper value limit and/or a lower value
- limit.
- """
-
- def __init__(self, value, lower_limit=None, upper_limit=None):
- TutoriusProperty.__init__(self)
- self.type = "int"
- self.upper_limit = UpperLimitConstraint(upper_limit)
- self.lower_limit = LowerLimitConstraint(lower_limit)
-
- self.default = self.validate(value)
-
-class TFloatProperty(TutoriusProperty):
- """
- Represents a floating point number. Can have an upper value limit and/or
- a lower value limit.
- """
- def __init__(self, value, lower_limit=None, upper_limit=None):
- TutoriusProperty.__init__(self)
- self.type = "float"
-
- self.upper_limit = UpperLimitConstraint(upper_limit)
- self.lower_limit = LowerLimitConstraint(lower_limit)
-
- self.default = self.validate(value)
-
-class TStringProperty(TutoriusProperty):
- """
- Represents a string. Can have a maximum size limit.
- """
- def __init__(self, value, size_limit=None):
- TutoriusProperty.__init__(self)
- self.type = "string"
- self.size_limit = MaxSizeConstraint(size_limit)
-
- self.default = self.validate(value)
-
-class TArrayProperty(TutoriusProperty):
- """
- Represents an array of properties. Can have a maximum number of element
- limit, but there are no constraints on the content of the array.
- """
- def __init__(self, value, min_size_limit=None, max_size_limit=None):
- TutoriusProperty.__init__(self)
- self.type = "array"
- self.max_size_limit = MaxSizeConstraint(max_size_limit)
- self.min_size_limit = MinSizeConstraint(min_size_limit)
- self.default = self.validate(value)
-
-class TColorProperty(TutoriusProperty):
- """
- Represents a RGB color with 3 8-bit integer values.
-
- The value of the property is the array [R, G, B]
- """
- def __init__(self, red=None, green=None, blue=None):
- TutoriusProperty.__init__(self)
- self.type = "color"
-
- self.color_constraint = ColorConstraint()
-
- self._red = red or 0
- self._green = green or 0
- self._blue = blue or 0
-
- self.default = self.validate([self._red, self._green, self._blue])
-
-class TFileProperty(TutoriusProperty):
- """
- Represents a path to a file on the disk.
- """
- def __init__(self, path):
- """
- Defines the path to an existing file on disk file.
-
- For now, the path may be relative or absolute, as long as it exists on
- the local machine.
- TODO : Make sure that we have a file scheme that supports distribution
- on other computers (LP 355197)
- """
- TutoriusProperty.__init__(self)
-
- self.type = "file"
-
- self.file_constraint = FileConstraint()
-
- self.default = self.validate(path)
-
-class TEnumProperty(TutoriusProperty):
- """
- Represents a value in a given enumeration. This means that the value will
- always be one in the enumeration and nothing else.
-
- """
- def __init__(self, value, accepted_values):
- """
- Creates the enumeration property.
-
- @param value The initial value of the enum. Must be part of
- accepted_values
- @param accepted_values A list of values that the property can take
- """
- TutoriusProperty.__init__(self)
-
- self.type = "enum"
-
- self.enum_constraint = EnumConstraint(accepted_values)
-
- self.default = self.validate(value)
-
-class TBooleanProperty(TutoriusProperty):
- """
- Represents a True or False value.
- """
- def __init__(self, value=False):
- TutoriusProperty.__init__(self)
-
- self.type = "boolean"
-
- self.boolean_constraint = BooleanConstraint()
-
- self.default = self.validate(value)
-
-class TUAMProperty(TutoriusProperty):
- """
- Represents a widget of the interface by storing its UAM.
- """
- # TODO : Pending UAM check-in (LP 355199)
- pass
-
-class TAddonProperty(TutoriusProperty):
- """
- Reprensents an embedded tutorius Addon Component (action, trigger, etc.)
- The purpose of this class is to flag the container for proper
- serialization, as the contained object can't be directly dumped to text
- for its attributes to be saved.
- """
- class NullAction(TPropContainer):
- def do(self): pass
- def undo(self): pass
-
- def __init__(self):
- super(TAddonProperty, self).__init__()
- self.type = "addon"
- self.default = self.NullAction()
-
- def validate(self, value):
- if isinstance(value, TPropContainer):
- return super(TAddonProperty, self).validate(value)
- raise ValueError("Expected TPropContainer instance as TaddonProperty value")
-
-class TAddonListProperty(TutoriusProperty):
- """
- Reprensents an embedded tutorius Addon List Component.
- See TAddonProperty
- """
- def __init__(self):
- super(TAddonProperty, self).__init__()
- self.type = "addonlist"
- self.default = []
-
-
diff --git a/src/sugar/tutorius/services.py b/src/sugar/tutorius/services.py
deleted file mode 100644
index 9ed2e50..0000000
--- a/src/sugar/tutorius/services.py
+++ /dev/null
@@ -1,69 +0,0 @@
-# Copyright (C) 2009, Tutorius.org
-# Copyright (C) 2009, Vincent Vinet <vince.vinet@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
-"""
-Services
-
-This module supplies services to be used by States, FSMs, Actions and Filters.
-
-Services provided are:
--Access to the running activity
--Access to the running tutorial
-"""
-
-
-class ObjectStore(object):
- #Begin Singleton code
- instance=None
- def __new__(cls):
- if not ObjectStore.instance:
- ObjectStore.instance = ObjectStore.__ObjectStore()
-
- return ObjectStore.instance
-
- #End Singleton code
- class __ObjectStore(object):
- """
- The Object Store is a singleton class that allows access to
- the current runnign activity and tutorial.
- """
- def __init__(self):
- self._activity = None
- self._tutorial = None
- #self._fsm_path = []
-
- def set_activity(self, activity):
- """Setter for activity"""
- self._activity = activity
-
- def get_activity(self):
- """Getter for activity"""
- return self._activity
-
- activity = property(fset=set_activity,fget=get_activity,doc="activity")
-
- def set_tutorial(self, tutorial):
- """Setter for tutorial"""
- self._tutorial = tutorial
-
- def get_tutorial(self):
- """Getter for tutorial"""
- return self._tutorial
-
- tutorial = property(fset=set_tutorial,fget=get_tutorial,doc="tutorial")
-
- __doc__ = __ObjectStore.__doc__
-
diff --git a/src/sugar/tutorius/temp.py b/src/sugar/tutorius/temp.py
deleted file mode 100644
index 6c7fb85..0000000
--- a/src/sugar/tutorius/temp.py
+++ /dev/null
@@ -1 +0,0 @@
-Ex GUID : 79521158-22b4-11de-8cfa-000c293a027a \ No newline at end of file
diff --git a/src/sugar/tutorius/tests/actiontests.py b/src/sugar/tutorius/tests/actiontests.py
deleted file mode 100644
index 4e126b3..0000000
--- a/src/sugar/tutorius/tests/actiontests.py
+++ /dev/null
@@ -1,206 +0,0 @@
-# Copyright (C) 2009, Tutorius.org
-# Copyright (C) 2009, Michael Janelle-Montcalm <michael.jmontcalm@gmail.com>
-# Copyright (C) 2009, Vincent Vinet <vince.vinet@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
-"""
-Action tests
-
-The behavior of the actions must be tested here.
-"""
-
-import unittest
-import gtk
-
-from sugar.tutorius import addon
-from sugar.tutorius.actions import *
-from sugar.tutorius.services import ObjectStore
-
-test_props = {"prop_a":8, "prop_b":3, "prop_c":"Hi"}
-
-class PropertyAction(Action):
- prop_a = TIntProperty(test_props["prop_a"])
- prop_b = TIntProperty(test_props["prop_b"])
- prop_c = TStringProperty(test_props["prop_c"])
- def __init__(self, na):
- Action.__init__(self)
-
-def has_function(obj, function_name):
- """
- Checks whether the object has a function by that name.
- """
- if hasattr(obj, function_name) and hasattr(obj.__getattribute__(function_name), "__call__"):
- return True
- return False
-
-class PropsTest(unittest.TestCase):
- def test_get_properties(self):
- act = PropertyAction(8)
-
- assert act.get_properties() == test_props.keys(), "Action does not contain property 'a'"
-
- for prop_name in act.get_properties():
- assert getattr(act, prop_name) == test_props[prop_name], "Wrong initial value for property %s : %s"%(prop_name,str(getattr(act, prop_name)))
-
-class DialogMessageTest(unittest.TestCase):
- def setUp(self):
- self.dial = addon.create('DialogMessage', "Message text", [200, 300])
-
- def test_properties(self):
- assert self.dial.message == "Message text", "Wrong start value for the message"
-
- assert self.dial.position == [200, 300], "Wrong start value for the position"
-
-class BubbleMessageTest(unittest.TestCase):
- def setUp(self):
- self.bubble = addon.create('BubbleMessage', message="Message text", pos=[200, 300], tailpos=[-15, -25])
-
- def test_properties(self):
- props = self.bubble.get_properties()
-
- assert "message" in props, 'No message property of BubbleMessage'
-
- assert "position" in props, 'No position property in BubbleMessage'
-
- assert "tail_pos" in props, 'No tail position property in BubbleMessage'
-
-
-class CountAction(Action):
- """
- This action counts how many times it's do and undo methods get called
- """
- def __init__(self):
- Action.__init__(self)
- self.do_count = 0
- self.undo_count = 0
-
- def do(self):
- self.do_count += 1
-
- def undo(self):
- self.undo_count += 1
-
-
-class BaseActionTests(unittest.TestCase):
- def test_do_unimplemented(self):
- act = Action()
- try:
- act.do()
- assert False, "do() should trigger a NotImplemented"
- except NotImplementedError:
- assert True, "do() should trigger a NotImplemented"
-
- def test_undo(self):
- act = Action()
- act.undo()
- assert True, "undo() should never fail on the base action"
-
-
-class OnceWrapperTests(unittest.TestCase):
- def test_onceaction_toggle(self):
- """
- Validate that the OnceWrapper wrapper works properly using the
- CountAction
- """
- act = CountAction()
- wrap = OnceWrapper(act)
-
- assert act.do_count == 0, "do() should not have been called in __init__()"
- assert act.undo_count == 0, "undo() should not have been called in __init__()"
-
- wrap.undo()
-
- assert act.undo_count == 0, "undo() should not be called if do() has not been called"
-
- wrap.do()
- assert act.do_count == 1, "do() should have been called once"
-
- wrap.do()
- assert act.do_count == 1, "do() should have been called only once"
-
- wrap.undo()
- assert act.undo_count == 1, "undo() should have been called once"
-
- wrap.undo()
- assert act.undo_count == 1, "undo() should have been called only once"
-
-class ChainTester(Action):
- def __init__(self, witness):
- Action.__init__(self)
- self._witness = witness
-
- def do(self, **kwargs):
- self._witness.append([self,"do"])
-
- def undo(self):
- self._witness.append([self,"undo"])
-
-class ChainActionTest(unittest.TestCase):
- """Tester for ChainAction"""
- def test_empty(self):
- """If the expected empty behavior (do nothing) changes
- and starts throwing exceptions, this will flag it"""
- a = ChainAction()
- a.do()
- a.undo()
-
- def test_order(self):
- witness = []
- first = ChainTester(witness)
- second = ChainTester(witness)
-
- c = ChainAction(first, second)
- assert witness == [], "Actions should not be triggered on init"""
- c.do()
-
- assert witness[0][0] is first, "First triggered action must be 'first'"
- assert witness[0][1] is "do", "Action do() should be triggered"
-
- assert witness[1][0] is second, "second triggered action must be 'second'"
- assert witness[1][1] is "do", "Action do() should be triggered"
-
- assert len(witness) is 2, "Two actions should give 2 do's"
-
- #empty the witness list
- while len(witness):
- rm = witness.pop()
-
- c.undo()
- assert witness[1][0] is first, "second triggered action must be 'first'"
- assert witness[1][1] is "undo", "Action undo() should be triggered"
-
- assert witness[0][0] is second, "first triggered action must be 'second'"
- assert witness[0][1] is "undo", "Action undo() should be triggered"
-
- assert len(witness) is 2, "Two actions should give 2 undo's"
-
-class DisableWidgetActionTests(unittest.TestCase):
- def test_disable(self):
- btn = gtk.Button()
- ObjectStore().activity = btn
- btn.set_sensitive(True)
-
- assert btn.props.sensitive is True, "Callback should have been called"
-
- act = DisableWidgetAction("0")
- assert btn.props.sensitive is True, "Callback should have been called again"
- act.do()
- assert btn.props.sensitive is False, "Callback should not have been called again"
- act.undo()
- assert btn.props.sensitive is True, "Callback should have been called again"
-
-if __name__ == "__main__":
- unittest.main()
-
diff --git a/src/sugar/tutorius/tests/bundlertests.py b/src/sugar/tutorius/tests/bundlertests.py
deleted file mode 100644
index 8da2310..0000000
--- a/src/sugar/tutorius/tests/bundlertests.py
+++ /dev/null
@@ -1,65 +0,0 @@
-# Copyright (C) 2009, Tutorius.org
-# Copyright (C) 2009, Charles-Etienne Carriere <iso.swiffer@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
-"""
-Bundler tests
-
-This module contains all the tests for the storage mecanisms for tutorials
-This mean testing savins and loading tutorial, .ini file management and
-adding ressources to tutorial
-"""
-
-import unittest
-import os
-import uuid
-
-from sugar.tutorius import bundler
-
-class TutorialBundlerTests(unittest.TestCase):
-
- def setUp(self):
-
- #generate a test GUID
- self.test_guid = uuid.uuid1()
- self.guid_path = os.path.join(bundler._get_store_root(),str(self.test_guid))
- os.mkdir(self.guid_path)
-
- self.ini_file = os.path.join(self.guid_path, "meta.ini")
-
- f = open(self.ini_file,'w')
- f.write("[GENERAL_METADATA]")
- f.write(os.linesep)
- f.write("GUID:")
- f.write(str(self.test_guid))
- f.close()
-
- def tearDown(self):
- os.remove(self.ini_file)
- os.rmdir(self.guid_path)
-
- def test_add_ressource(self):
- bund = bundler.TutorialBundler(self.test_guid)
-
- temp_file = open("test.txt",'w')
- temp_file.write('test')
- temp_file.close()
-
- bund.add_resource("test.txt")
-
- assert os.path.exists(os.path.join(self.guid_path,"test.txt")), "add_ressource did not create the file"
-
-if __name__ == "__main__":
- unittest.main() \ No newline at end of file
diff --git a/src/sugar/tutorius/tests/constraintstests.py b/src/sugar/tutorius/tests/constraintstests.py
deleted file mode 100644
index b7b0a47..0000000
--- a/src/sugar/tutorius/tests/constraintstests.py
+++ /dev/null
@@ -1,233 +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
-
-import unittest
-
-from sugar.tutorius.constraints import *
-
-class ConstraintTest(unittest.TestCase):
- def test_base_class(self):
- cons = Constraint()
- try:
- cons.validate(1)
- assert False, "Base class should throw an assertion"
- except NotImplementedError:
- pass
-
-class ValueConstraintTest(unittest.TestCase):
- def test_limit_set(self):
- cons = ValueConstraint(12)
-
- assert cons.limit == 12
-
-class UpperLimitConstraintTest(unittest.TestCase):
- def test_empty_constraint(self):
- cons = UpperLimitConstraint(None)
- try:
- cons.validate(20)
- except UpperLimitConstraintError:
- assert False, "Empty contraint should not raise an exception"
-
- def test_validate(self):
- cons = UpperLimitConstraint(10)
-
- try:
- cons.validate(20)
- assert False, "Validation of UpperLimit(10) on 20 should raise an exception"
- except UpperLimitConstraintError:
- pass
-
- try:
- cons.validate(5)
- except UpperLimitConstraintError:
- assert True, "Validation of UpperLimit(10) on 5 should not raise an exception"
-
-class LowerLimitConstraintTest(unittest.TestCase):
- def test_empty_constraint(self):
- cons = LowerLimitConstraint(None)
- try:
- cons.validate(20)
- except LowerLimitConstraintError:
- assert False, "Empty contraint should not raise an exception"
-
- def test_validate(self):
- cons = LowerLimitConstraint(10)
-
- try:
- cons.validate(5)
- assert False, "Validation of LowerLimit(10) on 5 should raise an exception"
- except LowerLimitConstraintError:
- pass
-
- try:
- cons.validate(20)
- except LowerLimitConstraintError:
- assert True, "Validation of LowerLimit(10) on 20 should not raise an exception"
-
-class MaxSizeConstraintTest(unittest.TestCase):
- def test_empty_constraint(self):
- cons = MaxSizeConstraint(None)
- try:
- cons.validate(20)
- except MaxSizeConstraintError:
- assert False, "Empty contraint should not raise an exception"
-
- def test_validate(self):
- cons = MaxSizeConstraint(10)
-
- try:
- cons.validate(range(0, 20))
- assert False, "Validation of MaxSizeConstraint(10) on list of length 20 should raise an exception"
- except MaxSizeConstraintError:
- pass
-
- try:
- cons.validate(range(0,5))
- except MaxSizeConstraintError:
- assert True, "Validation of MaxSizeConstraint(10) on list of length 5 should not raise an exception"
-
-class MinSizeConstraintTest(unittest.TestCase):
- def test_empty_constraint(self):
- cons = MinSizeConstraint(None)
- try:
- cons.validate(20)
- except MinSizeConstraintError:
- assert False, "Empty contraint should not raise an exception"
-
- def test_validate(self):
- cons = MinSizeConstraint(10)
-
- try:
- cons.validate(range(0, 5))
- assert False, "Validation of MinSizeConstraint(10) on list of length 20 should raise an exception"
- except MinSizeConstraintError:
- pass
-
- try:
- cons.validate(range(0,20))
- except MinSizeConstraintError:
- assert True, "Validation of MinSizeConstraint(10) on list of length 5 should not raise an exception"
-
-class ColorConstraintTest(unittest.TestCase):
- def test_validate(self):
- cons = ColorConstraint()
-
- try:
- cons.validate([0, 0])
- assert False, "ColorConstraint on list of length 2 should raise an exception"
- except ColorArraySizeError:
- pass
- except ColorConstraintError:
- assert False, "ColorConstraint threw the wrong type of error"
-
- try:
- cons.validate([0, 0, "str"])
- assert False, "ColorConstraint on with non-integers values should raise an exception"
- except ColorTypeError:
- pass
- except ColorConstraintError:
- assert False, "ColorConstraint threw the wrong type of error"
-
- try:
- cons.validate([0, "str", 0])
- assert False, "ColorConstraint on with non-integers values should raise an exception"
- except ColorTypeError:
- pass
- except ColorConstraintError:
- assert False, "ColorConstraint threw the wrong type of error"
-
- try:
- cons.validate(["str", 0, 0])
- assert False, "ColorConstraint on with non-integers values should raise an exception"
- except ColorTypeError:
- pass
- except ColorConstraintError:
- assert False, "ColorConstraint threw the wrong type of error"
-
- try:
- cons.validate([1, 2, 300])
- assert False, "ColorConstraint on with non-integers values should raise an exception"
- except ColorValueError:
- pass
- except ColorConstraintError:
- assert False, "ColorConstraint threw the wrong type of error"
-
- try:
- cons.validate([1, -100, 30])
- assert False, "ColorConstraint on with non-integers values should raise an exception"
- except ColorValueError:
- pass
- except ColorConstraintError:
- assert False, "ColorConstraint threw the wrong type of error"
-
- try:
- cons.validate([999999, 2, 300])
- assert False, "ColorConstraint on with non-integers values should raise an exception"
- except ColorValueError:
- pass
- except ColorConstraintError:
- assert False, "ColorConstraint threw the wrong type of error"
-
- try:
- cons.validate([12, 23, 34])
- except LowerLimitConstraintError:
- assert True, "ColorConstraint on expected input should not raise an exception"
-
-class BooleanConstraintTest(unittest.TestCase):
- def test_validate(self):
- cons = BooleanConstraint()
-
- cons.validate(True)
- cons.validate(False)
-
- try:
- cons.validate(18)
- assert False, "Setting integer on constraint should raise an error"
- except BooleanConstraintError:
- pass
- except:
- assert False, "Wrong exception type raised when setting wrong type"
-
-class EnumConstraintTest(unittest.TestCase):
- def test_validate(self):
- cons = EnumConstraint([1,2,3,7,8,9, "ex"])
-
- cons.validate(8)
-
- cons.validate("ex")
-
- try:
- cons.validate(4)
- assert False, "There should be an exception on setting a value out of the enum"
- except EnumConstraintError:
- pass
- except:
- assert False, "Wrong exception type thrown"
-
-class FileConstraintTest(unittest.TestCase):
- def test_validate(self):
- cons = FileConstraint()
-
- cons.validate("run-tests.py")
-
- try:
- cons.validate("unknown/file.py")
- assert False, "Non-existing file check should throw an exception"
- except FileConstraintError:
- pass
-
-if __name__ == "__main__":
- unittest.main() \ No newline at end of file
diff --git a/src/sugar/tutorius/tests/coretests.py b/src/sugar/tutorius/tests/coretests.py
deleted file mode 100644
index eadea01..0000000
--- a/src/sugar/tutorius/tests/coretests.py
+++ /dev/null
@@ -1,597 +0,0 @@
-# Copyright (C) 2009, Tutorius.org
-# Copyright (C) 2009, Michael Janelle-Montcalm <michael.jmontcalm@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
-"""
-Core Tests
-
-This module contains all the tests that pertain to the usage of the Tutorius
-Core. This means that the Event Filters, the Finite State Machine and all the
-related elements and interfaces are tested here.
-
-Usage of actions and event filters is tested, but not the concrete actions
-and event filters. Those are in their separate test module
-
-"""
-
-import unittest
-
-import logging
-from sugar.tutorius.actions import Action, OnceWrapper, ClickAction, TypeTextAction
-from sugar.tutorius.core import *
-from sugar.tutorius.filters import *
-
-
-from actiontests import CountAction
-
-# Helper classes to help testing
-class SimpleTutorial(Tutorial):
- """
- Fake tutorial
- """
- def __init__(self, start_name="INIT"):
- #Tutorial.__init__(self, "Simple Tutorial", None)
- self.current_state_name = start_name
- self.activity = "TODO : This should be an activity"
-
- def set_state(self, name):
- self.current_state_name = name
-
-class TutorialWithFSM(Tutorial):
- """
- Fake tutorial, but associated with a FSM.
- """
- def __init__(self, start_name="INIT", fsm=None):
- Tutorial.__init__(self, start_name, fsm)
- self.activity = activity.Activity()
-
-class TrueWhileActiveAction(Action):
- """
- This action's active member is set to True after a do and to False after
- an undo.
-
- Used to verify that a State correctly triggers the do and undo actions.
- """
- def __init__(self):
- Action.__init__(self)
- self.active = False
-
- def do(self):
- self.active = True
-
- def undo(self):
- self.active = False
-
-class ClickableWidget():
- """
- This class fakes a widget with a clicked() method
- """
- def __init__(self):
- self.click_count = 0
-
- def clicked(self):
- self.click_count += 1
-
-class FakeTextEntry():
- """
- This class fakes a widget with an insert_text() method
- """
- def __init__(self):
- self.text_lines = []
- self.last_entered_line = ""
- self.displayed_text = ""
-
- def insert_text(self, text, index):
- self.last_entered_line = text
- self.text_lines.append(text)
- self.displayed_text = self.displayed_text[0:index] + text + self.displayed_text[index+1:]
-
-class FakeParentWidget():
- """
- This class fakes a widet container, it implements the get_children() method
- """
- def __init__(self):
- self._children = []
-
- def add_child(self, child):
- self._children.append(child)
-
- def get_children(self):
- return self._children
-
-
-
-
-class TriggerEventFilter(EventFilter):
- """
- This event filter can be triggered by simply calling its do_callback function.
-
- Used to fake events and see the effect on the FSM.
- """
- def __init__(self, next_state):
- EventFilter.__init__(self, next_state)
- self.toggle_on_callback = False
-
- def install_handlers(self, callback, **kwargs):
- """
- Forsakes the incoming callback function and just set the inner one.
- """
- self._callback = self._inner_cb
-
- def _inner_cb(self, event_filter):
- self.toggle_on_callback = not self.toggle_on_callback
-
-class FakeEventFilter(TriggerEventFilter):
- """
- This is a fake event that is connected to the tutorial.
-
- The difference between this one and the TriggerEventFilter is that the
- tutorial's set_state will be called on the callback.
-
- Do not forget to add the do_callback() after creating the object.
- """
- def set_tutorial(self, tutorial):
- self.tutorial = tutorial
-
- def _inner_cb(self, event_filter):
- self.toggle_on_callback = not self.toggle_on_callback
- self.tutorial.set_state(event_filter.get_next_state())
-
-
-class ClickActionTests(unittest.TestCase):
- """
- Test class for click action
- """
- def test_do_action(self):
- activity = FakeParentWidget()
- widget = ClickableWidget()
- activity.add_child(widget)
- ObjectStore().activity = activity
-
- action = ClickAction("0.0")
-
- assert widget == ObjectStore().activity.get_children()[0],\
- "The clickable widget isn't reachable from the object store \
- the test cannot pass"
-
- action.do()
-
- assert widget.click_count == 1, "clicked() should have been called by do()"
-
- action.do()
-
- assert widget.click_count == 2, "clicked() should have been called by do()"
-
- def test_undo(self):
- activity = FakeParentWidget()
- widget = ClickableWidget()
- activity.add_child(widget)
- ObjectStore().activity = activity
-
- action = ClickAction("0.0")
-
- assert widget == ObjectStore().activity.get_children()[0],\
- "The clickable widget isn't reachable from the object store \
- the test cannot pass"
-
- action.undo()
-
- #There is no undo for this action so the test should not fail
- assert True
-
-
-
-class TypeTextActionTests(unittest.TestCase):
- """
- Test class for type text action
- """
- def test_do_action(self):
- activity = FakeParentWidget()
- widget = FakeTextEntry()
- activity.add_child(widget)
- ObjectStore().activity = activity
-
- test_text = "This is text"
-
-
- action = TypeTextAction("0.0", test_text)
-
- assert widget == ObjectStore().activity.get_children()[0],\
- "The clickable widget isn't reachable from the object store \
- the test cannot pass"
-
- action.do()
-
- assert widget.last_entered_line == test_text, "insert_text() should have been called by do()"
-
- action.do()
-
- assert widget.last_entered_line == test_text, "insert_text() should have been called by do()"
- assert len(widget.text_lines) == 2, "insert_text() should have been called twice"
-
- def test_undo(self):
- activity = FakeParentWidget()
- widget = FakeTextEntry()
- activity.add_child(widget)
- ObjectStore().activity = activity
-
- test_text = "This is text"
-
-
- action = TypeTextAction("0.0", test_text)
-
- assert widget == ObjectStore().activity.get_children()[0],\
- "The clickable widget isn't reachable from the object store \
- the test cannot pass"
-
- action.undo()
-
- #There is no undo for this action so the test should not fail
- assert True
-
-# State testing class
-class StateTest(unittest.TestCase):
- """
- This class has to test the State interface as well as the expected
- functionality.
- """
-
- def test_action_toggle(self):
- """
- Validate that the actions are properly done on setup and undone on
- teardown.
-
- Pretty awesome.
- """
- act = TrueWhileActiveAction()
-
- state = State("action_test", action_list=[act])
-
- assert act.active == False, "Action is not initialized properly"
-
- state.setup()
-
- assert act.active == True, "Action was not triggered properly"
-
- state.teardown()
-
- assert act.active == False, "Action was not undone properly"
-
- def test_event_filter(self):
- """
- Tests the fact that the event filters are correctly installed on setup
- and uninstalled on teardown.
- """
- event_filter = TriggerEventFilter("second_state")
-
- state = State("event_test", event_filter_list=[event_filter])
- state.set_tutorial(SimpleTutorial())
-
- assert event_filter.toggle_on_callback == False, "Wrong init of event_filter"
- assert event_filter._callback == None, "Event filter has a registered callback before installing handlers"
-
- state.setup()
-
- assert event_filter._callback != None, "Event filter did not register callback!"
-
- # 'Trigger' the event - This is more like a EventFilter test.
- event_filter.do_callback()
-
- assert event_filter.toggle_on_callback == True, "Event filter did not execute callback"
-
- state.teardown()
-
- assert event_filter._callback == None, "Event filter did not remove callback properly"
-
- def test_warning_set_tutorial_twice(self):
- """
- Calls set_tutorial twice and expects a warning on the second.
- """
- state = State("start_state")
- tut = SimpleTutorial("First")
- tut2 = SimpleTutorial("Second")
-
- state.set_tutorial(tut)
-
- try:
- state.set_tutorial(tut2)
- assert False, "No RuntimeWarning was raised on second set_tutorial"
- except :
- pass
-
- def test_add_action(self):
- """
- Tests on manipulating the actions inside a state.
- """
- state = State("INIT")
-
- act1 = CountAction()
- act2 = CountAction()
- act3 = CountAction()
-
- # Try to add the actions
- assert state.add_action(act1), "Could not add the first action"
- assert state.add_action(act2), "Could not add the second action"
- assert state.add_action(act3), "Could not add the third action"
-
- # Try to add a second time an action that was already inserted
- assert state.add_action(act1) == False, "Not supposed to insert an action twice"
-
- # Fetch the associated actions
- actions = state.get_action_list()
-
- # Make sure all the actions are present in the state
- assert act1 in actions and act2 in actions and act3 in actions,\
- "The actions were not properly inserted in the state"
-
- # Clear the list
- state.clear_actions()
-
- # Make sure the list of actions is empty now
- assert len(state.get_action_list()) == 0, "Clearing of actions failed"
-
- def test_add_event_filter(self):
- state = State("INIT")
-
- event1 = TriggerEventFilter("s")
- event2 = TriggerEventFilter("t")
- event3 = TriggerEventFilter("r")
-
- # Insert the event filters
- assert state.add_event_filter(event1), "Could not add event filter 1"
- assert state.add_event_filter(event2), "Could not add event filter 2"
- assert state.add_event_filter(event3), "Could not add event filter 3"
-
- # Make sure we cannot insert an event twice
- assert state.add_event_filter(event1) == False, "Could add twice the event filter"
-
- # Get the list of event filters
- event_filters = state.get_event_filter_list()
-
- assert event1 in event_filters and event2 in event_filters and event3 in event_filters, \
- "The event filters were not all added inside the state"
-
- # Clear the list
- state.clear_event_filters()
-
- assert len(state.get_event_filter_list()) == 0, \
- "Could not clear the event filter list properly"
-
-class FSMTest(unittest.TestCase):
- """
- This class needs to text the interface and functionality of the Finite
- State Machine.
- """
-
- def test_sample_usage(self):
- act_init = TrueWhileActiveAction()
- act_second = TrueWhileActiveAction()
-
- event_init = FakeEventFilter("SECOND")
-
- content = {
- "INIT": State("INIT", action_list=[act_init],event_filter_list=[event_init]),
- "SECOND": State("SECOND", action_list=[act_second])
- }
-
- fsm = FiniteStateMachine("SampleUsage", state_dict=content)
-
- assert fsm is not None, "Unable to create FSM"
-
- tut = Tutorial("SampleUsageTutorial", fsm)
-
- tut.attach(None)
- event_init.set_tutorial(tut)
-
- assert fsm.current_state.name == "INIT", "Unable to set state to initial state"
-
- assert act_init.active, "FSM did not call the state's action DO properly"
-
- # Trigger the event of the INIT state
- event_init.do_callback()
-
- assert act_init.active == False, "FSM did not teardown INIT properly"
-
- assert fsm.current_state.name == "SECOND", "FSM did not switch to SECOND state"
-
- assert act_second.active == True, "FSM did not setup SECOND properly"
-
- tut.detach()
-
- assert act_second.active == False, "FSM did not teardown SECOND properly"
-
-
- def test_state_insert(self):
- """
- This is a simple test to insert, then find a state.
- """
- st1 = State("FakeState")
-
- fsm = FiniteStateMachine("StateInsertTest")
-
- fsm.add_state(st1)
-
- inserted_state = fsm.get_state_by_name(st1.name)
-
- assert inserted_state is st1, "Inserting, then fetching a state did not work"
-
- # Make sure we cannot insert it twice
- try :
- fsm.add_state(st1)
- assert False, "No error raised on addition of an already present state"
- except KeyError:
- pass
-
- def test_state_find_by_name(self):
- """
- Tests the interface for fetching a state by name.
- - Basic functionnality
- - Non-existent state
- """
-
- st1 = State("INIT")
-
- st2 = State("second")
-
- fsm = FiniteStateMachine("StateFindTest")
-
- fsm.add_state(st1)
- fsm.add_state(st2)
-
- # Test the fetch by name
- fetched_st1 = fsm.get_state_by_name(st1.name)
-
- assert fetched_st1 is st1, "Fetched state is not the same as the inserted one"
-
- fetched_st2 = fsm.get_state_by_name(st2.name)
-
- assert fetched_st2 is st2, "Fetched state is not the same as the inserted one"
-
- try:
- fsm.get_state_by_name("no such state")
- assert False, "Did not get a KeyError on non-existing key search"
- except KeyError:
- pass
- except Exception:
- assert False, "Did not get the right error on non-existing key search"
-
- def test_state_removal(self):
- """
- This test removes a state from the FSM. It also verifies that the links
- from other states going into the removed state are gone.
- """
- st1 = State("INIT", event_filter_list=[TriggerEventFilter("second")])
- st2 = State("second", event_filter_list=[TriggerEventFilter("third")])
- st3 = State("third", event_filter_list=[TriggerEventFilter("second")])
-
- fsm = FiniteStateMachine("StateRemovalTest")
-
- fsm.add_state(st1)
- fsm.add_state(st2)
- fsm.add_state(st3)
-
- # First tests - Removing a non-existing state and make sure we get a
- # KeyError
- try:
- fsm.remove_state("Non-existing")
- assert False, "Removing a non-existing state did not throw a KeyError"
- except KeyError:
- pass
- except Exception:
- assert False, "Removing a non-existing state dit not throw the right kind of exception"
-
- # Now try removing the second state
- fsm.remove_state("second")
-
- # Make sure it cannot be fetched
- try :
- fetched_state = fsm.get_state_by_name("second")
- assert False, "The supposedly removed state is still present in the FSM"
- except KeyError:
- pass
-
- # Make sure that there is no link to the removed state in the rest
- # of the FSM
- assert "second" not in fsm.get_following_states("INIT"),\
- "The link to second from INIT still exists after removal"
-
- assert "second" not in fsm.get_following_states("third"),\
- "The link to second from third still exists after removal"
-
- def test_set_same_state(self):
- fsm = FiniteStateMachine("Set same state")
-
- st1 = State("INIT")
- st1.add_action(CountAction())
-
- fsm.add_state(st1)
-
- tut = SimpleTutorial()
-
- fsm.set_tutorial(tut)
-
- fsm.set_state("INIT")
-
- assert fsm.get_state_by_name("INIT").get_action_list()[0].do_count == 1, \
- "The action was not triggered on 'INIT'"
-
- fsm.set_state("INIT")
-
- do_count = fsm.get_state_by_name("INIT").get_action_list()[0].do_count
- assert fsm.get_state_by_name("INIT").get_action_list()[0].do_count == 1, \
- "The action was triggered a second time, do_count = %d"%do_count
-
- undo_count = fsm.get_state_by_name("INIT").get_action_list()[0].undo_count
- assert fsm.get_state_by_name("INIT").get_action_list()[0].undo_count == 0,\
- "The action has been undone unappropriately, undo_count = %d"%undo_count
-
-class FSMExplorationTests(unittest.TestCase):
- def setUp(self):
- self.buildFSM()
-
- def buildFSM(self):
- """
- Create a sample FSM to play with in the rest of the tests.
- """
- st1 = State("INIT")
- st1.add_action(CountAction())
- st1.add_event_filter(TriggerEventFilter("Second"))
- st1.add_event_filter(TriggerEventFilter("Third"))
-
- st2 = State("Second")
- st2.add_action(TrueWhileActiveAction())
- st2.add_event_filter(TriggerEventFilter("Third"))
- st2.add_event_filter(TriggerEventFilter("Fourth"))
-
- st3 = State("Third")
- st3.add_action(CountAction())
- st3.add_action(TrueWhileActiveAction())
-
- self.fsm = FiniteStateMachine("ExplorationTestingMachine")
- self.fsm.add_state(st1)
- self.fsm.add_state(st2)
- self.fsm.add_state(st3)
-
- def validate_following_states(self, in_name, out_name_list):
- nextStates = self.fsm.get_following_states(in_name)
- assert list(nextStates).sort() == list(out_name_list).sort(), \
- "The following states for %s are wrong : got %s"%\
- (in_name, str(nextStates))
-
- def validate_previous_states(self, in_name, out_name_list):
- prevStates = self.fsm.get_previous_states(in_name)
- assert list(prevStates).sort() == list(out_name_list).sort(), \
- "The following states for %s are wrong : got %s"%\
- (in_name, str(prevStates))
-
- def test_get_following_states(self):
- self.validate_following_states("INIT", ('Second', 'Third'))
-
- self.validate_following_states("Second", ("Third", "Fourth"))
-
- self.validate_following_states("Third", ())
-
- def test_get_previous_states(self):
- self.validate_previous_states("INIT", ())
-
- self.validate_previous_states("Second", ("INIT"))
-
- self.validate_previous_states("Third", ("INIT", "Second"))
-
- self.validate_previous_states("Fourth", ("Second"))
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/src/sugar/tutorius/tests/filterstests.py b/src/sugar/tutorius/tests/filterstests.py
deleted file mode 100644
index 3e79bcc..0000000
--- a/src/sugar/tutorius/tests/filterstests.py
+++ /dev/null
@@ -1,201 +0,0 @@
-# Copyright (C) 2009, Tutorius.org
-# Copyright (C) 2009, Vincent Vinet <vince.vinet@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
-"""
-Filters Tests
-
-This module contains all the tests that pertain to the usage of the Tutorius
-Event Filters
-"""
-
-import unittest
-import time
-import gobject
-import gtk
-
-from sugar.tutorius.filters import EventFilter, TimerEvent, GtkWidgetTypeFilter
-from sugar.tutorius import addon
-from gtkutilstests import SignalCatcher
-
-class BaseEventFilterTests(unittest.TestCase):
- """Test the behavior of the Base EventFilter class"""
- def test_properties(self):
- """Test EventFilter properties"""
- e = EventFilter("NEXTSTATE")
-
- assert e.next_state == "NEXTSTATE", "next_state should have value used in constructor"
-
- e.next_state = "NEWSTATE"
-
- assert e.next_state == "NEWSTATE", "next_state should have been changed by setter"
-
-
- def test_callback(self):
- """Test the callback mechanism"""
- e = EventFilter("Next")
- s = SignalCatcher()
-
- #Trigger the do_callback, shouldn't do anything
- e.do_callback()
-
- #Install the handler
- e.install_handlers(s.callback)
-
- #Trigger the do_callback, s should receive e
- e.do_callback()
- assert s.data[0] is e
-
- s.data = None
-
- e.remove_handlers()
-
- #Trigger callback, nothing should happen again
- e.do_callback()
-
- assert s.data is None
-
-
-
-
-
-class TestTimerEvent(unittest.TestCase):
- """Tests for timer"""
- def test_timer(self):
- """Make sure timer gets called once, and only once"""
- gobject.threads_init()
- ctx = gobject.MainContext()
- main = gobject.MainLoop(ctx)
-
- e = TimerEvent("Next",1) #1 second should be enough :s
- s = SignalCatcher()
-
- e.install_handlers(s.callback)
-
- assert s.data is None, "Callback should not have been called yet"
-
- #process events
- while gtk.events_pending():
- gtk.main_iteration(block=False)
- while ctx.pending():
- ctx.iteration(may_block=False)
-
- #Wait 1.4 sec
- time.sleep(1.4)
-
- #process events
- while gtk.events_pending():
- gtk.main_iteration(block=False)
- while ctx.pending():
- ctx.iteration(may_block=False)
-
- assert not s.data is None, "Callback should have been called"
-
- s.data = None
-
- #Wait 1.4 sec
- time.sleep(1.4)
-
- #process events
- while gtk.events_pending():
- gtk.main_iteration(block=False)
- while ctx.pending():
- ctx.iteration(may_block=False)
-
- assert s.data is None, "Callback should not have been called again"
-
- def test_timer_stop(self):
- """Make sure timer can be stopped"""
- gobject.threads_init()
- ctx = gobject.MainContext()
- main = gobject.MainLoop(ctx)
-
- e = TimerEvent("Next",1) #1 second should be enough :s
- s = SignalCatcher()
-
- e.install_handlers(s.callback)
-
- assert s.data is None, "Callback should not have been called yet"
-
- #process events
- while gtk.events_pending():
- gtk.main_iteration(block=False)
- while ctx.pending():
- ctx.iteration(may_block=False)
-
- assert s.data is None, "Callback should not have been called yet"
-
- #Wait 0.5 sec
- time.sleep(0.5)
-
- e.remove_handlers()
-
- #Wait 0.5 sec
- time.sleep(0.7)
-
- #process events
- while gtk.events_pending():
- gtk.main_iteration(block=False)
- while ctx.pending():
- ctx.iteration(may_block=False)
-
- assert s.data is None, "Callback should not have been called"
-
- s.data = None
-
-
-class TestGtkWidgetEventFilter(unittest.TestCase):
- """Tests for GtkWidgetEventFilter"""
- def __init__(self,*args):
- unittest.TestCase.__init__(self,*args)
- self.top=None
- self.btn1=None
-
- def setUp(self):
- self.top = gtk.Window()
- self.btn1 = gtk.Button()
- self.top.add(self.btn1)
-
- def test_install(self):
- h = addon.create('GtkWidgetEventFilter', "Next","0","whatever")
- try:
- h.install_handlers(None)
-
- assert False, "Install handlers should have failed"
- except TypeError:
- assert True, "Install should have failed"
-
- def test_button_clicks(self):
- h = addon.create('GtkWidgetEventFilter', "Next","0.0","clicked")
- s = SignalCatcher()
-
- h.install_handlers(s.callback, activity=self.top)
-
- assert s.data is None, "no callback to call yet"
-
- self.btn1.clicked()
- assert not s.data is None, "callback should have been called"
- s.data = None
-
- h.remove_handlers()
-
- assert s.data is None, "callback must not be called again"
-
- self.btn1.clicked()
-
- assert s.data is None, "callback must not be called again"
-
-
-
diff --git a/src/sugar/tutorius/tests/gtkutilstests.py b/src/sugar/tutorius/tests/gtkutilstests.py
deleted file mode 100644
index 41634ae..0000000
--- a/src/sugar/tutorius/tests/gtkutilstests.py
+++ /dev/null
@@ -1,211 +0,0 @@
-# Copyright (C) 2009, Tutorius.org
-# Copyright (C) 2009, Simon Poirier <simpoir@gmail.com>
-# Copyright (C) 2009, Vincent Vinet <vince.vinet@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
-"""
-Gtk Utils Tests
-
-This module contains all the tests that pertain to the usage of the Tutorius
-gtkutils
-"""
-
-import unittest
-
-import logging
-import gtk, gobject
-from sugar.tutorius.gtkutils import find_widget, register_signals_numbered, register_signals, get_children
-
-class SignalCatcher(object):
- """Test class that store arguments received on it's callback method.
- Useful for testing callbacks"""
- def __init__(self):
- """Constructor"""
- self.data = None
-
- def callback(self, *args):
- """Callback function, stores argument list in self.data"""
- self.data = args
-
-def disconnect_handlers(hlist):
- """Disconnect handles in handler list. hlist must be a list of
- two-tuples (widget, handler_id)"""
- for widget, handler in hlist:
- try:
- widget.handler_disconnect(handler)
- except:
- pass
-
-class GtkUtilsTests(unittest.TestCase):
- def __init__(self,*args):
- unittest.TestCase.__init__(self,*args)
- self.widgets = {}
- self.top = None
-
- def setUp(self):
- #create hierarchy
- self.top = gtk.Window(type=gtk.WINDOW_TOPLEVEL)
- self.top.set_name("Top")
- self.widgets["top"] = {"named":"Top","numbered":"0","widget":self.top}
-
- hbox = gtk.HBox()
- self.top.add(hbox)
- hbox.show()
- self.widgets["hbox0"] = {"name":"Top.GtkHBox","numbered":"0.0","widget":hbox}
-
- btn1 = gtk.Button()
- btn1.set_name("Button1")
- hbox.pack_start(btn1)
- btn1.show()
- self.widgets["btn1"] = {"name":"Top.GtkHBox.Button1","numbered":"0.0.0","widget":btn1}
-
- btn2 = gtk.Button()
- btn2.set_name("Button2")
- hbox.pack_start(btn2)
- btn2.show()
- self.widgets["btn2"] = {"name":"Top.GtkHBox.Button2","numbered":"0.0.1","widget":btn2}
-
- vbox = gtk.VBox()
- vbox.set_name("VBox1")
- hbox.pack_start(vbox)
- hbox.show()
- self.widgets["vbox0"] = {"name":"Top.GtkHBox.VBox1","numbered":"0.0.2","widget":vbox}
-
- btn3 = gtk.Button()
- btn3.set_name("Button3")
- vbox.pack_start(btn3)
- btn3.show()
- self.widgets["btn3"] = {"name":"Top.GtkHBox.VBox1.Button3","numbered":"0.0.2.0","widget":btn3}
-
- btn4 = gtk.Button()
- vbox.pack_start(btn4)
- btn4.show()
- self.widgets["btn4"] = {"name":"Top.GtkHBox.VBox1.GtkButton","numbered":"0.0.2.1","widget":btn4}
-
- def tearDown(self):
- #destroy hierarchy
- self.top.destroy()
- self.top = None
- self.widgets = {}
-
- def test_named(self):
- #def register_signals(target, handler, prefix=None, max_depth=None):
- s=SignalCatcher()
-
- #Test 0 depth
- handler_list = register_signals(self.top, s.callback, max_depth=0)
-
- #remove duplicates in widget list
- widget_list = dict.fromkeys([w for w, h in handler_list]).keys()
-
- assert len(widget_list) == 1, "register_signals should not have recursed (%d objects registered)" % len(widget_list)
-
- assert widget_list[0] == self.top, "register_signals should have gotten only the top"
-
- disconnect_handlers(handler_list)
-
- #Test 2 depth
- handler_list = register_signals(self.top, s.callback, max_depth=2)
-
- #remove duplicates in widget list
- widget_list = dict.fromkeys([w for w, h in handler_list]).keys()
-
- assert len(widget_list) == 5, "expected %d objects (got %d)" % (len(widget_list), 5)
-
- disconnect_handlers(handler_list)
-
- #Test Infinite depth
- handler_list = register_signals(self.top, s.callback, max_depth=None)
-
- #remove duplicates in widget list
- widget_list = dict.fromkeys([w for w, h in handler_list]).keys()
-
- assert len(widget_list) == 7, "expected %d objects (got %d)" % (len(widget_list), 7)
-
- disconnect_handlers(handler_list)
-
-
- def test_numbered(self):
- s=SignalCatcher()
- #def register_signals_numbered(target, handler, prefix="0", max_depth=None):
-
- #Test 0 depth
- handler_list = register_signals_numbered(self.top, s.callback, max_depth=0)
-
- #remove duplicates in widget list
- widget_list = dict.fromkeys([w for w, h in handler_list]).keys()
-
- assert len(widget_list) == 1, "register_signals should not have recursed (%d objects registered)" % len(widget_list)
-
- assert widget_list[0] == self.top, "register_signals should have gotten only the top"
-
- disconnect_handlers(handler_list)
-
- #Test 1 depth
- handler_list = register_signals_numbered(self.top, s.callback, max_depth=1)
-
- #remove duplicates in widget list
- widget_list = dict.fromkeys([w for w, h in handler_list]).keys()
-
- assert len(widget_list) == 2, "expected %d objects (got %d)" % (len(widget_list), 2)
-
- disconnect_handlers(handler_list)
-
- #Test Infinite depth
- handler_list = register_signals_numbered(self.top, s.callback, max_depth=None)
-
- #remove duplicates in widget list
- widget_list = dict.fromkeys([w for w, h in handler_list]).keys()
-
- assert len(widget_list) == 7, "expected %d objects (got %d)" % (len(widget_list), 7)
-
- disconnect_handlers(handler_list)
-
-
- def test_find_widget(self):
- #Test individual values in the defined widgets
- for widget in self.widgets.values():
- f = find_widget(self.top, widget["numbered"])
- assert f is widget["widget"], "Widget %s found with path %s, expected %s" % (f, widget["numbered"], widget["widget"])
-
- #Test out of index
- f = find_widget(self.top, "0.99.1.2")
- assert f is self.top, "Should have returned top widget"
-
- def test_register_args_numbered(self):
- #Need to check the signal catcher and stuff... grreat
- while gtk.events_pending():
- gtk.main_iteration(block=False)
-
-
- def test_register_args_normal(self):
- #Need to check the signal catcher and stuff... grreat
- while gtk.events_pending():
- gtk.main_iteration(block=False)
-
- def test_notwidget(self):
- """Test the get_children function"""
- o = object()
- res = get_children(o)
-
- assert len(res) == 0, "object has no children"
-
- top_children = get_children(self.top)
- expected = [self.widgets["hbox0"]["widget"],]
- assert top_children == expected, "expected %s for top's children, got %s" % (str(expected),str(top_children))
-
-if __name__ == "__main__":
- unittest.main()
-
diff --git a/src/sugar/tutorius/tests/linear_creatortests.py b/src/sugar/tutorius/tests/linear_creatortests.py
deleted file mode 100644
index dcded57..0000000
--- a/src/sugar/tutorius/tests/linear_creatortests.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# Copyright (C) 2009, Tutorius.org
-# Greatly influenced by sugar/activity/namingalert.py
-#
-# 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
-
-from sugar.tutorius.core import *
-from sugar.tutorius.actions import *
-from sugar.tutorius.filters import *
-from sugar.tutorius.linear_creator import *
-from coretests import TriggerEventFilter
-from actiontests import CountAction
-import unittest
-
-class CreatorTests(unittest.TestCase):
-
- def test_simple_usage(self):
- creator = LinearCreator()
- fsm_name = "SimpleUsageTest"
-
- creator.set_name(fsm_name)
-
- # Generate an FSM using the steps
- creator.action(CountAction())
- creator.action(CountAction())
-
- creator.event(TriggerEventFilter("Not important"))
-
- creator.action(CountAction())
-
- creator.event(TriggerEventFilter("Not good either..."))
-
- fsm = creator.generate_fsm()
-
- # Make sure everything worked!
- assert fsm.name == fsm_name, "Name was not set properly"
-
- init_state = fsm.get_state_by_name("INIT")
-
- assert len(init_state.get_action_list()) == 2, "Creator did not insert all the actions"
-
- assert init_state.get_event_filter_list()[0].get_next_state() == "State 1" , "expected next state to be 'State 1' but got %s" % init_state.get_event_filter_list()[0].get_next_state()
-
- state1 = fsm.get_state_by_name("State 1")
-
- assert len(state1.get_action_list()) == 1, "Creator did not insert all the actions"
-
- assert state1.get_event_filter_list()[0].get_next_state() == "State 2"
-
- # Make sure we have the final state and that it's empty
- state2 = fsm.get_state_by_name("State2")
-
- assert len(state2.get_action_list()) == 0, "Creator inserted extra actions on wrong state"
-
- assert len(state2.get_event_filter_list()) == 0, "Creator assigner events to the final state"
-
- creator.action(CountAction())
-
- fsm = creator.generate_fsm()
-
- state2 = fsm.get_state_by_name("State2")
-
- assert len(state2.get_action_list()) == 1, "Creator did not add the action"
-
- assert len(state2.get_event_filter_list()) == 0, "Creator assigner events to the final state"
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/src/sugar/tutorius/tests/overlaytests.py b/src/sugar/tutorius/tests/overlaytests.py
deleted file mode 100644
index 783377c..0000000
--- a/src/sugar/tutorius/tests/overlaytests.py
+++ /dev/null
@@ -1,119 +0,0 @@
-# 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
-"""
-GUI Tests
-
-This module contains all the tests that pertain to the usage of the Tutorius
-overlay mechanism used to display objects on top of the application.
-"""
-
-import unittest
-
-import logging
-import gtk, gobject
-from sugar.tutorius.actions import Action
-import sugar.tutorius.overlayer as overlayer
-
-class CanvasDrawable(object):
- def __init__(self):
- self._no_expose = False
- self.exposition_count = 0
- def _set_no_expose(self, value):
- self._no_expose = value
- def draw_with_context(self, context):
- self.exposition_count += 1
- no_expose = property(fset=_set_no_expose)
-
-
-class OverlayerTest(unittest.TestCase):
- def test_cairodrawable_iface(self):
- """
- Quickly validates that all our cairo widgets have a minimal interface
- implemented.
- """
- drawables = [overlayer.TextBubble]
- for widget in drawables:
- for attr in filter(lambda s:s[0]!='_', dir(CanvasDrawable)):
- assert hasattr(widget, attr), \
- "%s not implementing CanvasDrawable iface"%widget.__name__
-
-
- def test_drawn(self):
- """
- Ensures a cairo widget draw method is called at least once in
- a real gui app.
- """
- win = gtk.Window(type=gtk.WINDOW_TOPLEVEL)
-
- btn = gtk.Button()
- btn.show()
- overlay = overlayer.Overlayer(btn)
- win.add(overlay)
- # let's also try to draw substitute button label
- lbl = overlayer.TextBubble("test!")
- assert lbl.label == 'test!', \
- "label property mismatch"
- btn.show()
- lbl.show()
- btn.add(lbl)
-
- lbl.no_expose = True
- assert lbl.no_expose, "wrong no_expose evaluation"
- lbl.no_expose = False
- assert not lbl.no_expose, "wrong no_expose evaluation"
-
-
- widg = gtk.Button('bo')
- widg.show()
- overlay.put(widg, 50,50)
- #widget = overlayer.TextBubble("testing msg!", tailpos=(10,-20))
- #widget.exposition_count = 0
- ## override draw method
- #def counter(ctx, self=widget):
- # self.exposition_count += 1
- # self.real_exposer(ctx)
- #widget.real_exposer = widget.draw_with_context
- #widget.draw_with_context = counter
- ## centering allows to test the blending with the label
- #overlay.put(widget, 50, 50)
- #widget.show()
- #assert widget.no_expose, \
- # "Overlay should overide exposition handling of widget"
- #assert not lbl.no_expose, \
- # "Non-overlayed cairo should expose as usual"
-
- # force widget realization
- # the child is flagged to be redrawn, the overlay should redraw too.
- win.set_default_size(100, 100)
- win.show()
-
- while gtk.events_pending():
- gtk.main_iteration(block=False)
- # visual validation: there should be 2 visible bubbles, one as label,
- # one as overlay
- import time
- time.sleep(1)
- # as x11 events are asynchronous, wait a bit before assuming it went
- # wrong.
- while gtk.events_pending():
- gtk.main_iteration(block=False)
- time.sleep(10)
- assert widget.exposition_count>0, "overlay widget should expose"
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/src/sugar/tutorius/tests/propertiestests.py b/src/sugar/tutorius/tests/propertiestests.py
deleted file mode 100644
index 46346c4..0000000
--- a/src/sugar/tutorius/tests/propertiestests.py
+++ /dev/null
@@ -1,402 +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
-
-import unittest
-
-from sugar.tutorius.constraints import *
-from sugar.tutorius.properties import *
-
-# Helper function to test the wrong types on a property, given its type
-def try_wrong_values(obj):
- typ = type(obj).prop.type
- if typ != "int":
- try:
- obj.prop = 3
- assert False, "Able to insert int value in property of type %s"%typ
- except:
- pass
-
- if typ != "float":
- try:
- obj.prop = 1.1
- assert False, "Able to insert float value in property of type %s"%typ
- except:
- pass
-
- if typ != "string":
- try:
- obj.prop = "Fake string"
- assert False, "Able to insert string value in property of type %s"%typ
- except:
- pass
-
- if typ != "array":
- try:
- obj.prop = [1, 2000, 3, 400]
- assert False, "Able to insert array value in property of type %s"%typ
- except:
- pass
-
- if typ != "color":
- try:
- obj.prop = [1,2,3]
- if typ != "array":
- assert False, "Able to insert color value in property of type %s"%typ
- except:
- pass
-
- if typ != "boolean":
- try:
- obj.prop = True
- if typ != "boolean":
- assert False, "Able to set boolean value in property of type %s"%typ
- except:
- pass
-
-class BasePropertyTest(unittest.TestCase):
- def test_base_class(self):
- class klass(TPropContainer):
- prop = TutoriusProperty()
- obj = klass()
-
- assert klass.prop.default == None, "There should not be an initial value in the base property"
-
- assert klass.prop.type == None, "There should be no type associated with the base property"
-
- assert klass.prop.get_constraints() == [], "There should be no constraints on the base property"
-
- obj.prop = 2
-
- assert obj.prop == 2, "Unable to set a value on base class"
-
-class TIntPropertyTest(unittest.TestCase):
- def test_int_property(self):
- class klass(TPropContainer):
- prop = TIntProperty(22)
- obj = klass()
-
- assert obj.prop == 22, "Could not set value on property via constructor"
-
- assert klass.prop.type == "int", "Wrong type on int property : %s" % prop.type
- cons = klass.prop.get_constraints()
- assert len(cons) == 2, "Not enough constraints on the int property"
-
- obj.prop = 12
-
- assert obj.prop == 12, "Could not set value"
-
- def test_wrong_values(self):
- class klass(TPropContainer):
- prop = TIntProperty(33)
- obj = klass()
-
- # Try setting values of other types
- try_wrong_values(obj)
-
- def test_limit_constructor(self):
- class klass(TPropContainer):
- prop = TIntProperty(22, 0, 30)
- obj = klass()
-
- try:
- obj.prop = -22
- assert False, "Assigning an out-of-range value should trigger LowerLimitConstraint"
- except LowerLimitConstraintError:
- pass
- except Exception:
- assert False, "Wrong exception triggered by assignation"
-
- try:
- obj.prop = 222
- assert False, "Assigning an out-of-range value should trigger UpperLimitConstraint"
- except UpperLimitConstraintError:
- pass
- except Exception:
- assert False, "Wrong exception triggered by assignation"
-
- def test_failing_constructor(self):
- try:
- prop = TIntProperty(100, 0, 20)
- assert False, "Creation of the property should fail."
- except UpperLimitConstraintError:
- pass
- except:
- assert False, "Wrong exception type on failed constructor"
-
- try:
- prop = TIntProperty(-100, 0, 20)
- assert False, "Creation of the property should fail."
- except LowerLimitConstraintError:
- pass
- except:
- assert False, "Wrong exception type on failed constructor"
-
-class TFloatPropertyTest(unittest.TestCase):
- def test_float_property(self):
- class klass(TPropContainer):
- prop = TFloatProperty(22)
- obj = klass()
-
- assert obj.prop == 22, "Could not set value on property via constructor"
-
- assert klass.prop.type == "float", "Wrong type on float property : %s" % klass.prop.type
- cons = klass.prop.get_constraints()
- assert len(cons) == 2, "Not enough constraints on the float property"
-
- obj.prop = 12
-
- assert obj.prop == 12, "Could not set value"
-
- def test_wrong_values(self):
- class klass(TPropContainer):
- prop = TFloatProperty(33)
- obj = klass()
-
- # Try setting values of other types
- try_wrong_values(obj)
-
- def test_limit_constructor(self):
- class klass(TPropContainer):
- prop = TFloatProperty(22.4, 0.1, 30.5223)
- obj = klass()
-
- try:
- obj.prop = -22.8
- assert False, "Assigning an out-of-range value should trigger LowerLimitConstraint"
- except LowerLimitConstraintError:
- pass
- except Exception:
- assert False, "Wrong exception triggered by assignation"
-
- try:
- obj.prop = 222.2
- assert False, "Assigning an out-of-range value should trigger UpperLimitConstraint"
- except UpperLimitConstraintError:
- pass
- except Exception:
- assert False, "Wrong exception triggered by assignation"
-
- def test_failing_constructor(self):
- try:
- prop = TFloatProperty(100, 0, 20)
- assert False, "Creation of the property should fail."
- except UpperLimitConstraintError:
- pass
- except:
- assert False, "Wrong exception type on failed constructor"
-
- try:
- prop = TFloatProperty(-100, 0, 20)
- assert False, "Creation of the property should fail."
- except LowerLimitConstraintError:
- pass
- except:
- assert False, "Wrong exception type on failed constructor"
-
-class TStringPropertyTest(unittest.TestCase):
- def test_basic_string(self):
- class klass(TPropContainer):
- prop = TStringProperty("Starter string")
- obj = klass()
-
- assert obj.prop == "Starter string", "Could not set string value via constructor"
-
- assert klass.prop.type == "string", "Wrong type for string property : %s" % klass.prop.type
-
- def test_size_limit(self):
- class klass(TPropContainer):
- prop = TStringProperty("Small", 10)
- obj = klass()
-
- try:
- obj.prop = "My string is too big!"
- assert False, "String should not set to longer than max size"
- except MaxSizeConstraintError:
- pass
- except:
- assert False, "Wrong exception type thrown"
-
- def test_wrong_values(self):
- class klass(TPropContainer):
- prop = TStringProperty("Beginning")
- obj = klass()
-
- try_wrong_values(obj)
-
- def test_failing_constructor(self):
- try:
- prop = TStringProperty("This is normal", 5)
- assert False, "Creation of the property should fail."
- except MaxSizeConstraintError:
- pass
- except:
- assert False, "Wrong exception type on failed constructor"
-
-class TArrayPropertyTest(unittest.TestCase):
- def test_basic_array(self):
- class klass(TPropContainer):
- prop = TArrayProperty([1, 2, 3, 4])
- obj = klass()
-
- assert obj.prop == [1,2,3,4], "Unable to set initial value via constructor"
-
- assert klass.prop.type == "array", "Wrong type for array : %s"%klass.prop.type
-
- def test_wrong_values(self):
- class klass(TPropContainer):
- prop = TArrayProperty([1,2,3,4,5])
- obj = klass()
-
- try_wrong_values(obj)
-
- def test_size_limits(self):
- class klass(TPropContainer):
- prop = TArrayProperty([1,2], None, 4)
- obj = klass()
-
- try:
- obj.prop = [1,2,4,5,6,7]
- assert False, "Maximum size limit constraint was not properly applied"
- except MaxSizeConstraintError:
- pass
-
- class klass(TPropContainer):
- prop = TArrayProperty([1,2,3,4], 2)
- obj = klass()
-
- try:
- obj.prop = [1]
- assert False, "Minimum size limit constraint was not properly applied"
- except MinSizeConstraintError:
- pass
-
- def test_failing_constructor(self):
- try:
- prop = TArrayProperty([100, 0, 20], None, 2)
- assert False, "Creation of the property should fail."
- except MaxSizeConstraintError:
- pass
- try:
- prop = TArrayProperty([100, 0, 20], 4, None)
- assert False, "Creation of the property should fail."
- except MinSizeConstraintError:
- pass
-
-class TColorPropertyTest(unittest.TestCase):
- def test_basic_color(self):
- class klass(TPropContainer):
- prop = TColorProperty(20, 40, 60)
- obj = klass()
-
- assert obj.prop == [20, 40, 60], "Could not set initial value with constructor"
-
- assert klass.prop.type == "color", "Wrong type on color : %s"%klass.prop.type
-
- def test_wrong_values(self):
- class klass(TPropContainer):
- prop = TColorProperty(250, 250, 250)
- obj = klass()
-
- try_wrong_values(obj)
-
- def test_failing_constructor(self):
- try:
- prop = TColorProperty(0, "str", 0)
- assert False, "Creation of the property should fail."
- except ColorTypeError:
- pass
- except:
- assert False, "Wrong exception type on failed constructor"
-
-class TBooleanPropertyTest(unittest.TestCase):
- def setUp(self):
- class klass(TPropContainer):
- prop = TBooleanProperty(False)
- self.obj = klass()
-
- def test_basic_boolean(self):
- assert self.obj.prop == False, "Could not set initial value via constructor"
-
- assert self.obj.__class__.prop.type == "boolean", "Wrong type for TBooleanProperty : %s"%self.obj.__class__.prop.type
-
- self.obj.prop = True
-
- assert self.obj.prop == True, "Could not change the value via set"
-
- self.obj.prop = False
-
- assert self.obj.prop == False, "Could not change the value via set"
-
- def test_wrong_types(self):
- try_wrong_values(self.obj)
-
- def test_failing_constructor(self):
- try:
- prop = TBooleanProperty(64)
- assert False, "Creation of the property should fail with non-boolean value"
- except BooleanConstraintError:
- pass
- except:
- assert False, "Wrong exception type on failed constructor"
-
-class TEnumPropertyTest(unittest.TestCase):
- def setUp(self):
- class klass(TPropContainer):
- prop = TEnumProperty("hello", [1, 2, "hello", "world", True, None])
- self.obj = klass()
-
- def test_basic_enum(self):
- assert self.obj.prop == "hello", "Could not set initial value on property"
-
- assert type(self.obj).prop.type == "enum", "Wrong type for TEnumProperty : %s"%type(self.obj).prop.type
-
- self.obj.prop = True
-
- assert self.obj.prop, "Could not change the value via set"
-
- try:
- self.obj.prop = 4
- assert False, "Switched to a value outside the enum"
- except EnumConstraintError:
- pass
-
- def test_wrong_type(self):
- try_wrong_values(self.obj)
-
-class TFilePropertyTest(unittest.TestCase):
- def setUp(self):
- class klass(TPropContainer):
- prop = TFileProperty("propertiestests.py")
- self.obj = klass()
-
- def test_basic_file(self):
- assert self.obj.prop == "propertiestests.py", "Could not set initial value"
-
- assert type(self.obj).prop.type == "file", "Wrong type for TFileProperty : %s"%type(self.obj).prop.type
-
- self.obj.prop = "run-tests.py"
-
- assert self.obj.prop == "run-tests.py", "Could not change value"
-
- try:
- self.obj.prop = "unknown/file/on/disk.gif"
- assert False, "An exception should be thrown on unknown file"
- except FileConstraintError:
- pass
-
-if __name__ == "__main__":
- unittest.main()
-
diff --git a/src/sugar/tutorius/tests/run-tests.py b/src/sugar/tutorius/tests/run-tests.py
deleted file mode 100755
index d41aa0a..0000000
--- a/src/sugar/tutorius/tests/run-tests.py
+++ /dev/null
@@ -1,74 +0,0 @@
-#!/usr/bin/python
-# This is a dumb script to run tests on the sugar-jhbuild installed files
-# The path added is the default path for the jhbuild build
-
-INSTALL_PATH="../../../../../../install/lib/python2.5/site-packages/"
-
-import os, sys
-sys.path.insert(0,
- os.path.abspath(INSTALL_PATH)
-)
-
-FULL_PATH = os.path.join(INSTALL_PATH,"sugar/tutorius")
-SUBDIRS = ["uam"]
-GLOB_PATH = os.path.join(FULL_PATH,"*.py")
-import unittest
-from glob import glob
-def report_files():
- ret = glob(GLOB_PATH)
- for dir in SUBDIRS:
- ret += glob(os.path.join(FULL_PATH,dir,"*.py"))
- return ret
-
-import sys
-if __name__=='__main__':
- if "--coverage" in sys.argv:
- sys.argv=[arg for arg in sys.argv if arg != "--coverage"]
- import coverage
- coverage.erase()
- #coverage.exclude('raise NotImplementedError')
- coverage.start()
-
- import coretests
- import servicestests
- import gtkutilstests
- #import overlaytests # broken
- import linear_creatortests
- import actiontests
- import uamtests
- import filterstests
- import constraintstests
- import propertiestests
- import serializertests
- suite = unittest.TestSuite()
- suite.addTests(unittest.findTestCases(coretests))
- suite.addTests(unittest.findTestCases(servicestests))
- suite.addTests(unittest.findTestCases(gtkutilstests))
- #suite.addTests(unittest.findTestCases(overlaytests)) # broken
- suite.addTests(unittest.findTestCases(linear_creatortests))
- suite.addTests(unittest.findTestCases(actiontests))
- suite.addTests(unittest.findTestCases(uamtests))
- suite.addTests(unittest.findTestCases(filterstests))
- suite.addTests(unittest.findTestCases(constraintstests))
- suite.addTests(unittest.findTestCases(propertiestests))
- suite.addTests(unittest.findTestCases(serializertests))
- runner = unittest.TextTestRunner()
- runner.run(suite)
- coverage.stop()
- coverage.report(report_files())
- coverage.erase()
- else:
- from coretests import *
- from servicestests import *
- from gtkutilstests import *
- #from overlaytests import * # broken
- from actiontests import *
- from linear_creatortests import *
- from uamtests import *
- from filterstests import *
- from constraintstests import *
- from propertiestests import *
- from actiontests import *
- from serializertests import *
-
- unittest.main()
diff --git a/src/sugar/tutorius/tests/serializertests.py b/src/sugar/tutorius/tests/serializertests.py
deleted file mode 100644
index 6c25bae..0000000
--- a/src/sugar/tutorius/tests/serializertests.py
+++ /dev/null
@@ -1,197 +0,0 @@
-# Copyright (C) 2009, Tutorius.org
-# Copyright (C) 2009, Jean-Christophe Savard <savard.jean.christophe@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
-"""
-Serialization Tests
-
-This module contains all the tests that pertain to the usage of the Tutorius
-Serializer object. This means testing saving a tutorial dictionary to a .tml
-file, loading the list of tutorials for this activity and building chosen
-tutorial.
-"""
-
-import unittest
-
-import os
-import shutil
-
-from sugar.tutorius import bundler, addon
-from sugar.tutorius.core import State, FiniteStateMachine
-from sugar.tutorius.actions import *
-from sugar.tutorius.filters import *
-from sugar.tutorius.bundler import XMLSerializer, Serializer
-import sugar
-from uuid import uuid1
-
-class SerializerInterfaceTest(unittest.TestCase):
- """
- For completeness' sake.
- """
- def test_save(self):
- ser = Serializer()
-
- try:
- ser.save_fsm(None)
- assert False, "save_fsm() should throw an unimplemented error"
- except:
- pass
-
- def test_load(self):
- ser = Serializer()
-
- try:
- ser.load_fsm(str(uuid.uuid1()))
- assert False, "load_fsm() should throw an unimplemented error"
- except:
- pass
-
-class XMLSerializerTest(unittest.TestCase):
- """
- Tests the transformation of XML to FSM, then back.
- """
- def setUp(self):
- # Make the serializer believe the test is in a activity path
- self.testpath = "/tmp/testdata/"
- os.environ["SUGAR_BUNDLE_PATH"] = self.testpath
- os.environ["SUGAR_PREFIX"] = self.testpath
- os.environ["SUGAR_PROFILE"] = 'test'
-## os.mkdir(sugar.tutorius.bundler._get_store_root())
-
- # Create the sample FSM
- self.fsm = FiniteStateMachine("testingMachine")
-
- # Add a few states
- act1 = addon.create('BubbleMessage', message="Hi", pos=[300, 450])
- ev1 = addon.create('GtkWidgetEventFilter', "0.12.31.2.2", "clicked", "Second")
- act2 = addon.create('BubbleMessage', message="Second message", pos=[250, 150], tailpos=[1,2])
-
- st1 = State("INIT")
- st1.add_action(act1)
- st1.add_event_filter(ev1)
-
- st2 = State("Second")
-
- st2.add_action(act2)
-
- self.fsm.add_state(st1)
- self.fsm.add_state(st2)
-
- self.uuid = uuid1()
-
- # Flag to set to True if the output can be deleted after execution of
- # the test
- self.remove = True
-
- def tearDown(self):
- """
- Removes the created files, if need be.
- """
- if self.remove == True:
- shutil.rmtree(os.path.join(os.getenv("HOME"),".sugar",os.getenv("SUGAR_PROFILE")))
- if os.path.isdir(self.testpath):
- shutil.rmtree(self.testpath)
-
- def test_save(self):
- """
- Writes an FSM to disk, then compares the file to the expected results.
- "Remove" boolean argument specify if the test data must be removed or not
- """
- xml_ser = XMLSerializer()
- os.makedirs(os.path.join(sugar.tutorius.bundler._get_store_root(), str(self.uuid)))
- #rpdb2.start_embedded_debugger('flakyPass')
- xml_ser.save_fsm(self.fsm, bundler.TUTORIAL_FILENAME, os.path.join(sugar.tutorius.bundler._get_store_root(), str(self.uuid)))
-
- def test_save_and_load(self):
- """
- Load up the written FSM and compare it with the object representation.
- """
- self.test_save()
- testpath = "/tmp/testdata/"
- #rpdb2.start_embedded_debugger('flakyPass')
- xml_ser = XMLSerializer()
-
- # This interface needs to be redone... It's not clean because there is
- # a responsibility mixup between the XML reader and the bundler.
- loaded_fsm = xml_ser.load_fsm(str(self.uuid))
-
- # Compare the two FSMs
- assert loaded_fsm._states.get("INIT").name == self.fsm._states.get("INIT").name, \
- 'FSM underlying dictionary differ from original to pickled/reformed one'
- assert loaded_fsm._states.get("Second").name == self.fsm._states.get("Second").name, \
- 'FSM underlying dictionary differ from original to pickled/reformed one'
- assert loaded_fsm._states.get("INIT").get_action_list()[0].message == \
- self.fsm._states.get("INIT").get_action_list()[0].message, \
- 'FSM underlying State underlying Action differ from original to reformed one'
- assert len(loaded_fsm.get_action_list()) == 0, "FSM should not have any actions on itself"
-
- def test_all_actions(self):
- """
- Inserts all the known action types in a FSM, then attempt to load it.
- """
- st = State("INIT")
-
- act1 = addon.create('BubbleMessage', "Hi!", pos=[10,120], tailpos=[-12,30])
- act2 = addon.create('DialogMessage', "Hello again.", pos=[120,10])
- act3 = WidgetIdentifyAction()
- act4 = DisableWidgetAction("0.0.0.1.0.0.0")
- act5 = TypeTextAction("0.0.0.1.1.1.0.0", "New text")
- act6 = ClickAction("0.0.1.0.1.1")
- act7 = OnceWrapper(act1)
- act8 = ChainAction([act1, act2, act3, act4])
- actions = [act1, act2, act3, act4, act5, act6, act7, act8]
-
- for action in actions:
- st.add_action(action)
-
- self.fsm.remove_state("Second")
- self.fsm.remove_state("INIT")
- self.fsm.add_state(st)
-
- xml_ser = XMLSerializer()
-
- self.test_save()
-
- reloaded_fsm = xml_ser.load_fsm(str(self.uuid))
- assert self.fsm == reloaded_fsm, "Expected equivalence before saving vs after loading."
-
- def test_all_filters(self):
- """
- Inserts all the known action types in a FSM, then attempt to load it.
- """
- st = State("INIT")
-
- ev1 = TimerEvent("Second", 1000)
- ev2 = addon.create('GtkWidgetEventFilter', "Second", "0.0.1.1.0.0.1", "clicked")
- ev3 = GtkWidgetTypeFilter("Second", "0.0.1.1.1.2.3", text="Typed stuff")
- ev4 = GtkWidgetTypeFilter("Second", "0.0.1.1.1.2.3", strokes="acbd")
- filters = [ev1, ev2, ev3, ev4]
-
- for filter in filters:
- st.add_event_filter(filter)
-
- self.fsm.remove_state("INIT")
- self.fsm.add_state(st)
-
- xml_ser = XMLSerializer()
-
- self.test_save()
-
- reloaded_fsm = xml_ser.load_fsm(str(self.uuid))
-
- assert self.fsm == reloaded_fsm, "Expected equivalence before saving vs after loading."
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/src/sugar/tutorius/tests/servicestests.py b/src/sugar/tutorius/tests/servicestests.py
deleted file mode 100644
index d669012..0000000
--- a/src/sugar/tutorius/tests/servicestests.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# Copyright (C) 2009, Tutorius.org
-# Copyright (C) 2009, Michael Janelle-Montcalm <michael.jmontcalm@gmail.com>
-# Copyright (C) 2009, Vincent Vinet <vince.vinet@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
-"""Services tests"""
-
-import unittest
-
-from sugar.tutorius.services import *
-
-
-class ObjectStoreTests(unittest.TestCase):
- def setUp(self):
- self.os1 = ObjectStore()
- self.os2 = ObjectStore()
-
- def tearDown(self):
- del self.os1
- del self.os2
- ObjectStore.instance = None
-
- def test_singleton(self):
- """
- Validate that the object store is a singleton
- """
- assert self.os1 is self.os2, "Both objectstore objects should be the same"
-
- def test_activity(self):
- """Validate the activity property"""
- act = object()
- self.os1.activity = act
- assert self.os1.activity is self.os2.activity
-
- def test_tutorial(self):
- """Validate the tutorial property"""
- tut = object()
- self.os1.tutorial = tut
- assert self.os1.tutorial is self.os2.tutorial
-
-
diff --git a/src/sugar/tutorius/tests/uamtests.py b/src/sugar/tutorius/tests/uamtests.py
deleted file mode 100644
index b2a5901..0000000
--- a/src/sugar/tutorius/tests/uamtests.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# Copyright (C) 2009, Tutorius.org
-# Copyright (C) 2009, Michael Janelle-Montcalm <michael.jmontcalm@gmail.com>
-# Copyright (C) 2009, Vincent Vinet <vince.vinet@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
-
-import unittest
-
-from sugar.tutorius.uam import parse_uri, SchemeError
-
-PARSE_SUITE={
-#URI SCHEME HOST PARAMS PATH QUERY FRAGMENT
-"tap://act.tut.org/": ["tap", "act.tut.org","", "/", "", ""],
-"tap.gtk://a.t.o/0/1": ["tap.gtk","a.t.o","","/0/1","","",""],
-"tap.gobject://a.t.o/Timer?timeout=5":["tap.gobject","a.t.o","","/Timer","timeout=5",""],
-}
-
-class ParseUriTests(unittest.TestCase):
- """Tests the UAM parsers"""
- def test_parse_uri(self):
- """Test parsing results"""
- for uri, test in PARSE_SUITE.items():
- res = parse_uri(uri)
-
- assert res.scheme == test[0], "%s : Expected scheme %s, got %s" % (uri, test[0], res.scheme)
- assert res.netloc == test[1], "%s : Expected netloc %s, got %s" % (uri, test[1], res.netloc)
- assert res.params == test[2], "%s : Expected params %s, got %s" % (uri, test[2], res.params)
- assert res.path == test[3], "%s : Expected path %s, got %s" % (uri, test[3], res.path)
- assert res.query == test[4], "%s : Expected query %s, got %s" % (uri, test[4], res.query)
- assert res.fragment == test[5], "%s : Expected fragment %s, got %s" % (uri, test[5], res.fragment)
-
- def test_errors(self):
- """Test exceptions"""
- try:
- parse_uri("http://something.org/path")
- assert False, "Parsing http should fail"
- except SchemeError:
- pass
-
- try:
- parse_uri("tap.notarealsubscheme://something.org/path")
- assert False, "Invalid Subscheme should fail"
- except SchemeError:
- pass
-
-
-if __name__ == "__main__":
- unittest.main()
-
diff --git a/src/sugar/tutorius/testwin.py b/src/sugar/tutorius/testwin.py
deleted file mode 100644
index ef92b7f..0000000
--- a/src/sugar/tutorius/testwin.py
+++ /dev/null
@@ -1,92 +0,0 @@
-#!/usr/bin/env python
-# 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
-
-import gtk
-import dragbox
-import textbubble
-
-box = None
-
-def _destroy(widget, data=None):
- gtk.main_quit()
-
-def _delete_event(widget, event, data=None):
- print "quitting"
- return False
-
-def blublu(widget, data=""):
- print data
-
-def _drag_toggle(widget, data=None):
- global box
- box.dragMode = not box.dragMode
-
-
-def addBtn(widget, data, bubble=0, btns=[0]):
- if bubble == 1:
- bt = textbubble.TextBubble("Bubble(%d)"%btns[0])
- else:
- bt = gtk.Button("Bubble(%d)"%btns[0])
- ##bt.set_size_request(60,40)
- bt.show()
- data.attach(bt)
- btns[0] += 1
-
-def main():
- global box
- win = gtk.Window(type=gtk.WINDOW_TOPLEVEL)
- win.connect("delete_event", _delete_event)
- win.connect("destroy", _destroy)
-
- win.set_default_size(800,600)
-
- vbox = gtk.VBox()
- vbox.show()
- win.add(vbox)
-
- check = gtk.CheckButton(label="dragMode")
- check.connect("toggled", _drag_toggle)
- check.show()
- vbox.pack_start(check, expand=False)
-
- btnadd = gtk.Button("Add Bubble")
- btnadd.show()
- vbox.pack_start(btnadd, expand=False)
- btnadd2 = gtk.Button("Add Button")
- btnadd2.show()
- vbox.pack_start(btnadd2, expand=False)
-
-## bubble = textbubble.TextBubble("Bubbles!")
-## bubble.show()
-## bubble.set_size_request(40,40)
-## vbox.pack_start(bubble, expand=False)
-
- box = dragbox.DragBox()
- box.set_border_width(10)
- box.show()
- vbox.pack_start(box, expand=True, fill=True)
-
- btnadd.connect("clicked", addBtn, box, 1)
- btnadd2.connect("clicked", addBtn, box)
-
- win.show()
- gtk.main()
-
-
-if __name__ == "__main__":
- main()
-
diff --git a/src/sugar/tutorius/textbubble.py b/src/sugar/tutorius/textbubble.py
deleted file mode 100644
index e09b298..0000000
--- a/src/sugar/tutorius/textbubble.py
+++ /dev/null
@@ -1,109 +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 represents TextBubble widget. Also, it aims to be a short example
-of drawing with Cairo.
-"""
-
-import gtk
-from math import pi as M_PI
-import cairo
-
-# FIXME set as subclass of gtk.Widget, not EventBox
-class TextBubble(gtk.EventBox):
- def __init__(self, label):
- gtk.EventBox.__init__(self)
-
- ##self.set_app_paintable(True) # else may be blank
- # FIXME ensure previous call does not interfere with widget stacking
- self.label = label
- self.lineWidth = 5
-
- self.connect("expose-event", self._on_expose)
-
- def __draw_with_cairo__(self, context):
- """
-
- """
- pass
-
- def _on_expose(self, widget, event):
- """Redraw event callback."""
- # TODO
- ctx = self.window.cairo_create()
-
- # set drawing region. Useless since this widget has its own window.
- ##region = gtk.gdk.region_rectangle(self.allocation)
- ##region.intersect(gtk.gdk.region_rectangle(event.area))
- ##ctx.region(region)
- ##ctx.rectangle(event.area.x, event.area.y, event.area.width, event.area.height)
- ##ctx.clip()
-
- ##import pdb; pdb.set_trace()
- ##ctx.set_operator(cairo.OPERATOR_CLEAR)
- ##ctx.paint()
- ##ctx.set_operator(cairo.OPERATOR_OVER)
-
- width = self.allocation.width
- height = self.allocation.height
- xradius = width/2
- yradius = height/2
- width -= self.lineWidth
- height -= self.lineWidth
- ctx.move_to(self.lineWidth, yradius)
- ctx.curve_to(self.lineWidth, self.lineWidth,
- self.lineWidth, self.lineWidth, xradius, self.lineWidth)
- ctx.curve_to(width, self.lineWidth,
- width, self.lineWidth, width, yradius)
- ctx.curve_to(width, height, width, height, xradius, height)
- ctx.curve_to(self.lineWidth, height,
- self.lineWidth, height, self.lineWidth, yradius)
- ctx.set_source_rgb(1.0, 1.0, 1.0)
- ctx.fill_preserve()
- ctx.set_line_width(self.lineWidth)
- ctx.set_source_rgb(0.0, 0.0, 0.0)
- ctx.stroke()
-
- _, _, textWidth, textHeight, _, _ = ctx.text_extents(self._label)
- ctx.move_to(int((self.allocation.width-textWidth)/2),
- int((self.allocation.height+textHeight)/2))
- ctx.text_path(self._label)
- ctx.fill()
-
- return True
-
-
- def _set_label(self, value):
- """Sets the label and flags the widget to be redrawn."""
- self._label = value
- # FIXME hack to calculate size. necessary because may not have been
- # realized
- surf = cairo.SVGSurface("/dev/null", 0, 0)
- ctx = cairo.Context(surf)
- _, _, width, height, _, _ = ctx.text_extents(self._label)
- del ctx, surf
-
- # FIXME bogus values follows
- self.set_size_request(int(width+20), int(height+40))
- # TODO test changing a realized label
-
- def _get_label(self):
- """Getter method for the label property"""
- return self._label
-
- label = property(fget=_get_label, fset=_set_label,\
- doc="Text label which is to be painted on the top of the widget")
-
diff --git a/src/sugar/tutorius/uam/Makefile.am b/src/sugar/tutorius/uam/Makefile.am
deleted file mode 100644
index 219291e..0000000
--- a/src/sugar/tutorius/uam/Makefile.am
+++ /dev/null
@@ -1,5 +0,0 @@
-sugardir = $(pythondir)/sugar/tutorius/uam
-sugar_PYTHON = \
- gobjectparser.py \
- gtkparser.py \
- __init__.py
diff --git a/src/sugar/tutorius/uam/__init__.py b/src/sugar/tutorius/uam/__init__.py
deleted file mode 100644
index 7cf5671..0000000
--- a/src/sugar/tutorius/uam/__init__.py
+++ /dev/null
@@ -1,88 +0,0 @@
-# Copyright (C) 2009, Tutorius.org
-# Copyright (C) 2009, Vincent Vinet <vince.vinet@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
-"""
-Universal Addressing Mechanism module
-
-Allows addressing Events, signals, widgets, etc for supported platforms
-"""
-
-from urllib2 import urlparse
-
-import gtkparser
-import gobjectparser
-
-
-SCHEME="tap" #Tutorius Adressing Protocol
-
-__parsers = {
- gtkparser.SCHEME:gtkparser.parse_gtk,
- gobjectparser.SCHEME:gobjectparser.parse_gobject,
-}
-
-def __add_to_urlparse(name):
- #Add to uses_netloc
- if not name in urlparse.uses_netloc:
- urlparse.uses_netloc.append(name)
-
- #Add to uses_relative
- if not name in urlparse.uses_relative:
- urlparse.uses_relative.append(name)
-
-# #Add to uses_params
-# if not name in urlparse.uses_params:
-# urlparse.uses_params.append(name)
-
- #Add to uses_query
- if not name in urlparse.uses_query:
- urlparse.uses_query.append(name)
-
- #Add to uses_frament
- if not name in urlparse.uses_fragment:
- urlparse.uses_fragment.append(name)
-
-
-#Add schemes to urlparse
-__add_to_urlparse(SCHEME)
-
-for subscheme in [".".join([SCHEME,s]) for s in __parsers]:
- __add_to_urlparse(subscheme)
-
-
-class SchemeError(Exception):
- def __init__(self, message):
- Exception.__init__(self, message)
- self.message = message
-
-
-def parse_uri(uri):
- res = urlparse.urlparse(uri)
-
- scheme = res.scheme.split(".")[0]
- subscheme = ".".join(res.scheme.split(".")[1:])
- if not scheme == SCHEME:
- raise SchemeError("Scheme %s not supported" % scheme)
-
- if subscheme != "" and not subscheme in __parsers:
- raise SchemeError("SubScheme %s not supported" % subscheme)
-
- if subscheme:
- return __parsers[subscheme](res)
-
- return res
-
-
-
diff --git a/src/sugar/tutorius/uam/gobjectparser.py b/src/sugar/tutorius/uam/gobjectparser.py
deleted file mode 100644
index c1fba3d..0000000
--- a/src/sugar/tutorius/uam/gobjectparser.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# Copyright (C) 2009, Tutorius.org
-# Copyright (C) 2009, Vincent Vinet <vince.vinet@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
-"""
-UAM Parser for gobject subscheme
-
-To be completed
-"""
-
-SCHEME="gobject"
-
-def parse_gobject(parsed_uri):
- """Do nothing for now"""
- return parsed_uri
diff --git a/src/sugar/tutorius/uam/gtkparser.py b/src/sugar/tutorius/uam/gtkparser.py
deleted file mode 100644
index ede2f03..0000000
--- a/src/sugar/tutorius/uam/gtkparser.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright (C) 2009, Tutorius.org
-# Copyright (C) 2009, Vincent Vinet <vince.vinet@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
-"""
-UAM Parser for gtk subscheme
-
-Allows addressing Gtk Events, signals, widgets
-
-The gtk subscheme for tutorius is
-
-<scheme>://<activity>/<path>[?<params>#<ptype>]
-
-where:
-
-<scheme> is the uam.SCHEME + "." + SCHEME
-
-<activity> is the activity's dns identifier, such as battleship.tutorius.org
-
-<path> is the Hierarchical path to the widget, where 0 is the activity, such as /0/0/1/0/1/0
-
-<params> can be used to specify additionnal parameters required for an event handler or action, such as event=clicked
-
-<ptype> must be used with params to specify which action or eventfilter to use, such as "DialogMessage"
-
-"""
-
-SCHEME="gtk"
-
-def parse_gtk(parsed_uri):
- """Do nothing for now"""
- return parsed_uri