Package AccessEngine :: Package AEWalkers :: Module AccessibleItemWalker
[hide private]
[frames] | no frames]

Source Code for Module AccessEngine.AEWalkers.AccessibleItemWalker

  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   
21 -class AccessibleItemWalker(AEWalker):
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
53 - def _getNextItem(self, por):
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 # try to get the next item 69 item = IItemNav(por).getNextItem(not self.allow_invisible) 70 return (item, None) 71 except (LookupError, IndexError): 72 # there's no next item 73 pass 74 return (por, self._getFirstChild)
75
76 - def _getFirstChild(self, por):
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 # try to get first child accessible 93 child = IAccessibleNav(por).getFirstAccChild() 94 ai = IAccessibleInfo(child) 95 if not self.allow_invisible and not ai.isAccVisible(): 96 # don't use this child or its children if it is not visible 97 return (child, self._getNextPeer) 98 elif not self.allow_trivial and ai.isAccTrivial(): 99 # skip this child and move a level deeper if it is trivial 100 return (child, self._getFirstChild) 101 else: 102 # use this child 103 return (child, None) 104 except (LookupError, IndexError): 105 # there's no child accessible 106 return (por, self._getNextPeer)
107
108 - def _getNextPeer(self, por):
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 # don't navigate peers if this accessible is embedded 127 if IAccessibleInfo(por).isAccEmbedded(): 128 raise IndexError 129 # try getting the next peer accessible 130 next = IAccessibleNav(por).getNextAcc() 131 ai = IAccessibleInfo(next) 132 if not self.allow_invisible and not ai.isAccVisible(): 133 # skip to the next peer if this one is not visible 134 return (next, self._getNextPeer) 135 elif not self.allow_trivial and ai.isAccTrivial(): 136 # skip this peer and move to its children if it is trivial 137 return (next, self._getFirstChild) 138 else: 139 # use this peer 140 return (next, None) 141 except (LookupError, IndexError): 142 # there's no next peer accessible 143 return (por, self._getParentNextItem)
144
145 - def _getParentNextItem(self, por):
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 # stop if the parent is the root of the active window 164 raise IndexError 165 except (LookupError, IndexError): 166 # there's no parent, so bail because we're on the last accessible 167 return (None, None) 168 # convert a child accessible to an item if possible 169 try: 170 # try to navigate to the next item 171 por = IItemNav(parent).getAccAsItem(por) 172 return (por, self._getNextItem) 173 except (LookupError, IndexError): 174 # get the next peer of the parent accessible 175 return (parent, self._getNextPeer)
176
177 - def _getPrevItem(self, por):
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 # try to get the previous item 193 item = IItemNav(por).getPrevItem(not self.allow_invisible) 194 if item.isSameAcc(por): 195 # use the item if it's in the same accessible 196 return (item, None) 197 else: 198 # otherwise try to get the last child 199 return (item, self._getLastChild) 200 except (LookupError, IndexError): 201 # there's no previous item 202 pass 203 return (por, self._getPrevPeer)
204
205 - def _getPrevPeer(self, por):
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 # don't navigate peers if this accessible is embedded 222 if IAccessibleInfo(por).isAccEmbedded(): 223 raise IndexError 224 # try to get the previous peer 225 prev = IAccessibleNav(por).getPrevAcc() 226 ai = IAccessibleInfo(prev) 227 if ai.isAccTopLevelWindow(): 228 # stop if the parent is the root of the active window 229 return (None, None) 230 elif self.allow_invisible or ai.isAccVisible(): 231 # get the deepest last child 232 return (prev, self._getLastChild) 233 else: 234 # skip this peer and its children 235 return (prev, self._getPrevPeer) 236 except (LookupError, IndexError): 237 # there's no previous peer accessible 238 return (por, self._getParentPrevItem)
239
240 - def _getParentPrevItem(self, por):
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 # there's no parent, so bail because we're on the last accessible 262 return (None, None) 263 # convert a child accessible to an item if possible 264 try: 265 # try to navigate to the previous item 266 por = IItemNav(parent).getAccAsItem(por) 267 return (por, self._getPrevItem) 268 except (LookupError, IndexError): 269 # get the previous peer of the parent accessible 270 return (por, self._getParent)
271
272 - def _getLastChild(self, por):
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 # try to get the last child 291 child = IAccessibleNav(por).getLastAccChild() 292 ai = IAccessibleInfo(child) 293 if self.allow_invisible or ai.isAccVisible(): 294 # try for the next deeper last child 295 return (child, self._getLastChild) 296 else: 297 # try for the previous peer of the invisible child 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 # try the previous peer of the invisible or trivial accessible 304 return (por, self._getPrevPeer) 305 else: 306 # use the last item in this accessible 307 return (por, self._getLastItem)
308
309 - def _getLastItem(self, por):
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 # use this por if the item is in the same accessible 325 return (item, None) 326 elif self.allow_invisible or ai.isAccVisible(): 327 # try for the next deeper last child 328 return (item, self._getLastChild) 329 else: 330 # try for the previous peer of the invisible item 331 return (item, self._getPrevItem) 332 except (LookupError, IndexError): 333 # just use the current por if there is an error 334 return (por, None)
335
336 - def _getParent(self, por):
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 # try to get the parent 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 # skip the parent and move to its previous peer 359 return (parent, self._getPrevPeer) 360 else: 361 # use the parent, but get its last item 362 return (parent, None) 363 except (LookupError, IndexError): 364 # there's no parent, so bail because we're at the root 365 return (None, None)
366
367 - def getNextPOR(self):
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 #print por, state.func_name 383 por, state = state(por) 384 if por is not None: 385 self.por = por 386 #print 'STOP', por 387 return por
388
389 - def getPrevPOR(self):
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 #print state.func_name, str(por) 405 por, state = state(por) 406 if por is not None: 407 self.por = por 408 #print 'STOP prev', por 409 return por
410
411 - def getFirstPOR(self):
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
433 - def getLastPOR(self):
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 # get to the first POR first so we can traverse the very last branch 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 # get the last item 455 try: 456 self.por = IItemNav(self.por).getLastItem(not self.allow_invisible) 457 except LookupError: 458 pass 459 if self.por == old: 460 # only stop if we didn't move, else continue checking 461 return self.por 462 else: 463 # back off until we find a non-trivial accessible 464 return self.getPrevPOR()
465
466 - def getParentPOR(self):
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 # try to get the parent 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 # there's no parent, so bail because we're at the root 484 return None
485