61cad1e6f17a01205596012b039a93fdfd883d02
[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 import relation
23 import state
24 import registry
25
26 __all__ = [
27                 "setCacheLevel",
28                 "getCacheLevel",
29                 "clearCache",
30                 "printCache",
31                 "getInterfaceIID",
32                 "getInterfaceName",
33                 "listInterfaces",
34                 "stringToConst",
35                 "stateToString",
36                 "relationToString",
37                 "allModifiers",
38                 "findDescendant",
39                 "findAllDescendants",
40                 "findAncestor",
41                 "getPath",
42          ]
43
44 def setCacheLevel(level):
45         pass
46
47 def getCacheLevel():
48         return None
49
50 def clearCache():
51         pass
52
53 def printCache():
54         print "Print cache function is deprecated";
55
56 def getInterfaceIID(obj):
57         """
58         Gets the ID of an interface class or object in string format for use in
59         queryInterface.
60
61         @param obj: Class representing an AT-SPI interface or instance
62         @type obj: object
63         @return: IID for the interface
64         @rtype: string
65         @raise AttributeError: When the parameter does not provide typecode info
66
67         WARNING!! DEPRECATED!!
68
69         In current D-Bus version of pyatspi this simply returns a null string.
70         """
71         return ""
72
73 def getInterfaceName(obj):
74         """
75         Gets the human readable name of an interface class or object in string
76         format.
77
78         @param obj: Class representing an AT-SPI interface or instance
79         @type obj: class
80         @return: Name of the interface
81         @rtype: string
82         @raise AttributeError: When the parameter does not provide typecode info
83         """
84         return obj._dbus_interface.lstrip("org.freedesktop.atspi.")
85
86 def listInterfaces(obj):
87         """
88         Gets a list of the names of all interfaces supported by this object. The
89         names are the short-hand interface names like "Accessible" and "Component",
90         not the full interface identifiers.
91
92         @param obj: Arbitrary object to query for all accessibility related
93         interfaces. Must provide a queryInterface method.
94         @type obj: object
95         @return: Set of supported interface names
96         @rtype: set
97         @raise AttributeError: If the object provide does not implement
98         queryInterface
99         """
100         return [itf.lstrip("org.freedesktop.atspi.") for itf in obj.interfaces]
101
102 def stringToConst(prefix, suffix):
103         """
104         Maps a string name to an AT-SPI constant. The rules for the mapping are as 
105         follows:
106                 - The prefix is captalized and has an _ appended to it.
107                 - All spaces in the suffix are mapped to the _ character. 
108                 - All alpha characters in the suffix are mapped to their uppercase.
109
110         The resulting name is used with getattr to look up a constant with that name
111         in the L{constants} module. If such a constant does not exist, the string
112         suffix is returned instead.
113
114         This method allows strings to be used to refer to roles, relations, etc.
115         without direct access to the constants. It also supports the future expansion
116         of roles, relations, etc. by allowing arbitrary strings which may or may not
117         map to the current standard set of roles, relations, etc., but may still
118         match some non-standard role, relation, etc. being reported by an
119         application.
120
121         @param prefix: Prefix of the constant name such as role, relation, state, 
122                 text, modifier, key
123         @type prefix: string
124         @param suffix: Name of the role, relation, etc. to use to lookup the constant
125         @type suffix: string
126         @return: The matching constant value
127         @rtype: object
128         """
129         name = prefix.upper()+'_'+suffix.upper().replace(' ', '_')
130         return getattr(constants, name, suffix)
131
132 def stateToString(value):
133         """
134         Converts a state value to a string based on the name of the state constant in
135         the L{constants} module that has the given value.
136
137         @param value: An AT-SPI state
138         @type value: Accessibility.StateType
139         @return: Human readable, untranslated name of the state
140         @rtype: string
141         """
142         return state.STATE_VALUE_TO_NAME.get(value)
143
144 def relationToString(value):
145         """
146         Converts a relation value to a string based on the name of the state constant
147         in the L{constants} module that has the given value.
148
149         @param value: An AT-SPI relation
150         @type value: Accessibility.RelationType
151         @return: Human readable, untranslated name of the relation
152         @rtype: string
153         """
154         return relation.RELATION_VALUE_TO_NAME.get(value)
155
156 def allModifiers():
157         """
158         Generates all possible keyboard modifiers for use with 
159         L{registry.Registry.registerKeystrokeListener}.
160         """
161         mask = 0
162         while mask <= (1 << registry.MODIFIER_NUMLOCK):
163                 yield mask
164                 mask += 1
165
166 def findDescendant(acc, pred, breadth_first=False):
167         """
168         Searches for a descendant node satisfying the given predicate starting at 
169         this node. The search is performed in depth-first order by default or
170         in breadth first order if breadth_first is True. For example,
171
172         my_win = findDescendant(lambda x: x.name == 'My Window')
173
174         will search all descendants of x until one is located with the name 'My
175         Window' or all nodes are exausted. Calls L{_findDescendantDepth} or
176         L{_findDescendantBreadth} to start the recursive search.
177
178         @param acc: Root accessible of the search
179         @type acc: Accessibility.Accessible
180         @param pred: Search predicate returning True if accessible matches the 
181                         search criteria or False otherwise
182         @type pred: callable
183         @param breadth_first: Search breadth first (True) or depth first (False)?
184         @type breadth_first: boolean
185         @return: Accessible matching the criteria or None if not found
186         @rtype: Accessibility.Accessible or None
187         """
188         if breadth_first:
189                 return _findDescendantBreadth(acc, pred)
190
191         for child in acc:
192                 try:
193                         ret = _findDescendantDepth(acc, pred)
194                 except Exception:
195                         ret = None
196                 if ret is not None: return ret
197
198 def _findDescendantBreadth(acc, pred):
199         """
200         Internal function for locating one descendant. Called by L{findDescendant} to
201         start the search.
202
203         @param acc: Root accessible of the search
204         @type acc: Accessibility.Accessible
205         @param pred: Search predicate returning True if accessible matches the 
206                         search criteria or False otherwise
207         @type pred: callable
208         @return: Matching node or None to keep searching
209         @rtype: Accessibility.Accessible or None
210         """
211         for child in acc:
212                 try:
213                         if pred(child): return child
214                 except Exception:
215                         pass
216         for child in acc:
217                 try:
218                         ret = _findDescendantBreadth(child, pred)
219                 except Exception:
220                         ret = None
221                 if ret is not None: return ret
222
223 def _findDescendantDepth(acc, pred):
224         """
225         Internal function for locating one descendant. Called by L{findDescendant} to
226         start the search.
227
228         @param acc: Root accessible of the search
229         @type acc: Accessibility.Accessible
230         @param pred: Search predicate returning True if accessible matches the 
231                 search criteria or False otherwise
232         @type pred: callable
233         @return: Matching node or None to keep searching
234         @rtype: Accessibility.Accessible or None
235         """
236         try:
237                 if pred(acc): return acc
238         except Exception:
239                 pass
240         for child in acc:
241                 try:
242                         ret = _findDescendantDepth(child, pred)
243                 except Exception:
244                         ret = None
245                 if ret is not None: return ret
246
247 def findAllDescendants(acc, pred):
248         """
249         Searches for all descendant nodes satisfying the given predicate starting at 
250         this node. Does an in-order traversal. For example,
251
252         pred = lambda x: x.getRole() == pyatspi.ROLE_PUSH_BUTTON
253         buttons = pyatspi.findAllDescendants(node, pred)
254
255         will locate all push button descendants of node.
256
257         @param acc: Root accessible of the search
258         @type acc: Accessibility.Accessible
259         @param pred: Search predicate returning True if accessible matches the 
260                         search criteria or False otherwise
261         @type pred: callable
262         @return: All nodes matching the search criteria
263         @rtype: list
264         """
265         matches = []
266         _findAllDescendants(acc, pred, matches)
267         return matches
268
269 def _findAllDescendants(acc, pred, matches):
270         """
271         Internal method for collecting all descendants. Reuses the same matches
272         list so a new one does not need to be built on each recursive step.
273         """
274         for child in acc:
275                 try:
276                         if pred(child): matches.append(child)
277                 except Exception:
278                         pass
279                 _findAllDescendants(child, pred, matches)
280         
281 def findAncestor(acc, pred):
282         """
283         Searches for an ancestor satisfying the given predicate. Note that the
284         AT-SPI hierarchy is not always doubly linked. Node A may consider node B its
285         child, but B is not guaranteed to have node A as its parent (i.e. its parent
286         may be set to None). This means some searches may never make it all the way
287         up the hierarchy to the desktop level.
288
289         @param acc: Starting accessible object
290         @type acc: Accessibility.Accessible
291         @param pred: Search predicate returning True if accessible matches the 
292                 search criteria or False otherwise
293         @type pred: callable
294         @return: Node matching the criteria or None if not found
295         @rtype: Accessibility.Accessible
296         """
297         if acc is None:
298                 # guard against bad start condition
299                 return None
300         while 1:
301                 if acc.parent is None:
302                         # stop if there is no parent and we haven't returned yet
303                         return None
304                 try:
305                         if pred(acc.parent): return acc.parent
306                 except Exception:
307                         pass
308                 # move to the parent
309                 acc = acc.parent
310
311 def getPath(acc):
312         """
313         Gets the path from the application ancestor to the given accessible in
314         terms of its child index at each level.
315
316         @param acc: Target accessible
317         @type acc: Accessibility.Accessible
318         @return: Path to the target
319         @rtype: list of integer
320         @raise LookupError: When the application accessible cannot be reached
321         """
322         path = []
323         while 1:
324                 if acc.parent is None:
325                         path.reverse()
326                         return path
327                 try:
328                         path.append(acc.getIndexInParent())
329                 except Exception:
330                         raise LookupError
331                 acc = acc.parent