From 95dcfcf4cf6dae79fe0d402ddc9cdf507df4e3b6 Mon Sep 17 00:00:00 2001 From: Mark Doffman Date: Wed, 20 Aug 2008 14:34:03 +0100 Subject: [PATCH] 2008-08-20 Mark Doffman * 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 | 3 +- pyatspi/__init__.py | 1 + pyatspi/accessible.py | 6 +- pyatspi/base.py | 23 +++-- pyatspi/component.py | 166 +++++++++++++++++++++++++++++++++ pyatspi/other.py | 136 --------------------------- tests/apps/component-app.c | 7 +- tests/pyatspi/accessibletest.py | 4 +- tests/pyatspi/componenttest.py | 199 ++++++++++++++++++++++++++-------------- 9 files changed, 322 insertions(+), 223 deletions(-) create mode 100644 pyatspi/component.py diff --git a/atk-adaptor/component.c b/atk-adaptor/component.c index 1b342dc..8230dc1 100644 --- a/atk-adaptor/component.c +++ b/atk-adaptor/component.c @@ -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; } diff --git a/pyatspi/__init__.py b/pyatspi/__init__.py index d0ee7c5..f8e4ed8 100644 --- a/pyatspi/__init__.py +++ b/pyatspi/__init__.py @@ -22,6 +22,7 @@ from constants import * from accessible import * from application import * +from component import * from stateset import * from relation import * diff --git a/pyatspi/accessible.py b/pyatspi/accessible.py index 5853778..5b0ee64 100644 --- a/pyatspi/accessible.py +++ b/pyatspi/accessible.py @@ -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 = \ """ diff --git a/pyatspi/base.py b/pyatspi/base.py index 7231e8a..6869ecd 100644 --- a/pyatspi/base.py +++ b/pyatspi/base.py @@ -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 index 0000000..adce5d6 --- /dev/null +++ b/pyatspi/component.py @@ -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---------------------------------------------------------------------------- diff --git a/pyatspi/other.py b/pyatspi/other.py index 53e3e1a..a243d5d 100644 --- a/pyatspi/other.py +++ b/pyatspi/other.py @@ -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 = { diff --git a/tests/apps/component-app.c b/tests/apps/component-app.c index f395b98..d51e1ac 100644 --- a/tests/apps/component-app.c +++ b/tests/apps/component-app.c @@ -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 diff --git a/tests/pyatspi/accessibletest.py b/tests/pyatspi/accessibletest.py index bd95ba6..3aa4ca3 100644 --- a/tests/pyatspi/accessibletest.py +++ b/tests/pyatspi/accessibletest.py @@ -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,)) diff --git a/tests/pyatspi/componenttest.py b/tests/pyatspi/componenttest.py index 9a1564d..a6d4cc9 100644 --- a/tests/pyatspi/componenttest.py +++ b/tests/pyatspi/componenttest.py @@ -1,82 +1,143 @@ -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 -- 2.7.4