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
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
43
44
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
99
100 }
101
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
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
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
158 et = event.type.major
159 role = event.source.getRole()
160 except Exception:
161
162 return
163
164
165 is_win = role == Constants.ROLE_WINDOW
166
167 if et == 'activate' and (vm.setRawView(event.source) or
168 not vm.getRawActive()):
169
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
176 vm.setRawActive(False)
177 collector(ViewChange(por, AEConstants.EVENT_VIEW_LOST))
178 elif not vm.getRawActive() and is_win:
179
180 if et == 'create' and vm.setRawView(event.source):
181 collector(ViewChange(vm.getRawView(), AEConstants.EVENT_VIEW_GAINED))
182
183
184 DefaultEventHandlerAdapter.last_focus = None
185 elif et == 'destroy':
186 collector(ViewChange(POR(event.source), AEConstants.EVENT_VIEW_LOST))
187
188 @pyLinAcc.errorToLookupError
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
206 name = EVENT_HANDLERS[event.type.name]
207 method = getattr(self, name)
208 except (KeyError, AttributeError):
209 return
210
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
219
220 return
221
222
223
224
225
226
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
233 return
234
235 try:
236
237 name = EVENT_HANDLERS[event.type.name]
238 method = getattr(self, name)
239 except (KeyError, AttributeError):
240
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
251 return
252
253
254
255 events = method(event, focused=focused)
256
257 if events:
258 if method == self._handleFocusEvent:
259
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
265 collector(*events)
266
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
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
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
312
313 item = IAccessibleInfo(por).getAccItemText()
314
315 kwargs['focused'] = True
316 return (FocusChange(por, True, **kwargs),
317 SelectorChange(por, item, **kwargs))
318
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
338 name = event.type.minor[len('accessible-'):]
339 if name == 'value':
340
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
347 value = unicode(event.any_data, 'utf-8')
348 return (PropertyChange(por, name, value, **kwargs),)
349
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
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
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
405 it = Interfaces.ITable(por.accessible)
406
407 index1 = it.getIndexAt(event.detail1, 0)
408
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
414 is_row = name.startswith('row')
415
416 if name.endswith('inserted'):
417 added = True
418 elif name.endswith('deleted'):
419 added = False
420 else:
421 added = None
422 return (TableChange(por, is_row, added, first_child_por,
423 last_child_por, **kwargs),)
424
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
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
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