Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVincent Vinet <vince.vinet@gmail.com>2009-02-12 05:04:41 (GMT)
committer Vincent Vinet <vince.vinet@gmail.com>2009-02-23 20:50:01 (GMT)
commit1aa26594ad7f55cf91fbd140b006c062c247fb13 (patch)
tree5a845d1d19d5c5baaa42a7100db249397a5c6b64
parent3fbf20adbecf56f363c58f72b77c744f325f72c4 (diff)
Add the tutorius module to sugar-toolkit
-rw-r--r--src/sugar/Makefile.am2
-rw-r--r--src/sugar/tutorius/Makefile.am5
-rw-r--r--src/sugar/tutorius/__init__.py0
-rw-r--r--src/sugar/tutorius/dialog.py39
-rw-r--r--src/sugar/tutorius/tutorial.py162
5 files changed, 207 insertions, 1 deletions
diff --git a/src/sugar/Makefile.am b/src/sugar/Makefile.am
index fb87bf6..c3eeeaf 100644
--- a/src/sugar/Makefile.am
+++ b/src/sugar/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = activity bundle graphics presence datastore
+SUBDIRS = activity bundle graphics presence datastore tutorius
sugardir = $(pythondir)/sugar
sugar_PYTHON = \
diff --git a/src/sugar/tutorius/Makefile.am b/src/sugar/tutorius/Makefile.am
new file mode 100644
index 0000000..0c85c33
--- /dev/null
+++ b/src/sugar/tutorius/Makefile.am
@@ -0,0 +1,5 @@
+sugardir = $(pythondir)/sugar/tutorius
+sugar_PYTHON = \
+ __init__.py \
+ tutorial.py \
+ dialog.py
diff --git a/src/sugar/tutorius/__init__.py b/src/sugar/tutorius/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/sugar/tutorius/__init__.py
diff --git a/src/sugar/tutorius/dialog.py b/src/sugar/tutorius/dialog.py
new file mode 100644
index 0000000..d15cc52
--- /dev/null
+++ b/src/sugar/tutorius/dialog.py
@@ -0,0 +1,39 @@
+# 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
+
+class TutoriusDialog(gtk.Dialog):
+ def __init__(self, label="Hint", button_clicked_cb=None, response_cb=None):
+ 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 setButtonClickedCallback(self, funct):
+ self._button.connect("clicked", funct)
+
+ def closeSelf(self, Arg=None):
+ self.destroy()
diff --git a/src/sugar/tutorius/tutorial.py b/src/sugar/tutorius/tutorial.py
new file mode 100644
index 0000000..5236127
--- /dev/null
+++ b/src/sugar/tutorius/tutorial.py
@@ -0,0 +1,162 @@
+# 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 gtk
+import logging
+
+from sugar.tutorius.dialog import TutoriusDialog
+
+
+logger = logging.getLogger("tutorius")
+
+class Event:
+ def __init__(self, object_name, event_name ):
+ self.object_name = object_name
+ self.event_name = event_name
+
+ def test(self, sig, name):
+ if self.object_name == name and self.event_name == sig:
+ return True
+ return False
+
+
+class Tutorial (object):
+ 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):
+ object.__init__(self)
+ self.name = name
+ self.state_machine = fsm
+
+ self.handlers = []
+ self.activity = None
+ #self.setState("INIT")
+ #self.state="INIT"
+ #self.register_signals(self.activity, self.handleEvent, max_depth=10)
+
+ def attach(self, activity):
+ #For now, absolutely detach if a previous one!
+ if self.activity:
+ self.detach()
+ self.activity = activity
+ self.state="INIT"
+ self.register_signals(self.activity,self.handleEvent, max_depth=10)
+
+ def detach(self):
+ self.disconnectHandlers()
+ self.activity = None
+
+ def handleEvent(self, *args):
+ 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):
+ logger.debug("====NEW STATE: %s====" % next)
+ self.state = next
+ dlg = TutoriusDialog(self.state_machine[self.state]["Message"])
+ dlg.setButtonClickedCallback(dlg.closeSelf)
+ dlg.run()
+
+# @staticmethod
+# def logEvent(obj, *args):
+# logger.debug("%s" % str(args[-1]))
+
+ def disconnectHandlers(self):
+ for t, id in self.handlers:
+ t.disconnect_handler(id)
+
+# def setState(self,name):
+# self.disconnectHandlers()
+# self.state = name
+# newstate = ABIWORD_MEF.get(name,())
+# for event, n in newstate:
+# target = self.activity
+# try:
+# for obj in event.object_name.split("."):
+# target = getattr(target,obj)
+# id = target.connect(self.handler,(event.object_name, event.event_name))
+# self.handlers.append(target, id)
+# id = target.connect(Tutorial.logEvent,"EVENT %s ON %s" % (event.object_name, event.event_name))
+# self.handlers.append(target, id)
+# except Exception, e:
+# logger.debug(str(e))
+
+ def register_signals(self,object,handler,prefix=None,max_depth=None):
+ """
+ Recursive function to register event handlers on an object
+ 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 object 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.
+
+ Example arg tuple added:
+ ("focus", "Activity.Toolbox.Bold")
+ Side effects:
+ -Handlers connected on the various objects
+ -Handler ID's stored in self.handlers
+
+ @param object the object to recurse on
+ @param handler the handler function to connect
+ @param prefix name prepended to the object name to form a chain
+ @param max_depth maximum recursion depth, None for infinity
+ """
+ #Gtk Containers have a get_children() function
+ if hasattr(object,"get_children") and \
+ hasattr(object.get_children,"__call__"):
+ for child in object.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, object.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, object.get_name()) \
+ if not (p is None or p in Tutorial.IGNORED_WIDGETS)] \
+ )
+ #register events on the object if a widget XXX necessary to check this?
+ if isinstance(object,gtk.Widget):
+ for sig in Tutorial.EVENTS:
+ try:
+ self.handlers.append( (object,object.connect(sig,handler,(sig, name) )) )
+ except TypeError:
+ continue
+
+