From 8ab1e32a479c018766f330ecf71670ef71492300 Mon Sep 17 00:00:00 2001 From: mike Date: Wed, 15 Apr 2009 02:55:10 +0000 Subject: Merge commit 'origin/lp349874' Conflicts: source/external/source/sugar-toolkit/src/sugar/tutorius/tests/actiontests.py source/external/source/sugar-toolkit/src/sugar/tutorius/tests/filterstests.py source/external/source/sugar-toolkit/src/sugar/tutorius/tests/gtkutilstests.py source/external/source/sugar-toolkit/src/sugar/tutorius/tests/run-tests.py --- diff --git a/configure.ac b/configure.ac index 2f473cb..66e139b 100644 --- a/configure.ac +++ b/configure.ac @@ -42,6 +42,7 @@ src/sugar/bundle/Makefile src/sugar/graphics/Makefile src/sugar/presence/Makefile src/sugar/tutorius/Makefile +src/sugar/tutorius/uam/Makefile src/sugar/datastore/Makefile po/Makefile.in ]) diff --git a/src/sugar/tutorius/Makefile.am b/src/sugar/tutorius/Makefile.am index deabd25..02f832b 100644 --- a/src/sugar/tutorius/Makefile.am +++ b/src/sugar/tutorius/Makefile.am @@ -1,3 +1,5 @@ +SUBDIRS = uam + sugardir = $(pythondir)/sugar/tutorius sugar_PYTHON = \ __init__.py \ diff --git a/src/sugar/tutorius/actions.py b/src/sugar/tutorius/actions.py index 7681dea..2c76bd7 100644 --- a/src/sugar/tutorius/actions.py +++ b/src/sugar/tutorius/actions.py @@ -229,3 +229,24 @@ class ChainAction(Action): 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) diff --git a/src/sugar/tutorius/editor.py b/src/sugar/tutorius/editor.py index bf70216..42cc718 100644 --- a/src/sugar/tutorius/editor.py +++ b/src/sugar/tutorius/editor.py @@ -142,6 +142,38 @@ class WidgetIdentifier(gtk.Window): 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 @@ -163,6 +195,59 @@ class WidgetIdentifier(gtk.Window): ) + 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 diff --git a/src/sugar/tutorius/filters.py b/src/sugar/tutorius/filters.py index a69055a..594ad6a 100644 --- a/src/sugar/tutorius/filters.py +++ b/src/sugar/tutorius/filters.py @@ -16,9 +16,14 @@ # 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 + + class EventFilter(object): """ Base class for an event filter @@ -168,4 +173,68 @@ class GtkWidgetEventFilter(EventFilter): self._widget.handler_disconnect(self._handler_id) self._handler_id=None - +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/tests/actiontests.py b/src/sugar/tutorius/tests/actiontests.py index 9c398d4..ab9cdba 100644 --- a/src/sugar/tutorius/tests/actiontests.py +++ b/src/sugar/tutorius/tests/actiontests.py @@ -153,6 +153,21 @@ class ChainActionTest(unittest.TestCase): 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/filterstests.py b/src/sugar/tutorius/tests/filterstests.py index 4b7ae61..8ee6cc8 100644 --- a/src/sugar/tutorius/tests/filterstests.py +++ b/src/sugar/tutorius/tests/filterstests.py @@ -26,7 +26,7 @@ import time import gobject import gtk -from sugar.tutorius.filters import EventFilter, TimerEvent, GtkWidgetEventFilter +from sugar.tutorius.filters import EventFilter, TimerEvent, GtkWidgetEventFilter, GtkWidgetTypeFilter from gtkutilstests import SignalCatcher class BaseEventFilterTests(unittest.TestCase): diff --git a/src/sugar/tutorius/tests/gtkutilstests.py b/src/sugar/tutorius/tests/gtkutilstests.py index 5dfd363..41634ae 100644 --- a/src/sugar/tutorius/tests/gtkutilstests.py +++ b/src/sugar/tutorius/tests/gtkutilstests.py @@ -184,6 +184,17 @@ class GtkUtilsTests(unittest.TestCase): 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() diff --git a/src/sugar/tutorius/tests/run-tests.py b/src/sugar/tutorius/tests/run-tests.py index 1cda76e..63a8de1 100755 --- a/src/sugar/tutorius/tests/run-tests.py +++ b/src/sugar/tutorius/tests/run-tests.py @@ -10,7 +10,7 @@ sys.path.insert(0, ) FULL_PATH = os.path.join(INSTALL_PATH,"sugar/tutorius") -SUBDIRS = [] +SUBDIRS = ["uam"] GLOB_PATH = os.path.join(FULL_PATH,"*.py") import unittest from glob import glob @@ -36,6 +36,7 @@ if __name__=='__main__': import overlaytests import linear_creatortests import actiontests + import uamtests import filterstests import constraintstests import propertiestests @@ -47,6 +48,7 @@ if __name__=='__main__': suite.addTests(unittest.findTestCases(overlaytests)) 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)) @@ -65,6 +67,7 @@ if __name__=='__main__': from overlaytests import * from actiontests import * from linear_creatortests import * + from uamtests import * from filterstests import * unittest.main() diff --git a/src/sugar/tutorius/tests/uamtests.py b/src/sugar/tutorius/tests/uamtests.py new file mode 100644 index 0000000..b2a5901 --- /dev/null +++ b/src/sugar/tutorius/tests/uamtests.py @@ -0,0 +1,61 @@ +# Copyright (C) 2009, Tutorius.org +# Copyright (C) 2009, Michael Janelle-Montcalm +# Copyright (C) 2009, Vincent Vinet +# +# 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/uam/Makefile.am b/src/sugar/tutorius/uam/Makefile.am new file mode 100644 index 0000000..219291e --- /dev/null +++ b/src/sugar/tutorius/uam/Makefile.am @@ -0,0 +1,5 @@ +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 new file mode 100644 index 0000000..7cf5671 --- /dev/null +++ b/src/sugar/tutorius/uam/__init__.py @@ -0,0 +1,88 @@ +# Copyright (C) 2009, Tutorius.org +# Copyright (C) 2009, Vincent Vinet +# +# 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 new file mode 100644 index 0000000..c1fba3d --- /dev/null +++ b/src/sugar/tutorius/uam/gobjectparser.py @@ -0,0 +1,27 @@ +# Copyright (C) 2009, Tutorius.org +# Copyright (C) 2009, Vincent Vinet +# +# 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 new file mode 100644 index 0000000..ede2f03 --- /dev/null +++ b/src/sugar/tutorius/uam/gtkparser.py @@ -0,0 +1,44 @@ +# Copyright (C) 2009, Tutorius.org +# Copyright (C) 2009, Vincent Vinet +# +# 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 + +:///[?#] + +where: + + is the uam.SCHEME + "." + SCHEME + + is the activity's dns identifier, such as battleship.tutorius.org + + is the Hierarchical path to the widget, where 0 is the activity, such as /0/0/1/0/1/0 + + can be used to specify additionnal parameters required for an event handler or action, such as event=clicked + + 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 -- cgit v0.9.1