1 '''
2 Defines a class for managing the L{AEScript <AEScript.AEScript>}s loaded for a single instance of an
3 application.
4
5 @author: Peter Parente
6 @author: Pete Brunet
7 @author: Larry Weiss
8 @author: Scott Haeger
9 @organization: IBM Corporation
10 @copyright: Copyright (c) 2005, 2007 IBM Corporation
11 @license: The BSD License
12
13 @author: Frank Zenker
14 @author: Ramona Bunk
15 @author: Nicole Anacker
16 @organization: IT Science Center Ruegen gGmbH, Germany
17 @copyright: Copyright (c) 2007, 2008 ITSC Ruegen
18 @license: The BSD License
19
20 All rights reserved. This program and the accompanying materials are made
21 available under the terms of the BSD license which accompanies
22 this distribution, and is available at
23 U{http://www.opensource.org/licenses/bsd-license.php}
24 '''
25
26 import logging, weakref, traceback, sys, os
27 import types
28 import AccessEngine
29 from AccessEngine import AEState, AEInput
30 import AEScript, AEConstants, AccessEngineAPI
31
32
33 log = logging.getLogger('AETier')
34
36 '''
37 Manages the L{AEScript <AEScript.AEScript>}s for a single instance of a running program (a process).
38 Supports the addition and removal of L{AEScript <AEScript.AEScript>}s from its stack. Executes
39 script tasks registered in each L{AEScript <AEScript.AEScript>} in response to L{AEEvent}s. Provides
40 methods to assist L{AEScript <AEScript.AEScript>}s in finding registered tasks throughout the
41 L{AETier}.
42
43 @ivar aid: ID uniquely identifying this L{AETier} from all other L{AETier}s
44 @type aid: opaque
45 @ivar name: Name of this L{AETier}
46 @type name: string
47 @ivar pointer_por: Pointer of regard for the user focus
48 @type pointer_por: L{AEPor}
49 @ivar focus_por: Point of regard for the application focus
50 @type focus_por: L{AEPor}
51 @ivar virtual_por: review pointer
52 @type virtual_por: L{AEPor}
53 @ivar last_key: Stores the key code, key sym, and modifiers for the last key
54 pressed
55 @type last_key: 3-tuple of integer, string, integer
56
57 @ivar scripts: List of L{AEScript <AEScript.AEScript>}s treated as a stack (last in, first executed)
58 @type scripts: list [L{AEScript <AEScript.AEScript>}]
59
60 @ivar wanted_events: Lookup table for what L{AEEvent}s are desired by any
61 task in any L{AEScript <AEScript.AEScript>} in this L{AETier}. Used to optimize event dispatch and
62 processing. Unwanted events are ignored. ::
63 Dictionary {
64 key: <class 'AEEvent-Class-Type'>,
65 value: integer
66 }
67
68 @type wanted_events: dictionary
69 @ivar script_refs: References to L{AEScript <AEScript.AEScript>}s keyed by task names, commands,
70 identifiers, etc. Many to one mapping. See L{addTaskRef} and
71 L{removeTaskRef}.::
72 WeakValueDictionary {
73 key: ('Task Identity', script.getClassName()), value: Script;
74 key: id(Chooser), value: Script;
75 key: L{AEInput.GestureList}, value: Script
76 }
77
78 @type script_refs: weakref.WeakValueDictionary
79 @ivar chain_refs: References to L{AEScript <AEScript.AEScript>}s keyed by task names. Many to many
80 mapping. See L{addChainRef} and L{removeChainRef}. ::
81 Dictionary {
82 key: ('Task Identity', script.getClassName()),
83 value: { key: Script, value: None}
84 }
85
86 @type chain_refs: dictionary
87 @ivar chain_stack: Stack of task names currently executing in one or more
88 chains before, after, or around one or more anchors ::
89 Tuple ('Task Identity', script.getClassName())
90
91 @type chain_stack: list of tuple
92 @ivar temp_data: Arbitrary name/value pairs stored by tasks executing in
93 this L{AETier} in response to a single event. The data is persistent until
94 the event has been handled by all tasks at which time it is cleared. ::
95 Dictionary {
96 key: keyName immutable,
97 value: object
98 }
99
100 @type temp_data: dictionary
101 @ivar task_history: Stack of task and L{AEScript <AEScript.AEScript>} names having already
102 executed in response to an L{AEEvent}, including all chained tasks and
103 all tasks executed from within other tasks ::
104 Tuple ('Task Identity', script.getClassName())
105
106 @type task_history: list of tuple
107
108 @ivar show: Call show* methods on L{AETierManager} to log events? Set by the
109 L{AETierManager} based on whether monitors are loaded or not. Optimization.
110 @type show: boolean
111 @ivar event_layer:
112 @type event_layer: integer
113 '''
115 '''
116 Stores a reference to the L{AETierManager} that created this L{AETier}. Creates
117 an empty list of L{AEScript <AEScript.AEScript>}s. Initializes the stored focus L{AEPor} to None and
118 the pointer L{AEPor} to the application. Creates the weak dictionary for
119 L{AEScript <AEScript.AEScript>} tasks and stores other information about the L{AETier}.
120
121 @param name: Name of this L{AETier}
122 @type name: string
123 @param aid: ID uniquely identifying this L{AETier} from all other L{AETier}s
124 @type aid: opaque
125 @param por: First point of regard to the application represented by
126 this L{AETier}. Typically, the top most accessible.
127 @type por: L{AEPor}
128 '''
129 self.name = name
130 self.aid = aid
131 self.pointer_por = por
132 self.focus_por = None
133 self.virtual_por = None
134 self.last_key = (None, None, None)
135 self.scripts = []
136 self.wanted_events = {}
137 self.event_layer = None
138 self.script_refs = weakref.WeakValueDictionary()
139 self.chain_refs = {}
140
141 self.clearState()
142
144 '''
145 Clears all L{AEScript <AEScript.AEScript>}s and closes them.
146 '''
147 self.clearScripts()
148
150 '''
151 Clears all stored L{temp_data}, the L{chain_stack}, L{task_history}, and
152 the L{show} flag.
153 '''
154 self.temp_data = {}
155 self.task_history = []
156 self.chain_stack = []
157 self.show = False
158
160 '''
161 @return: Name of this L{AETier} (i.e. name of the app with which it is
162 associated)
163 @rtype: string
164 '''
165 return self.name
166
168 '''
169 @return: Unique identifier for this L{AETier}
170 @rtype: opaque
171 '''
172 return self.aid
173
175 '''
176 Gets the user L{AEPor} for this L{AETier}.
177
178 @return: Point of regard of user attention
179 @rtype: L{AEPor}
180 '''
181 return self.pointer_por
182
184 '''
185 Sets the user L{AEPor} for this L{AETier}.
186
187 @param por: Point of regard of user attention
188 @type por: L{AEPor}
189 '''
190 self.pointer_por = por
191
193 '''
194 Gets the application focus L{AEPor} for this L{AETier}.
195
196 @return: Point of regard of application focus
197 @rtype: L{AEPor}
198 '''
199 return self.focus_por
200
202 '''
203 Gets the last key code, key sym, and key modifiers. Useful for dealing with
204 applications that do a poor job of synthesizing caret and text events.
205
206 @return: The key code, key sym, and modifiers for the last key pressed
207 @rtype: 3-tuple of integer, string, integer
208 '''
209 return self.last_key
210
212 '''
213 Gets data from L{temp_data}.
214
215 @param name: Name of the stored data
216 @type name: immutable
217 @return: Value stored under the given name
218 @rtype: object
219 @raise KeyError: When no data is stored under the given name
220 '''
221 return self.temp_data[name]
222
224 '''
225 Stores data for the duration of the execution of an L{AEEvent}.
226
227 @param name: Name to associate with the value
228 @type name: immutable
229 @param value: Any value to store
230 @type value: object
231 '''
232 self.temp_data[name] = value
233
235 '''
236 Sets whether show methods should be called on the L{AETierManager} when
237 handling events or not based on the presence of L{AEMonitor}s in the
238 manager.
239
240 @param show: Call show methods to notify monitors?
241 @type show: boolean
242 '''
243 self.show = show
244
246 '''
247 '''
248
249 return self.event_layer
250
252 '''
253 Adds one or more L{AEScript <AEScript.AEScript>}s to the top of the stack. If more than one L{AEScript <AEScript.AEScript>}
254 is specified, the last specified L{AEScript <AEScript.AEScript>} will be at the top of the stack
255 when this method completes. That is, the behavior of pushing more than one
256 L{AEScript <AEScript.AEScript>} at a time is the same as if each L{AEScript <AEScript.AEScript>} were pushed individually.
257
258 @param scripts: L{AEScript <AEScript.AEScript>}s to add
259 @type scripts: list of L{AEScript <AEScript.AEScript>}
260 '''
261 self.insertScript(0, *scripts)
262
264 '''
265 Adds one L{AEScript <AEScript.AEScript>} to the stack at the insertion index. Negative indices are
266 valid per Python list convention.
267
268 @param index: Index at which the L{AEScript <AEScript.AEScript>} should be inserted in the stack
269 @type index: integer
270 @param scripts: L{AEScript <AEScript.AEScript>}s to add
271 @type scripts: list of L{AEScript <AEScript.AEScript>}
272 '''
273 for script in scripts:
274
275 self.scripts.insert(index, script)
276
277 script.preInit(self)
278 self.event_layer = AEConstants.LAYER_FOCUS
279 try:
280
281 script.init()
282 except Exception, e:
283
284 self.scripts.pop(index)
285
286 try:
287 script.close()
288 except Exception:
289
290 pass
291 script.postClose()
292
293 info = traceback.extract_tb(sys.exc_info()[2])
294 log.debug('cannot initialize %s (%s, %d): %s', script.getClassName(),
295 os.path.basename(info[-1][0]),
296 info[-1][1], str(e))
297
299 '''
300 Removes one or more L{AEScript <AEScript.AEScript>}s from the stack given
301 their indices. Negative indices are valid per Python list convention. Calls
302 L{AEScript.AEScript.close} on each to ensure they have a chance to persist
303 state.
304
305 @param indices: Indices of L{AEScript <AEScript.AEScript>}s to remove
306 @type indices: list of integer
307 @raise IndexError: When the stack is empty
308 @raise ValueError: When a specific index is out of the bounds of the stack
309 '''
310 indices = list(indices)
311
312 indices.sort(reverse=True)
313 scripts = map(self.scripts.pop, indices)
314 for script in scripts:
315 try:
316 script.close()
317 except Exception:
318
319 pass
320 script.postClose()
321
323 '''
324 Removes all L{AEScript <AEScript.AEScript>}s from the stack. Calls
325 L{AEScript.AEScript.close} on each to ensure they have a chance to persist
326 state.
327 '''
328 for script in self.scripts:
329 try:
330 script.close()
331 except Exception:
332
333 pass
334 script.postClose()
335 self.scripts = []
336 self.script_refs.clear()
337
339 '''
340 Gets all L{AEScript <AEScript.AEScript>}s pushed onto this L{AETier} in execution order.
341
342 @note: This method does not return a copy of the stack. Modifications will
343 affect this L{AETier}!
344 @return: All L{AEScript <AEScript.AEScript>}s pushed onto this L{AETier}
345 @rtype: list of L{AEScript <AEScript.AEScript>}
346 '''
347 return self.scripts
348
350 '''
351 Updates the L{wanted_events} dictionary to indicate that a task in a
352 L{AEScript <AEScript.AEScript>} in this L{AETier} has been registered for a particular kind of
353 L{AEEvent}. This information is used for optimization purposes such that no
354 processing will occur on the event unless at least one task is
355 registered that will use it.
356
357 @param kind: Kind of L{AEEvent} of interest to a task
358 @type kind: L{AEEvent} class
359 @param wants: Does a L{AEScript <AEScript.AEScript>} want an event (i.e. a task is registering
360 for it or no longer want an event (i.e. a task is unregistering for
361 it)?
362 @type wants: boolean
363 '''
364 count = self.wanted_events.setdefault(kind, 0)
365 if wants:
366 count += 1
367 else:
368 count -= 1
369 if count <= 0:
370 del self.wanted_events[kind]
371 else:
372 self.wanted_events[kind] = count
373
374 AccessEngine.AETierManager.setEventInterest(kind, wants)
375
377 '''
378 Adds a key that can be used later to quickly look up a reference to a
379 L{AEScript <AEScript.AEScript>} in this L{AETier}. Optimization for the
380 L{getChooserTask}, L{getCommandTask}, and L{getRegisteredTasks} methods.
381
382 @param key: Key under which to hash the L{AEScript <AEScript.AEScript>}
383 @type key: immutable
384 @param script: Script to store in the hash
385 @type script: L{AEScript <AEScript.AEScript>}
386 '''
387 self.script_refs[key] = script
388
390 '''
391 Removes a key used to quickly look up a reference to a L{AEScript <AEScript.AEScript>} in this
392 L{AETier}. Keys are cleaned up automatically when L{AEScript <AEScript.AEScript>}s are destroyed.
393 However, this method may be used to manually remove L{AEScript <AEScript.AEScript>}s at any time.
394
395 @param task_key: Key under which to hash the L{AEScript <AEScript.AEScript>}
396 @type task_key: immutable
397 '''
398 try:
399 del self.script_refs[task_key]
400 except KeyError:
401 pass
402
404 '''
405 Gets the keys of all script tasks currently registered in this L{AETier}.
406
407 @return: List of all registered script task keys
408 @rtype: list of tuple ('task name', 'script name')
409 '''
410 names = []
411 for script in self.scripts:
412 names.extend(script.getTaskKeys())
413 return names
414
416 '''
417 Finds all tasks with the given name over all scripts in this tier. None is
418 returned if not found.
419
420 @param task_name: the name of the wanted tasks.
421 @type task_name: string
422 @return: list of the found keys or None.
423 @rtype: list of tuples ('Task Identitiy', script.getClassName())
424 '''
425 keys = self.script_refs.keys()
426 found = []
427 for key in keys:
428 if type(key) is types.TupleType and key[0] == task_name:
429 found.append(key)
430 if len(found) > 0:
431 return found
432 else:
433 return None
434
436 '''
437 Gets a task with the given key. None is returned if not found.
438
439 @param task_key: Key to the task to find
440 @type task_key: tuple of ('task name', 'script name')
441 @return: the execute and update functions to the task
442 @rtype: list of functions [exec_function, update_function]
443 '''
444 try:
445 script = self.script_refs[task_key]
446 except KeyError:
447 return None
448 return script.getRegisteredTask(task_key)
449
451 '''
452 Looks whether a task is already registered.
453
454 @param task_key: Key to the task to find
455 @type task_key: tuple of ('task name', 'script name')
456 @return: True when the task is already registered, otherwise False
457 @rtype: boolean
458 '''
459 try:
460 script = self.script_refs[task_key]
461 return True
462 except KeyError:
463 return False
464
466 '''
467 Gets the identity of the task to which the current task is chained
468 and its corresponding L{AEScript <AEScript.AEScript>}. If there is no anchor for the executing
469 task, this method returns None.
470
471 @return: task identity and L{AEScript <AEScript.AEScript>} class name or None
472 @rtype: 2-tuple of string
473 '''
474 try:
475 return self.chain_stack[-1]
476 except IndexError:
477 return None
478
480 '''
481 Adds a weak reference to a L{AEScript <AEScript.AEScript>} as one to check for tasks which are
482 part of a the chain for the target. Optimization for the L{manageChain}
483 method.
484
485 @param target: Name of the task to link to
486 @type target: string or tuple of ('task name', 'script name')
487 @param script: Script have at least one link in the chain for the target
488 @type script: L{AEScript <AEScript.AEScript>}
489 '''
490
491 if type(target) is not types.TupleType:
492 target = (target, script.getClassName())
493 chain = self.chain_refs.setdefault(target, weakref.WeakKeyDictionary())
494 chain[script] = None
495
497 '''
498 Removes a weak reference to a L{AEScript <AEScript.AEScript>} as one to no longer check for
499 tasks which are part of the chain for the target. Optimization for the
500 L{manageChain} method.
501
502 @param target: Name of the task to unlink from
503 @type target: string or tuple of ('task name', 'script name')
504 @param script: Script have at least one link in the chain for the target
505 @type script: L{AEScript <AEScript.AEScript>}
506 '''
507
508 if type(target) is not types.TupleType:
509 target = (target, script.getClassName())
510 try:
511 chain = self.chain_refs[target]
512 del chain[script]
513 if len(chain) == 0:
514 del self.chain_refs[target]
515 except (KeyError, ValueError):
516 pass
517
519 '''
520 Gets if this L{AETier} wants a particular kind of L{AEEvent} given that one of
521 its L{AEScript <AEScript.AEScript>}s has a task that wants to handle it.
522
523 @param event: Event to be tested
524 @type event: L{AEEvent}
525 @rtype: boolean
526 '''
527 return self.wanted_events.has_key(type(event))
528
530 '''
531 Gets a task registered under a particular key set to execute in response
532 to events. None is returned if not found.
533
534 @param task_key: Unique ID for locating a registered task
535 @type task_key: integer
536 @return: task functions to execute in response to the event or None
537 @rtype: function
538 '''
539 try:
540 script = self.script_refs[task_key]
541 except KeyError:
542 return None
543 return script.getChooserTask(task_key)
544
546 '''
547 Gets a script task registered to execute in response to the L{AEInput.Gesture}.
548 None is returned if not found.
549
550 @param gesture_list: Gestures and device expressed as a list of virtual key
551 codes
552 @type gesture_list: L{AEInput.Gesture}
553 @return: Name of the task to execute in response to the input gesture
554 or None if no task registered to execute
555 @rtype: tuple of ('task name', 'script name')
556 '''
557 try:
558 script = self.script_refs[gesture_list]
559 except KeyError:
560 return None
561 return script.getCommandTask(gesture_list)
562
564 '''
565 Gets all tasks registered to handle the given type of event on the
566 given layer by iterating through the registered {Script}s. The tasks are
567 returned in the order they will be executed both within and across
568 L{AEScript <AEScript.AEScript>}s in this L{AETier}.
569
570 @param event_type: Desired type of L{AEEvent}
571 @type event_type: L{AEEvent} class
572 @param task_layer: Layer on which the desired tasks are registered
573 @type task_layer: integer
574 @return: List of all tasks registered to handle the given type of event
575 on the given layer
576 @rtype: list of tuple ('task name', 'script name')
577 '''
578 tasks = []
579 map(tasks.extend, [s.getEventTasks(event_type, task_layer) for s in
580 self.scripts])
581 return tasks
582
584 '''
585 Gets all tasks linked to the target by iterating through L{AEScript <AEScript.AEScript>}
586 references established by L{addChainRef}. The tasks are returned in the
587 order in which they were registered within a L{AEScript <AEScript.AEScript>}. Ordering is undefined
588 across L{AEScript <AEScript.AEScript>}s within a chain segment.
589
590 @param target_key: Name and script of the task to link to
591 @type target_key: tuple of string ('task name', 'script name')
592 @return: task keys for before, around, and after segments
593 @rtype: 3-tuple of list, task key, list
594 @raise KeyError: When the target has no chained tasks
595 '''
596 befores = []
597 afters = []
598 scripts = self.chain_refs[target_key].keys()
599 arounds = [s.mergeChain(target_key, befores, afters) for s in scripts]
600 return befores, arounds[0], afters
601
602 - def getHistory(self):
603 '''
604 Gets the entire linear history of tasks that have executed and the
605 L{AEScript <AEScript.AEScript>}s they are in.
606
607 @return: List of task identities and L{AEScript <AEScript.AEScript>} class names
608 @rtype: list of 2-tuple of string
609 '''
610 return self.task_history
611
612 - def getTaskHistory(self):
613 '''
614 Gets the identities of all tasks that have executed in response to the
615 event currently being processed. This includes all chained tasks and all
616 tasks executed using L{AEScript.doTask <AEScript.AEScript.doTask>} flattened
617 into a linear history.
618
619 If any task that executed did not have an identity, None is substituted
620 in its place.
621
622 The history is ordered from the first task to respond to an event to
623 the most recent task to respond, including the current task.
624
625 @return: Identities of the tasks
626 @rtype: list of string
627 '''
628 return [names[0] for names in self.task_history]
629
631 '''
632 Gets the class names of all L{AEScript <AEScript.AEScript>}s that have executed in response to the
633 event currently being processed. The contents of the list returned by this
634 method have a one-to-one pairing with the list contents from
635 L{getTaskHistory}.
636
637 The history is ordered from the first L{AEScript <AEScript.AEScript>} to respond to an event to
638 the most recent L{AEScript <AEScript.AEScript>} to respond, including the current L{AEScript <AEScript.AEScript>}.
639
640 @return: Class names of the L{AEScript <AEScript.AEScript>}s
641 @rtype: list of string
642 '''
643 return [names[1] for names in self.task_history]
644
645 - def _executeTask(self, por, layer, task_key, task_params, propagate,
646 script = None):
647 '''
648 Executes the given task in response to the given L{AEEvent}.
649
650 @param por: Point of regard where the event occurred
651 @type por: L{AEPor}
652 @param layer: Layer on which the L{AEEvent} occurred, one of
653 L{AEConstants.Event.LAYER_FOCUS}, L{AEConstants.Event.LAYER_TIER},
654 L{AEConstants.Event.LAYER_BACKGROUND}
655 @type layer: integer
656 @param task_key: A task registered to handle the given event
657 @type task_key: tuple of string ('task name', 'script name')
658 @param task_params: Keyword arguments to be provided to the task function as
659 parameters
660 @type task_params: dictionary
661 @param propagate: Should this event be propagated to the tasks execute
662 function or should we call the tasks update function instead for
663 housekeeping?
664 @type propagate: boolean
665 @param script: The L{AEScript <AEScript.AEScript>} containing the task
666 @type script: L{AEScript <AEScript.AEScript>}
667
668 @return: Should the next registered task be executed or only updated?
669 @rtype: boolean
670 '''
671 reset_pointer_por = True
672
673
674 task_params.setdefault('por', por or self.pointer_por)
675 task_params['task_name'] = task_key[0]
676 task_params.setdefault('layer', layer)
677
678
679 if script is None:
680 try:
681 script = self.script_refs[task_key]
682 except KeyError:
683 return
684
685
686 task_functions = script.getRegisteredTask(task_key)
687 if task_functions:
688 execute = task_functions[0]
689 update = task_functions[1]
690 try:
691 if propagate:
692
693 rv = execute(**task_params)
694 else:
695
696 if update:
697 update(**task_params)
698 rv = False
699 except AccessEngineAPI.AEApiError.AEApiError, ex:
700
701 kwargs=task_params
702 kwargs['text'] = str(ex)
703 if AccessEngine.AETierManager.getState().Trap:
704 AccessEngineAPI.sayError(script, cap='audio', role='output', **kwargs)
705 else:
706 log.exception(ex)
707 rv = True
708 except Exception:
709
710 log.exception('%s: %s %s', task_key, layer, task_params)
711
712 rv = True
713 if rv is None:
714
715 rv = True
716 elif not rv:
717
718 reset_pointer_por = False
719
720 if reset_pointer_por and AEConstants.LAYER_FOCUS == layer:
721 self.pointer_por = task_params['por']
722 return rv
723
724 - def manageChain(self, por, layer, task_key, task_params, propagate, chain):
725 '''
726 Executes all script tasks chained to the given task, recursively if
727 chain=True.
728
729 @note: This method is purposely long because much code that could be
730 refactored into other methods is inlined for performance reasons.
731
732 @param por:
733 @type por: L{AEPor}
734 @param layer: Layer in which to execute the task, one of
735 L{AEConstants.Event.LAYER_FOCUS}, L{AEConstants.Event.LAYER_TIER},
736 L{AEConstants.Event.LAYER_BACKGROUND}
737 @type layer: integer
738 @param task_key: Key of the task to execute
739 @type task_key: tuple of string ('task name', 'script name')
740 @param task_params: Keyword data to pass to the task function as parameters
741 on execution or update
742 @type task_params: dictionary
743 @param propagate: Should chained tasks be executed or updated?
744 @type propagate: boolean
745 @param chain: Execute all tasks chained to the one named or not?
746 @type chain: boolean
747 '''
748
749 show = self.show
750
751 if not chain:
752
753
754
755 self.task_history.append(task_key)
756 if show:
757
758
759 AccessEngine.AETierManager.showTask(task_key[0], self.script_refs[task_key])
760 propagate = self._executeTask(por, layer, task_key, task_params, propagate)
761 AccessEngine.AETierManager.showPropagate(propagate)
762 else:
763
764 propagate = self._executeTask(por, layer, task_key, task_params, propagate)
765
766 else:
767
768 try:
769 befores, around, afters = self.getChainedTasks(task_key)
770 except KeyError:
771
772
773
774 self.task_history.append(task_key)
775 if show:
776
777 AccessEngine.AETierManager.showTask(task_key[0], self.script_refs[task_key])
778 propagate = self._executeTask(por, layer, task_key, task_params,
779 propagate)
780 AccessEngine.AETierManager.showPropagate(propagate)
781 else:
782
783 propagate = self._executeTask(por, layer, task_key, task_params,
784 propagate)
785
786 else:
787
788 self.chain_stack.append(task_key)
789
790
791 for before in befores:
792 if show:
793 AccessEngine.AETierManager.showChain(AEConstants.CHAIN_BEFORE, task_key[0])
794
795 if before[0] is not None:
796 propagate = self.manageChain(por, layer, before, task_params,
797 propagate, chain)
798
799
800 if around is None:
801
802 cache = self.chain_stack.pop()
803
804
805 self.task_history.append(task_key)
806 if show:
807
808 AccessEngine.AETierManager.showTask(task_key[0], self.script_refs[task_key])
809 propagate = self._executeTask(por, layer, task_key, task_params,
810 propagate)
811 AccessEngine.AETierManager.showPropagate(propagate)
812 else:
813
814 propagate = self._executeTask(por, layer, task_key, task_params,
815 propagate)
816
817 por = self.getPointer()
818
819 self.chain_stack.append(cache)
820 else:
821 if show:
822 AccessEngine.AETierManager.showChain(AEConstants.CHAIN_AROUND, task_key[0])
823
824 task_key = around
825
826 if task_key[0] is not None:
827
828 propagate = self.manageChain(por, layer, around, task_params,
829 propagate, chain)
830
831 por = self.getPointer()
832
833
834 for after in afters:
835 if show:
836 AccessEngine.AETierManager.showChain(AEConstants.CHAIN_AFTER, task_key[0])
837
838 if after[0] is not None:
839 propagate = self.manageChain(por, layer, after, task_params,
840 propagate, chain)
841
842 self.chain_stack.pop()
843
844 return propagate
845
847 '''
848 Manages an event by iterating through the L{AEScript <AEScript.AEScript>} stack (top to bottom) and
849 checking for registered tasks of the given type. Executes the registered
850 tasks (last registered, first executed) in each L{AEScript <AEScript.AEScript>} until one of the
851 following conditions is met:
852 - All tasks have executed
853 - A task returns False
854 - A task raises an exception
855
856 In the latter two cases, no additional tasks in the current L{AEScript <AEScript.AEScript>} or
857 additional L{AEScript <AEScript.AEScript>}s in this L{AETier} are executed. Instead the
858 update methods are called to allow housekeeping operations (e.g updating
859 state) to be performed.
860
861 If a task returns neither True or False (e.g. it returns None) a
862 warning is logged and the return value is treated as if it were True. This
863 likely means the task forgot to specify a return value.
864
865 @param event: Event to process
866 @type event: L{AEEvent.Base.AEEvent}
867 '''
868 try:
869
870 self.focus_por = event.getFocusPOR()
871 except AttributeError:
872 pass
873
874 if not self.wantsEvent(event):
875
876 return
877
878
879 show = self.show
880
881
882 if show:
883 AccessEngine.AETierManager.showEvent(event, self.name)
884
885
886 event_por = event.getPOR()
887 task_layer = event.getLayer()
888 task_params = event.getDataForTask()
889 event_type = type(event)
890
891
892 propagate = True
893 for script in self.scripts:
894
895 for task in script.getEventTasks(event_type, task_layer):
896
897
898 self.task_history.append(task)
899
900 if show:
901
902 AccessEngine.AETierManager.showTask(task[0], script)
903 propagate = self.manageChain(event_por, task_layer, task, task_params,
904 propagate, True)
905 AccessEngine.AETierManager.showPropagate(propagate)
906 else:
907
908 propagate = self.manageChain(event_por, task_layer, task, task_params,
909 propagate, True)
910
912 '''
913 Manages an event by getting the L{AEInput.Gesture} that triggered it and
914 locating a task registered to execute in response to it. If a task
915 could not be found for the given event, the task registered for invalid
916 gestures is executed instead.
917
918 @param event: Event to process
919 @type event: L{AEEvent.InputGesture}
920 @param count: Number of times this gesture has been issued without
921 interruption
922 @type count: integer
923 '''
924
925 task_key = self.getCommandTask(event.getTaskKey())
926 if task_key is None: return
927
928
929 if self.show:
930 AccessEngine.AETierManager.showEvent(event, self.name)
931
932 layer = event.getLayer()
933 task_params = event.getDataForTask()
934
935 task_params['cycle_count'] = count
936
937
938
939 self.manageChain(None, layer, task_key, task_params, True, True)
940
942 '''
943 Manages a TimerAlert event by locating a task registered to execute in
944 response to it.
945
946 @param event: Event to process
947 @type event: L{AEEvent}
948 '''
949 task_key = event.getTaskKey()
950
951 show = self.show
952
953 layer = event.getLayer()
954 task_params = event.getDataForTask()
955 if show:
956 AccessEngine.AETierManager.showEvent(event, self.name)
957 script = self.script_refs[task_key]
958
959 AccessEngine.AETierManager.showTask(task_key[0], script)
960 rv = self._executeTask(None, layer, task_key, task_params, True, script)
961 AccessEngine.AETierManager.showPropagate(rv)
962 pass
963 else:
964 self._executeTask(None, layer, task_key, task_params, True)
965
967 '''
968 Manages an event by locating a task under a particular key (not keyboard
969 key, but some immutable identifier) registered to execute in response to
970 it.
971
972 @param event: Event to process
973 @type event: L{AEEvent}
974 '''
975 chooser_id = event.getTaskKey()
976
977 task_key = self.getChooserTask(chooser_id)
978
979 show = self.show
980
981 if show:
982 AccessEngine.AETierManager.showEvent(event, self.name)
983 if task_key is not None:
984 layer = event.getLayer()
985
986 script = self.script_refs[chooser_id]
987 task_params = event.getDataForTask()
988 if show:
989
990 AccessEngine.AETierManager.showTask(task_key[0], script)
991 rv = self._executeTask(None, layer, task_key, task_params, True, script)
992 AccessEngine.AETierManager.showPropagate(rv)
993 else:
994 self._executeTask(None, layer, task_key, task_params, True, script)
995
997 '''
998 Pulls private data from an event and stores it in appropriate instance
999 variables. Does not forward the event to L{AEScript <AEScript.AEScript>}s and tasks. Rather,
1000 the stored information is made public through L{AccessEngineAPI} methods.
1001
1002 @param event: Event to process
1003 @type event: L{AEEvent}
1004 '''
1005 data = event.getDataForTask()
1006 kind = data['kind']
1007 if kind == event.KEY_PRESS:
1008
1009 self.last_key = data['code'], data['sym'], data['mod']
1010