1 '''
2 Defines default L{AEAccAdapter}s for the
3 L{AEAccInterfaces.IAccessibleInfo} interface on
4 C{pyatspi.Accessibility.Accessible} objects.
5
6 @author: Peter Parente
7 @author: Pete Brunet
8 @author: Brett Clippingdale
9 @author: Eirikur Hallgrimsson
10 @organization: IBM Corporation
11 @copyright: Copyright (c) 2005, 2007 IBM Corporation
12
13 @author: Frank Zenker
14 @organization: IT Science Center Ruegen gGmbH, Germany
15 @copyright: Copyright (c) 2007, 2008 ITSC Ruegen
16
17 @license: I{The BSD License}
18 All rights reserved. This program and the accompanying materials are made
19 available under the terms of the BSD license which accompanies
20 this distribution, and is available at
21 U{http://www.opensource.org/licenses/bsd-license.php}
22 '''
23 import pyatspi
24 from AccessEngine.AEPor import AEPor
25 from AccessEngine.AEAccAdapter import PORAdapter
26 from AccessEngine.AEAccInterfaces import *
27
29 '''
30 Adapts all AT-SPI accessibles to the L{IAccessibleNav} interface. No
31 condition for adaption is given imp`lying that this adapter is used as a
32 default by L{AEAccAdapter} when no better adapter is available. Expects the
33 subject to be a L{AEPor}.
34
35 @cvar VISIBLE_STATES: States that an accessible must have to be considered
36 visible
37 @type VISIBLE_STATES: tuple
38 @cvar TRIVIAL_STATES: States that an accessible must not have to be
39 considered non-trivial
40 @type TRIVIAL_STATES: tuple
41 @cvar TRIVIAL_ROLES: Roles that an accessible must not have to be considered
42 non-trivial
43 @type TRIVIAL_ROLES: tuple
44 '''
45 provides = [IAccessibleInfo]
46
47 VISIBLE_STATES = (pyatspi.STATE_VISIBLE,
48 pyatspi.STATE_SHOWING)
49 TRIVIAL_STATES = (pyatspi.STATE_DEFUNCT,
50 pyatspi.STATE_INVALID,
51 pyatspi.STATE_INDETERMINATE)
52 TRIVIAL_ROLES = (pyatspi.ROLE_INVALID,
53 pyatspi.ROLE_GLASS_PANE,
54 pyatspi.ROLE_FILLER,
55 pyatspi.ROLE_VIEWPORT,
56 pyatspi.ROLE_MENU_BAR,
57 pyatspi.ROLE_TOOL_BAR,
58 pyatspi.ROLE_SCROLL_BAR,
59 pyatspi.ROLE_PANEL,
60 pyatspi.ROLE_SPLIT_PANE,
61 pyatspi.ROLE_SCROLL_PANE,
62 pyatspi.ROLE_PAGE_TAB_LIST,
63 pyatspi.ROLE_SEPARATOR)
64
65
67 '''
68 Gets a list of name:value attribute pairs for the subject.
69
70 @return: Name:value pairs of the object attributes
71 @rtype: dictionary
72 @raise LookupError: When the accessible object is dead
73 @raise NotImplementedError: When this accessible does not support object
74 attributes
75 '''
76 try:
77 attrs = self.accessible.getAttributes()
78 except AttributeError:
79 try:
80 doc = (self.accessible).queryDocument()
81 attrs = doc.getAttributes()
82 except AttributeError:
83 raise NotImplementedError
84 return dict([attr.split(':', 1) for attr in attrs])
85
86
88 '''
89 Gets the minimum, maximum, and step for the floating point value exposed
90 by the subject.
91
92 @return: Minimum, maximum, and step values
93 @rtype: 3-tuple of float
94 @raise LookupError: When the accessible object is dead
95 @raise NotImplementedError: When this accessible does not support the Value
96 interface
97 '''
98 val = (self.accessible).queryValue()
99 min = val.minimumValue
100 max = val.maximumValue
101
102 try:
103 inc = val.minimumIncrement
104 except NotImplementedError:
105 inc = 1.0
106 return (min,max,inc)
107
108
110 '''
111 Gets the current floating point value of the subject
112
113 @return: Current value
114 @rtype: float
115 @raise LookupError: When the accessible object is dead
116 @raise NotImplementedError: When this accessible does not support the Value
117 interface
118 '''
119 val = (self.accessible).queryValue()
120 return val.currentValue
121
122
124 '''
125 Gets all of the default text attributes assigned to the subject.
126
127 @return: Name:value pairs of the default text attributes
128 @rtype: dictionary
129 @raise LookupError: When the accessible object is dead
130 @raise NotImplementedError: When this accessible does not support the text
131 interface
132 '''
133 text = (self.accessible).queryText()
134 try:
135 attrs = text.getDefaultAttributeSet()
136 if attrs is None:
137 return {}
138 except AttributeError:
139 val = text.getDefaultAttributes()
140 if val is '':
141 return {}
142 attrs = val.split('; ', 1)
143 return dict([attr.split(':', 1) for attr in attrs])
144
145
147 '''
148 Gets the number of discontiguous selected text ranges.
149
150 @return: Number of selections
151 @rtype: integer
152 @raise LookupError: When the accessible object is dead
153 @raise NotImplementedError: When the accessible does not support the Text
154 interface
155 '''
156 return (self.accessible).queryText().getNSelections()
157
158
160 '''
161 Gets the current offset of the caret in this text object as a L{AEPor}.
162
163 @return: Point of regard of the text caret in this object
164 @rtype: L{AEPor}
165 @raise LookupError: When the accessible object is dead
166 @raise NotImplementedError: When the accessible does not support the Text
167 interface
168 '''
169 text = (self.accessible).queryText()
170 caret = text.caretOffset
171
172 line, start, end = \
173 text.getTextAtOffset(caret,pyatspi.TEXT_BOUNDARY_LINE_START)
174
175 return AEPor(self.accessible, start, caret - start)
176
177
179 '''
180 Gets the number of characters in a text body.
181
182 @return: Total number of characters
183 @rtype: integer
184 @raise LookupError: When the accessible object is dead
185 @raise NotImplementedError: When the accessible does not support the Text
186 interface
187 '''
188 return (self.accessible).queryText().characterCount
189
190
191 - def getAccTextSelection(self, n=None):
192 '''
193 Gets a list of all text selections in the subject if n is None, or the nth
194 selection if it is specified.
195
196 @param n: Index of selection to be returned, or None to indicate all
197 @type n: integer
198 @return: List of selected text runs
199 @rtype: list of string
200 @raise LookupError: When the accessible object is dead
201 @raise NotImplementedError: When this accessible does not support the Text
202 interface
203 '''
204 tex = (self.accessible).queryText()
205 if n is not None:
206 ranges = [tex.getSelection(n)]
207 else:
208 ranges = (tex.getSelection(sel) for sel in xrange(tex.getNSelections()))
209 return [tex.getText(*range) for range in ranges]
210
211
212 - def getAccText(self, por):
213 '''
214 Gets all text starting at item offset + char offset up to the offset given
215 by the L{AEPor}. Gets from the beginning of the text if subject item offset
216 is None. Gets to the end of the text if the L{AEPor} item offset is None.
217
218 @param por: Point of regard to the end of the text range to get
219 @type por: L{AEPor}
220 @return: Text between the offsets
221 @rtype: string
222 @raise LookupError: When the accessible object is dead
223 @raise NotImplementedError: When this accessible does not support the Text
224 interface
225 '''
226 if self.item_offset is None:
227 start = 0
228 else:
229 start = self.item_offset+self.char_offset
230 if por.item_offset is None:
231
232 end = -1
233 else:
234 end = por.item_offset + por.char_offset
235 tex = (self.accessible).queryText()
236 return tex.getText(start, end)
237
238
240 '''
241 Returns the number of rows and columns in a table as a 2-tuple of integers
242 Not implemented for non-Table objects.
243
244 @return: We never return.
245 @raise NotImplementedError: Always
246 '''
247 raise NotImplementedError
248
249
251 '''
252 Gets the size of the bounding box of an accessible.
253
254 @return: Width and height
255 @rtype: 2-tuple of integer
256 @raise NotImplementedError: When this accessible does not support the
257 Component interface
258 '''
259 c = (self.accessible).queryComponent()
260 bb = c.getExtents(pyatspi.DESKTOP_COORDS)
261 return bb.width, bb.height
262
263
265 '''
266 Gets the visual position of the focal point wihin an accessible object,
267 the center in a simple box-like control.
268
269 @return: x,y coordinate
270 @rtype: 2-tuple integer
271 @raise NotImplementedError: When this accessible does not support the
272 Component interface
273 '''
274 try:
275 text = (self.accessible).queryText()
276 except NotImplementedError:
277 text = None
278
279 if text:
280
281 off = self.char_offset
282 x, y, w, h = text.getCharacterExtents(off, pyatspi.DESKTOP_COORDS)
283 return x, y
284 else:
285
286 c = (self.accessible).queryComponent()
287 bb = c.getExtents(pyatspi.DESKTOP_COORDS)
288 return bb.x+bb.width/2, bb.y+bb.height/2
289
290
292 '''
293 Gets the position of the accessible object, usually upper-left corner.
294
295 @return: x,y coordinate
296 @rtype: 2-tuple integer
297 @raise NotImplementedError: When this accessible does not support the
298 Component interface
299 '''
300 c = (self.accessible).queryComponent()
301 return c.getPosition(pyatspi.DESKTOP_COORDS)
302
303
304 - def getAccTextAttr(self, name):
305 '''
306 Gets the value of a given attribute name.
307
308 @param name: Name of the attribute (eg. fgcolor, bgcolor)
309 @type name: string
310 @return: Value of the accessible attribute
311 @rtype: string
312 @raise LookupError: When the accessible object is dead
313 @raise NotImplementedError: When this accessible does not support the Text
314 interface
315 '''
316 try:
317 text = (self.accessible).queryText()
318 io = self.item_offset or 0
319 val, s, e, d = text.getAttributeValue(io+self.char_offset, name)
320 return val
321 except AttributeError:
322 attrs = self.getAccAllTextAttrs()
323 return attrs.get(name, '')
324
325
327 '''
328 Gets the index of an item as its child index in its parent.
329
330 @return: Zero indexed child offset under the parent accessible
331 @rtype: integer
332 @raise LookupError: When the table or item is no longer valid
333 '''
334 i = self.accessible.getIndexInParent()
335 if i < 0:
336 raise LookupError
337 return i
338
340 '''
341 Gets the (tree) level for the item object implementing this interface. This
342 is NOT IMPLEMENTED for non-tree objects.
343
344 @raise NotImplementedError: Always
345 '''
346 raise NotImplementedError
347
349 '''
350 Gets the row of the item object implementing this interface. This
351 is NOT IMPLEMENTED for non-table objects.
352
353 @raise NotImplementedError: Always
354 '''
355 raise NotImplementedError
356
358 '''
359 Gets the column of the item object implementing this interface. This
360 is NOT IMPLEMENTED for non-table objects.
361
362 @raise NotImplementedError: Always
363 '''
364 raise NotImplementedError
365
367 '''
368 Gets the 1D index of the cell at the given 2D row and column. This is not
369 implemented for non-table objects.
370
371 @param row: Row index
372 @type row: integer
373 @param col: Column index
374 @type col: integer
375 @raise NotImplementedError: Always
376 '''
377 raise NotImplementedError
378
380 '''
381 Gets the column header text of the item object implementing this interface.
382 This is NOT IMPLEMENTED for non-table objects.
383
384 @raise NotImplementedError: Always
385 '''
386 raise NotImplementedError
387
389 '''
390 Gets the row header text of the item object implementing this interface.
391 This is NOT IMPLEMENTED for non-table objects.
392
393 @raise NotImplementedError: Always
394 '''
395 raise NotImplementedError
396
397
399 '''
400 Gets all the text attributes of a given accessible.
401
402 @return: Name:value pairs of the text attributes at the character offset
403 @rtype: dictionary
404 @raise LookupError: When the accessible object is dead
405 @raise NotImplementedError: When this accessible does not support the text
406 interface
407 '''
408 text = (self.accessible).queryText()
409
410 if self.item_offset is None:
411 io = 0
412 else:
413 io = self.item_offset
414
415
416 index = io+self.char_offset
417 try:
418 raise AttributeError
419
420 attrs, start, end = text.getAttributeRun(index, False)
421 if attrs is None:
422 return {}
423 except AttributeError:
424 val, start, end = text.getAttributes(index)
425 if val is '':
426 return {}
427 attrs = val.split('; ', 1)
428 return dict([attr.split(':', 1) for attr in attrs])
429
430
432 '''
433 Gets a list L{AEPor}s referring to the selected child acessibles within the
434 subject accessible.
435
436 @return: Points of regard to selected accessibles
437 @rtype: list of L{AEPor}s
438 @raise LookupError: When the subject accessible is dead
439 '''
440 try:
441 sel = (self.accessible).querySelection()
442 except NotImplementedError:
443 return []
444
445 return [AEPor(sel.getSelectedChild(i)) for i in range(sel.nSelectedChildren)]
446
447
449 '''
450 Gets the number of child accessibles for the object implementing this
451 interface.
452
453 @return: Number of accessible children
454 @rtype: integer
455 @raise LookupError: When the accessible object is dead
456 '''
457 return self.accessible.childCount
458
459
461 '''
462 Gets the name of the application application to which the subject accessible
463 belongs.
464
465 @return: Name of the subject's application
466 @rtype: string
467 @raise LookupError: When the accessible or any parent accessible up to the
468 application is dead
469 '''
470 app = self.accessible.getApplication()
471 if app is None:
472 raise LookupError
473 try:
474 return unicode(app.name, 'utf-8')
475 except UnicodeDecodeError:
476
477 return ''
478
479
481 '''
482 Gets a unique ID identifying the application to which the subject accessible
483 belongs.
484
485 Currently, the name and ID of the application are used. See
486 U{http://www.linuxbase.org/~gk4/a11y/idl/interfaceAccessibility_1_1Application.html#o2}
487 for information about the application ID.
488
489 @return: Application's name and runtime ID
490 @rtype: 2-tuple of string, integer
491 @raise LookupError: When the accessible or any parent accessible up to the
492 application is dead
493 '''
494 app = self.accessible.getApplication()
495 try:
496 return (app.name, app.id)
497 except AttributeError:
498 return None
499
500
502 '''
503 Gets the POSIX locale of an application as a string. At the present time
504 this routine returns only the locale set for messages.
505
506 @return: POSIX locale as string
507 @rtype: string
508 @raise LookupError: When the accessible or any parent accessible up to the
509 application is dead
510 '''
511 app = (self.accessible.getApplication()).queryApplication()
512 return app.getLocale(pyatspi.LOCALE_TYPE_MESSAGES)
513
514
516 '''
517 Gets the accessible role name of the subject. Ignores the given item offset
518 because the default adapter assumes there are no items.
519
520 @return: Localized role name
521 @rtype: string
522 @raise LookupError: When the accessible object is dead
523 '''
524 return unicode(self.accessible.getLocalizedRoleName(), 'utf-8')
525
526
528 '''
529 Gets the accessible role of the subject. Ignores the given item offset
530 because the default adapter assumes there are no items.
531
532 @return: Unlocalized role
533 @rtype: string
534 @raise LookupError: When the accessible object is dead
535 '''
536 return self.accessible.getRoleName()
537
538
540 '''
541 Gets the accessible name of the subject. Ignores the given item offset
542 because the default adapter assumes there are no items.
543
544 @return: Accessible name of requested object
545 @rtype: string
546 @raise LookupError: When the subject accessible is dead
547 '''
548 return unicode(self.accessible.name, 'utf-8')
549
550
552 '''
553 Gets the accessible description of the subject. Ignores the given item
554 offset because the default adapter assumes there are no items.
555
556 @return: Accessible description of requested object
557 @rtype: string
558 @raise LookupError: When the subject accessible is dead
559 '''
560 return unicode(self.accessible.description, 'utf-8')
561
562
563 - def getAccItemText(self):
564 '''
565 Gets the accessible text of this object or its name if the text is not
566 available. Ignores the given item offset because the default adapter
567 assumes there are no items.
568
569 @return: Accessible text of requested item
570 @rtype: string
571 @raise LookupError: When the accessible object is dead
572 '''
573 try:
574 text = (self.accessible).queryText().getText(0, -1)
575 if text:
576 return unicode(text, 'utf-8')
577 except NotImplementedError:
578 pass
579 return self.getAccName()
580
581
583 '''
584 Gets a list of accessibles related to the subject accessible in the manner
585 given by kind.
586
587 @param kind: Name specifying the kind of relations to retrieve
588 @type kind: string
589 @return: List of L{AEPor}s related to subject accessible in the manner
590 given by kind
591 @rtype: list of L{AEPor}
592 @raise LookupError: When the accessible object is dead
593 '''
594
595 kind = pyatspi.stringToConst('relation', kind)
596
597 rs = self.accessible.getRelationSet()
598 pors = []
599 for r in rs:
600 if r.getRelationType() == kind:
601
602 for i in range(r.getNTargets()):
603
604
605 por = IPORFactory(r.getTarget(i)).create()
606 pors.append(por)
607 break
608 return pors
609
610
612 '''
613 Gets a list of names of states indicating the current state of the given
614 accessible. Ignores the given item offset because the default adapter
615 assumes there are no items.
616
617 @return: Names of all states
618 @rtype: list
619 @raise LookupError: When the accessible object is dead
620 '''
621 role = self.accessible.getRoleName()
622 states = set([pyatspi.stateToString(val) for val in
623 self.accessible.getState().getStates()])
624
625
626 if 'expandable' in states and 'expanded' not in states:
627 states.add('collapsed')
628
629 if ('enabled' not in states and role != 'icon' and
630 ('focusable' in states or 'editable' in states or
631 'selectable' in states)):
632 states.add('disabled')
633
634 if (role.find('check') > -1 and 'checked' not in states):
635 states.add('unchecked')
636 return states
637
638
640 '''
641 Gets whether or not the subject accessible is a top-level container in the
642 accessible hierarchy.
643
644 @return: Is the subject a top-level container or not?
645 @rtype: boolean
646 @raise LookupError: When the accessible object is dead
647 '''
648 try:
649 app = (self.accessible.parent).queryApplication()
650 return True
651 except NotImplementedError:
652 return False
653
654
656 '''
657 Gets if the accessible should be considered trivial. This implementation
658 considers an accessible trivial if the state is one of L{TRIVIAL_STATES}
659 or the role is one of L{TRIVIAL_ROLES}.
660
661 @return: Does the accessible consider itself trivial?
662 @rtype: boolean
663 @raise LookupError: When the accessible object is dead
664 '''
665
666 name = self.accessible.name
667
668
669 if name and name.strip():
670 return False
671
672 if self.accessible.getRole() in self.TRIVIAL_ROLES:
673 return True
674 ss = self.accessible.getState()
675
676 for state in self.TRIVIAL_STATES:
677 if ss.contains(state):
678 return True
679 return False
680
681
683 '''
684 Gets if an accessible is visible. This implementation considers an
685 accessible visible if it has all of the states in L{VISIBLE_STATES}.
686
687 @return: Does the accessible consider itself visible?
688 @rtype: boolean
689 @raise LookupError: When the accessible object is dead
690 '''
691 ss = self.accessible.getState()
692
693 for state in self.VISIBLE_STATES:
694 if not ss.contains(state):
695 return False
696 return True
697
698
700 '''
701 Gets if the subject has the given state.
702
703 @param state: State name (e.g. 'focused', 'selected', 'selectable')
704 @type state: string
705 @return: Does the accessible have the given state?
706 @rtype: boolean
707 @see: L{getAccStates}
708 '''
709 return state in self.getAccStates()
710
711
713 '''
714 Gets if the subject has at least one of the given states.
715
716 @param states: States names (e.g. 'focused', 'selected', 'selectable')
717 @type states: string
718 @return: Does the accessible have at least one of the given states?
719 @rtype: boolean
720 @see: L{getAccStates}
721 '''
722 actual = self.getAccStates()
723 for desired in states:
724 if desired in actual:
725 return True
726 return False
727
728
730 '''
731 Gets if the subject has all of the given states.
732
733 @param states: State names (e.g. 'focused', 'selected', 'selectable')
734 @type states: string
735 @return: Does the accessible have all of the given states?
736 @rtype: boolean
737 @see: L{getAccStates}
738 '''
739 actual = self.getAccStates()
740 for desired in states:
741 if desired not in actual:
742 return False
743 return True
744
745
747 '''
748 Gets if the subject has the given role. The given string role is compared
749 to the unlocalized string role of the accessible.
750
751 @param role: Name of the role (e.g. 'terminal', 'glass pane')
752 @type role: string
753 @return: Does the accessible have the given role?
754 @rtype: boolean
755 '''
756 return role == self.accessible.getRoleName()
757
758
760 '''
761 Gets if the subject has any one of the given roles. The given role strings
762 are compared to the unlocalized string role of the accessible.
763
764 @param roles: Names of the roles (e.g. 'terminal', 'glass pane')
765 @type roles: list of string
766 @return: Does the accessible have any one of the given roles?
767 @rtype: boolean
768 '''
769 return self.accessible.getRoleName() in roles
770
771
772
774 '''
775 Gets the list of all action names defined by the subject.
776
777 @return: List of all action names
778 @rtype: list of string
779 @raise NotImplementedError: When the subject does not support actions
780 '''
781 act = (self.accessible).queryAction()
782 return [act.getName(i) for i in xrange(act.nActions)]
783
784
786 '''
787 Gets the list of all action descriptions defined by the subject.
788
789 @return: List of all action descriptions
790 @rtype: list of string
791 @raise NotImplementedError: When the subject does not support actions
792 '''
793 act = (self.accessible).queryAction()
794 return [act.getDescription(i) for i in xrange(act.nActions)]
795
796
798 '''
799 Gets the number of available actions on the subject.
800
801 @return: Number of actions available
802 @rtype: integer
803 @raise NotImplementedError: When the subject does not support actions
804 '''
805 act = (self.accessible).queryAction()
806 return act.nActions
807
808
810 '''
811 Gets the key bindings associated with all available actions. Parses the
812 key bindings into tuples since some actions can have multiple ways of
813 activating them dependent on the context.
814
815 @return: List of tuples, each containing one or more string keysym names
816 indicating the keys to press in sequence to perform the action
817 @rtype: list of tuple of string
818 @raise NotImplementedError: When the subject does not support actions
819 '''
820 act = (self.accessible).queryAction()
821 keys = (act.getKeyBinding(i) for i in xrange(act.nActions))
822 return [k.split(';') for k in keys]
823
825 '''
826 Always False. Default accessibles don't allow embedding.
827
828 @return: False
829 @rtype: boolean
830 '''
831 return False
832
834 '''
835 Gets whether or not the accessible considers itself embedded in its
836 parent's content by calling the parent's L{allowsAccEmbeds} method.
837
838 @return: Does the accessible consider itself embedded?
839 @rtype: boolean
840 @raise LookupError: When the accessible object is dead
841 '''
842 parent = IAccessibleNav(self.subject).getParentAcc()
843 return IAccessibleInfo(parent).allowsAccEmbeds()
844
846 '''
847 Obtain a resource locator ('URI') string which can be used to access the
848 content to which this link "points" or is connected.
849
850 @return: URI string
851 @rtype: string
852 @raise LookupError: When the accessible object is invalid
853 '''
854
855 if self.subject.accessible.getApplication().toolkitName == 'Gecko':
856 ht = (self.subject.accessible.parent).queryHypertext()
857 c = IAccessibleInfo(self.subject).getAccIndex()
858 h = ht.getLink(c)
859 return h.getURI(index)
860 else:
861 return (self.subject).queryHyperlink().getURI(index)
862
864 '''
865 Obtain he number of separate anchors associated with this accessible.
866
867 @return: Number of anchors
868 @rtype: integer
869 @raise LookupError: When the accessible object is invalid
870 '''
871 return (self.subject).queryHyperlink().nAnchors
872