1 '''
2 Defines a mixin class that extend the functionality of the existing AT-SPI
3 objects with features such as searching on any criteria and comparison of
4 two AT-SPI Accessibles.
5
6 @author: Peter Parente
7 @organization: IBM Corporation
8 @copyright: Copyright (c) 2005, 2007 IBM Corporation
9 @license: The BSD License
10
11 All rights reserved. This program and the accompanying materials are made
12 available under the terms of the BSD license which accompanies
13 this distribution, and is available at
14 U{http://www.opensource.org/licenses/bsd-license.php}
15 '''
16
17 import new, types
18 import Constants, Interfaces
19
20 -def stirInto(cls, new_cls, include=None, exclude=None):
21 '''
22 Adds the methods in new_cls named in include or those not in exclude, or all
23 methods if neither is specified, to cls. After stirring, all instances of
24 cls will have the new methods. If there is a method name clash, the method
25 already in cls will be prefixed with an underscore before the new method of
26 the same name is mixed in.
27
28 Based on code from http://wwwx.cs.unc.edu/~parente/cgi-bin/RuntimeClassMixins.
29
30 @param cls: Existing class to mix features into
31 @type cls: class
32 @param new_cls: Class containing features to add
33 @type new_cls: class
34 @param include: Method names to add
35 @type include: list
36 @param exclude: Method names to avoid adding
37 @type exclude: list
38 '''
39
40 for name in new_cls.__dict__.keys():
41
42 if isinstance(new_cls.__dict__[name], types.FunctionType) and \
43 (include is None or name in include) and \
44 (exclude is None or name not in exclude):
45
46 func = new_cls.__dict__[name]
47
48 method = new.function(func.func_code, func.func_globals, name,
49 func.func_defaults, func.func_closure)
50 try:
51
52 old_method = getattr(cls, name)
53 except AttributeError:
54 pass
55 else:
56
57 setattr(cls, '_'+name, old_method)
58
59 setattr(cls, name, method)
60
62 '''
63 Defines additional methods to be added to the AT-SPI Accessible object. The
64 features defined here will be added to the Accessible class at run time so
65 that all instances of Accessible have them (i.e. there is no need to
66 explicitly wrap an Accessible in this class or derive a new class from it.)
67 '''
69 '''
70 Decrements the reference count on the accessible object when there are no
71 Python references to this object. This provides automatic reference
72 counting for AT-SPI objects.
73 '''
74 try:
75 self.unref()
76 except Exception:
77 pass
78
80 '''
81 Iterator that yields one accessible child per iteration. If an exception is
82 encountered, None is yielded instead.
83
84 @return: A child accessible
85 @rtype: Accessibility.Accessible
86 '''
87 for i in xrange(self.childCount):
88 try:
89 yield self.getChildAtIndex(i)
90 except Constants.CORBAException:
91 yield None
92
94 '''
95 Gets a human readable representation of the accessible.
96
97 @return: Role and name information for the accessible
98 @rtype: string
99 '''
100 try:
101 return '[%s | %s]' % (self.getLocalizedRoleName(), self.name)
102
103 except Exception:
104 return '[DEAD]'
105
107 '''
108 @return: True, always
109 @rtype: boolean
110 '''
111 return True
112
114 '''
115 Thin wrapper around getChildAtIndex.
116
117 @param index: Index of desired child
118 @type index: integer
119 @return: Accessible child
120 @rtype: Accessibility.Accessible
121 '''
122 return self.getChildAtIndex(index)
123
125 '''
126 Thin wrapper around childCount.
127
128 @return: Number of child accessibles
129 @rtype: integer
130 '''
131 return self.childCount
132
134 '''
135 Gets the index of this accessible in its parent. Uses the implementation of
136 this method provided by the Accessibility.Accessible object, but checks the
137 bound of the value to ensure it is not outside the range of childCount
138 reported by this accessible's parent.
139
140 @return: Index of this accessible in its parent
141 @rtype: integer
142 '''
143 i = self._getIndexInParent()
144 try:
145
146 return min(self.parent.childCount-1, i)
147 except AttributeError:
148
149 return -1
150
152 '''
153 Searches for a descendant node satisfying the given predicate starting at
154 this node. The search is performed in depth-first order by default or
155 in breadth first order if breadth_first is True. For example,
156
157 my_win = node.findDescendant(lambda x: x.name == 'My Window')
158
159 will search all descendants of node until one is located with the name 'My
160 Window' or all nodes are exausted. Calls L{_findDescendantDepth} or
161 L{_findDescendantBreadth} to start the recursive search.
162
163 @param pred: Search predicate returning True if accessible matches the
164 search criteria or False otherwise
165 @type pred: callable
166 @param breadth_first: Search breadth first (True) or depth first (False)?
167 @type breadth_first: boolean
168 @return: Accessible matching the criteria or None if not found
169 @rtype: Accessibility.Accessible or None
170 '''
171 if breadth_first:
172 return self._findDescendantBreadth(pred)
173 for i in xrange(self.childCount):
174 try:
175 c = self.getChildAtIndex(i)
176 ret = c._findDescendantDepth(pred)
177 except Exception:
178 ret = None
179 if ret is not None: return ret
180
182 '''
183 Internal function for locating one descendant. Called by
184 L{AccessibleMixin.findDescendant} to start the search.
185
186 @param pred: Search predicate returning True if accessible matches the
187 search criteria or False otherwise
188 @type pred: callable
189 @return: Matching node or None to keep searching
190 @rtype: Accessibility.Accessible or None
191 '''
192 for i in xrange(self.childCount):
193 try:
194 c = self.getChildAtIndex(i)
195 if pred(c): return c
196 except Exception:
197 pass
198 for i in xrange(self.childCount):
199 try:
200 c = self.getChildAtIndex(i)
201 ret = c._findDescendantBreadth(pred)
202 except Exception:
203 ret = None
204 if ret is not None: return ret
205
207 '''
208 Internal function for locating one descendant. Called by
209 L{AccessibleMixin.findDescendant} to start the search.
210
211 @param pred: Search predicate returning True if accessible matches the
212 search criteria or False otherwise
213 @type pred: callable
214 @return: Matching node or None to keep searching
215 @rtype: Accessibility.Accessible or None
216 '''
217 try:
218 if pred(self): return self
219 except Exception:
220 pass
221 for i in xrange(self.childCount):
222 try:
223 c = self.getChildAtIndex(i)
224 ret = c._findDescendantDepth(pred)
225 except Exception:
226 ret = None
227 if ret is not None: return ret
228
230 '''
231 Searches for all descendant nodes satisfying the given predicate starting at
232 this node. For example,
233
234 pred = lambda x: x.getRole() == Accessible.ROLE_PUSH_BUTTON
235 buttons = node.findAllDescendants(pred)
236
237 will locate all push button descendants of the current node. Calls
238 L{findIterDescendants} to start the exhaustive recursive search.
239
240 @param pred: Search predicate returning True if accessible matches the
241 search criteria or False otherwise
242 @type pred: callable
243 @return: All nodes matching the search criteria
244 @rtype: list
245 '''
246 return [result for result in self.findIterDescendants(pred)]
247
249 '''
250 Iterator that yields Accessibility.Accessible that passed search criteria
251 defined by pred. The search is performed in depth-first order and the
252 results are returned in that order.
253
254 @param pred: Search predicate returning True if accessible matches the
255 search criteria or False otherwise
256 @type pred: callable
257 @return: A node matching the search criteria
258 @rtype: Accessibility.Accessible or None
259 '''
260 for i in xrange(self.childCount):
261 try:
262 c = self.getChildAtIndex(i)
263 if pred(self): yield c
264 except AttributeError:
265 continue
266
268 '''
269 Searches for an ancestor satisfying the given predicate. Note that the
270 AT-SPI DOM is not perfectly doubly linked. Node A may consider node B its
271 child, but B is not guaranteed to have node A as its parent (i.e. its parent
272 may be set to None). This means some searches may never make it all the
273 way up the DOM to the desktop level.
274
275 @param pred: Search predicate returning True if accessible matches the
276 search criteria or False otherwise
277 @type pred: callable
278 @return: Node matching the criteria or None if not found
279 @rtype: Accessibility.Accessible or None
280 '''
281 if self.parent is None:
282 return None
283 else:
284 try:
285 if pred(self.parent): return self.parent
286 except Exception:
287 pass
288 return self.parent.findAncestor(pred)
289
291 '''
292 Gets the most-parent accessible (the application) of this accessible. Tries
293 using the getApplication method introduced in AT-SPI 1.7.0 first before
294 resorting to traversing parent links.
295
296 @warning: Cycles involving more than the previously traversed accessible
297 are not detected by this code.
298 @return: Application object
299 @rtype: Accessibility.Application
300 '''
301 try:
302 return self._getApplication()
303 except AttributeError:
304 pass
305 curr = self
306 try:
307 while curr.parent is not None and (not curr.parent == curr):
308 curr = curr.parent
309 return Interfaces.IApplication(curr)
310 except Exception:
311 pass
312
313 return None
314
316 '''
317 Verifies that the underlying CORBA object still exists.
318
319 @return: True if the CORBA object is still valid. False if not.
320 @rtype: boolean
321 '''
322 try:
323 self.id
324 except Exception:
325 return False
326 else:
327 return True
328
329
330
331
332 import Accessibility
333 stirInto(Accessibility.Accessible, AccessibleMixin)
334