2008-08-20 Mark Doffman <mark.doffman@codethink.co.uk>
authorMark Doffman <mdoff@silver-wind.(none)>
Wed, 20 Aug 2008 13:34:03 +0000 (14:34 +0100)
committerMark Doffman <mdoff@silver-wind.(none)>
Wed, 20 Aug 2008 13:34:03 +0000 (14:34 +0100)
* atk-adaptor/component.c
Fix a bug with marshalling the MDIZOrder.

* pyatspi/*
Fix a huge bug where child Accessibles shared
a common D-Bus object.

* pyatspi/component.py
Add a component interface.

* test/pyatspi/componenttest.py
Add a unit test for the component interface.

atk-adaptor/component.c
pyatspi/__init__.py
pyatspi/accessible.py
pyatspi/base.py
pyatspi/component.py [new file with mode: 0644]
pyatspi/other.py
tests/apps/component-app.c
tests/pyatspi/accessibletest.py
tests/pyatspi/componenttest.py

index 1b342dc..8230dc1 100644 (file)
@@ -233,8 +233,7 @@ impl_getMDIZOrder (DBusConnection * bus, DBusMessage * message,
   reply = dbus_message_new_method_return (message);
   if (reply)
     {
-      dbus_message_append_args (reply, DBUS_TYPE_UINT32, &rv,
-                               DBUS_TYPE_INVALID);
+      dbus_message_append_args (reply, DBUS_TYPE_INT16, &rv, DBUS_TYPE_INVALID);
     }
   return reply;
 }
index d0ee7c5..f8e4ed8 100644 (file)
@@ -22,6 +22,7 @@ from constants import *
 
 from accessible import *
 from application import *
+from component import *
 from stateset import *
 from relation import *
 
index 5853778..5b0ee64 100644 (file)
@@ -43,7 +43,7 @@ class Accessible(BaseProxy):
                                 self._app_name,
                                 application_root,
                                 interfaces.ATSPI_APPLICATION,
-                                dbus_object=self._dbus_object)
+                                connection=self._cache._connection)
     
     def getAttributes(self):
         """
@@ -91,7 +91,7 @@ class Accessible(BaseProxy):
                                 self._app_name,
                                 path,
                                 interfaces.ATSPI_ACCESSIBLE,
-                                dbus_object=self._dbus_object)
+                                connection=self._cache._connection)
     
     def getIndexInParent(self):
         """
@@ -199,7 +199,7 @@ class Accessible(BaseProxy):
                                         self._app_name,
                                         self.cached_data.parent,
                                         interfaces.ATSPI_ACCESSIBLE,
-                                        dbus_object=self._dbus_object)
+                                        connection=self._cache._connection)
 
     _parentDoc = \
         """
index 7231e8a..6869ecd 100644 (file)
@@ -38,8 +38,8 @@ class Enum(int):
 
 
 class BaseProxyMeta(type):
-       def __init__(cls, *args, **kwargs):
-               type.__init__(cls, *args, **kwargs)
+       def __new__(meta, *args, **kwargs):
+               cls = type.__new__(meta, *args, **kwargs)
 
                queryable_interfaces = { 
                        'Accessible':interfaces.ATSPI_ACCESSIBLE,
@@ -60,11 +60,16 @@ class BaseProxyMeta(type):
                        'Value':interfaces.ATSPI_VALUE,
                }
 
+               def return_query(interface):
+                       def new_query(self):
+                               return self.queryInterface(interface)
+                       return new_query
+
                for interface in queryable_interfaces.keys():
                        name = 'query%s' % interface
-                       def new_query(self, object):
-                               return self.queryInterface(object, queryable_interfaces[interface])
-                       setattr(cls, name, new_query) 
+                       setattr(cls, name, return_query(queryable_interfaces[interface])) 
+
+               return cls
 
 #------------------------------------------------------------------------------
 
@@ -101,6 +106,9 @@ class BaseProxy(Interface):
                self._pgetter = self.get_dbus_method("Get", dbus_interface="org.freedesktop.DBus.Properties")
                self._psetter = self.get_dbus_method("Set", dbus_interface="org.freedesktop.DBus.Properties")
 
+       def __getattr__(*args, **kwargs):
+               return object.__getattr__(*args, **kwargs)
+
        def get_dbus_method(self, *args, **kwargs):
                method =  Interface.get_dbus_method(self, *args, **kwargs)
 
@@ -126,7 +134,7 @@ class BaseProxy(Interface):
 
        @property
        def interfaces(self):
-               return self._data.interfaces
+               return self.cached_data.interfaces
 
        def queryInterface(self, interface):
                """
@@ -134,11 +142,10 @@ class BaseProxy(Interface):
                or raises a NotImplemented error if the given interface
                is not supported.
                """
-               if interface in self._data.interfaces:
+               if interface in self.interfaces:
                        return create_accessible(self._cache,
                                                 self._app_name,
                                                 self._acc_path,
-                                                self._parent,
                                                 interface,
                                                 dbus_object=self._dbus_object)
                else:
diff --git a/pyatspi/component.py b/pyatspi/component.py
new file mode 100644 (file)
index 0000000..adce5d6
--- /dev/null
@@ -0,0 +1,166 @@
+#Copyright (C) 2008 Codethink Ltd
+
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU Lesser General Public
+#License version 2 as published by the Free Software Foundation.
+
+#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 Lesser General Public License
+#along with this program; if not, write to the Free Software
+#Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+import interfaces
+from base import BaseProxy, Enum
+from factory import create_accessible, add_accessible_class
+
+from dbus.types import Int16
+
+__all__ = [
+          "Component",
+          "CoordType",
+          "XY_SCREEN",
+          "XY_WINDOW",
+         ]
+
+#------------------------------------------------------------------------------
+
+class CoordType(Enum):
+    _enum_lookup = {
+        0:'XY_SCREEN',
+        1:'XY_WINDOW',
+    }
+
+XY_SCREEN = CoordType(0)
+XY_WINDOW = CoordType(1)
+
+#------------------------------------------------------------------------------
+
+class Component(BaseProxy):
+    """
+    The Component interface is implemented by objects which occupy
+    on-screen space, e.g. objects which have onscreen visual representations.
+    The methods in Component allow clients to identify where the
+    objects lie in the onscreen coordinate system, their relative
+    size, stacking order, and position. It also provides a mechanism
+    whereby keyboard focus may be transferred to specific user interface
+    elements programmatically. This is a 2D API, coordinates of 3D
+    objects are projected into the 2-dimensional screen view for
+    purposes of this interface.
+    """
+    
+    def contains(self, *args, **kwargs):
+        """
+        @return True if the specified point lies within the Component's
+        bounding box, False otherwise.
+        """
+        func = self.get_dbus_method("contains")
+        return func(*args, **kwargs)
+    
+    def deregisterFocusHandler(self, *args, **kwargs):
+        """
+        Request that an EventListener registered via registerFocusHandler
+        no longer be notified when this object receives keyboard focus.
+        """
+        func = self.get_dbus_method("deregisterFocusHandler")
+        return func(*args, **kwargs)
+    
+    def getAccessibleAtPoint(self, *args, **kwargs):
+        """
+        @return the Accessible child whose bounding box contains the
+        specified point.
+        """
+        func = self.get_dbus_method("getAccessibleAtPoint")
+        return func(*args, **kwargs)
+    
+    def getAlpha(self, *args, **kwargs):
+        """
+        Obtain the alpha value of the component. An alpha value of 1.0
+        or greater indicates that the object is fully opaque, and an
+        alpha value of 0.0 indicates that the object is fully transparent.
+        Negative alpha values have no defined meaning at this time.
+        """
+        func = self.get_dbus_method("getAlpha")
+        return func(*args, **kwargs)
+    
+    def getExtents(self, coord_type):
+        """
+        Obtain the Component's bounding box, in pixels, relative to the
+        specified coordinate system. 
+       @param coord_type
+        @return a BoundingBox which entirely contains the object's onscreen
+        visual representation.
+        """
+        func = self.get_dbus_method("getExtents")
+        return func(Int16(coord_type))
+    
+    def getLayer(self, *args, **kwargs):
+        """
+        @return the ComponentLayer in which this object resides.
+        """
+        func = self.get_dbus_method("getLayer")
+        return func(*args, **kwargs)
+    
+    def getMDIZOrder(self):
+        """
+        Obtain the relative stacking order (i.e. 'Z' order) of an object.
+        Larger values indicate that an object is on "top" of the stack,
+        therefore objects with smaller MDIZOrder may be obscured by objects
+        with a larger MDIZOrder, but not vice-versa. 
+        @return an integer indicating the object's place in the stacking
+        order.
+        """
+        func = self.get_dbus_method("getMDIZOrder")
+        return func()
+    
+    def getPosition(self, coord_type):
+        """
+        Obtain the position of the current component in the coordinate
+        system specified by coord_type. 
+        @param : coord_type
+        @param : x
+        an out parameter which will be back-filled with the returned
+        x coordinate. 
+        @param : y
+        an out parameter which will be back-filled with the returned
+        y coordinate.
+        """
+        func = self.get_dbus_method("getPosition")
+        return func(Int16(coord_type))
+    
+    def getSize(self, *args, **kwargs):
+        """
+        Obtain the size, in the coordinate system specified by coord_type,
+        of the rectangular area which fully contains the object's visual
+        representation, without accounting for viewport clipping. 
+        @param : width
+        the object's horizontal extents in the specified coordinate system.
+        @param : height
+        the object's vertical extents in the specified coordinate system.
+        """
+        func = self.get_dbus_method("getSize")
+        return func(*args, **kwargs)
+    
+    def grabFocus(self, *args, **kwargs):
+        """
+        Request that the object obtain keyboard focus.
+        @return True if keyboard focus was successfully transferred to
+        the Component.
+        """
+        func = self.get_dbus_method("grabFocus")
+        return func(*args, **kwargs)
+    
+    def registerFocusHandler(self, *args, **kwargs):
+        """
+        Register an EventListener for notification when this object receives
+        keyboard focus.
+        """
+        func = self.get_dbus_method("registerFocusHandler")
+        return func(*args, **kwargs)
+
+# Register the Accessible class with the accessible factory.
+add_accessible_class(interfaces.ATSPI_COMPONENT, Component)
+
+#END----------------------------------------------------------------------------
index 53e3e1a..a243d5d 100644 (file)
@@ -274,142 +274,6 @@ class CommandListener(_BaseProxy):
         return func(*args, **kwargs)
 
 
-class Component(_BaseProxy):
-    
-    
-    """
-    The Component interface is implemented by objects which occupy
-    on-screen space, e.g. objects which have onscreen visual representations.
-    The methods in Component allow clients to identify where the
-    objects lie in the onscreen coordinate system, their relative
-    size, stacking order, and position. It also provides a mechanism
-    whereby keyboard focus may be transferred to specific user interface
-    elements programmatically. This is a 2D API, coordinates of 3D
-    objects are projected into the 2-dimensional screen view for
-    purposes of this interface.
-    """
-    
-    
-    def contains(self, *args, **kwargs):
-        """
-        @return True if the specified point lies within the Component's
-        bounding box, False otherwise.
-        """
-        func = self.get_dbus_method("contains")
-        return func(*args, **kwargs)
-    
-    def deregisterFocusHandler(self, *args, **kwargs):
-        """
-        Request that an EventListener registered via registerFocusHandler
-        no longer be notified when this object receives keyboard focus.
-        """
-        func = self.get_dbus_method("deregisterFocusHandler")
-        return func(*args, **kwargs)
-    
-    def getAccessibleAtPoint(self, *args, **kwargs):
-        """
-        @return the Accessible child whose bounding box contains the
-        specified point.
-        """
-        func = self.get_dbus_method("getAccessibleAtPoint")
-        return func(*args, **kwargs)
-    
-    def getAlpha(self, *args, **kwargs):
-        """
-        Obtain the alpha value of the component. An alpha value of 1.0
-        or greater indicates that the object is fully opaque, and an
-        alpha value of 0.0 indicates that the object is fully transparent.
-        Negative alpha values have no defined meaning at this time.
-        """
-        func = self.get_dbus_method("getAlpha")
-        return func(*args, **kwargs)
-    
-    def getExtents(self, *args, **kwargs):
-        """
-        Obtain the Component's bounding box, in pixels, relative to the
-        specified coordinate system. 
-        @return a BoundingBox which entirely contains the object's onscreen
-        visual representation.
-        """
-        func = self.get_dbus_method("getExtents")
-        return func(*args, **kwargs)
-    
-    def getLayer(self, *args, **kwargs):
-        """
-        @return the ComponentLayer in which this object resides.
-        """
-        func = self.get_dbus_method("getLayer")
-        return func(*args, **kwargs)
-    
-    def getMDIZOrder(self, *args, **kwargs):
-        """
-        Obtain the relative stacking order (i.e. 'Z' order) of an object.
-        Larger values indicate that an object is on "top" of the stack,
-        therefore objects with smaller MDIZOrder may be obscured by objects
-        with a larger MDIZOrder, but not vice-versa. 
-        @return an integer indicating the object's place in the stacking
-        order.
-        """
-        func = self.get_dbus_method("getMDIZOrder")
-        return func(*args, **kwargs)
-    
-    def getPosition(self, *args, **kwargs):
-        """
-        Obtain the position of the current component in the coordinate
-        system specified by coord_type. 
-        @param : coord_type
-        @param : x
-        an out parameter which will be back-filled with the returned
-        x coordinate. 
-        @param : y
-        an out parameter which will be back-filled with the returned
-        y coordinate.
-        """
-        func = self.get_dbus_method("getPosition")
-        return func(*args, **kwargs)
-    
-    def getSize(self, *args, **kwargs):
-        """
-        Obtain the size, in the coordinate system specified by coord_type,
-        of the rectangular area which fully contains the object's visual
-        representation, without accounting for viewport clipping. 
-        @param : width
-        the object's horizontal extents in the specified coordinate system.
-        @param : height
-        the object's vertical extents in the specified coordinate system.
-        """
-        func = self.get_dbus_method("getSize")
-        return func(*args, **kwargs)
-    
-    def grabFocus(self, *args, **kwargs):
-        """
-        Request that the object obtain keyboard focus.
-        @return True if keyboard focus was successfully transferred to
-        the Component.
-        """
-        func = self.get_dbus_method("grabFocus")
-        return func(*args, **kwargs)
-    
-    def registerFocusHandler(self, *args, **kwargs):
-        """
-        Register an EventListener for notification when this object receives
-        keyboard focus.
-        """
-        func = self.get_dbus_method("registerFocusHandler")
-        return func(*args, **kwargs)
-    
-    def unImplemented(self, *args, **kwargs):
-        func = self.get_dbus_method("unImplemented")
-        return func(*args, **kwargs)
-    
-    def unImplemented2(self, *args, **kwargs):
-        func = self.get_dbus_method("unImplemented2")
-        return func(*args, **kwargs)
-    
-    def unImplemented3(self, *args, **kwargs):
-        func = self.get_dbus_method("unImplemented3")
-        return func(*args, **kwargs)
-
 
 class ComponentLayer(_Enum):
     _enum_lookup = {
index f395b98..d51e1ac 100644 (file)
@@ -7,7 +7,7 @@ static gchar *tdata_path = NULL;
 static AtkComponent *comps[] = {NULL, NULL, NULL};
 static const AtkRectangle extents[] = {{0,0,30,20}, {40,30,30,40}, {0,0,70,70}};
 static const AtkLayer layers[] = {ATK_LAYER_WINDOW, ATK_LAYER_WIDGET, ATK_LAYER_MDI};
-static const guint zorders[] = {0, G_MININT, 100};
+static const guint zorders[] = {0, -100, 100};
 static const gboolean extent_may_changed[] = {TRUE, FALSE, TRUE};
 
 G_MODULE_EXPORT void
@@ -31,8 +31,9 @@ test_init (gchar *path)
       
       comps[i] = ATK_COMPONENT(mycomp);
     }
-    atk_object_set_parent((AtkObject*)comps[0],(AtkObject*)comps[2]);
-    atk_object_set_parent((AtkObject*)comps[1],(AtkObject*)comps[2]);
+    
+  my_atk_object_add_child(MY_ATK_OBJECT(comps[2]), MY_ATK_OBJECT(comps[0]));
+  my_atk_object_add_child(MY_ATK_OBJECT(comps[2]), MY_ATK_OBJECT(comps[1]));
 }
 
 G_MODULE_EXPORT void
index bd95ba6..3aa4ca3 100644 (file)
@@ -127,7 +127,7 @@ class AccessibleTest(_PasyTest):
 
                a = root.getChildAtIndex(1)
                a = a.getChildAtIndex(0)
-               ans = "window"
+               ans = "html container"
                res = a.getLocalizedRoleName()
                test.assertEqual(ans, res,
                                 "Expected LocalizedRoleName - \"%s\". Recieved - \"%s\"" % (ans, res,))
@@ -152,7 +152,7 @@ class AccessibleTest(_PasyTest):
 
                a = root.getChildAtIndex(1)
                a = a.getChildAtIndex(0)
-               ans = "window"
+               ans = "html container"
                res = a.getRoleName()
                test.assertEqual(ans, res,
                                 "Expected roleName - \"%s\". Recieved - \"%s\"" % (ans, res,))
index 9a1564d..a6d4cc9 100644 (file)
-import testutil
-
 import dbus
 import gobject
 import os.path
-import coretest
-from dbus.mainloop.glib import DBusGMainLoop
-
-from accessible_cache import AccessibleCache
-from accessible_cache import ATSPI_COMPONENT
 
 from xml.dom import minidom
+import os
+
+from pasytest import PasyTest as _PasyTest
+
+import pyatspi
+from pyatspi import Accessible
 
 ATSPI_LAYER_WIDGET = 3
 ATSPI_LAYER_MDI = 4
 ATSPI_LAYER_WINDOW = 7
 
 extents_expected = [(0,0,30,20), (40,30,30,40), (0,0,70,70)]
+sizes_expected = [(30,20), (30,40), (70,70)]
+positions_expected = [(0,0), (40,30), (0,0)]
 layers_expected = [ATSPI_LAYER_WINDOW, ATSPI_LAYER_WIDGET, ATSPI_LAYER_MDI]
-zorders_expected = [0, -100, 100]
-
-def supportsInterface(accessible, interface):
-       for itf in accessible.interfaces:
-               if itf == interface:
-                       return True
-       return False
-
-class ComponentTestCase(coretest.CoreTestCase):
-       def runTest(self):
-               self._app = testutil.runTestApp("libcomponentapp.so", self._name)
-               self._loop.run()
-
-       def post_application_test(self):
-               #----------------------------------------
-               comps = [None, None, None]
-               comps[2] = self._cache.getRootAccessible()
-
-               self.assertEqual(comps[2].numChildren, 2,
-               """
-               Number of child components = %d
-               Correct number of components = 2
-               """ % comps[2].numChildren)
-               #----------------------------------------
-               comps[0] = comps[2].getChild(0)
-               comps[1] = comps[2].getChild(1)
-
-               for comp in comps:
-                       self.assert_(supportsInterface(comp, ATSPI_COMPONENT),
-                               """
-                               An accessible object provided does not support the
-                               component interface.
-                               """)
-               #----------------------------------------
-               for (expected, comp) in zip(extents_expected, comps):
-                       extents = comp.getExtents(dbus.types.UInt32(0))
-                       self.assertEquals(extents, expected,
-                                       """
-                                       Extents of component do not match.
-                                       Expected: %s
-                                       Recieved: %s
-                                       """ % (str(expected), str(extents)))
-               #----------------------------------------
-               for (expected, comp) in zip(layers_expected, comps):
+zorders_expected = [-100, 100]
+
+class ComponentTest(_PasyTest):
+
+       __tests__ = ["setup",
+                    "test_contains",
+                    "test_getAccessibleAtPoint",
+                    "test_getExtents",
+                    "test_getPosition",
+                    "test_getSize",
+                    "test_getLayer",
+                    "test_getMDIZOrder",
+                    "test_grabFocus",
+                    "test_registerFocusHandler",
+                    "test_deregisterFocusHandler",
+                    "test_getAlpha",
+                    "teardown",
+                    ]
+
+       def __init__(self, bus, path):
+               _PasyTest.__init__(self, "Accessible", False)
+               self._bus = bus
+               self._path = path
+
+       def setup(self, test):
+               self._cache = pyatspi.TestApplicationCache(self._bus, self._path)
+
+       def test_contains(self, test):
+               pass
+
+       def test_getAccessibleAtPoint(self, test):
+               pass
+
+       def test_getExtents(self, test):
+               root = self._cache.root
+               one = root.getChildAtIndex(0)
+               two = root.getChildAtIndex(1)
+
+               comps = [one.queryComponent(),
+                        two.queryComponent(),
+                        root.queryComponent(),]
+               for expected, comp in zip(extents_expected, comps):
+                       extents = comp.getExtents(0)
+                       test.assertEqual(extents, expected, 
+                                        "Extents not correct. Expected (%d, %d, %d, %d), Recieved (%d, %d, %d, %d)"
+                                        % (expected[0], expected[1], expected[2], expected[3], 
+                                               extents[0], extents[1], extents[2], extents[3]))
+
+       def test_getPosition(self, test):
+               pass
+               root = self._cache.root
+               one = root.getChildAtIndex(0)
+               two = root.getChildAtIndex(1)
+
+               comps = [one.queryComponent(),
+                        two.queryComponent(),
+                        root.queryComponent(),]
+               for expected, comp in zip(positions_expected, comps):
+                       position = comp.getPosition(0)
+                       test.assertEqual(position, expected, 
+                                        "Position not correct. Expected (%d, %d) Recieved (%d, %d)"
+                                        % (expected[0], expected[1], position[0], position[1]))
+
+       def test_getSize(self, test):
+               root = self._cache.root
+               one = root.getChildAtIndex(0)
+               two = root.getChildAtIndex(1)
+
+               comps = [one.queryComponent(),
+                        two.queryComponent(),
+                        root.queryComponent(),]
+               for expected, comp in zip(sizes_expected, comps):
+                       size = comp.getSize()
+                       test.assertEqual(size, expected, 
+                                        "Size not correct. Expected (%d, %d) Recieved (%d, %d)"
+                                        % (expected[0], expected[1], size[0], size[1]))
+
+       def test_getLayer(self, test):
+               root = self._cache.root
+               one = root.getChildAtIndex(0)
+               two = root.getChildAtIndex(1)
+
+               comps = [one.queryComponent(),
+                        two.queryComponent(),
+                        root.queryComponent(),]
+               for expected, comp in zip(layers_expected, comps):
                        layer = comp.getLayer()
-                       self.assertEquals(layer, expected,
-                                       """
-                                       Layer of component does not match.
-                                       Expected: %s
-                                       Recieved: %s
-                                       """ % (str(expected), str(layer)))
-               #----------------------------------------
-               #There is no defined value for the result when the layer is not WINDOW or MDI
-               #for (expected, comp) in zip(zorders_expected, [comps[0], comps[2]]):
-               #       zorder = comp.getMDIZOrder()
-               #       print zorder, expected
-               #       self.assertEquals(layer, expected,
-               #                       """
-               #                       ZOrder of component does not match.
-               #                       Expected: %s
-               #                       Recieved: %s
-               #                       """ % (str(expected), str(zorder)))
-               #----------------------------------------
+                       test.assertEqual(layer, expected, 
+                                        "Layer not correct. Expected %d, Recieved %d"
+                                        % (layer, expected))
+
+       def test_getMDIZOrder(self, test):
+               root = self._cache.root
+               one = root.getChildAtIndex(0)
+               two = root.getChildAtIndex(1)
+
+               comps = [two.queryComponent(),
+                        root.queryComponent(),]
+               for expected, comp in zip(zorders_expected, comps):
+                       mdizo = comp.getMDIZOrder()
+                       test.assertEqual(mdizo, expected, 
+                                        "ZOrder not correct. Expected %d, Recieved %d"
+                                        % (expected, mdizo))
+
+       def test_grabFocus(self, test):
+               pass
+
+       def test_registerFocusHandler(self, test):
+               pass
+
+       def test_deregisterFocusHandler(self, test):
+               pass
+
+       def test_getAlpha(self, test):
+               pass
+
+       def teardown(self, test):
+               pass