2008-07-28 Mark Doffman <mark.doffman@codethink.co.uk>
[platform/core/uifw/at-spi2-atk.git] / pyatspi / utils.py
1 #Copyright (C) 2008 Codethink Ltd
2 #copyright: Copyright (c) 2005, 2007 IBM Corporation
3
4 #This library is free software; you can redistribute it and/or
5 #modify it under the terms of the GNU Lesser General Public
6 #License version 2 as published by the Free Software Foundation.
7
8 #This program is distributed in the hope that it will be useful,
9 #but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #GNU General Public License for more details.
12 #You should have received a copy of the GNU Lesser General Public License
13 #along with this program; if not, write to the Free Software
14 #Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15
16 #Portions of this code originally licensed and copyright (c) 2005, 2007
17 #IBM Corporation under the BSD license, available at
18 #U{http://www.opensource.org/licenses/bsd-license.php}
19
20 #authors: Peter Parente, Mark Doffman
21
22 #TODO Re-implement and import the stateset server
23
24 def getInterfaceIID(obj):
25         """
26         Gets the ID of an interface class or object in string format for use in
27         queryInterface.
28         
29         @param obj: Class representing an AT-SPI interface or instance
30         @type obj: object
31         @return: IID for the interface
32         @rtype: string
33         @raise AttributeError: When the parameter does not provide typecode info
34
35         WARNING!! DEPRECATED!!
36
37         In current D-Bus version of pyatspi this simply returns a null string.
38         """
39         return ""
40
41 def getInterfaceName(obj):
42         """
43         Gets the human readable name of an interface class or object in string
44         format.
45         
46         @param obj: Class representing an AT-SPI interface or instance
47         @type obj: class
48         @return: Name of the interface
49         @rtype: string
50         @raise AttributeError: When the parameter does not provide typecode info
51         """
52         return obj._dbus_interface.lstrip("org.freedesktop.atspi.")
53
54 # we're importing here to avoid cyclic importants; constants relies on the
55 # two functions above
56 import constants
57
58 def listInterfaces(obj):
59         """
60         Gets a list of the names of all interfaces supported by this object. The
61         names are the short-hand interface names like "Accessible" and "Component",
62         not the full interface identifiers.
63
64         @param obj: Arbitrary object to query for all accessibility related
65         interfaces. Must provide a queryInterface method.
66         @type obj: object
67         @return: Set of supported interface names
68         @rtype: set
69         @raise AttributeError: If the object provide does not implement
70         queryInterface
71         """
72         return [itf.lstrip("org.freedesktop.atspi.") for itf in obj.interfaces]
73
74 def stringToConst(prefix, suffix):
75         """
76         Maps a string name to an AT-SPI constant. The rules for the mapping are as 
77         follows:
78                 - The prefix is captalized and has an _ appended to it.
79                 - All spaces in the suffix are mapped to the _ character. 
80                 - All alpha characters in the suffix are mapped to their uppercase.
81                 
82         The resulting name is used with getattr to look up a constant with that name
83         in the L{constants} module. If such a constant does not exist, the string
84         suffix is returned instead.
85
86         This method allows strings to be used to refer to roles, relations, etc.
87         without direct access to the constants. It also supports the future expansion
88         of roles, relations, etc. by allowing arbitrary strings which may or may not
89         map to the current standard set of roles, relations, etc., but may still
90         match some non-standard role, relation, etc. being reported by an
91         application.
92         
93         @param prefix: Prefix of the constant name such as role, relation, state, 
94                 text, modifier, key
95         @type prefix: string
96         @param suffix: Name of the role, relation, etc. to use to lookup the constant
97         @type suffix: string
98         @return: The matching constant value
99         @rtype: object
100         """
101         name = prefix.upper()+'_'+suffix.upper().replace(' ', '_')
102         return getattr(constants, name, suffix)
103
104 def stateToString(value):
105         """
106         Converts a state value to a string based on the name of the state constant in
107         the L{constants} module that has the given value.
108         
109         @param value: An AT-SPI state
110         @type value: Accessibility.StateType
111         @return: Human readable, untranslated name of the state
112         @rtype: string
113         """
114         return constants.STATE_VALUE_TO_NAME.get(value)
115
116 def relationToString(value):
117         """
118         Converts a relation value to a string based on the name of the state constant
119         in the L{constants} module that has the given value.
120         
121         @param value: An AT-SPI relation
122         @type value: Accessibility.RelationType
123         @return: Human readable, untranslated name of the relation
124         @rtype: string
125         """
126         return constants.RELATION_VALUE_TO_NAME.get(value)
127
128 def allModifiers():
129         """
130         Generates all possible keyboard modifiers for use with 
131         L{registry.Registry.registerKeystrokeListener}.
132         """
133         mask = 0
134         while mask <= (1 << constants.MODIFIER_NUMLOCK):
135                 yield mask
136                 mask += 1
137
138 def findDescendant(acc, pred, breadth_first=False):
139         """
140         Searches for a descendant node satisfying the given predicate starting at 
141         this node. The search is performed in depth-first order by default or
142         in breadth first order if breadth_first is True. For example,
143         
144         my_win = findDescendant(lambda x: x.name == 'My Window')
145         
146         will search all descendants of x until one is located with the name 'My
147         Window' or all nodes are exausted. Calls L{_findDescendantDepth} or
148         L{_findDescendantBreadth} to start the recursive search.
149         
150         @param acc: Root accessible of the search
151         @type acc: Accessibility.Accessible
152         @param pred: Search predicate returning True if accessible matches the 
153                         search criteria or False otherwise
154         @type pred: callable
155         @param breadth_first: Search breadth first (True) or depth first (False)?
156         @type breadth_first: boolean
157         @return: Accessible matching the criteria or None if not found
158         @rtype: Accessibility.Accessible or None
159         """
160         if breadth_first:
161                 return _findDescendantBreadth(acc, pred)
162
163         for child in acc:
164                 try:
165                         ret = _findDescendantDepth(acc, pred)
166                 except Exception:
167                         ret = None
168                 if ret is not None: return ret
169
170 def _findDescendantBreadth(acc, pred):
171         """             
172         Internal function for locating one descendant. Called by L{findDescendant} to
173         start the search.
174         
175         @param acc: Root accessible of the search
176         @type acc: Accessibility.Accessible
177         @param pred: Search predicate returning True if accessible matches the 
178                         search criteria or False otherwise
179         @type pred: callable
180         @return: Matching node or None to keep searching
181         @rtype: Accessibility.Accessible or None
182         """
183         for child in acc:
184                 try:
185                         if pred(child): return child
186                 except Exception:
187                         pass
188         for child in acc:
189                 try:
190                         ret = _findDescendantBreadth(child, pred)
191                 except Exception:
192                         ret = None
193                 if ret is not None: return ret
194
195 def _findDescendantDepth(acc, pred):
196         """
197         Internal function for locating one descendant. Called by L{findDescendant} to
198         start the search.
199
200         @param acc: Root accessible of the search
201         @type acc: Accessibility.Accessible
202         @param pred: Search predicate returning True if accessible matches the 
203                 search criteria or False otherwise
204         @type pred: callable
205         @return: Matching node or None to keep searching
206         @rtype: Accessibility.Accessible or None
207         """
208         try:
209                 if pred(acc): return acc
210         except Exception:
211                 pass
212         for child in acc:
213                 try:
214                         ret = _findDescendantDepth(child, pred)
215                 except Exception:
216                         ret = None
217                 if ret is not None: return ret
218                 
219 def findAllDescendants(acc, pred):
220         """
221         Searches for all descendant nodes satisfying the given predicate starting at 
222         this node. Does an in-order traversal. For example,
223         
224         pred = lambda x: x.getRole() == pyatspi.ROLE_PUSH_BUTTON
225         buttons = pyatspi.findAllDescendants(node, pred)
226         
227         will locate all push button descendants of node.
228         
229         @param acc: Root accessible of the search
230         @type acc: Accessibility.Accessible
231         @param pred: Search predicate returning True if accessible matches the 
232                         search criteria or False otherwise
233         @type pred: callable
234         @return: All nodes matching the search criteria
235         @rtype: list
236         """
237         matches = []
238         _findAllDescendants(acc, pred, matches)
239         return matches
240
241 def _findAllDescendants(acc, pred, matches):
242         """
243         Internal method for collecting all descendants. Reuses the same matches
244         list so a new one does not need to be built on each recursive step.
245         """
246         for child in acc:
247                 try:
248                         if pred(child): matches.append(child)
249                 except Exception:
250                         pass
251                 _findAllDescendants(child, pred, matches)
252         
253 def findAncestor(acc, pred):
254         """
255         Searches for an ancestor satisfying the given predicate. Note that the
256         AT-SPI hierarchy is not always doubly linked. Node A may consider node B its
257         child, but B is not guaranteed to have node A as its parent (i.e. its parent
258         may be set to None). This means some searches may never make it all the way
259         up the hierarchy to the desktop level.
260         
261         @param acc: Starting accessible object
262         @type acc: Accessibility.Accessible
263         @param pred: Search predicate returning True if accessible matches the 
264                 search criteria or False otherwise
265         @type pred: callable
266         @return: Node matching the criteria or None if not found
267         @rtype: Accessibility.Accessible
268         """
269         if acc is None:
270                 # guard against bad start condition
271                 return None
272         while 1:
273                 if acc.parent is None:
274                         # stop if there is no parent and we haven't returned yet
275                         return None
276                 try:
277                         if pred(acc.parent): return acc.parent
278                 except Exception:
279                         pass
280                 # move to the parent
281                 acc = acc.parent
282
283 def getPath(acc):
284         """
285         Gets the path from the application ancestor to the given accessible in
286         terms of its child index at each level.
287         
288         @param acc: Target accessible
289         @type acc: Accessibility.Accessible
290         @return: Path to the target
291         @rtype: list of integer
292         @raise LookupError: When the application accessible cannot be reached
293         """
294         path = []
295         while 1:
296                 if acc.parent is None:
297                         path.reverse()
298                         return path
299                 try:
300                         path.append(acc.getIndexInParent())
301                 except Exception:
302                         raise LookupError
303                 acc = acc.parent