Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authormike <michael.jmontcalm@gmail.com>2009-04-15 02:55:10 (GMT)
committer mike <michael.jmontcalm@gmail.com>2009-04-15 02:55:10 (GMT)
commit8ab1e32a479c018766f330ecf71670ef71492300 (patch)
tree529350bcb00d23c8c404510c594e4a6c5ffa6394 /src
parent29177ac24ce70208a6225669a1275f67ac0f647a (diff)
parent0aae7813af4bb9ccdc4f02ba7cd483c8967078f1 (diff)
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
Diffstat (limited to 'src')
-rw-r--r--src/sugar/tutorius/Makefile.am2
-rw-r--r--src/sugar/tutorius/actions.py21
-rw-r--r--src/sugar/tutorius/editor.py85
-rw-r--r--src/sugar/tutorius/filters.py73
-rw-r--r--src/sugar/tutorius/tests/actiontests.py15
-rw-r--r--src/sugar/tutorius/tests/filterstests.py2
-rw-r--r--src/sugar/tutorius/tests/gtkutilstests.py11
-rwxr-xr-xsrc/sugar/tutorius/tests/run-tests.py5
-rw-r--r--src/sugar/tutorius/tests/uamtests.py61
-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
13 files changed, 435 insertions, 4 deletions
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 <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/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 <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
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 <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
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 <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