Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/src/sugar/tutorius/core.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/sugar/tutorius/core.py')
-rw-r--r--src/sugar/tutorius/core.py237
1 files changed, 40 insertions, 197 deletions
diff --git a/src/sugar/tutorius/core.py b/src/sugar/tutorius/core.py
index 62caee0..45eee3a 100644
--- a/src/sugar/tutorius/core.py
+++ b/src/sugar/tutorius/core.py
@@ -25,63 +25,14 @@ import gtk
import logging
from sugar.tutorius.dialog import TutoriusDialog
-
+from sugar.tutorius.gtkutils import find_widget
logger = logging.getLogger("tutorius")
-class Event:
- """Event descriptor class.
- This class is used to describe events that are expected to happen.
- """
-
- def __init__(self, object_name, event_name ):
- """Constructor for Event
- @param object_name str name of the object that will send the event
- @param event_name str name of the event
-
- Example:
- evt=Event("0.0.1.1.2", "clicked")
- """
- self.object_name = object_name
- self.event_name = event_name
-
- def test(self, sig, name):
- """Utility method for testing the equality between a signal and object
- names and their expected values.
- @param sig str signal name
- @param name str object name
- @return True if both match the expected values, False otherwise
- """
- if self.object_name == name and self.event_name == sig:
- return True
- return False
-
-
-
-
class Tutorial (object):
"""
Tutorial Class, used to run through the FSM.
"""
- 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 __init__(self, name, fsm):
"""Create an unattached tutorial
@@ -107,34 +58,17 @@ class Tutorial (object):
def detach(self):
"""Detach from the current activity"""
- self.disconnect_handlers()
- self.activity = None
-
- def handle_event(self, *args):
- """Default event handler for the Tutorial.
- Tests the received object and signal names onto each defined
- transition and changes to the next state if successful.
-
- The last parameter should be a two-tuple containing the
- (signal_name, object_name)
- """
- sig, objname = args[-1]
- logger.debug("EVENT %s ON %s" % (sig, objname) )
- for transition, next in self.state_machine[self.state]["Events"]:
- if transition.test(sig, objname):
- self.set_state(next)
+ #Remove handlers
+ for eventfilter in self.state_machine.get(self.state,{}).get("EventFilters",()):
+ eventfilter.remove_handlers()
-# @staticmethod
-# def logEvent(obj, *args):
-# logger.debug("%s" % str(args[-1]))
-
- def disconnect_handlers(self):
- """Disconnect all event handlers attached by self"""
- #Loop through handlers
- for obj, hid in self.handlers:
- obj.handler_disconnect(hid)
- self.handlers = []
+ #Undo actions
+ for act in self.state_machine.get(self.state,{}).get("Actions",()):
+ act.undo()
+ #FIXME There should be some amount of resetting done here...
+ self.activity = None
+
def set_state(self, name):
"""Switch to a new state"""
@@ -142,8 +76,10 @@ class Tutorial (object):
return
logger.debug("====NEW STATE: %s====" % name)
- #Remove handlers (TODO replace by EventFilter unregister)
- self.disconnect_handlers()
+ #Remove handlers
+ for eventfilter in self.state_machine.get(self.state,{}).get("EventFilters",()):
+ eventfilter.remove_handlers()
+
#Undo actions
for act in self.state_machine.get(self.state,{}).get("Actions",()):
act.undo()
@@ -151,131 +87,38 @@ class Tutorial (object):
#Switch to new state
self.state = name
newstate = self.state_machine.get(name)
- #Add handlers (TODO replace by EventFilter register)
- for event, unused in newstate["Events"]:
- self.register_signal(self.handle_event, \
- event.object_name, event.event_name)
-
+
+ #Register handlers for eventfilters
+ for eventfilter in newstate["EventFilters"]:
+ eventfilter.install_handlers(self._eventfilter_state_done,
+ activity=self.activity)
+
#Do actions
for act in newstate.get("Actions",()):
act.do()
- def register_signals(self, target, handler, prefix=None, max_depth=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
- Tutorial.EVENTS and omits widgets with a name matching
- Tutorial.IGNORED_WIDGETS from the name hierarchy.
+ 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
- Example arg tuple added:
- ("focus", "Activity.Toolbox.Bold")
- Side effects:
- -Handlers connected on the various targets
- -Handler ID's stored in self.handlers
+ #Swith to the next state pointed by the eventfilter
+ self.set_state(eventfilter.get_next_state())
- @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
- """
- #Gtk Containers have a get_children() function
- if hasattr(target, "get_children") and \
- hasattr(target.get_children, "__call__"):
- for child in target.get_children():
- 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 Tutorial.IGNORED_WIDGETS)] \
- )
- self.register_signals(child, handler, pre, max_depth-1)
- name = ".".join( \
- [p for p in (prefix, target.get_name()) \
- if not (p is None or p in Tutorial.IGNORED_WIDGETS)] \
- )
- #register events on the target if a widget XXX necessary to check this?
- if isinstance(target, gtk.Widget):
- for sig in Tutorial.EVENTS:
- try:
- self.handlers.append( \
- (target, target.connect(sig, handler, (sig, name) )) \
- )
- except TypeError:
- continue
-
- def register_signals_numbered(self, \
- target, handler, prefix="0", max_depth=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
- Tutorial.EVENTS
-
- Example arg tuple added:
- ("focus", "1.1.2")
- Side effects:
- -Handlers connected on the various targets
- -Handler ID's stored in self.handlers
-
- @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
- """
- #Gtk Containers have a get_children() function
- if hasattr(target, "get_children") and \
- hasattr(target.get_children, "__call__"):
- children = target.get_children()
- 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
- self.register_signals_numbered(child, handler, pre, dep)
- #register events on the target if a widget XXX necessary to check this?
- if isinstance(target, gtk.Widget):
- for sig in Tutorial.EVENTS:
- try:
- self.handlers.append( \
- (target, target.connect(sig, handler, (sig, prefix) ))\
- )
- except TypeError:
- continue
-
- def register_signal(self, handler, obj_fqdn, signal_name):
- """Register a signal handler onto a specific widget
- @param handler function to attach as a handler
- @param obj_fqdn fqdn-style object name
- @param signal_name signal name to connect to
-
- Side effects:
- the object found and the handler id obtained by connect() are
- appended in self.handlers
- """
- path = obj_fqdn.split(".")
- #We select the first object and pop the first zero
- obj = self.activity
- path.pop(0)
-
- while len(path) > 0:
- obj = obj.get_children()[int(path.pop(0))]
-
- self.handlers.append( \
- (obj, obj.connect(signal_name, handler, (signal_name, obj_fqdn) ))\
- )
+# def register_signal(self, handler, obj_fqdn, signal_name):
+# """Register a signal handler onto a specific widget
+# @param handler function to attach as a handler
+# @param obj_fqdn fqdn-style object name
+# @param signal_name signal name to connect to
+#
+# Side effects:
+# the object found and the handler id obtained by connect() are
+# appended in self.handlers
+# """
+# obj = find_widget(self.activity, obj_fqdn)
+# self.handlers.append( \
+# (obj, obj.connect(signal_name, handler, (signal_name, obj_fqdn) ))\
+# )
class State:
"""This is a step in a tutorial. The state represents a collection of