Package Adapters :: Package ATSPI :: Module TableAdapter
[hide private]
[frames] | no frames]

Source Code for Module Adapters.ATSPI.TableAdapter

  1  ''' 
  2  Defines L{AccAdapt.Adapter}s for AT-SPI table accessibles. Tables implement the 
  3  Table interface but not the Selection interface. 
  4   
  5  @author: Pete Brunet 
  6  @author: Peter Parente 
  7  @author: Eirikur Hallgrimsson 
  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  from POR import POR 
 18  from AEEvent import * 
 19  from AEInterfaces import * 
 20  from DefaultEventHandler import * 
 21  from DefaultNav import * 
 22  from ContainerAdapter import * 
 23  from pyLinAcc import Constants, Interfaces 
 24  import pyLinAcc 
 25   
 26  FUDGE_PX = 5 
 27   
28 -class TableAccInfoAdapter(ContainerAccInfoAdapter):
29 ''' 30 Overrides L{ContainerAccInfoAdapter} to generate selector events on focus 31 and on selection. Expects the subject to be a L{pyLinAcc.Accessible}. 32 33 Adapts subject accessibles that provide the L{pyLinAcc.Interfaces.ITable} 34 interface and have ROLE_TABLE. 35 ''' 36 provides = [IAccessibleInfo] 37 38 @staticmethod
39 - def when(por):
40 ''' 41 Tests if the given POR can be adapted by this class. 42 43 @param por: Accessible to test 44 @type por: L{POR} 45 @return: True when the subject meets the condition named in the docstring 46 for this class, False otherwise 47 @rtype: boolean 48 ''' 49 acc = por.accessible 50 r = acc.getRole() 51 # make sure the role is a table 52 if r != Constants.ROLE_TABLE: 53 return False 54 # make sure the table interface exists 55 tab = Interfaces.ITable(acc) 56 return True
57 58 @pyLinAcc.errorToLookupError
59 - def getAccRow(self):
60 ''' 61 Gets the row of an item in a table. 62 63 @return: Zero indexed row of the item 64 @rtype: integer 65 @raise LookupError: When the table or item is no longer valid 66 ''' 67 if self.item_offset is None: 68 return None 69 tab = Interfaces.ITable(self.accessible) 70 return tab.getRowAtIndex(self.item_offset)
71 72 @pyLinAcc.errorToLookupError
73 - def getAccColumn(self):
74 ''' 75 Gets the column of an item in a table. 76 77 @return: Zero indexed column of the item 78 @rtype: integer 79 @raise LookupError: When the table or item is no longer valid 80 ''' 81 if self.item_offset is None: 82 return None 83 tab = Interfaces.ITable(self.accessible) 84 return tab.getColumnAtIndex(self.item_offset)
85 86 @pyLinAcc.errorToLookupError
87 - def getAccRowColIndex(self, row, col):
88 ''' 89 Gets the 1D index of the cell at the given 2D row and column. 90 91 @param row: Row index 92 @type row: integer 93 @param col: Column index 94 @type col: integer 95 @return: 1D index into the table 96 @rtype: integer 97 @raise IndexError: When the row/column offsets are invalid 98 @raise LookupError: When the table is no longer valid 99 ''' 100 tab = Interfaces.ITable(self.accessible) 101 i = tab.getIndexAt(row, col) 102 if i < 0: 103 raise IndexError 104 return i
105 106 @pyLinAcc.errorToLookupError
107 - def getAccRowHeader(self):
108 ''' 109 Gets the text description of a row in a table. 110 111 @return: The descriptive text. 112 @rtype: string 113 @raise LookupError: When the table or item is no longer valid 114 ''' 115 tab = Interfaces.ITable(self.accessible) 116 if self.item_offset is not None: 117 row = tab.getRowAtIndex(self.item_offset) 118 return tab.getRowDescription(row) 119 return None
120 121 @pyLinAcc.errorToLookupError
122 - def getAccColumnHeader(self):
123 ''' 124 Gets the text description of a column in a table. 125 126 @return: The descriptive text. 127 @rtype: string 128 @raise LookupError: When the table or item is no longer valid 129 ''' 130 tab = Interfaces.ITable(self.accessible) 131 if self.item_offset is not None: 132 col = tab.getColumnAtIndex(self.item_offset) 133 return tab.getColumnDescription(col) 134 return None
135 136 @pyLinAcc.errorToLookupError
137 - def getAccTableExtents(self):
138 ''' 139 Returns the number of rows and columns in the table. 140 141 @return: Count of rows and columns 142 @rtype: 2-tuple of integer 143 @raise LookupError: When the table is no longer valid 144 ''' 145 tab = Interfaces.ITable(self.accessible) 146 return (tab.nRows, tab.nColumns)
147
148 -class TableNavAdapter(DefaultNavAdapter):
149 ''' 150 Overrides L{DefaultNavAdapter} to provide navigation over table cells as 151 items. Expects the subject to be a L{POR}. Does not walk headers. 152 Those can be gotten and reported separately as context information. 153 154 Note that not all tables properly respond to requests for accessibles at 155 (x,y) coordinates on the screen. Most tables seem to always return their 156 first accessible (not first visible accessible) for the top left corner and 157 last accessible (not last visible accessible) for the bottom right corner, but 158 this depends on the application. 159 160 Adapts subject accessibles that provide the L{pyLinAcc.Interfaces.ITable} 161 interface. 162 ''' 163 provides = [IAccessibleNav, IItemNav] 164 165 @staticmethod
166 - def when(subject):
167 ''' 168 Tests if the given subject can be adapted by this class. 169 170 @param subject: L{POR} containing an accessible to test 171 @type subject: L{POR} 172 @return: True when the subject meets the condition named in the docstring 173 for this class, False otherwise 174 @rtype: boolean 175 ''' 176 acc = subject.accessible 177 ss = acc.getState() 178 if not ss.contains(Constants.STATE_MANAGES_DESCENDANTS): 179 return False 180 return Interfaces.ITable(acc)
181 182 @pyLinAcc.errorToLookupError
183 - def _getVisibleItemExtents(self, only_visible):
184 ''' 185 Gets the item offsets of the first and last items in a table of cells. 186 187 @param only_visible: Only consider the first and last cells visible in 188 the table (True) or the absolute first and last cells (False)? 189 @type only_visible: boolean 190 @return: First and last item offsets 191 @rtype: 2-tuple of integer 192 @raise LookupError: When the first or last item or parent accessible is 193 not available 194 ''' 195 acc = self.accessible 196 if only_visible: 197 comp = Interfaces.IComponent(acc) 198 e = comp.getExtents(Constants.WINDOW_COORDS) 199 # get the first item 200 x, y = e.x+FUDGE_PX, e.y+FUDGE_PX 201 try: 202 first = comp.getAccessibleAtPoint(x, y, Constants.WINDOW_COORDS) 203 except TypeError: 204 first = None 205 # get the last item 206 x, y = e.x+e.width-FUDGE_PX, e.y+e.height-FUDGE_PX 207 try: 208 last = comp.getAccessibleAtPoint(x, y, Constants.WINDOW_COORDS) 209 except TypeError: 210 last = None 211 else: 212 first = None 213 last = None 214 # compute indices 215 if first: 216 i = first.getIndexInParent() 217 else: 218 i = 0 219 if last: 220 j = last.getIndexInParent() 221 else: 222 t = Interfaces.ITable(acc) 223 j = t.getIndexAt(t.nRows-1, t.nColumns-1) 224 return i, j
225 226 @pyLinAcc.errorToLookupError
227 - def getNextItem(self, only_visible=True):
228 ''' 229 Gets the next item relative to the one indicated by the L{POR} 230 providing this interface. 231 232 @param only_visible: True when Item in the returned L{POR} must be visible 233 @type only_visible: boolean 234 @return: Point of regard to the next item in the same accessible 235 @rtype: L{POR} 236 @raise IndexError: When there is no next item 237 @raise LookupError: When lookup for the next item fails even though it may 238 exist 239 ''' 240 acc = self.accessible 241 off = self.item_offset 242 # get the first and last indices 243 i, j = self._getVisibleItemExtents(only_visible) 244 if off is None or off < i: 245 # return the first visible, non-header item 246 if not IAccessibleInfo(POR(acc.getChildAtIndex(i))).isAccVisible(): 247 raise IndexError 248 return POR(acc, i, 0) 249 elif off+1 >= i and off+1 <= j: 250 if IAccessibleInfo(POR(acc.getChildAtIndex(off+1))).isAccVisible(): 251 # use the next higher index if it was found to be visible 252 return POR(acc, off+1, 0) 253 else: 254 # wrap to the first visible item in the next row 255 # compute the row and column offsets 256 t = Interfaces.ITable(acc) 257 r = t.getRowAtIndex(off) 258 r += 1 259 c = t.getColumnAtIndex(i) 260 n_off = t.getIndexAt(r, c) 261 if not IAccessibleInfo(POR(acc.getChildAtIndex(n_off))).isAccVisible(): 262 raise IndexError 263 return POR(acc, n_off, 0) 264 else: 265 # no more visible items 266 raise IndexError
267 268 @pyLinAcc.errorToLookupError
269 - def getPrevItem(self, only_visible=True):
270 ''' 271 Gets the previous item relative to the one indicated by the L{POR} providing 272 this interface. 273 274 @param only_visible: True when Item in the returned L{POR} must be visible 275 @type only_visible: boolean 276 @return: Point of regard to the previous item in the same accessible 277 @rtype: L{POR} 278 @raise IndexError: When there is no previous item 279 @raise LookupError: When lookup for the previous item fails even though it 280 may exist 281 ''' 282 acc = self.accessible 283 off = self.item_offset 284 comp = Interfaces.IComponent(acc) 285 # get the first and last indices 286 i, j = self._getVisibleItemExtents(only_visible) 287 # and check if the first item is visible since it might be a header 288 if off is None: 289 # no more visible items 290 raise IndexError 291 elif off > j: 292 # return the last visible item 293 return POR(acc, j, 0) 294 elif off-1 >= i and off-1 <= j: 295 # compute the row and column offsets 296 if IAccessibleInfo(POR(acc.getChildAtIndex(off-1))).isAccVisible(): 297 # use the next higher index if it is visible 298 return POR(acc, off-1, 0) 299 else: 300 # wrap to the first visible item in the next row 301 # compute the row and column offsets 302 t = Interfaces.ITable(acc) 303 r, c = t.getRowAtIndex(off), t.getColumnAtIndex(j) 304 r -= 1 305 n_off = t.getIndexAt(r, c) 306 if n_off <= i: 307 raise IndexError 308 return POR(acc, n_off, 0) 309 else: 310 # return the table iteself 311 return POR(acc, None, 0)
312 313 @pyLinAcc.errorToLookupError
314 - def getLastItem(self, only_visible=True):
315 ''' 316 Gets the last item relative to the one indicated by the L{POR} 317 providing this interface. 318 319 @param only_visible: True when Item in the returned L{POR} must be visible 320 @type only_visible: boolean 321 @return: Point of regard to the last item in the same accessible 322 @rtype: L{POR} 323 @raise LookupError: When lookup for the last item fails even though it may 324 exist 325 ''' 326 acc = self.accessible 327 comp = Interfaces.IComponent(acc) 328 # try getting the last item by index first 329 child = acc.getChildAtIndex(acc.childCount-1) 330 if IAccessibleInfo(POR(child)).isAccVisible() or not only_visible: 331 return POR(acc, acc.childCount-1, 0) 332 # use coords to get the last visible item 333 i, j = self._getVisibleItemExtents(only_visible) 334 return POR(acc, j, 0)
335 336 @pyLinAcc.errorToLookupError
337 - def getFirstItem(self, only_visible=True):
338 ''' 339 Gets the first item relative to the one indicated by the L{POR} 340 providing this interface. 341 342 @param only_visible: True when Item in the returned L{POR} must be visible 343 @type only_visible: boolean 344 @return: Point of regard to the last item in the same accessible 345 @rtype: L{POR} 346 @raise LookupError: When lookup for the last item fails even though it may 347 exist 348 ''' 349 acc = self.accessible 350 comp = Interfaces.IComponent(acc) 351 # try getting the first item by index first 352 child = acc.getChildAtIndex(0) 353 if IAccessibleInfo(POR(child)).isAccVisible() or not only_visible: 354 return POR(acc, 0, 0) 355 # use coords to get the first visible item 356 i, j = self._getVisibleItemExtents(only_visible) 357 return POR(acc, i, 0)
358 359 @pyLinAcc.errorToLookupError
360 - def getFirstAccChild(self):
361 ''' 362 Always raises LookupError. Tables have items but no children. 363 364 @raise LookupError: Always 365 ''' 366 raise LookupError
367 368 @pyLinAcc.errorToLookupError
369 - def getLastAccChild(self):
370 ''' 371 Always raises LookupError. Tables have items but no children. 372 373 @raise LookupError: Always 374 ''' 375 raise LookupError
376 377 @pyLinAcc.errorToLookupError
378 - def getChildAcc(self, index):
379 ''' 380 Always raises LookupError. Tables have items but no children. 381 382 @raise LookupError: Always 383 ''' 384 raise LookupError
385
386 -class DegenerateTableEventHandlerAdapter(DefaultEventHandlerAdapter):
387 ''' 388 Overrides L{DefaultEventHandlerAdapter} to generate selector events on 389 selection change. Does not generate the ideal selector events on focus 390 because the degenerate subject does not implement the Selection interface. As 391 a result, the active descendant cannot be determined. Expects the subject to 392 be a raw L{pyLinAcc.Accessible}. 393 394 Adapts subject accessibles that provide the L{pyLinAcc.Interfaces.ITable} 395 interface. 396 ''' 397 @staticmethod
398 - def when(subject):
399 ''' 400 Tests if the given subject can be adapted by this class. 401 402 @param subject: Accessible to test 403 @type subject: L{pyLinAcc.Accessible} 404 @return: True when the subject meets the condition named in the docstring 405 for this class, False otherwise 406 @rtype: boolean 407 ''' 408 return Interfaces.ITable(subject)
409
410 - def _handleDescendantEvent(self, event, **kwargs):
411 ''' 412 Creates an L{AEEvent.SelectorChange} indicating the "selector" moved in this 413 accessible. 414 415 @param event: Raw decendent changed event 416 @type event: L{pyLinAcc.Event.Event} 417 @param kwargs: Parameters to be passed to any created L{AEEvent} 418 @type kwargs: dictionary 419 @return: L{AEEvent.SelectorChange} 420 @rtype: tuple of L{AEEvent} 421 ''' 422 return (self._getSelectorEvent(event.any_data, event.detail1, **kwargs),)
423
424 - def _getSelectorEvent(self, accessible, item_offset, **kwargs):
425 ''' 426 Creates an L{AEEvent.SelectorChange} indicating the selector moved in this 427 accessible. 428 429 This method corrects for the possibility that the selected item actually 430 have children that have the important information which are themselves not 431 selected but returned as children of the even source. Right now, the last 432 child in such a case appears to carry the information. More robust 433 processing may be needed in the future. 434 435 @param accessible: Accessible that generated this event 436 @type accessible: L{pyLinAcc.Accessible} 437 @param item_offset: Offset of item involved in the selection event 438 @type item_offset: integer 439 @param kwargs: Parameters to be passed to any created L{AEEvent} 440 @type kwargs: dictionary 441 @return: Selection event 442 @rtype: L{AEEvent.SelectorChange} 443 ''' 444 if accessible.childCount > 0: 445 accessible = accessible.getChildAtIndex(accessible.childCount - 1) 446 447 # Notes: 448 # When adding support for more than one selection, we'll probably have 449 # to keep track of each state change (SELECTED or not) and report the most 450 # recently changed item or pair of items. Some examples: 451 # - a single item became selected or unselected 452 # - one item became unselected and an adjacent one became selected 453 # In the second case the perk should probably receive both items 454 # The Selection interface has a list of one or more selected items. 455 # selection.nSelectedChildren indicates how many there are and 456 # selection.getSelectedChild(index) accesses one of them. 457 458 459 # create a POR, pass it and the item text at the POR to the Tier 460 por = POR(self.subject, item_offset, 0) 461 acc_child_text = IAccessibleInfo(por).getAccItemText() 462 return SelectorChange(por, acc_child_text, **kwargs)
463
464 -class TableEventHandlerAdapter(DegenerateTableEventHandlerAdapter):
465 ''' 466 Overrides L{DegenerateTableEventHandlerAdapter} to generate selector events 467 on focus and on selection. Expects the subject to be a raw 468 L{pyLinAcc.Accessible}. 469 470 Adapts subject accessibles that provide the L{pyLinAcc.Interfaces.ISelection}, 471 interface and have ROLE_TABLE or ROLE_TREE_TABLE. 472 ''' 473 provides = [IEventHandler] 474 475 @staticmethod
476 - def when(subject):
477 ''' 478 Tests if the given subject can be adapted by this class. 479 480 @param subject: Accessible to test 481 @type subject: L{pyLinAcc.Accessible} 482 @return: True when the subject meets the condition named in the docstring 483 for this class, False otherwise 484 @rtype: boolean 485 ''' 486 r = subject.getRole() 487 c = Constants 488 return (r in (c.ROLE_TABLE, c.ROLE_TREE_TABLE) and 489 Interfaces.ISelection(subject))
490 491 #def _handleDescendantEvent(self, event, **kwargs): 492 #''' 493 #Creates an L{AEEvent.SelectorChange} indicating the "selector" moved in this 494 #accessible. 495 496 #@param event: Raw decendent changed event 497 #@type event: L{pyLinAcc.Event.Event} 498 #@param kwargs: Parameters to be passed to any created L{AEEvent} 499 #@type kwargs: dictionary 500 #@return: L{AEEvent.SelectorChange} 501 #@rtype: tuple of L{AEEvent} 502 #''' 503 ## clear the seen descendant state for this POR 504 #try: 505 #del self.selection_state[por] 506 #except KeyError: 507 #pass 508 #parent = super(TableEventHandlerAdapter, self) 509 #return parent._handleDescendantEvent(event, **kwargs) 510 511 #def _handleSelectionChangedEvent(self, event, **kwargs): 512 #''' 513 #Creates an L{AEEvent.SelectorChange} indicating an item was added to or 514 #removed from a selection. 515 516 #@param event: Raw selection changed event 517 #@type event: L{pyLinAcc.Event.Event} 518 #@param kwargs: Parameters to be passed to any created L{AEEvent} 519 #@type kwargs: dictionary 520 #@return: L{AEEvent.SelectorChange} 521 #@rtype: tuple of L{AEEvent} 522 #''' 523 #if event.type.minor == 'add': 524 #return (self._getSelectorEvent(event.any_data, event.detail1, **kwargs),) 525 #else: 526 #return (self._getSelectorEvent(event.any_data, event.detail1, **kwargs),) 527
528 - def _handleFocusEvent(self, event, **kwargs):
529 ''' 530 Creates an L{AEEvent.FocusChange} indicating that the accessible being 531 adapted has gained the focus. Also creates a L{AEEvent.SelectorChange}. 532 These two L{AEEvent}s will be posted by the caller. 533 534 @param event: Raw focus change event 535 @type event: L{pyLinAcc.Event.Event} 536 @param kwargs: Parameters to be passed to any created L{AEEvent} 537 @type kwargs: dictionary 538 @return: L{AEEvent.FocusChange} and L{AEEvent.SelectorChange} 539 @rtype: tuple of L{AEEvent} 540 ''' 541 # build a focus event 542 kwargs['focused'] = True 543 por = POR(self.subject, None, 0) 544 focus_event = FocusChange(por, True, **kwargs) 545 546 # get the selection interface 547 selection = Interfaces.ISelection(self.subject) 548 # ignore selector events when nothing is selected 549 if selection.nSelectedChildren == 0: 550 return (focus_event,) 551 # get the selected object, for now, just the first item in the selection 552 acc_child = selection.getSelectedChild(0) 553 if acc_child is None: 554 return (focus_event,) 555 item_offset = acc_child.getIndexInParent() 556 # build and return a selector event 557 return focus_event, self._getSelectorEvent(acc_child, item_offset,**kwargs)
558