2 Utility functions for AT-SPI for querying interfaces, searching the hierarchy,
3 converting constants to strings, and so forth.
6 @organization: IBM Corporation
7 @copyright: Copyright (c) 2005, 2007 IBM Corporation
10 This library is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Library General Public
12 License as published by the Free Software Foundation; either
13 version 2 of the License, or (at your option) any later version.
15 This library is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 Library General Public License for more details.
20 You should have received a copy of the GNU Library General Public
21 License along with this library; if not, write to the
22 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 Boston, MA 02111-1307, USA.
25 Portions of this code originally licensed and copyright (c) 2005, 2007
26 IBM Corporation under the BSD license, available at
27 U{http://www.opensource.org/licenses/bsd-license.php}
29 import Accessibility__POA
31 def getInterfaceIID(obj):
33 Gets the ID of an interface class or object in string format for use in
36 @param obj: Class representing an AT-SPI interface or instance
38 @return: IID for the interface
40 @raise AttributeError: When the parameter does not provide typecode info
42 return obj.__typecode__.repo_id
44 def getInterfaceName(obj):
46 Gets the human readable name of an interface class or object in string
49 @param obj: Class representing an AT-SPI interface or instance
51 @return: Name of the interface
53 @raise AttributeError: When the parameter does not provide typecode info
55 return obj.__typecode__.name
57 # we're importing here to avoid cyclic importants; constants relies on the
61 def listInterfaces(obj):
63 Gets a list of the names of all interfaces supported by this object. The
64 names are the short-hand interface names like "Accessible" and "Component",
65 not the full interface identifiers.
67 @param obj: Arbitrary object to query for all accessibility related
68 interfaces. Must provide a queryInterface method.
70 @return: Set of supported interface names
72 @raise AttributeError: If the object provide does not implement
76 for ic in constants.ALL_INTERFACES:
77 io = obj.queryInterface(getInterfaceIID(ic))
80 names.add(getInterfaceName(ic))
83 def stringToConst(prefix, suffix):
85 Maps a string name to an AT-SPI constant. The rules for the mapping are as
87 - The prefix is captalized and has an _ appended to it.
88 - All spaces in the suffix are mapped to the _ character.
89 - All alpha characters in the suffix are mapped to their uppercase.
91 The resulting name is used with getattr to look up a constant with that name
92 in the L{constants} module. If such a constant does not exist, the string
93 suffix is returned instead.
95 This method allows strings to be used to refer to roles, relations, etc.
96 without direct access to the constants. It also supports the future expansion
97 of roles, relations, etc. by allowing arbitrary strings which may or may not
98 map to the current standard set of roles, relations, etc., but may still
99 match some non-standard role, relation, etc. being reported by an
102 @param prefix: Prefix of the constant name such as role, relation, state,
105 @param suffix: Name of the role, relation, etc. to use to lookup the constant
107 @return: The matching constant value
110 name = prefix.upper()+'_'+suffix.upper().replace(' ', '_')
111 return getattr(constants, name, suffix)
113 def stateToString(value):
115 Converts a state value to a string based on the name of the state constant in
116 the L{constants} module that has the given value.
118 @param value: An AT-SPI state
119 @type value: Accessibility.StateType
120 @return: Human readable, untranslated name of the state
123 return constants.STATE_VALUE_TO_NAME.get(value)
125 def relationToString(value):
127 Converts a relation value to a string based on the name of the state constant
128 in the L{constants} module that has the given value.
130 @param value: An AT-SPI relation
131 @type value: Accessibility.RelationType
132 @return: Human readable, untranslated name of the relation
135 return constants.RELATION_VALUE_TO_NAME.get(value)
139 Generates all possible keyboard modifiers for use with
140 L{registry.Registry.registerKeystrokeListener}.
143 while mask <= (1 << constants.MODIFIER_NUMLOCK):
147 def findDescendant(acc, pred, breadth_first=False):
149 Searches for a descendant node satisfying the given predicate starting at
150 this node. The search is performed in depth-first order by default or
151 in breadth first order if breadth_first is True. For example,
153 my_win = findDescendant(lambda x: x.name == 'My Window')
155 will search all descendants of x until one is located with the name 'My
156 Window' or all nodes are exausted. Calls L{_findDescendantDepth} or
157 L{_findDescendantBreadth} to start the recursive search.
159 @param acc: Root accessible of the search
160 @type acc: Accessibility.Accessible
161 @param pred: Search predicate returning True if accessible matches the
162 search criteria or False otherwise
164 @param breadth_first: Search breadth first (True) or depth first (False)?
165 @type breadth_first: boolean
166 @return: Accessible matching the criteria or None if not found
167 @rtype: Accessibility.Accessible or None
170 return _findDescendantBreadth(acc, pred)
174 ret = _findDescendantDepth(acc, pred)
177 if ret is not None: return ret
179 def _findDescendantBreadth(acc, pred):
181 Internal function for locating one descendant. Called by L{findDescendant} to
184 @param acc: Root accessible of the search
185 @type acc: Accessibility.Accessible
186 @param pred: Search predicate returning True if accessible matches the
187 search criteria or False otherwise
189 @return: Matching node or None to keep searching
190 @rtype: Accessibility.Accessible or None
194 if pred(child): return child
199 ret = _findDescedantBreadth(child, pred)
202 if ret is not None: return ret
204 def _findDescendantDepth(acc, pred):
206 Internal function for locating one descendant. Called by L{findDescendant} to
209 @param acc: Root accessible of the search
210 @type acc: Accessibility.Accessible
211 @param pred: Search predicate returning True if accessible matches the
212 search criteria or False otherwise
214 @return: Matching node or None to keep searching
215 @rtype: Accessibility.Accessible or None
218 if pred(acc): return acc
223 ret = _findDescendantDepth(child, pred)
226 if ret is not None: return ret
228 def findAllDescendants(acc, pred):
230 Searches for all descendant nodes satisfying the given predicate starting at
231 this node. Does an in-order traversal. For example,
233 pred = lambda x: x.getRole() == pyatspi.ROLE_PUSH_BUTTON
234 buttons = pyatspi.findAllDescendants(node, pred)
236 will locate all push button descendants of node.
238 @param acc: Root accessible of the search
239 @type acc: Accessibility.Accessible
240 @param pred: Search predicate returning True if accessible matches the
241 search criteria or False otherwise
243 @return: All nodes matching the search criteria
247 _findAllDescendants(acc, pred, matches)
250 def _findAllDescendants(acc, pred, matches):
252 Internal method for collecting all descendants. Reuses the same matches
253 list so a new one does not need to be built on each recursive step.
257 if pred(child): matches.append(child)
260 findAllDescendants(child, pred, matches)
262 def findAncestor(acc, pred):
264 Searches for an ancestor satisfying the given predicate. Note that the
265 AT-SPI hierarchy is not always doubly linked. Node A may consider node B its
266 child, but B is not guaranteed to have node A as its parent (i.e. its parent
267 may be set to None). This means some searches may never make it all the way
268 up the hierarchy to the desktop level.
270 @param acc: Starting accessible object
271 @type acc: Accessibility.Accessible
272 @param pred: Search predicate returning True if accessible matches the
273 search criteria or False otherwise
275 @return: Node matching the criteria or None if not found
276 @rtype: Accessibility.Accessible
279 # guard against bad start condition
282 if acc.parent is None:
283 # stop if there is no parent and we haven't returned yet
286 if pred(acc.parent): return acc.parent
294 Gets the path from the application ancestor to the given accessible in
295 terms of its child index at each level.
297 @param acc: Target accessible
298 @type acc: Accessibility.Accessible
299 @return: Path to the target
300 @rtype: list of integer
301 @raise LookupError: When the application accessible cannot be reached
305 if acc.parent is None:
309 path.append(acc.getIndexInParent())
314 class StateSet(Accessibility__POA.StateSet):
316 Convenience implementation of AT-SPI StateSet, for future use with Collection
319 @param states: Set of states
322 def __init__(self, *states):
323 '''Initializes the state set with the given states.'''
324 self.states = set(states)
326 def contains(self, state):
328 Checks if this L{StateSet} contains the given state.
330 @param state: State to check
331 @type state: Accessibility.StateType
332 @return: True if the set contains the given state
335 return state in self.states
337 def add(self, *state):
339 Adds one or more states to this set.
341 @param state: State(s) to add
342 @type state: Accessibility.StateType
344 self.states.add(state)
346 def remove(self, *state):
348 Removes one or more states from this set.
350 @param state: State(s) to remove
351 @type state: Accessibility.StateType
353 self.states.remove(state)
355 def equals(self, state_set):
357 Checks if this L{StateSet} contains exactly the same members as the given
360 @param state_set: Another set
361 @type state_set: L{StateSet}
362 @return: Are the sets equivalent in terms of their contents?
365 return self.state_set == self.states
367 def compare(self, state_set):
369 Computes the symmetric differences of this L{StateSet} and the given
372 @param state_set: Another set
373 @type state_set: L{StateSet}
374 @return: Elements in only one of the two sets
377 diff = self.states.symmetric_difference(state_set.states)
378 return StateSet(*diff)
382 Checks if this L{StateSet} is empty.
384 @return: Is it empty?
387 return len(self.states) == 0
391 Gets the sequence of all states in this set.
393 @return: List of states
396 return list(self.states)