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

Source Code for Module Adapters.ATSPI.ComboboxAdapter

  1  ''' 
  2  Defines L{AccAdapt.Adapter}s for combo boxes that receive the focus but have 
  3  children that fire events. 
  4   
  5   
  6  @author: Peter Parente 
  7  @organization: IBM Corporation 
  8  @copyright: Copyright (c) 2005, 2007 IBM Corporation 
  9  @license: The BSD License 
 10   
 11  All rights reserved. This program and the accompanying materials are made 
 12  available under the terms of the BSD license which accompanies 
 13  this distribution, and is available at 
 14  U{http://www.opensource.org/licenses/bsd-license.php} 
 15  ''' 
 16   
 17  from POR import POR 
 18  from AEEvent import * 
 19  from AEInterfaces import * 
 20  from TextAdapter import TextEventHandlerAdapter 
 21  from pyLinAcc import Constants, Interfaces 
 22  import pyLinAcc 
 23  from DefaultNav import * 
 24   
25 -class ComboboxEventHandlerAdapter(TextEventHandlerAdapter):
26 ''' 27 Overrides L{TextEventHandlerAdapter} to enable processing of events from 28 text areas within combo boxes where the combo box gets focus, the text area 29 doesn't, and the text area is the source of all text events. Fires a 30 L{AEEvent.FocusChange} and either a L{AEEvent.CaretChange} or 31 L{AEEvent.SelectorChange} on focus. Expects the subject to be a 32 L{pyLinAcc.Accessible}. 33 34 Adapts subject accessibles that have a role of combo box or whose parent have 35 have a role of combo box. 36 ''' 37 provides = [IEventHandler] 38 39 @staticmethod
40 - def when(subject):
41 ''' 42 Tests if the given subject can be adapted by this class. 43 44 @param subject: Accessible to test 45 @type subject: L{pyLinAcc.Accessible} 46 @return: True when the subject meets the condition named in the docstring 47 for this class, False otherwise 48 @rtype: boolean 49 ''' 50 r = subject.getRole() 51 pr = subject.parent.getRole() 52 c = Constants 53 roles = (c.ROLE_COMBO_BOX, c.ROLE_EDITBAR, c.ROLE_AUTOCOMPLETE) 54 return (r in roles or pr in roles)
55
56 - def _getTextArea(self):
57 ''' 58 Looks for a widgets supporting the text interface within the combo box. 59 60 @return: The accessible that provides the text interface and the accessible 61 already queried to that interface 62 @rtype: 2-tuple of L{pyLinAcc.Accessible} 63 ''' 64 text = None 65 # see if the combobox is acting as a proxy for the text box 66 try: 67 text = Interfaces.IText(self.subject) 68 return self.subject, text 69 except NotImplementedError: 70 pass 71 # if not, find the child text box 72 for i in xrange(self.subject.childCount): 73 try: 74 acc = self.subject.getChildAtIndex(i) 75 text = Interfaces.IText(acc) 76 return acc, text 77 except NotImplementedError: 78 pass 79 raise NotImplementedError
80
81 - def _isFocused(self):
82 ''' 83 Gets if the subject or its parent is focused. 84 85 @return: Does the subject or its parent have focus? 86 @rtype: boolean 87 ''' 88 f = Constants.STATE_FOCUSED 89 if (self.subject.getState().contains(f) or 90 self.subject.parent.getState().contains(f)): 91 return True 92 return False
93
94 - def _handleFocusEvent(self, event, **kwargs):
95 ''' 96 Creates an L{AEEvent.FocusChange} indicating that the accessible being 97 adapted has gained the focus. Also a L{AEEvent.CaretChange}. This sequence 98 of L{AEEvent}s will be posted by the caller. 99 100 @param event: Raw focus change event 101 @type event: L{pyLinAcc.Event.Event} 102 @param kwargs: Parameters to be passed to any created L{AEEvent} 103 @type kwargs: dictionary 104 @return: L{AEEvent.FocusChange} and L{AEEvent.CaretChange} 105 @rtype: tuple of L{AEEvent} 106 ''' 107 # build a focus event on the subject 108 kwargs['focused'] = True 109 por = POR(self.subject, None, 0) 110 focus_evt = FocusChange(por, True, **kwargs) 111 112 # try to locate the text area in the combo box 113 try: 114 acc, text = self._getTextArea() 115 except NotImplementedError: 116 # if we don't support text, then we should have a selection at least 117 item = IAccessibleInfo(por).getAccItemText() 118 return (focus_evt, SelectorChange(por, item, **kwargs)) 119 120 char_offset = text.caretOffset 121 # get the text of the caret line, it's starting and ending offsets 122 line, sOff, eOff = \ 123 text.getTextAtOffset(char_offset, Constants.TEXT_BOUNDARY_LINE_START) 124 # create a POR with start offset of the line and relative caret offset 125 por = POR(acc, sOff, char_offset - sOff) 126 127 # create a focus event for this POR 128 # and add a caret event to indicate the current position 129 return (focus_evt, CaretChange(por, unicode(line, 'utf-8'), sOff, **kwargs))
130
131 - def _handleTextEvent(self, event, focused, **kwargs):
132 ''' 133 Called when text is inserted or deleted (object:text-changed:insert & 134 object:text-changed:delete). Creates and returns an L{AEEvent.CaretChange} 135 to indicate a change in the caret context. 136 137 L{pyLinAcc.Event.Event}.type.minor is "insert" or "delete". 138 L{pyLinAcc.Event.Event}.detail1 has start offset of text change. 139 L{pyLinAcc.Event.Event}.detail2 has length of text change. 140 L{pyLinAcc.Event.Event}.any_data has text inserted/deleted. 141 142 @param event: Raw text-changed event 143 @type event: L{pyLinAcc.Event.Event} 144 @param kwargs: Parameters to be passed to any created L{AEEvent} 145 @type kwargs: dictionary 146 @return: L{AEEvent.CaretChange} 147 @rtype: tuple of L{AEEvent} 148 ''' 149 # try to locate the text area in the combo box 150 try: 151 acc, text = self._getTextArea() 152 except NotImplementedError: 153 return None 154 155 # recompute focus 156 focused = focused or self._isFocused() 157 158 # get the text of the caret line, it's starting and ending offsets 159 line, sOff, eOff = \ 160 text.getTextAtOffset(text.caretOffset, Constants.TEXT_BOUNDARY_LINE_START) 161 # create a POR with start offset of the line and relative caret offset 162 por = POR(self.subject, sOff, text.caretOffset - sOff) 163 164 # create Caret event with current POR, text added/removed, where it changed, 165 # and whether it was inserted. 166 return (CaretChange(por, unicode(event.any_data, 'utf-8'), 167 event.detail1, (event.type.minor == 'insert'), 168 focused=focused, **kwargs),)
169
170 - def _handleCaretEvent(self, event, focused, **kwargs):
171 ''' 172 Creates and returns an L{AEEvent.CaretChange} indicating the caret moved in 173 the subject accessible. 174 175 L{pyLinAcc.Event.Event}.detail1 has caret offset. 176 177 @param event: Raw caret movement event 178 @type event: L{pyLinAcc.Event.Event} 179 @param kwargs: Parameters to be passed to any created L{AEEvent} 180 @type kwargs: dictionary 181 @return: L{AEEvent.CaretChange} 182 @rtype: tuple of L{AEEvent} 183 ''' 184 # try to locate the text area in the combo box 185 try: 186 acc, text = self._getTextArea() 187 except NotImplementedError: 188 return None 189 190 # recompute focus 191 focused = focused or self._isFocused() 192 193 # detail1 has new caret position 194 caret_offset = event.detail1 195 196 # get the text of the new caret line, it's starting and ending offsets 197 line, sOff, eOff = \ 198 text.getTextAtOffset(caret_offset, Constants.TEXT_BOUNDARY_LINE_START) 199 200 # create a POR with start offset of the line and relative caret offset 201 por = POR(self.subject, sOff, caret_offset - sOff) 202 203 # create Caret event with new POR, line at caret, and offset of caret 204 return (CaretChange(por, unicode(line, 'utf-8'), caret_offset, 205 focused=focused, **kwargs),)
206 207
208 -class ComboBoxNavAdapter(DefaultNavAdapter):
209 ''' 210 Overrides L{DefaultNavAdapter} to provide navigation over text lines as 211 items and to avoid traversing text children as separate accessible children 212 in the L{IAccessibleNav} interface. 213 214 Adapts accessibles that have a role of terminal or have a state of multiline, 215 single line, or editable and provide the Text interface. 216 Does not adapt L{POR} accessibles with role of page tab. 217 ''' 218 provides = [IItemNav, IAccessibleNav] 219 220 @staticmethod
221 - def when(subject):
222 ''' 223 Tests if the given subject can be adapted by this class. 224 225 @param subject: Accessible to test 226 @type subject: L{pyLinAcc.Accessible} 227 @return: True when the subject meets the condition named in the docstring 228 for this class, False otherwise 229 @rtype: boolean 230 ''' 231 acc = subject.accessible 232 r = acc.getRole() 233 pr = acc.parent.getRole() 234 c = Constants 235 roles = (c.ROLE_COMBO_BOX, c.ROLE_EDITBAR, c.ROLE_AUTOCOMPLETE) 236 return (r in roles or pr in roles)
237
238 - def getNextAcc(self):
239 ''' 240 Gets the next accessible relative to the one providing this interface. 241 242 @return: Point of regard to the next accessible 243 @rtype: L{POR} 244 @raise IndexError: When there is no next accessible 245 @raise LookupError: When lookup for the next accessible fails even though 246 it may exist 247 ''' 248 if self.accessible.getRole() == Constants.ROLE_COMBO_BOX: 249 cb = self.accessible 250 else: 251 cb = self.accessible.parent 252 253 i = cb.getIndexInParent() 254 has_parent = cb.parent is not None 255 if i < 0 or not has_parent: 256 # indicate lookup of the next peer failed 257 raise LookupError 258 # get the accessible at the next index (the peer) 259 child = cb.parent.getChildAtIndex(i+1) 260 if child is None: 261 # indicate there is no next peer 262 raise IndexError 263 return POR(child, None, 0)
264
265 - def getPrevAcc(self):
266 ''' 267 Gets the previous accessible relative to the one providing this interface. 268 269 @return: Point of regard to the previous accessible 270 @rtype: L{POR} 271 @raise IndexError: When there is no previous accessible 272 @raise LookupError: When lookup for the previous accessible fails even 273 though it may exist 274 ''' 275 if self.accessible.getRole() == Constants.ROLE_COMBO_BOX: 276 cb = self.accessible 277 else: 278 cb = self.accessible.parent 279 280 i = cb.getIndexInParent() 281 has_parent = cb.parent is not None 282 if i <= 0 or not has_parent: 283 # indicate lookup of the previous peer failed 284 raise LookupError 285 # get the accessible at the previous index (the peer) 286 child = cb.parent.getChildAtIndex(i-1) 287 if child is None: 288 # indicate there is no previous peer 289 raise IndexError 290 return POR(child, None, 0)
291
292 - def getParentAcc(self):
293 ''' 294 Gets the parent accessible relative to the one providing this interface. 295 296 @return: Point of regard to the parent accessible 297 @rtype: L{POR} 298 @raise LookupError: When lookup for the parent accessible fails because it 299 does not exist 300 ''' 301 if self.accessible.getRole() == Constants.ROLE_COMBO_BOX: 302 cb = self.accessible 303 else: # probably textbox 304 cb = self.accessible.parent 305 306 parent = cb.parent 307 if parent is None: 308 raise LookupError 309 return POR(parent, None, 0)
310
311 - def getFirstAccChild(self):
312 ''' 313 Gets the first accessible child in the combobox list. 314 315 @return: Point of regard to the first list item 316 @rtype: L{POR} 317 @raise LookupError: When lookup for child fails because it does not exist 318 ''' 319 if self.accessible.getRole() == Constants.ROLE_COMBO_BOX: 320 cb = self.accessible 321 else: # probably textbox 322 cb = self.accessible.parent 323 324 # find head of list based on known parent roles 325 listhead = self._findListHead(cb) 326 if listhead is None: 327 raise LookupError 328 329 return POR(listhead, None, 0)
330
331 - def getLastAccChild(self):
332 ''' 333 Gets the last accessible child in combobox list. 334 335 @return: Point of regard to the last child accessible 336 @rtype: L{POR} 337 @raise LookupError: When lookup for child fails because it does not exist 338 ''' 339 if self.accessible.getRole() == Constants.ROLE_COMBO_BOX: 340 cb = self.accessible 341 else: # probably textbox 342 cb = self.accessible.parent 343 344 listhead = self._findListHead(cb) 345 if listhead is None: 346 raise LookupError 347 348 child = listhead.parent.getChildAtIndex(listhead.parent.childCount-1) 349 if child is None: 350 raise LookupError 351 return POR(child, None, 0)
352
353 - def _findListHead(self, cb):
354 ''' 355 Performs a depth only search to find the first list item in a 356 combobox list. Could be of type menu item or list item. 357 358 @return: Point of regard to the first child accessible 359 @rtype: L{POR} 360 @raise LookupError: When lookup for child fails because it does not exist 361 ''' 362 listhead = None 363 c = cb.getChildAtIndex(0) 364 while c is not None: 365 c_role = c.getRoleName() 366 if c_role == 'menu' or c_role == 'list': 367 if c.childCount > 0: 368 listhead = c.getChildAtIndex(0) 369 else: 370 listhead = cb 371 break 372 c = c.getChildAtIndex(0) 373 return listhead
374