Module Perk
[hide private]
[frames] | no frames]

Source Code for Module Perk

  1  ''' 
  2  Defines a L{Perk} class that can register L{Task}s which will be executed in 
  3  response to L{AEEvent}s and L{AEInput.Gesture}s. 
  4   
  5  @author: Peter Parente 
  6  @author: Pete Brunet 
  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  import logging, weakref, os, sys 
 17  import Task, AEInput, AEEvent, AEInterfaces, UIElement, AEConstants 
 18  from i18n import _ 
 19  from AEState import AEState as PerkState 
 20   
 21  log = logging.getLogger('Perk') 
 22   
23 -def _postTimerEvent(interval, tid, aid, wtask, weman):
24 ''' 25 Callback for timer events. Tries to post an L{AEEvent} for the L{Task} 26 intended to handle the event if the L{Task} is still alive. 27 28 @param interval: Interval in seconds on which the timer fires 29 @type interval: interval 30 @param tid: ID of the L{Task} to execute in response to the 31 L{AEEvent.TimerAlert} 32 @type tid: integer 33 @param wtask: Weak reference to the L{Task} to execute. Used to determine if 34 the L{Task} is still alive. If not, the timer is unregistered. 35 @type wtask: weakref.ref 36 @param weman: Weak reference to the L{EventManager} to use to post 37 L{AEEvent.TimerAlert} events. 38 @type weman: weakref.proxy 39 @return: Continue timer alerts on the interval (True) or stop them (False). 40 @rtype: boolean 41 ''' 42 if not wtask(): 43 return False 44 try: 45 weman.postEvents(AEEvent.TimerAlert(aid, tid, interval)) 46 except ReferenceError: 47 return False 48 return True
49
50 -class Perk(Task.Tools.All, UIElement.UIE):
51 ''' 52 Registers and maintains a collection of L{Task}s that will execute in 53 response to L{AccessEngine} events and commands (L{AEEvent}s and 54 L{AEInput.Gesture}s.). This class should be sublclassed by L{Perk} developers 55 who wish to define, modify, or extend the LSR user interface. This class 56 derives from L{Task.Tools.All} to allow the use of the convenience methods 57 defined in that class during L{Perk} intialization. 58 59 @cvar STATE: Class to instantiate and use to store state information across 60 all instances of this L{Perk}. Defaults to the L{AEState} base class, but 61 can be overridden with a subclass of L{AEState}. 62 @type STATE: L{AEState.AEState} class 63 @ivar named_tasks: L{Task}s registered by name 64 @type named_tasks: dictionary string : Task instance 65 @ivar keyed_tasks: L{Task}s registered by unique integer ID 66 @type keyed_tasks: dictionary integer : Task instance 67 @ivar event_tasks: L{Task}s registered by type 68 @type event_tasks: dictionary of (Task subclass : list of Task instance) 69 @ivar commands: Name of a L{Task} to execute in response to a 70 L{AEInput.GestureList} sequence on some L{AEInput} device. The name should 71 be hashed into the L{named_tasks} dictionary to fully resolve the L{Task}. 72 @type commands: weakref.WeakValueDictionary of (GestureList : string) 73 @ivar state: Settings for this L{Perk}. Defaults to an empty L{PerkState} 74 object that can be used to store state information that is not 75 configurable or persistable unless L{STATE} is specified. 76 @type state: L{PerkState} 77 @ivar out_caps: Capabilities set by L{setIdealOutput} as ideal for output 78 from this L{Perk}. These are used to "late bind" a device for use during 79 execution of this L{Perk} and its L{Task}s. Defaults to audio. 80 @type out_caps: list of string 81 @ivar before_chains: Mapping from target L{Task} name to a list of L{Task} 82 names which should be executed before the target 83 @type before_chains: dictionary of {string : list of string} 84 @ivar after_chains: Mapping from target L{Task} name to a list of L{Task} 85 names which should be executed after the target 86 @type after_chains: dictionary of {string : list of string} 87 @ivar around_chains: Mapping from target L{Task} name to one L{Task} name 88 which should be executed instead of the target 89 @type around_chains: dictionary of {string : list of string} 90 @ivar registered_modifiers: Mapping from device to a registered modifier for 91 that device 92 @type registered_modifiers: weakref.WeakKeyDictionary 93 ''' 94 STATE = PerkState 95
96 - def __init__(self, *args, **kwargs):
97 ''' 98 Creates empty dictionaries for named, event, command, and chooser L{Task}s. 99 Create a state object for storage using the class in L{STATE} in case the 100 L{Perk} hasn't previously persisted its settings on disk. 101 102 @note: L{Perk} writers should override L{init} to do initialization, not 103 this method. 104 ''' 105 super(Perk, self).__init__(*args, **kwargs) 106 self.named_tasks = {} 107 self.keyed_tasks = {} 108 self.event_tasks = {} 109 self.commands = {} 110 # default to audio output 111 self.out_caps = ['audio'] 112 # define empty chains for tasks 113 self.before_chains = {} 114 self.after_chains = {} 115 self.around_chains = {} 116 self.registered_modifiers = weakref.WeakKeyDictionary() 117 118 # instantiate an instance of the STATE class; it may be filled with data 119 # when preInit is called with state already persisted to disk 120 self.state = self.STATE() 121 # initialize state settings 122 self.state.init()
123
124 - def preInit(self, ae, *args, **kwargs):
125 ''' 126 Overrides pre-initialization to load persisted state from disk. Fetches the 127 managers from the L{AccessEngine} first so that state can be loaded using 128 the L{SettingsManager}. Loads the state, then invokes the super class 129 version of this method to finish initialization. 130 131 @note: L{Perk} writers should override L{init} to do initialization, not 132 this method. 133 @param ae: Reference to AccessEngine for event context 134 @type ae: L{AccessEngine} 135 ''' 136 # get managers, this duplicates some work done in the super class, but 137 # it's harmless and only done once per Perk 138 ae.loanManagers(self) 139 try: 140 # use the settings manager to try to load persisted state for this Perk 141 self.state = self.sett_man.loadState(self.getClassName(), self.state) 142 except KeyError: 143 # do nothing, a blank state object has already been created 144 pass 145 # invoke the super class to complete initialization 146 super(Perk, self).preInit(ae, *args, **kwargs)
147
148 - def postClose(self):
149 ''' 150 Frees all L{Task}s managed by this L{Perk}. 151 152 @note: L{Perk} writers should override L{close} to do finialization, not 153 this method. 154 ''' 155 # let the tools postClose run second 156 super(Perk, self).postClose() 157 # finalize all Tasks 158 for tasks in self.event_tasks.values(): 159 map(Task.Task.close, tasks) 160 map(Task.Task.postClose, tasks) 161 for tasks in (self.named_tasks.values(), self.keyed_tasks.values()): 162 map(Task.Task.close, tasks) 163 map(Task.Task.postClose, tasks) 164 # unregister modifiers 165 for dev, codes in self.registered_modifiers.iteritems(): 166 self.unregisterModifiers(dev, codes) 167 168 # throw away all references 169 self.named_tasks = {} 170 self.keyed_tasks = {} 171 self.event_tasks = {} 172 self.commands = {} 173 self.registered_modifiers = weakref.WeakKeyDictionary()
174
175 - def setIdealOutput(self, *capabilities):
176 ''' 177 Sets the ideal device capabilities to be used to locate a device for all 178 output methods invoked from the caller's L{Perk} module, including the 179 Perk class itself and all of its registered L{Task}s. The capabilities list 180 should include strings naming L{AEOutput} interfaces ("audio" and/or 181 "braille") at present. 182 183 @param capabilities: Names of capabilities required on the device 184 @type capabilities: list of string 185 ''' 186 self.out_caps = capabilities
187
188 - def getIdealOutput(self):
189 ''' 190 Gets the ideal device capabilities to locate a device to be used by all 191 output methods invoked from the caller's L{Perk} module, including the Perk 192 class itself and all of its registered L{Task}s. 193 194 @return: Names of capabilities set as ideal for a default output device 195 @rtype: list of string 196 ''' 197 return self.out_caps
198
199 - def getState(self):
200 ''' 201 @return: Returns the settings for this L{Perk} or a dummy object that can 202 be used to store state information that is neither user configurable or 203 persistable 204 @rtype: L{AEState.AEState} 205 ''' 206 return self.state
207
208 - def _initTask(self, task):
209 ''' 210 Initializes a L{Task} by calling L{Task.Base.Task.preInit} and 211 L{Task.Base.Task.init} if it has not already been initialized. 212 213 @param task: L{Task} to initialize 214 @type task: L{Task} 215 ''' 216 if task.preInit(self.acc_eng, self.tier, self): 217 task.init()
218
219 - def registerTimerTask(self, task, interval):
220 ''' 221 Registers a L{Task} to be called on a set interval. 222 223 @param interval: Interval in seconds which the L{Task} will be notified 224 @type interval: integer 225 ''' 226 tid = id(task) 227 aid = self.tier.getIdentity() 228 wtask = weakref.ref(task) 229 weman = weakref.proxy(self.event_man) 230 self._initTask(task) 231 self.keyed_tasks[tid] = task 232 # use the access engine timer to notify our callback 233 self.acc_eng.addTimer(_postTimerEvent, interval, interval, tid, aid, 234 wtask, weman) 235 self.tier.addTaskRef(tid, self)
236
237 - def registerCommandTask(self, device, codes, name, propagate=False):
238 ''' 239 Registers a L{Task} in this L{Tier} to be executed in response to an 240 L{AEEvent} indicating that the given action codes were input on the given 241 L{AEInput} device. 242 243 @param device: Input device to monitor 244 @type device: L{AEInput} 245 @param codes: List of lists of action codes forming the L{AEInput.Gesture} 246 that will trigger the execution of the named L{Task}. For example, 247 codes=[[Keyboard.AEK_CTRL, Keyboard.AEK_TILDE]] indicates the single 248 gesture of simultaneously pressing Ctrl and ~ on the keyboard device. 249 @type codes: list of list of integer 250 @param name: Name of the L{Task} registered via L{registerNamedTask} to 251 execute when the input gesture is detected on the device 252 @type name: string 253 @param propagate: Should the input gesture be allowed to propagate to the 254 OS after we receive it? 255 @type propagate: boolean 256 @raise ValueError: When a L{Task} with the given name is not registered 257 ''' 258 # construct the key list and find the Task with the given name 259 gl = AEInput.GestureList(device, codes) 260 if self.tier.getCommandTask(gl) is not None: 261 # raise a value error if there is already a command registered for this 262 # sequence of keys in this Tier 263 raise ValueError( 264 'command already registered with action codes %s on device %s' % 265 (codes, device.getName())) 266 # add key list to our dictionary of commands and store the task to execute 267 # as its value 268 self.commands[gl] = name 269 self.tier.addTaskRef(gl, self) 270 if not propagate: 271 # try to add a filter to the device, ignore if not supported 272 try: 273 device.addFilter(gl) 274 except NotImplementedError: 275 pass 276 # accept a KEY_CMD from the device, ignore if not supported 277 try: 278 device.addKeyCmd(codes) 279 except NotImplementedError: 280 pass
281
282 - def registerChooserTask(self, chooser, task):
283 ''' 284 Registers a L{Task} with the given name to be executed in response to a 285 change in the given L{AEChooser}. 286 287 @param chooser: Chooser that the L{Task} should observe 288 @type chooser: L{AEChooser} 289 @param task: Instance of a L{Task} that should observe the chooser 290 @type task: L{Task.ChooserTask} 291 ''' 292 cid = id(chooser) 293 if self.tier.getKeyedTask(cid) is not None: 294 raise ValueError('Chooser Task already registered') 295 self._initTask(task) 296 self.keyed_tasks[cid] = task 297 self.tier.addTaskRef(cid, self)
298
299 - def registerNamedTask(self, task, name):
300 ''' 301 Registers a new L{Task} under the given name if no L{Task} is already 302 registered with that name in this L{Tier}. 303 304 Only one L{Task} can be registered under a name in a L{Tier}. If a L{Task} 305 is already registered under the given name, any other registration with 306 that name is ignored. 307 308 @param task: L{Task} to register 309 @type task: L{Task.Base.Task} 310 @param name: Name to associate with the L{Task} 311 @type name: string 312 @raise ValueError: When a L{Task} with the given name is already registered 313 in this L{Tier} 314 ''' 315 if self.tier.getNamedTask(name) is None: 316 # initialize the Task 317 self._initTask(task) 318 self.named_tasks[name] = task 319 self.tier.addTaskRef(name, self) 320 else: 321 raise ValueError(name)
322
323 - def registerEventTask(self, task, focus=False, tier=False, background=False):
324 ''' 325 Registers a new L{Task} in this L{Perk} under the L{Task} type. The type 326 determines which kind of L{AEEvent} will trigger the execution of the 327 registered L{Task}. If one or more Tasks are already registered for this 328 type, the given L{Task} will be inserted at the top of the registered stack 329 of L{Task}s (i.e. it will be executed first for the appropriate event). 330 331 The L{focus}, L{tier}, and L{background} parameters specify on which layer 332 the L{Task} will handle events. If focus is True, the L{Task} will be 333 executed in response to an event from a focused control within this 334 L{Tier}. If tier is True, the L{Task} will be executed in response to an 335 event from an unfocused control within this L{Tier}. If background is True, 336 the L{Task} will be executed in response to an event from any control 337 within the tier when the L{Tier} is not active. 338 339 The three layers are mutually exclusive. You may set any combination of 340 focus, tier, and background to True to register the given L{task} on each 341 selected layer in one call. If all three parameters are False, the 342 registration defaults to the focus layer. 343 344 The L{Task} passed to this method must implement the interface defined by 345 L{Task.EventTask}. 346 347 @param task: L{Task} to register 348 @type task: L{Task.Base.Task} 349 @param focus: Should this L{Task} handle events from focused accessibles in 350 this L{Tier}? 351 @type focus: boolean 352 @param tier: Should this L{Task} handle events from unfocused accessibles 353 in this L{Tier}? 354 @type tier: boolean 355 @param background: Should this L{Task} handle events from any accessible in 356 this L{Tier} when the L{Tier} is inactive? 357 @type background: boolean 358 @raise AttributeError: When the L{Task} does not implement 359 L{Task.EventTask} 360 @raise NotImplementedError: When the L{Task} does not properly subclass 361 L{Task.EventTask} 362 ''' 363 # default to the focus layer if no layer is specified 364 default = not (focus or tier or background) 365 d = {AEConstants.LAYER_FOCUS : focus or default, 366 AEConstants.LAYER_TIER : tier, 367 AEConstants.LAYER_BACKGROUND : background} 368 # get the kind of AEEvent the Task 369 kind = task.getEventType() 370 # initialize the Task 371 self._initTask(task) 372 # opt: inform the Tier of the kind of event that is desired by this Perk 373 try: 374 self.tier.setEventInterest(kind, True) 375 except KeyError: 376 # ignore unknown event types, could be internal 377 pass 378 for layer, val in d.items(): 379 if val: 380 # get the list of Task registered for this type and layer or insert an 381 # empty list and get it instead 382 curr = self.event_tasks.setdefault((kind, layer), []) 383 # insert the task at the front of the list 384 curr.insert(0, task)
385
386 - def unregisterTimerTask(self, task):
387 ''' 388 Unregisters a L{Task} from being called on a set interval. 389 390 @param task: L{Task} to unregister 391 @type task: L{Task} 392 @raise KeyError: When the L{Task} is not registered 393 ''' 394 tid = id(task) 395 del self.keyed_tasks[tid] 396 # let the task finalize itself 397 task.close() 398 task.postClose() 399 self.tier.removeTaskRef(tid)
400
401 - def unregisterCommandTask(self, device, codes):
402 ''' 403 Unregisters a L{Task} set to execute in response to the given action codes 404 on the given device B{from this L{Perk} only}. 405 406 @param device: Input device to monitor 407 @type device: L{AEInput} 408 @param codes: List of lists of action codes forming the L{AEInput.Gesture} 409 that will trigger the execution of the named L{Task}. For example, 410 codes=[[Keyboard.AEK_CTRL, Keyboard.AEK_TILDE]] indicates the single 411 gesture of simultaneously pressing Ctrl and ~ on the keyboard device. 412 @type codes: list of list of integer 413 @raise KeyError: When a L{AEInput.GestureList} is not registered 414 ''' 415 # GestureList has __eq__ and __hash__ overridden so two different instances 416 # can hash to the same dictionary location as long as their devices and 417 # codes are the same 418 gl = AEInput.GestureList(device, codes) 419 del self.commands[gl] 420 self.tier.removeTaskRef(gl) 421 # try to remove a filter from the device, ignore if not supported 422 try: 423 device.removeFilter(gl) 424 except (NotImplementedError, AttributeError): 425 pass 426 427 # remove an accepted KEY_CMD from the device, ignore if not supported 428 try: 429 device.removeKeyCmd(codes) 430 except NotImplementedError: 431 pass
432
433 - def unregisterChooserTask(self, chooser):
434 ''' 435 Unregisters a L{Task} so it is no longer executed in response to a change 436 in the given L{AEChooser}. 437 438 @param chooser: Chooser that the L{Task} should no longer observe 439 @type chooser: L{AEChooser} 440 @raise KeyError: When the given L{AEChooser} is not registered 441 ''' 442 cid = id(chooser) 443 task = self.keyed_tasks[cid] 444 del self.keyed_tasks[cid] 445 446 # We don't want to close the Task because technically it's not getting 447 # unregistered, just decoupled from the chooser it was managing. The Task 448 # must stay registered so that the chooser can be invoked again in the 449 # future. Do not call Task.close() here! 450 451 self.tier.removeTaskRef(cid)
452
453 - def unregisterNamedTask(self, name):
454 ''' 455 Unregisters a named L{Task} B{from this L{Perk} only}. If a Task with the 456 given name is not found in this L{Perk}, an exception is raised. 457 458 @param name: Name of the L{Task} to unregister 459 @type name: string 460 @raise KeyError: When a L{Task} with the given name is not registered 461 ''' 462 task = self.named_tasks[name] 463 del self.named_tasks[name] 464 # let the task finalize itself 465 task.close() 466 task.postClose() 467 self.tier.removeTaskRef(name)
468
469 - def unregisterEventTask(self, task, focus=False, tier=False, 470 background=False):
471 ''' 472 Unregisters the given L{Task} instance B{from this L{Perk} only}. If the 473 given L{Task} instance was not registered for an event in this L{Perk}, an 474 exception is raised. The L{focus}, L{tier}, and L{background} parameters 475 state from which layer(s) this L{Task} should be unregistered. 476 477 The L{Task} passed to this method must implement the interface defined by 478 L{Task.EventTask}. 479 480 @param task: L{Task} to unregister 481 @type task: L{Task.Base.Task} 482 @raise KeyError: When there are no L{Task}s registered with the type of the 483 given L{Task} 484 @raise ValueError: When the given L{Task} is not registered on one of the 485 specified layers 486 @raise AttributeError: When the L{Task} does not implement 487 L{Task.EventTask} 488 @raise NotImplementedError: When the L{Task} does not properly subclass 489 L{Task.EventTask} 490 @see: L{registerEventTask} 491 ''' 492 # default to the focus layer if no layer is specified 493 default = not (focus or tier or background) 494 d = {AEConstants.LAYER_FOCUS : focus or default, AEConstants.LAYER_TIER : tier, 495 AEConstants.LAYER_BACKGROUND : background} 496 # get the task type 497 kind = task.getEventType() 498 # let the Task finalize itself 499 task.close() 500 task.postClose() 501 # opt: inform the Tier of the kind of event that is no longer 502 # desired by this Perk 503 try: 504 self.tier.setEventInterest(kind, False) 505 except KeyError: 506 # ignore unknown event types, could be internal 507 pass 508 for layer, val in d.items(): 509 if val: 510 # get the list of Task registered for this type and layer or insert an 511 # empty list and get it instead 512 curr = self.event_tasks[(kind, layer)] 513 curr.remove(task)
514
515 - def getCommandTask(self, gesture_list):
516 ''' 517 Gets the L{Task} registered B{in this L{Perk} only} to execute in response 518 to the given L{AEInput.GestureList}. 519 520 Called by L{Tier.Tier.getCommandTask} during a search through all L{Perk}s 521 in the owning L{Tier} for the given key list. 522 523 @param gesture_list: Gestures and device on which they were performed 524 @type gesture_list: L{AEInput.GestureList} 525 @return: Name of the L{Task} set to execute in response to the input 526 gesture or None 527 @rtype: string 528 ''' 529 return self.commands.get(gesture_list)
530
531 - def getKeyedTask(self, key):
532 ''' 533 Gets a L{Task} registered under a particular key set to execute in response 534 to events. None is returned if not found. 535 536 Called by L{Tier.Tier.getKeyedTask} during a search through all L{Perk}s 537 in the owning L{Tier} for the given L{AEChooser}. 538 539 @param key: Unique key identifying this L{Task} 540 @type key: integer 541 @return: L{Task} set to execute in response to the chooser or None 542 @rtype: L{Task.ChooserTask.ChooserTask} 543 ''' 544 return self.keyed_tasks.get(key)
545
546 - def getNamedTask(self, name):
547 ''' 548 Gets the L{Task} with the given name if it is registered B{in this L{Perk} 549 only}. If no L{Task} is registered under the given name in this L{Perk}, 550 returns None. 551 552 Called by L{Tier.Tier.getNamedTask} during a search through all 553 L{Perk}s in the owning L{Tier} for the given name. 554 555 @param name: Name of the L{Task} to locate 556 @type name: string 557 @return: L{Task} with the given name or None 558 @rtype: L{Task.Base.Task} 559 ''' 560 return self.named_tasks.get(name)
561
562 - def getEventTasks(self, event_type, task_layer):
563 ''' 564 Get all registered L{Task}s registered to handle the given L{AEEvent} type 565 B{in this L{Perk} only}. Makes a copy of the list of tasks such that 566 registering or unregistering L{Task}s does not affect the list gotten 567 by this method. 568 569 Called by L{Tier.Tier.getEventTasks} during a search through all 570 L{Perk}s in the owning L{Tier} for the given task type. 571 572 @param event_type: Desired type of L{AEEvent} 573 @type event_type: L{AEEvent} class 574 @param task_layer: Layer on which the desired L{Task}s are registered 575 @type task_layer: integer 576 @return: List of all L{Task}s that handle the given event type on the given 577 layer in this L{Perk} 578 @rtype: list of L{Task.Base.Task} 579 ''' 580 return list(self.event_tasks.get((event_type, task_layer), []))
581
582 - def getChainSegment(self, target, link):
583 ''' 584 Gets a copy of the segment of the L{Task} chain for target specified by the 585 link type. A copy is returned to avoid iteration errors if the list changes 586 size during execution of the L{Task}s in the chain. 587 588 @param target: Name of the L{Task} to link to 589 @type target: string 590 @param link: One of the CHAIN constants in L{AEConstants.Tools} 591 @type link: integer 592 @return: L{Task} names chained in the given manner 593 @rtype: list of string 594 ''' 595 if link == AEConstants.CHAIN_BEFORE: 596 return self.before_chains.get(target, [])[:] 597 elif link == AEConstants.CHAIN_AFTER: 598 return self.after_chains.get(target, [])[:] 599 elif link == AEConstants.CHAIN_AROUND: 600 alist = [] 601 if self.around_chains.has_key(target): 602 alist.append(self.around_chains[target]) 603 return alist
604
605 - def mergeChain(self, target, before, after):
606 ''' 607 Adds the before and after segments of the chain for the target specified. 608 The parameters before and after are lists, possibly containing segments 609 from other L{Perk}s. These lists are modified in place for performance. 610 The around segment is returned since it is a single value. 611 612 @param target: Name of the L{Task} to link to 613 @type target: string 614 @return: L{Task} name for around link type, or None if nothing is linked 615 around the target 616 @rtype: string 617 ''' 618 try: 619 before.extend(self.before_chains[target]) 620 except KeyError: 621 pass 622 try: 623 after.extend(self.after_chains[target]) 624 except KeyError: 625 pass 626 return self.around_chains.get(target, None)
627
628 - def linkTaskToChain(self, name, link, target):
629 ''' 630 Links a L{Task} to the one named in target. The L{Task} will be added 631 either before, after, or around depending on the value of link. Does not 632 allow a L{Task} to link to itself (i.e. name cannot equal target). 633 634 Invokes L{Tier.Tier.addChainRef} to add a reference to this L{Perk} to the 635 L{Tier} which will later have to traverse the chain across L{Perk}s. 636 637 @param name: Name of the L{Task} to link 638 @type name: string 639 @param link: One of the CHAIN constants in L{AEConstants.Tools} 640 @type link: integer 641 @param target: Name of the L{Task} to link to 642 @type target: string 643 @raise ValueError: When the link type is unknown 644 @raise AssertionError: When the name equals the target 645 ''' 646 assert name != target 647 if link == AEConstants.CHAIN_BEFORE: 648 self.before_chains.setdefault(target, []).append(name) 649 elif link == AEConstants.CHAIN_AFTER: 650 self.after_chains.setdefault(target, []).append(name) 651 elif link == AEConstants.CHAIN_AROUND: 652 self.around_chains[target] = name 653 else: 654 raise ValueError(link) 655 # add a reference to this Perk to the Tier 656 self.tier.addChainRef(target, self)
657
658 - def unlinkTaskFromChain(self, name, link, target):
659 ''' 660 Unlinks a L{Task} from the one named in target. The L{Task} will be 661 unlinked either before, after, or around depending on the value of link. 662 If link is None, the L{Task} will be unlinked from all cases if 663 possible. 664 665 Invokes L{Tier.Tier.removeChainRef} to delete a reference to this L{Perk} 666 to the L{Tier} which will later have to traverse the chain across L{Perk}s. 667 668 @param name: Name of the L{Task} to link 669 @type name: string 670 @param link: One of the CHAIN constants in L{AEConstants.Tools} 671 @type link: integer 672 @param target: Name of the L{Task} to unlink from 673 @type target: string 674 @raise ValueError: When the named L{Task} is not unlinked at least once or 675 the link type is unknown 676 @raise KeyError: When the target L{Task} does not have a chain segement in 677 this L{Perk} for the given link type 678 ''' 679 if link is not None: 680 if link == AEConstants.CHAIN_BEFORE: 681 self.before_chains[target].remove(name) 682 if len(self.before_chains[target]) == 0: 683 del self.before_chains[target] 684 elif link == AEConstants.CHAIN_AFTER: 685 self.after_chains[target].remove(name) 686 if len(self.after_chains[target]) == 0: 687 del self.after_chains[target] 688 elif link == AEConstants.CHAIN_AROUND: 689 # don't unlink around unless it matches 690 if self.around_chains[target] == name: 691 del self.around_chains[target] 692 else: 693 raise ValueError(name) 694 else: 695 raise ValueError(link) 696 else: 697 # track how many removals we make 698 count = 0 699 # try removing from all chain segments 700 try: 701 self.before_chains[target].remove(name) 702 if len(self.before_chains[target]) == 0: 703 del self.before_chains[target] 704 count += 1 705 except (KeyError, ValueError): 706 pass 707 try: 708 self.after_chains[target].remove(name) 709 if len(self.after_chains[target]) == 0: 710 del self.after_chains[target] 711 count += 1 712 except (KeyError, ValueError): 713 pass 714 try: 715 if self.around_chains[target] == name: 716 # there is only one so get rid of key and value 717 del self.around_chains[target] 718 count += 1 719 except (KeyError, ValueError): 720 pass 721 722 if count == 0: 723 # error if we didn't remove anything 724 raise ValueError 725 # remove reference in tier if they are no longer needed 726 if not self.around_chains.has_key(target) \ 727 and not self.before_chains.has_key(target) \ 728 and not self.after_chains.has_key(target) : 729 self.tier.removeChainRef(target, self)
730
731 - def getTaskNames(self):
732 ''' 733 Gets the names of all L{Task}s registered in this L{Perk}. 734 735 @return: All names 736 @rtype: list of string 737 ''' 738 return self.named_tasks.keys()
739
740 - def unregisterModifiers(self, dev, modifiers):
741 ''' 742 Unregisters a list of input modifiers. Ignores the error cases when the 743 device has no registered modifiers or one of the given modifiers is not 744 registered. 745 746 @param dev: An L{AEInput} device. 747 @type dev: L{AEInput} 748 @param modifiers: A list of key codes used as modifiers. 749 @type modifiers: integer 750 ''' 751 for m in modifiers: 752 try: 753 self.registered_modifiers[dev].remove(m) 754 except (KeyError, ValueError): 755 pass
756
757 - def registerModifiers(self, dev, modifiers):
758 ''' 759 Registers a list of input modifiers. 760 761 @param dev: An L{AEInput} device. 762 @type dev: L{AEInput} 763 @param modifiers: A list of key codes used as modifiers. 764 @type modifiers: integer 765 ''' 766 self.perk.registered_modifiers.setdefault(dev, []).extend(modifiers)
767