2008-08-15 Mark Doffman <mark.doffman@codethink.co.uk>
[platform/core/uifw/at-spi2-atk.git] / pyatspi / base.py
1 #Copyright (C) 2008 Codethink Ltd
2
3 #This library is free software; you can redistribute it and/or
4 #modify it under the terms of the GNU Lesser General Public
5 #License version 2 as published by the Free Software Foundation.
6
7 #This program is distributed in the hope that it will be useful,
8 #but WITHOUT ANY WARRANTY; without even the implied warranty of
9 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 #GNU General Public License for more details.
11 #You should have received a copy of the GNU Lesser General Public License
12 #along with this program; if not, write to the Free Software
13 #Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
14
15 import dbus as _dbus
16 from dbus.proxies import Interface as _Interface
17
18 from dbus.exceptions import *
19 from factory import create_accessible, add_accessible_class
20
21 import constants
22
23 class AccessibleObjectNoLongerExists(Exception):
24         pass
25
26 #------------------------------------------------------------------------------
27
28 class _Enum(int):
29         def __str__(self):
30                 return self._enum_lookup(int(self))
31
32 #------------------------------------------------------------------------------
33
34
35 class _BaseProxyMeta(type):
36         def __init__(cls, *args, **kwargs):
37                 type.__init__(cls, *args, **kwargs)
38
39                 queryable_interfaces = { 
40                         'Accessible':constants.ATSPI_ACCESSIBLE,
41                         'Action':constants.ATSPI_ACTION,
42                         'Application':constants.ATSPI_APPLICATION,
43                         'Collection':constants.ATSPI_COLLECTION,
44                         'Component':constants.ATSPI_COMPONENT,
45                         'Desktop':constants.ATSPI_DESKTOP,
46                         'Document':constants.ATSPI_DOCUMENT,
47                         'EditableText':constants.ATSPI_EDITABLE_TEXT,
48                         'Hypertext':constants.ATSPI_HYPERTEXT,
49                         'Hyperlink':constants.ATSPI_HYPERLINK,
50                         'Image':constants.ATSPI_IMAGE,
51                         'Selection':constants.ATSPI_SELECTION,
52                         'StreamableContent':constants.ATSPI_STREAMABLE_CONTENT,
53                         'Table':constants.ATSPI_TABLE,
54                         'Text':constants.ATSPI_TEXT,
55                         'Value':constants.ATSPI_VALUE,
56                 }
57
58                 for interface in queryable_interfaces.keys():
59                         name = 'query%s' % interface
60                         def new_query(self, object):
61                                 return self.queryInterface(object, queryable_interfaces[interface])
62                         setattr(cls, name, new_query) 
63
64 #------------------------------------------------------------------------------
65
66 class _BaseProxy(_Interface):
67         """
68         The base D-Bus proxy for a remote object that implements one or more
69         of the AT-SPI interfaces.
70         """
71
72         __metaclass__ = _BaseProxyMeta
73
74         def __init__(self, cache, app_name, acc_path, parent, interface, dbus_object=None, connection=None):
75                 """
76                 Create a D-Bus Proxy for an ATSPI interface.
77
78                 cache - ApplicationCache, where the cached data for the accessible can be obtained.
79                 app_name - D-Bus bus name of the application this accessible belongs to.
80                 acc_path - D-Bus object path of the server side accessible object.
81                 parent - Parent accessible.
82                 interface - D-Bus interface of the object. Used to decide which accessible class to instanciate.
83                 dbus_object(kwarg) - The D-Bus proxy object used by the accessible for D-Bus method calls.
84                 """
85                 self._cache = cache
86                 self._app_name = app_name
87                 self._acc_path = acc_path
88                 self._parent = parent
89
90                 if not dbus_object:
91                         dbus_object = connection.get_object(self._app_name, self._acc_path, introspect=False)
92                 self._dbus_object = dbus_object
93
94                 _Interface.__init__(self, self._dbus_object, interface)
95
96                 self._pgetter = self.get_dbus_method("Get", dbus_interface="org.freedesktop.DBus.Properties")
97                 self._psetter = self.get_dbus_method("Set", dbus_interface="org.freedesktop.DBus.Properties")
98
99         def __getattr__(self, *args, **kwargs):
100                 method =  _Interface.__getattr__(self, *args, **kwargs)
101
102                 def dbus_method_func(*args, **kwargs):
103                         # TODO Need to throw an AccessibleObjectNoLongerExists exception
104                         # on D-Bus error of the same type.
105                         try:
106                                 method(*args, **kwargs)
107                         except UnknownMethodException, e:
108                                 raise NotImplementedError(e)
109                         except DBusException, e:
110                                 raise LookupError(e)
111
112                 return dbus_method_func
113
114         @property
115         def cached_data(self):
116                 try:
117                         return self._cache[self._app_name][self._acc_path]
118                 except KeyError:
119                         raise AccessibleObjectNoLongerExists, \
120                                 'Cache data cannot be found for path %s in app %s' % (self._acc_path, self._app_name)
121
122         @property
123         def interfaces(self):
124                 return self._data.interfaces
125
126         def queryInterface(self, interface):
127                 """
128                 Gets a different accessible interface for this object
129                 or raises a NotImplemented error if the given interface
130                 is not supported.
131                 """
132                 if interface in self._data.interfaces:
133                         return create_accessible(self._cache,
134                                                  self._app_name,
135                                                  self._acc_path,
136                                                  self._parent,
137                                                  interface,
138                                                  dbus_object=self._dbus_object)
139                 else:
140                         raise NotImplementedError(
141                                 "%s not supported by accessible object at path %s"
142                                 % (interface, self.path))
143
144 #------------------------------------------------------------------------------
145
146 class Accessible(_BaseProxy):
147     """
148     The base interface which is implemented by all accessible objects.
149     All objects support interfaces for querying their contained
150     'children' and position in the accessible-object hierarchy,
151     whether or not they actually have children.
152     """
153     
154     def getApplication(self):
155         """
156         Get the containing Application for this object.
157         @return the Application instance to which this object belongs.
158         """
159         application_root = self._cache.get_application_root(self._app_name)
160         return create_accessible(self._cache,
161                                  self._app_name,
162                                  application_root,
163                                  constants.NULL_BUS_NAME,
164                                  constants.NULL_OBJECT_PATH,
165                                  constants.ATSPI_ACCESSIBLE,
166                                  dbus_object=self._dbus_object)
167     
168     def getAttributes(self, *args, **kwargs):
169         """
170         Get a list of properties applied to this object as a whole, as
171         an AttributeSet consisting of name-value pairs. As such these
172         attributes may be considered weakly-typed properties or annotations,
173         as distinct from the strongly-typed interface instance data declared
174         using the IDL "attribute" keyword.
175         Not all objects have explicit "name-value pair" AttributeSet
176         properties.
177         Attribute names and values may have any UTF-8 string value, however
178         where possible, in order to facilitate consistent use and exposure
179         of "attribute" properties by applications and AT clients, attribute
180         names and values should chosen from a publicly-specified namespace
181         where appropriate.
182         Where possible, the names and values in the name-value pairs
183         should be chosen from well-established attribute namespaces using
184         standard semantics. For example, attributes of Accessible objects
185         corresponding to XHTML content elements should correspond to
186         attribute names and values specified in the w3c XHTML specification,
187         at http://www.w3.org/TR/xhtml2, where such values are not already
188         exposed via a more strongly-typed aspect of the AT-SPI API. Metadata
189         names and values should be chosen from the 'Dublin Core' Metadata
190         namespace using Dublin Core semantics: http://dublincore.org/dcregistry/
191         Similarly, relevant structural metadata should be exposed using
192         attribute names and values chosen from the CSS2 and WICD specification:
193         http://www.w3.org/TR/1998/REC-CSS2-19980512 WICD (http://www.w3.org/TR/2005/WD-WICD-20051121/).
194         @return : an AttributeSet encapsulating any "attribute values"
195         currently defined for the object.
196         """
197         func = self.get_dbus_method("getAttributes")
198         return func(*args, **kwargs)
199     
200     def getChildAtIndex(self, index):
201         """
202         Get the accessible child of this object at index. 
203         @param : index
204         an in parameter indicating which child is requested (zero-indexed).
205         @return : the 'nth' Accessible child of this object.
206         """
207         path = self.cached_data.children[index]
208         return create_accessible(self._cache,
209                                  self._app_name,
210                                  path,
211                                  self,
212                                  constants.ATSPI_ACCESSIBLE,
213                                  dbus_object=self._dbus_object)
214     
215     def getIndexInParent(self, *args, **kwargs):
216         """
217         Get the index of this object in its parent's child list. 
218         @return : a long integer indicating this object's index in the
219         parent's list.
220         """
221         func = self.get_dbus_method("getIndexInParent")
222         return func(*args, **kwargs)
223     
224     def getLocalizedRoleName(self, *args, **kwargs):
225         """
226         Get a string indicating the type of UI role played by this object,
227         translated to the current locale.
228         @return : a UTF-8 string indicating the type of UI role played
229         by this object.
230         """
231         func = self.get_dbus_method("getLocalizedRoleName")
232         return func(*args, **kwargs)
233     
234     def getRelationSet(self, *args, **kwargs):
235         """
236         Get a set defining this object's relationship to other accessible
237         objects. 
238         @return : a RelationSet defining this object's relationships.
239         """
240         func = self.get_dbus_method("getRelationSet")
241         return func(*args, **kwargs)
242     
243     def getRole(self):
244         """
245         Get the Role indicating the type of UI role played by this object.
246         @return : a Role indicating the type of UI role played by this
247         object.
248         """
249         return self.cached_data.role
250     
251     def getRoleName(self, *args, **kwargs):
252         """
253         Get a string indicating the type of UI role played by this object.
254         @return : a UTF-8 string indicating the type of UI role played
255         by this object.
256         """
257         func = self.get_dbus_method("getRoleName")
258         return func(*args, **kwargs)
259     
260     def getState(self, *args, **kwargs):
261         """
262         Get the current state of the object as a StateSet. 
263         @return : a StateSet encapsulating the currently true states
264         of the object.
265         """
266         func = self.get_dbus_method("getState")
267         return func(*args, **kwargs)
268     
269     def isEqual(self, accessible):
270         """
271         Determine whether an Accessible refers to the same object as
272         another. This method should be used rather than brute-force comparison
273         of object references (i.e. "by-value" comparison), as two object
274         references may have different apparent values yet refer to the
275         same object.
276         @param : obj
277         an Accessible object reference to compare to 
278         @return : a boolean indicating whether the two object references
279         point to the same object.
280         """
281         return  (self._app_name == accessible._app_name) and \
282                 (self._acc_path == accessible._acc_path)        
283     
284     def unimplemented(self, *args, **kwargs):
285         """
286         /cond future expansion
287         """
288         func = self.get_dbus_method("unimplemented")
289         return func(*args, **kwargs)
290     
291     def get_childCount(self):
292         return len(self.cached_data.children)
293     _childCountDoc = \
294         """
295         childCount: the number of children contained by this object.
296         """
297     childCount = property(fget=get_childCount, doc=_childCountDoc)
298     
299     def get_description(self):
300         return self.cached_data.description
301     _descriptionDoc = \
302         """
303         a string describing the object in more detail than name.
304         """
305     description = property(fget=get_description, doc=_descriptionDoc)
306     
307     def get_name(self):
308         return self.cached_data.name
309     _nameDoc = \
310         """
311         a (short) string representing the object's name.
312         """
313     name = property(fget=get_name, doc=_nameDoc)
314     
315     def get_parent(self):
316         # The parent attribute is part of the base proxy
317         return self._parent
318     _parentDoc = \
319         """
320         an Accessible object which is this object's containing object.
321         """
322     parent = property(fget=get_parent, doc=_parentDoc)
323
324 # ATTENTION - Register the Accessible class with the accessible factory.
325 add_accessible_class(constants.ATSPI_ACCESSIBLE, Accessible)
326
327 #END----------------------------------------------------------------------------