| 9 | | |
|---|
| 10 | | # can't derive from pyatspi.Accessibility.Accessible due to various issues |
|---|
| 11 | | # so we set the class to delegate |
|---|
| 12 | | class AccDecorator(object): |
|---|
| 13 | | ''' provides convienience functions for an acc ''' |
|---|
| 14 | | |
|---|
| 15 | | class_delegated = False |
|---|
| 16 | | |
|---|
| 17 | | def __init__(self, acc): |
|---|
| 18 | | self._acc = acc |
|---|
| 19 | | super(AccDecorator, self).__init__(self) # not sure about this |
|---|
| 20 | | if not AccDecorator.class_delegated: |
|---|
| 21 | | self._delegateSpecialMethods(acc) |
|---|
| 22 | | AccDecorator.class_delegated = True; |
|---|
| 23 | | |
|---|
| 24 | | def _delegateSpecialMethods(self, acc): |
|---|
| 25 | | #special methods don't get passed to __getattr__ for new style objects |
|---|
| 26 | | base_klass = acc.__class__ |
|---|
| 27 | | this_klass = self.__class__ |
|---|
| 28 | | for name in ('__del__', '__iter__', '__str__', '__getitem__', '__len__'): |
|---|
| 29 | | if hasattr(base_klass, name): |
|---|
| 30 | | #Set attribute to be a function that delegates to base class |
|---|
| 31 | | setattr(this_klass, name, AccDecorator.getter(name)) |
|---|
| 32 | | |
|---|
| 33 | | @staticmethod |
|---|
| 34 | | def getter(attrib): |
|---|
| 35 | | # lambda is a closure |
|---|
| 36 | | return lambda self, *args, **kwargs: getattr(self._acc, attrib)(*args, **kwargs) |
|---|
| 37 | | |
|---|
| 38 | | def __getattr__(self, attrib): |
|---|
| 39 | | # this is in leu of deriving and may have different visibility effects |
|---|
| 40 | | # delegate missing attributes to _acc |
|---|
| 41 | | return getattr(self._acc, attrib) |
|---|
| 42 | | |
|---|
| 43 | | # now the 'real' functions |
|---|
| 44 | | def hasStates(self, *states): |
|---|
| 45 | | act_states = set(self._acc.getState().getStates()) |
|---|
| 46 | | req_states = set(states) |
|---|
| 47 | | return req_states.issubset(act_states) |
|---|
| 48 | | |
|---|
| 49 | | def getStates(self, *states): |
|---|
| 50 | | return self._acc.getState().getStates() |
|---|
| 51 | | |
|---|
| 52 | | def hasStateIn(self, *states): |
|---|
| 53 | | act_states = set(self._acc.getState().getStates()) |
|---|
| 54 | | req_states = set(states) |
|---|
| 55 | | return len(req_states.intersection(act_states) <> 0) |
|---|
| 56 | | |
|---|
| 57 | | def hasInterface(self, iface): |
|---|
| 58 | | try: |
|---|
| 59 | | method = 'query' + iface |
|---|
| 60 | | try: |
|---|
| 61 | | im = getattr(self._acc, method) |
|---|
| 62 | | except AttributeError: |
|---|
| 63 | | log.debug('no such interface "%s"' % method) |
|---|
| 64 | | raise |
|---|
| 65 | | im() |
|---|
| 66 | | except (AttributeError, NotImplementedError): |
|---|
| 67 | | return False |
|---|
| 68 | | return True |
|---|
| 69 | | |
|---|
| 70 | | def hasInterfaces(self, *interfaces): |
|---|
| 71 | | for iface in interfaces: |
|---|
| 72 | | if not self.hasInterface(iface): |
|---|
| 73 | | return False |
|---|
| 74 | | return True |
|---|
| 75 | | |
|---|
| 76 | | def hasInterfaceIn(self, *interfaces): |
|---|
| 77 | | for iface in interfaces: |
|---|
| 78 | | if self.hasInterface(iface): |
|---|
| 79 | | return True |
|---|
| 80 | | return False |
|---|
| 81 | | |
|---|
| 82 | | def hasRoleIn(self, *roles): |
|---|
| 83 | | return self._acc.getRole() in roles |
|---|
| 84 | | |
|---|
| 85 | | def hasToolkitIn(self, *toolkits): |
|---|
| 86 | | app = self._acc.getApplication() |
|---|
| 87 | | try: |
|---|
| 88 | | ai = app.queryApplication() |
|---|
| 89 | | except NotImplementedError: |
|---|
| 90 | | return False |
|---|
| 91 | | return ai.toolkitName in toolkits |
|---|
| 92 | | |
|---|
| 93 | | def isInteractive(self): |
|---|
| 94 | | if self.hasRoleIn(pyatspi.ROLE_SEPARATOR) \ |
|---|
| 95 | | or not self.hasStates(pyatspi.STATE_SHOWING, pyatspi.STATE_VISIBLE, pyatspi.STATE_SENSITIVE): |
|---|
| 96 | | return False |
|---|
| 97 | | #print self, self.getStates() |
|---|
| 98 | | return self.isSelectable() or self.hasInterfaceIn('Action','EditableText', 'Hypertext') |
|---|
| 99 | | |
|---|
| 100 | | def isEditable(self): |
|---|
| 101 | | return self.hasInterface('EditableText') |
|---|
| 102 | | |
|---|
| 103 | | def isActionable(self): |
|---|
| 104 | | return self.hasInterface('Action') |
|---|
| 105 | | |
|---|
| 106 | | def isSelectable(self): |
|---|
| 107 | | return self.hasStates(pyatspi.STATE_SELECTABLE) |
|---|
| 108 | | |
|---|
| 109 | | def isSelection(self): |
|---|
| 110 | | return self.hasInterface('Selection') |
|---|
| 111 | | |
|---|
| 112 | | def isEditableText(): |
|---|
| 113 | | return self.hasInterface('EditableText') |
|---|
| 114 | | |
|---|
| 115 | | def hasInteractiveChild(self): |
|---|
| 116 | | rv = None |
|---|
| 117 | | for child in self._acc: |
|---|
| 118 | | # REVIEW we can stop desending a tree if not SHOWING - add filter to findDesc? |
|---|
| 119 | | rv = pyatspi.findDescendant(child, self.isInteractive) |
|---|
| 120 | | if rv is not None: break |
|---|
| 121 | | return bool(rv) |
|---|
| 122 | | |
|---|
| 123 | | def grabFocus(self): |
|---|
| 124 | | try: |
|---|
| 125 | | ic = self.queryComponent() |
|---|
| 126 | | ic.grabFocus() |
|---|
| 127 | | except NotImplementedError: |
|---|
| 128 | | pass |
|---|
| 129 | | |
|---|
| 130 | | def toggleSectable(self): |
|---|
| 131 | | selection = self._acc.parent |
|---|
| 132 | | try: |
|---|
| 133 | | si = selection.querySelection() |
|---|
| 134 | | except NotImplementedError: |
|---|
| 135 | | return |
|---|
| 136 | | |
|---|
| 137 | | index = self.getIndexInParent() |
|---|
| 138 | | if si.isChildSelected(index): |
|---|
| 139 | | si.deselectChild(index) |
|---|
| 140 | | else: |
|---|
| 141 | | si.selectChild(index) |
|---|
| 142 | | |
|---|
| 143 | | def getText(self): |
|---|
| 144 | | try: |
|---|
| 145 | | eti = self.queryEditableText() |
|---|
| 146 | | except NotImplementedError: |
|---|
| 147 | | return |
|---|
| 148 | | return eti.getText(0,-1) |
|---|
| 149 | | |
|---|
| 150 | | def generateClick(self): |
|---|
| 151 | | try: |
|---|
| 152 | | ci = self.queryComponent() |
|---|
| 153 | | except NotImplementedError: |
|---|
| 154 | | return |
|---|
| 155 | | rect = ci.getExtents(0) |
|---|
| 156 | | x = int(rect.x + rect.width / 2) |
|---|
| 157 | | y = int(rect.y + rect.height / 2) |
|---|
| 158 | | reg = pyatspi.Registry() |
|---|
| 159 | | reg.generateMouseEvent(x, y, pyatspi.MOUSE_B1P) |
|---|
| 160 | | reg.generateMouseEvent(x, y, pyatspi.MOUSE_B1R) |
|---|
| 161 | | reg.generateMouseEvent(x, y, pyatspi.MOUSE_B1C) |
|---|
| 162 | | |
|---|
| 163 | | def doAction(self): |
|---|
| 164 | | try: |
|---|
| 165 | | action_if = acc.queryAction() |
|---|
| 166 | | action_if.doAction(0) |
|---|
| 167 | | except: |
|---|
| 168 | | pass |
|---|