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

Source Code for Module Adapters.ATSPI.DefaultEventHandler

  1  ''' 
  2  Defines default L{AccAdapt.Adapter}s for the L{AEInterfaces.IEventHandler}  
  3  interface on L{pyLinAcc.Accessible} objects. 
  4   
  5  @var EVENT_HANDLERS: Maps event names to method names to be called to  
  6    handle them 
  7  @type EVENT_HANDLERS: dictionary 
  8  @var AE_MAP: Mapping from L{AEEvent}s to raw events that must be registered 
  9    to generate them 
 10  @type AE_MAP: dictionary 
 11   
 12  @author: Pete Brunet 
 13  @author: Peter Parente 
 14  @author: Brett Clippingdale 
 15  @organization: IBM Corporation 
 16  @copyright: Copyright (c) 2005, 2007 IBM Corporation 
 17  @license: The BSD License 
 18   
 19  All rights reserved. This program and the accompanying materials are made 
 20  available under the terms of the BSD license which accompanies 
 21  this distribution, and is available at 
 22  U{http://www.opensource.org/licenses/bsd-license.php} 
 23  ''' 
 24  from POR import POR 
 25  from AEEvent import * 
 26  from AccAdapt import Adapter 
 27  from AEInterfaces import * 
 28  from pyLinAcc import Interfaces, Constants 
 29  import pyLinAcc, AEConstants 
 30   
 31  # opt: AE to raw event mapping, only registers for raw events when desired 
 32  AE_MAP = {ViewChange : ['window:activate', 'window:deactivate',  
 33                          'window:create', 'window:destroy'], 
 34            FocusChange : ['focus', 'object:state-changed:focused'], 
 35            CaretChange : ['object:text-caret-moved', 
 36                           'object:text-changed'], 
 37            ChildrenChange : ['object:children-changed'], 
 38            PropertyChange : ['object:property-change'], 
 39            SelectorChange : ['object:text-selection-changed', 
 40                              'object:selection-changed', 
 41                              'object:active-descendant-changed'], 
 42            # opt: only register for state events that might be worth reporting  
 43            # to a script for the time being, lots of state change processing 
 44            # can really slow down the screen reader 
 45            StateChange : ['object:state-changed:expanded',  
 46                           'object:state-changed:checked', 
 47                           'object:state-changed:enabled', 
 48                           'object:state-changed:sensitive', 
 49                           'object:state-changed:animated', 
 50                           'object:state-changed:busy', 
 51                           'object:state-changed:visible', 
 52                           'object:state-changed:showing'], 
 53            TableChange : ['object:row-inserted', 
 54                           'object:row-deleted', 
 55                           'object:row-reordered', 
 56                           'object:column-inserted', 
 57                           'object:column-deleted', 
 58                           'object:column-reordered'], 
 59            ScreenChange : ['object:bounds-changed', 
 60                            'object:text-bounds-changed', 
 61                            'object:visible-data-changed'], 
 62            MouseChange : ['mouse:abs', 'mouse:button'], 
 63            PrivateChange : ['keyboard:press'] 
 64            } 
 65   
 66  EVENT_HANDLERS = { 
 67    'keyboard:press' : '_handleKeyPressEvent', 
 68    'window:activate': '_handleViewChange', 
 69    'focus' : '_handleFocusEvent', 
 70    'object:state-changed:focused' : '_handleFocusEvent', 
 71    'object:text-changed:insert' : '_handleTextEvent', 
 72    'object:text-changed:delete' : '_handleTextEvent', 
 73    'object:text-caret-moved' : '_handleCaretEvent', 
 74    'object:text-selection-changed' : '_handleTextSelectionEvent', 
 75    'object:selection-changed' : '_handleSelectionChangedEvent', 
 76    'object:active-descendant-changed': '_handleDescendantEvent', 
 77    'object:property-change:accessible-name': '_handlePropertyEvent', 
 78    'object:property-change:accessible-role': '_handlePropertyEvent', 
 79    'object:property-change:accessible-description': '_handlePropertyEvent', 
 80    'object:property-change:accessible-value': '_handlePropertyEvent', 
 81    'object:property-change:accessible-table-caption': '_handlePropertyEvent', 
 82    'object:property-change:accessible-table-summary' : '_handlePropertyEvent', 
 83    'object:property-change:accessible-table-column-description' :  
 84    '_handlePropertyEvent', 
 85    'object:property-change:accessible-table-row-description' :  
 86    '_handlePropertyEvent', 
 87    'object:children-changed:add' : '_handleChildrenEvent', 
 88    'object:children-changed:remove' : '_handleChildrenEvent', 
 89    'object:row-inserted' : '_handleTableEvent', 
 90    'object:row-deleted' : '_handleTableEvent', 
 91    'object:row-reordered' : '_handleTableEvent', 
 92    'object:column-inserted' : '_handleTableEvent', 
 93    'object:column-deleted' : '_handleTableEvent', 
 94    'object:column-reordered' : '_handleTableEvent', 
 95    'object:visible-data-changed' : '_handleScreenEvent', 
 96    'object:text-bounds-changed' : '_handleScreenEvent', 
 97    'object:object-bounds-changed' : '_handleScreenEvent' 
 98    # PP: not handling accessible parent as it seems to cause problems 
 99    #'object:property-change:accessible-parent' : '_handleHierarchyEvent', 
100  } 
101   
102 -class DefaultEventHandlerAdapter(Adapter):
103 ''' 104 Adapts all events from AT-SPI accessibles to the interfaces defined in 105 L{provides}. No condition for adaption is given implying that this adapter is 106 used as a default by L{AccAdapt} when no better adapter is available. 107 108 This class is meant to be subclassed by more specific event handlers. Only the 109 protected handler methods (those starting with _handle) need to be overridden 110 as the public method L{getAEEvents} will call the most child implementation of 111 the appropriate event handling method. 112 113 @cvar last_focus: Last accessible to receive focus 114 @type last_focus: pyLinAcc.Accessible 115 ''' 116 provides = [IEventHandler] 117 singleton = True 118 last_focus = None 119
120 - def getRawEvents(self, kind):
121 ''' 122 Gets a list of raw AT-SPI event names that map to the given kind of 123 L{AEEvent}. 124 125 @param kind: Indicates the type of L{AEEvent} some part of the system wants 126 to be able to process 127 @type kind: L{AEEvent} class 128 @return: List of AT-SPI event names 129 @rtype: list of string 130 @raise KeyError: When no mapping exists for the given event 131 ''' 132 return AE_MAP[kind]
133 134 @pyLinAcc.errorToLookupError
135 - def getAEViewEvents(self, event, collector, vm):
136 ''' 137 Determines if the active view has changed and what events need to be 138 fired in response. The possible cases include a normal view change 139 (app1 window to app2 window), a floating widget change (app1 window to 140 app1 floater), or a overlay change (app1 window to app2 floater). Resets 141 the last focus when the third case occurs so that the first application 142 can announce its restored focus when the floater goes away. For instance, 143 if the metacity task switcher appears and the user immediately closes it, 144 he lands right back in the window where he started. In this case, the 145 new focus is the last focus, but we still want to announce it. 146 147 @param event: Raw event 148 @type event: L{pyLinAcc.Event.Event} 149 @param collector: Callable object that collects L{AEEvent}s to process. 150 Accepts N parameters of type L{AEEvent}. 151 @type collector: callable 152 @param vm: Reference to the view manager that has information about the 153 current view and needs to store a reference to a new view 154 @type vm: L{ViewManager} 155 ''' 156 try: 157 # make sure we can get the event type and role 158 et = event.type.major 159 role = event.source.getRole() 160 except Exception: 161 # bad event 162 return 163 164 # get if this is a window 165 is_win = role == Constants.ROLE_WINDOW 166 167 if et == 'activate' and (vm.setRawView(event.source) or 168 not vm.getRawActive()): 169 # only a view change if the event source wasn't already the active view 170 vm.setRawActive(True) 171 collector(ViewChange(vm.getRawView(), AEConstants.EVENT_VIEW_GAINED)) 172 elif vm.getRawView() is not None and et == 'deactivate': 173 por = POR(event.source) 174 if por == vm.getRawView(): 175 # only unset the flag if another activate hasn't been received 176 vm.setRawActive(False) 177 collector(ViewChange(por, AEConstants.EVENT_VIEW_LOST)) 178 elif not vm.getRawActive() and is_win: 179 # some other application created a floating window 180 if et == 'create' and vm.setRawView(event.source): 181 collector(ViewChange(vm.getRawView(), AEConstants.EVENT_VIEW_GAINED)) 182 # forget about the last focus so that a focus event can be fired when 183 # the previous application is activated 184 DefaultEventHandlerAdapter.last_focus = None 185 elif et == 'destroy': 186 collector(ViewChange(POR(event.source), AEConstants.EVENT_VIEW_LOST))
187 188 @pyLinAcc.errorToLookupError
189 - def getAEEvents(self, event, collector):
190 ''' 191 Determines what L{AEEvent}s should be posted to L{EventManager} for later 192 execution by a L{Tier} based on the provided raw L{pyLinAcc.Event.Event}. 193 Makes an initial decision about whether the event occurred in a focused 194 control or an unfocused control. 195 196 @param event: Raw event 197 @type event: L{pyLinAcc.Event.Event} 198 @param collector: Callable object that collects L{AEEvent}s to process. 199 Accepts N parameters of type L{AEEvent}. 200 @type collector: callable 201 ''' 202 acc = self.subject 203 if acc is None: 204 try: 205 # handle the case where there is no subject, e.g. a device event 206 name = EVENT_HANDLERS[event.type.name] 207 method = getattr(self, name) 208 except (KeyError, AttributeError): 209 return 210 # always consider key events focused 211 focused = True 212 else: 213 try: 214 ss = acc.getState() 215 role = acc.getRole() 216 if (ss.contains(Constants.STATE_DEFUNCT) or 217 role == Constants.ROLE_REDUNDANT_OBJECT): 218 # abort immediately if the accessible has state defunct or if it has 219 # a redundant object role 220 return 221 222 # make a first guess as to whether the object is focused or not 223 # NOTE: fixed focus layer bug where events from unfocused controls 224 # came through as being from the focus; testing against last known 225 # focused control now, not just states except in case where no focus 226 # event has been received yet 227 lf = DefaultEventHandlerAdapter.last_focus 228 focused = (lf == event.source or 229 ss.contains(Constants.STATE_ACTIVE) or 230 (ss.contains(Constants.STATE_FOCUSED) and lf is None)) 231 except AttributeError: 232 # abort immediately if the CORBA object is dead 233 return 234 235 try: 236 # try to hash against an exact method 237 name = EVENT_HANDLERS[event.type.name] 238 method = getattr(self, name) 239 except (KeyError, AttributeError): 240 # if that fails, try to check the major event type 241 if event.type.klass == 'mouse': 242 method = self._handleMouseEvent 243 elif event.type.major == 'state-changed': 244 method = self._handleStateEvent 245 else: 246 return 247 248 if method == self._handleFocusEvent: 249 if self._filterFocusEvent(event): 250 # filter unwanted focus events 251 return 252 253 # call the method that handles this event 254 # keyword arguments will be passed as-is to AEEvent constructors 255 events = method(event, focused=focused) 256 257 if events: 258 if method == self._handleFocusEvent: 259 # synthesize focus lost events 260 if lf is not None: 261 lost_events = self._handleUnfocusEvent(lf) 262 if lost_events: collector(*lost_events) 263 DefaultEventHandlerAdapter.last_focus = event.source 264 # send the generated events 265 collector(*events)
266
267 - def _handleUnfocusEvent(self, source, **kwargs):
268 ''' 269 Creates an L{AEEvent.FocusChange} indicating that the accessible being 270 adapted has lost the focus. 271 272 @param source: Source of the last raw focus change event 273 @type source: L{pyLinAcc.Accessible} 274 @param kwargs: Parameters to be passed to any created L{AEEvent} 275 @type kwargs: dictionary 276 @return: L{AEEvent.FocusChange} 277 @rtype: tuple of L{AEEvent} 278 ''' 279 por = POR(source, None, 0) 280 return (FocusChange(por, False, **kwargs),)
281
282 - def _filterFocusEvent(self, event):
283 ''' 284 Determines if a focus event is a repeat on an already focused object or 285 not. 286 287 @param event: Source of the last raw focus change event 288 @type event: L{pyLinAcc.Accessible} 289 @return: True if this method believes the event should be ignored, False if 290 not 291 @rtype: boolean 292 ''' 293 return ((event.type.klass != 'focus' and not event.detail1) or 294 DefaultEventHandlerAdapter.last_focus == event.source or 295 event.source.getRole() == Constants.ROLE_PAGE_TAB_LIST)
296
297 - def _handleFocusEvent(self, event, **kwargs):
298 ''' 299 Creates an L{AEEvent.FocusChange} indicating that the accessible being 300 adapted has gained the focus. Also creates a L{AEEvent.SelectorChange}. 301 These two L{AEEvent}s will be posted by the caller. 302 303 @param event: Raw focus change event 304 @type event: L{pyLinAcc.Event.Event} 305 @param kwargs: Parameters to be passed to any created L{AEEvent} 306 @type kwargs: dictionary 307 @return: L{AEEvent.FocusChange} and L{AEEvent.SelectorChange} 308 @rtype: tuple of L{AEEvent} 309 ''' 310 por = POR(self.subject, None, 0) 311 # adapt the accessible to an adapter which provides an IAccesssibleInfo 312 # interface and get the accessible's item text 313 item = IAccessibleInfo(por).getAccItemText() 314 # focus events are always in the focus layer 315 kwargs['focused'] = True 316 return (FocusChange(por, True, **kwargs), 317 SelectorChange(por, item, **kwargs))
318
319 - def _handlePropertyEvent(self, event, **kwargs):
320 ''' 321 Creates an L{AEEvent.PropertyChange} indicating that some simple property 322 of an accessible changed. These two L{AEEvent}s will be posted by the 323 caller. 324 325 The L{POR} for the event soruce returned by this method will be marked as 326 incomplete as the accessible may actually be an item. It will be resolved 327 at a later time by the L{AEEvent} if the event will actually be processed. 328 329 @param event: Raw property change event 330 @type event: L{pyLinAcc.Event.Event} 331 @param kwargs: Parameters to be passed to any created L{AEEvent} 332 @type kwargs: dictionary 333 @return: Property change event 334 @rtype: tuple of L{AEEvent} 335 ''' 336 por = POR(self.subject, None, 0, incomplete=True) 337 # strip off the initial word 338 name = event.type.minor[len('accessible-'):] 339 if name == 'value': 340 # try to get the property value from the accessible 341 iv = Interfaces.IValue(self.subject) 342 value = iv.currentValue 343 elif name == 'role': 344 value = unicode(self.subject.getLocalizedRoleName(), 'utf-8') 345 else: 346 # otherwise use the string that ships with the event 347 value = unicode(event.any_data, 'utf-8') 348 return (PropertyChange(por, name, value, **kwargs),)
349
350 - def _handleStateEvent(self, event, **kwargs):
351 ''' 352 Creates an L{AEEvent.StateChange} indicating that some simple property of 353 an accessible changed. These two L{AEEvent}s will be posted by the caller. 354 355 The L{POR} for the event soruce returned by this method will be marked as 356 incomplete as the accessible may actually be an item. It will be resolved 357 at a later time by the L{AEEvent} if the event will actually be processed. 358 359 @param event: Raw state change event 360 @type event: L{pyLinAcc.Event.Event} 361 @param kwargs: Parameters to be passed to any created L{AEEvent} 362 @type kwargs: dictionary 363 @return: L{AEEvent.StateChange} event 364 @rtype: tuple of L{AEEvent} 365 ''' 366 por = POR(self.subject, None, 0, incomplete=True) 367 return (StateChange(por, event.type.minor, event.detail1, **kwargs),)
368
369 - def _handleChildrenEvent(self, event, **kwargs):
370 ''' 371 Creates an L{AEEvent.ChildrenChange} indicating that a child has been added/ 372 removed to an accessible. This L{AEEvent} will be posted by the caller. 373 374 The L{POR} for the event soruce returned by this method will be marked as 375 incomplete as the accessible may actually be an item. It will be resolved 376 at a later time by the L{AEEvent} if the event will actually be processed. 377 378 @param event: Raw children change event 379 @type event: L{pyLinAcc.Event.Event} 380 @param kwargs: Parameters to be passed to any created L{AEEvent} 381 @type kwargs: dictionary 382 @return: L{AEEvent.ChildrenChange} event 383 @rtype: tuple of L{AEEvent} 384 ''' 385 por = POR(self.subject, None, 0) 386 child_por = POR(event.any_data, None, 0, incomplete=True) 387 return (ChildrenChange(por, event.type.minor == 'add', child_por, 388 **kwargs),)
389
390 - def _handleTableEvent(self, event, **kwargs):
391 ''' 392 Creates an L{AEEvent.TableChange} indicating a insert/delete/reorder of a 393 row/column in a table-based accessible. This L{AEEvent} will be posted by 394 the caller. 395 396 @param event: Raw table change event 397 @type event: L{pyLinAcc.Event.Event} 398 @param kwargs: Parameters to be passed to any created L{AEEvent} 399 @type kwargs: dictionary 400 @return: L{AEEvent.TableChange} event 401 @rtype: tuple of L{AEEvent} 402 ''' 403 por = POR(self.subject, None, 0) 404 # per AT-SPI 1.7 specification 405 it = Interfaces.ITable(por.accessible) 406 # index of first child of ins/del/reorder 407 index1 = it.getIndexAt(event.detail1, 0) 408 # index of last child of ins/del/reorder 409 index2 = it.getIndexAt(event.detail1 + event.detail2 - 1, 0) 410 first_child_por = POR(self.subject, index1, 0) 411 last_child_por = POR(self.subject, index2, 0) 412 name = event.type.major 413 # determine whether a row or column event? 414 is_row = name.startswith('row') 415 # event: inserted, deleted or reordered? 416 if name.endswith('inserted'): 417 added = True 418 elif name.endswith('deleted'): 419 added = False 420 else: # re-ordered 421 added = None 422 return (TableChange(por, is_row, added, first_child_por, 423 last_child_por, **kwargs),)
424
425 - def _handleScreenEvent(self, event, **kwargs):
426 ''' 427 Creates an L{AEEvent.ScreenChange} event in response to a visible data or 428 bounds change on an object or text. 429 430 @param event: Raw visibility or bounds change event 431 @type event: L{pyLinAcc.Event.Event} 432 @param kwargs: Parameters to be passed to any created L{AEEvent} 433 @type kwargs: dictionary 434 @return: L{AEEvent.ScreenChange} event 435 @rtype: tuple of L{AEEvent} 436 ''' 437 major = event.type.major 438 if major.startswith('text'): 439 kind = AEConstants.EVENT_TEXT_BOUNDS 440 elif major.startswith('bounds'): 441 kind = AEConstants.EVENT_OBJECT_BOUNDS 442 else: 443 kind = AEConstants.EVENT_VISIBLE_DATA 444 por = POR(self.subject, None, 0, incomplete=True) 445 return (ScreenChange(por, kind, **kwargs),)
446
447 - def _handleMouseEvent(self, event, **kwargs):
448 ''' 449 Creates an L{AEEvent.ScreenChange} event in response to a visible data or 450 bounds change on an object or text. 451 452 @param event: Raw visibility or bounds change event 453 @type event: L{pyLinAcc.Event.Event} 454 @param kwargs: Parameters to be passed to any created L{AEEvent} 455 @type kwargs: dictionary 456 @return: L{AEEvent.ScreenChange} event 457 @rtype: tuple of L{AEEvent} 458 ''' 459 major = event.type.major 460 if major == 'abs': 461 return (MouseChange(AEConstants.EVENT_MOUSE_MOVE, pos=(event.detail1, 462 event.detail2)),) 463 elif event.type.minor.endswith('p'): 464 kind = AEConstants.EVENT_MOUSE_PRESS 465 else: 466 kind = AEConstants.EVENT_MOUSE_RELEASE 467 return (MouseChange(kind, button=int(event.type.minor[0])),)
468
469 - def _handleKeyPressEvent(self, event, **kwargs):
470 ''' 471 Creates an L{AEEvent.PrivateChange} event in response to a key press on 472 the keyboard. This event is kept private because L{Perk}s and L{Task}s 473 should not be relying on key codes and key syms to trigger actions, but 474 rather using abstracted L{AEEvent.InputGesture} events and 475 L{Task.InputTask}s. 476 477 @param event: Raw keyboard event 478 @type event: L{pyLinAcc.Event.Event} 479 @param kwargs: Parameters to be passed to any created L{AEEvent} 480 @type kwargs: dictionary 481 @return: L{AEEvent.PrivateChange} event 482 @rtype: tuple of L{AEEvent} 483 ''' 484 return (PrivateChange(PrivateChange.KEY_PRESS, sym=event.any_data[0], 485 code=event.detail2, mod=event.any_data[1]),)
486