1 '''
2 Defines a class unifying navigation over all accessibles and their items.
3
4 @todo: PP: need robust strategy for handling cycles (mark nodes as visited
5 somehow?)
6
7 @author: Peter Parente
8 @organization: IBM Corporation
9 @copyright: Copyright (c) 2005, 2007 IBM Corporation
10 @license: The BSD License
11
12 All rights reserved. This program and the accompanying materials are made
13 available under the terms of the BSD license which accompanies
14 this distribution, and is available at
15 U{http://www.opensource.org/licenses/bsd-license.php}
16 '''
17
18 from Base import AEWalker
19 from AccessEngine.AEAccInterfaces import *
20
22 '''
23 Walks the accessible hierarchy by accessibles and their items returning
24 L{AEPor}s. The walk order is an in-order traversal of the subtree of the
25 accessible hierarchy rooted at a top-level window accessible. The subtree is
26 assumed to have no loops, though logic could be added to detect them. The
27 supported flags determine whether invisible or trivial items are skipped or
28 not. This class uses the L{AEAccInterfaces} to leave the decision of what
29 constitutes an item, what constitutes a trivial accessible, and what
30 constitutes and invisible accessible to the objects that implement the
31 interfaces.
32
33 @ivar allow_invisible: Stop on invisible L{AEPor}s as well as visible?
34 @type allow_invisible: boolean
35 @ivar allow_trivial: Stop on trivial L{AEPor}s as well as non-trivial?
36 @type allow_trivial: boolean
37 '''
38 - def __init__(self, por, only_visible=True, allow_trivial=False):
39 '''
40 Stores the starting L{AEPor}.
41
42 @param only_visible: Stop only on visible L{AEPor}s?
43 @type only_visible: boolean
44 @param allow_trivial: Stop on trivial L{AEPor}s as well as non-trivial?
45 @type allow_trivial: boolean
46 @param por: The current L{AEPor} for the L{AEWalker}
47 @type por: L{AEPor}
48 '''
49 super(AccessibleItemWalker, self).__init__(por)
50 self.allow_invisible = not only_visible
51 self.allow_trivial = allow_trivial
52
54 '''
55 Gets the next item in the current accessible in the given L{AEPor}. Returns
56 that next item if it exists. If not or if the accessible in the given
57 L{AEPor} is not visible, returns the current L{AEPor} and the L{_getFirstChild}
58 as the method to call to continue the search.
59
60 @param por: Initial L{AEPor}
61 @type por: L{AEPor}
62 @return: L{AEPor} and the next method to call to continue the search, or
63 L{AEPor} and None to indicate the search is complete
64 @rtype: 2-tuple of L{AEPor}, callable
65 '''
66 if self.allow_invisible or IAccessibleInfo(por).isAccVisible():
67 try:
68
69 item = IItemNav(por).getNextItem(not self.allow_invisible)
70 return (item, None)
71 except (LookupError, IndexError):
72
73 pass
74 return (por, self._getFirstChild)
75
77 '''
78 Gets the first child of the current accessible in the given L{AEPor}. Returns
79 the child accessible if it exists, is visible, and is not trivial. If it
80 does not exist or is invisible, returns the given L{AEPor} and
81 L{_getNextPeer} as the method to call to continue the search. If it is
82 trivial, returns the child accessible and L{_getFirstChild} as the method
83 to call to continue the search.
84
85 @param por: Initial L{AEPor}
86 @type por: L{AEPor}
87 @return: L{AEPor} and the next method to call to continue the search, or
88 L{AEPor} and None to indicate the search is complete
89 @rtype: 2-tuple of L{AEPor}, callable
90 '''
91 try:
92
93 child = IAccessibleNav(por).getFirstAccChild()
94 ai = IAccessibleInfo(child)
95 if not self.allow_invisible and not ai.isAccVisible():
96
97 return (child, self._getNextPeer)
98 elif not self.allow_trivial and ai.isAccTrivial():
99
100 return (child, self._getFirstChild)
101 else:
102
103 return (child, None)
104 except (LookupError, IndexError):
105
106 return (por, self._getNextPeer)
107
109 '''
110 Gets the next peer of the current accessible in the given L{AEPor}. Returns
111 the peer accessible if it exists, is visible, and is not trivial. If it
112 does not exist, returns the given L{AEPor} and L{_getParentNextItem} as the
113 method to call to continue the search. If it is invisible, returns the peer
114 L{AEPor} and L{_getNextPeer} as the method to call to continue the search. If
115 it is trivial, returns the peer and L{_getFirstChild} as the method to call
116 to continue the search. If it is trivial, returns the child accessible and
117 L{_getFirstChild} as the method to call to continue the search.
118
119 @param por: Initial L{AEPor}
120 @type por: L{AEPor}
121 @return: L{AEPor} and the next method to call to continue the search, or
122 L{AEPor} and None to indicate the search is complete
123 @rtype: 2-tuple of L{AEPor}, callable
124 '''
125 try:
126
127 if IAccessibleInfo(por).isAccEmbedded():
128 raise IndexError
129
130 next = IAccessibleNav(por).getNextAcc()
131 ai = IAccessibleInfo(next)
132 if not self.allow_invisible and not ai.isAccVisible():
133
134 return (next, self._getNextPeer)
135 elif not self.allow_trivial and ai.isAccTrivial():
136
137 return (next, self._getFirstChild)
138 else:
139
140 return (next, None)
141 except (LookupError, IndexError):
142
143 return (por, self._getParentNextItem)
144
146 '''
147 Gets the next item in the parent accessible of the current accessible in
148 the given L{AEPor}. Returns the next item if it exists. Else, returns the
149 parent accessible and L{_getNextPeer} as the method to call to continue the
150 search if the parent exists. Returns a sentinel (None, None) if there is no
151 parent indicating the given L{AEPor} is the root of the subtree containing
152 the starting L{AEPor}.
153
154 @param por: Initial L{AEPor}
155 @type por: L{AEPor}
156 @return: L{AEPor} and the next method to call to continue the search, or
157 L{AEPor} and None to indicate the search is complete
158 @rtype: 2-tuple of L{AEPor}, callable
159 '''
160 try:
161 parent = IAccessibleNav(por).getParentAcc()
162 if IAccessibleInfo(parent).isAccTopLevelWindow():
163
164 raise IndexError
165 except (LookupError, IndexError):
166
167 return (None, None)
168
169 try:
170
171 por = IItemNav(parent).getAccAsItem(por)
172 return (por, self._getNextItem)
173 except (LookupError, IndexError):
174
175 return (parent, self._getNextPeer)
176
178 '''
179 Gets the previous item in the current accessible in the given L{AEPor}.
180 Returns that previous item if it exists. If not or if the accessible in the
181 given L{AEPor} is not visible, returns the current L{AEPor} and the
182 L{_getPrevPeer} as the method to call to continue the search.
183
184 @param por: Initial L{AEPor}
185 @type por: L{AEPor}
186 @return: L{AEPor} and the next method to call to continue the search, or
187 L{AEPor} and None to indicate the search is complete
188 @rtype: 2-tuple of L{AEPor}, callable
189 '''
190 if self.allow_invisible or IAccessibleInfo(por).isAccVisible():
191 try:
192
193 item = IItemNav(por).getPrevItem(not self.allow_invisible)
194 if item.isSameAcc(por):
195
196 return (item, None)
197 else:
198
199 return (item, self._getLastChild)
200 except (LookupError, IndexError):
201
202 pass
203 return (por, self._getPrevPeer)
204
206 '''
207 Gets the previous peer of the current accessible in the given L{AEPor}. If it
208 does not exist, returns the given L{AEPor} and L{_getParent} as the method to
209 call to continue the search. If it is not visible, returns the peer
210 accessible and L{_getPrevPeer} as the method to call to continue the search.
211 Otherwise, returns the peer accessible and L{_getLastChild} as the method to
212 call to continue the search.
213
214 @param por: Initial L{AEPor}
215 @type por: L{AEPor}
216 @return: L{AEPor} and the next method to call to continue the search, or
217 L{AEPor} and None to indicate the search is complete
218 @rtype: 2-tuple of L{AEPor}, callable
219 '''
220 try:
221
222 if IAccessibleInfo(por).isAccEmbedded():
223 raise IndexError
224
225 prev = IAccessibleNav(por).getPrevAcc()
226 ai = IAccessibleInfo(prev)
227 if ai.isAccTopLevelWindow():
228
229 return (None, None)
230 elif self.allow_invisible or ai.isAccVisible():
231
232 return (prev, self._getLastChild)
233 else:
234
235 return (prev, self._getPrevPeer)
236 except (LookupError, IndexError):
237
238 return (por, self._getParentPrevItem)
239
241 '''
242 Gets the previous item in the parent accessible of the current accessible
243 in the given L{AEPor}. Returns the previous item if it exists. Else, returns
244 the parent accessible and L{_getParent} as the method to call to continue
245 the search if the parent exists. Returns a sentinel (None, None) if there
246 is no parent indicating the given L{AEPor} is the root of the subtree
247 containing the starting L{AEPor}.
248
249 @param por: Initial L{AEPor}
250 @type por: L{AEPor}
251 @return: L{AEPor} and the next method to call to continue the search, or
252 L{AEPor} and None to indicate the search is complete
253 @rtype: 2-tuple of L{AEPor}, callable
254 '''
255 try:
256 ai = IAccessibleInfo(por)
257 if ai.isAccTopLevelWindow():
258 raise IndexError
259 parent = IAccessibleNav(por).getParentAcc()
260 except (LookupError, IndexError):
261
262 return (None, None)
263
264 try:
265
266 por = IItemNav(parent).getAccAsItem(por)
267 return (por, self._getPrevItem)
268 except (LookupError, IndexError):
269
270 return (por, self._getParent)
271
273 '''
274 Gets the last child of the accessible in the given L{AEPor}. If it does not
275 exist, checks if the given L{AEPor} is invisible or trivial. If so, returns
276 the given L{AEPor} and L{_getPrevPeer} to continue the search. If not,
277 returns a L{AEPor} to the last item in the given L{AEPor} as the result.
278
279 If the last child does exist, checks if it is visible. If so, returns the
280 child and L{_getLastChild} to continue the search. If not, returns the
281 child and L{_getPrevPeer} to continue the search.
282
283 @param por: Initial L{AEPor}
284 @type por: L{AEPor}
285 @return: L{AEPor} and the next method to call to continue the search, or
286 L{AEPor} and None to indicate the search is complete
287 @rtype: 2-tuple of L{AEPor}, callable
288 '''
289 try:
290
291 child = IAccessibleNav(por).getLastAccChild()
292 ai = IAccessibleInfo(child)
293 if self.allow_invisible or ai.isAccVisible():
294
295 return (child, self._getLastChild)
296 else:
297
298 return (child, self._getPrevPeer)
299 except (LookupError, IndexError), e:
300 ai = IAccessibleInfo(por)
301 if ((not self.allow_invisible and not ai.isAccVisible()) or
302 (not self.allow_trivial and ai.isAccTrivial())):
303
304 return (por, self._getPrevPeer)
305 else:
306
307 return (por, self._getLastItem)
308
310 '''
311 Gets the last visible item of the given L{AEPor}. Returns the given L{AEPor} if
312 any errors occur.
313
314 @param por: Initial L{AEPor}
315 @type por: L{AEPor}
316 @return: L{AEPor} pointing to the last item of the accessible in the given
317 L{AEPor}
318 @rtype: L{AEPor}
319 '''
320 try:
321 item = IItemNav(por).getLastItem(not self.allow_invisible)
322 ai = IAccessibleInfo(item)
323 if item.isSameAcc(por):
324
325 return (item, None)
326 elif self.allow_invisible or ai.isAccVisible():
327
328 return (item, self._getLastChild)
329 else:
330
331 return (item, self._getPrevItem)
332 except (LookupError, IndexError):
333
334 return (por, None)
335
337 '''
338 Gets the parent accessible of the one in the given L{AEPor}. Returns the last
339 item in the parent if it exists. If it does not exist, Returns a sentinel
340 (None, None) indicating the given L{AEPor} is the root of the subtree
341 containing the starting L{AEPor}. If the parent is invisible or trivial,
342 returns the parent and L{_getPrevPeer} as the method to call to continue
343 the search.
344
345 @param por: Initial L{AEPor}
346 @type por: L{AEPor}
347 @return: L{AEPor} and the next method to call to continue the search, or
348 L{AEPor} and None to indicate the search is complete, or None and None to
349 indicate we're at the root
350 @rtype: 2-tuple of L{AEPor}, callable
351 '''
352 try:
353
354 parent = IAccessibleNav(por).getParentAcc()
355 ai = IAccessibleInfo(parent)
356 if ((not self.allow_invisible and not ai.isAccVisible()) or
357 (not self.allow_trivial and ai.isAccTrivial())):
358
359 return (parent, self._getPrevPeer)
360 else:
361
362 return (parent, None)
363 except (LookupError, IndexError):
364
365 return (None, None)
366
368 '''
369 Gets the next L{AEPor} in the walk order. Calls L{_getNextItem},
370 L{_getFirstChild}, L{_getNextPeer}, and L{_getParentNextItem} to attempt to
371 get the next valid L{AEPor}. Each method determines whether the L{AEPor} is
372 valid as the next L{AEPor}, and, if not, which call to make next. Each method
373 potentially returns a L{AEPor} and the next method to call to continue the
374 search for the next L{AEPor}.
375
376 @return: Next L{AEPor} or None if this is the last L{AEPor}
377 @rtype: L{AEPor} or None
378 '''
379 state = self._getNextItem
380 por = self.por
381 while state is not None:
382
383 por, state = state(por)
384 if por is not None:
385 self.por = por
386
387 return por
388
390 '''
391 Gets the previous L{AEPor} in the walk order. Calls L{_getPrevItem},
392 L{_getPrevPeer}, L{_getLastChild}, and L{_getParentPrevItem} to attempt to
393 get the previous valid L{AEPor}. Each method determines whether the L{AEPor} is
394 valid as the previous L{AEPor}, and, if not, which call to make next. Each
395 method potentially returns a L{AEPor} and the next method to call to continue
396 the search for the previous L{AEPor}.
397
398 @return: Previous L{AEPor} or None if this is the first L{AEPor}
399 @rtype: L{AEPor} or None
400 '''
401 state = self._getPrevItem
402 por = self.por
403 while state is not None:
404
405 por, state = state(por)
406 if por is not None:
407 self.por = por
408
409 return por
410
412 '''
413 Gets the first L{AEPor} in the walk order. Searches up the accessible
414 hierarchy until an accessible with no parent is encountered. The last
415 visited child of that accessible is the first L{AEPor} (i.e. the top-level
416 window containing the starting L{AEPor}).
417
418 @return: First L{AEPor}
419 @rtype: L{AEPor}
420 '''
421 por = self.por
422 child = self.por
423 while 1:
424 try:
425 parent = IAccessibleNav(por).getParentAcc()
426 except LookupError:
427 self.por = child
428 return child
429 else:
430 child = por
431 por = parent
432
434 '''
435 Gets the last L{AEPor} in the walk order. Searches down the last child branch
436 of the accessible hierarchy to the deepest accessible. The search then
437 proceeds through previous L{AEPor}s in the walk order until the first visible,
438 non-trivial accessible is encountered. The last item of that accessible is
439 the last L{AEPor}.
440
441 @return: Last L{AEPor}
442 @rtype: L{AEPor}
443 '''
444
445 self.getFirstPOR()
446 while 1:
447 try:
448 self.por = IAccessibleNav(self.por).getLastAccChild()
449 except LookupError:
450 ai = IAccessibleInfo(self.por)
451 if ((not ai.isAccTrivial() or self.allow_trivial) and
452 (ai.isAccVisible() or self.allow_invisible)):
453 old = self.por
454
455 try:
456 self.por = IItemNav(self.por).getLastItem(not self.allow_invisible)
457 except LookupError:
458 pass
459 if self.por == old:
460
461 return self.por
462 else:
463
464 return self.getPrevPOR()
465
467 '''
468 Gets the parent L{AEPor} of the current L{AEPor}. Searches up the accessible
469 hierarchy to find the first non-trivial, visible containing element.
470
471 @return: Parent L{AEPor} or None if at root
472 @rtype: L{AEPor}
473 '''
474 while 1:
475 try:
476
477 self.por = IAccessibleNav(self.por).getParentAcc()
478 ai = IAccessibleInfo(self.por)
479 if ((self.allow_invisible or ai.isAccVisible()) and
480 (not ai.isAccTrivial() or self.allow_trivial)):
481 return self.por
482 except (LookupError, IndexError):
483
484 return None
485