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

Source Code for Module GTerminalScript

  1  ''' 
  2  Defines a special L{AEScript <AEScript.AEScript>} for the GNOME Terminal. 
  3  Corrects caret problems. 
  4   
  5  @author: Peter Parente 
  6  @author: Brett Clippingdale 
  7  @author: Eitan Isaacson 
  8  @author: Scott Haeger 
  9  @author: Luiz Rocha 
 10  @organization: IBM Corporation 
 11  @copyright: Copyright (c) 2005, 2007 IBM Corporation 
 12   
 13  @license: I{The BSD License} 
 14  All rights reserved. This program and the accompanying materials are made  
 15  available under the terms of the BSD license which accompanies 
 16  this distribution, and is available at 
 17  U{http://www.opensource.org/licenses/bsd-license.php} 
 18  ''' 
 19  from AccessEngine import AEScript, AccessEngineAPI 
 20  from AccessEngine import AEConstants 
 21  from AccessEngine.AEPor import AEPor 
 22  from Tools.i18n import bind, _ 
 23   
 24  __uie__ = dict(kind='script', tier='gnome-terminal', all_tiers=False) 
 25   
26 -class GTerminalScript(AEScript.AEScript):
27 ''' 28 A special L{AEScript <AEScript.AEScript>} for handling gnome terminal. 29 Corrects caret problems in in gnome-terminal. 30 31 @ivar actions: Maps all possible 'added' values to strings 32 @type actions: dictionary 33 @ivar move_keys: Stores all keys that allow movement 34 @type move_keys: tuple 35 @ivar delete_keys: Stores keys to delete something 36 @type delete_keys: tuple 37 @ivar space_key: Store the space key 38 @type space_key: string 39 @ivar ret_key: Store the return key 40 @type ret_key: string 41 @ivar last_caret: Stores the previous caret 42 @type last_caret: L{AEPor} 43 @ivar last_key: Stores the last key pressed for future comparision 44 @type last_key: tuple 45 '''
46 - def init(self):
47 ''' 48 Registers a L{event task <AEScript.event_tasks>} to handle 49 L{caret events <AEEvent.CaretChange>} and chain it around the default 50 L{BasicSpeechScript.onCaretChange 51 <BasicSpeechScript.BasicSpeechScript.onCaretChange>}-method. 52 Initialize script variables. 53 ''' 54 # initialize script variables 55 self.actions = {None: 'move', False: 'delete', True: 'insert'} 56 self.move_keys = ('Left', 'Right', 'Home', 'End') 57 self.delete_keys = ('Delete', 'BackSpace') 58 self.space_key = 'space' 59 self.ret_key = 'Return' 60 self.last_caret = AEPor(item_offset=0) 61 self.last_key = None 62 63 # set an audio device as the default output 64 AccessEngineAPI.setScriptIdealOutput(self, 'audio')
65 66 # register tasks 67 #self.registerTask('terminal caret', self.handleTerminalCaret) 68 69 # chain to other tasks 70 #self.chainTask('terminal caret', AEConstants.CHAIN_AROUND, 71 #'read caret', 'BasicSpeechScript') 72
73 - def getName(self):
74 ''' 75 Provides the localized name of this L{AEScript <AEScript.AEScript>}. 76 77 @return: Human readable translated name of this script. 78 @rtype: string 79 ''' 80 return _('GNOME Terminal')
81
82 - def getDescription(self):
83 ''' 84 Describe which L{AETier} this script applies to by default. 85 86 @return: Human readable translated description of this script. 87 @rtype: string 88 ''' 89 return _('Improve general accessibility of the GNOME Terminal.')
90
91 - def handleTerminalCaret(self, por, text, text_offset, added, **kwargs):
92 ''' 93 This task handles messy L{AEEvent.CaretChange} events sent from 94 gnome-terminal. This one is chained around the 95 L{BasicSpeechScript.onCaretChange 96 <BasicSpeechScript.BasicSpeechScript.onCaretChange>} event. The method 97 identifies the event, fix, propagate or ignore it althogether. 98 99 @param por: Point of regard where the caret event occurred 100 @type por: L{AEPor} 101 @param text: The text inserted, deleted or the line of the caret 102 @type text: string 103 @param text_offset: The offset of the inserted/deleted text or the line 104 offset when movement only 105 @type text_offset: integer 106 @param added: C{True} when text added, C{False} when text deleted, and 107 C{None} (the default) when event is for caret movement only. 108 @type added: boolean 109 @keyword layer: Layer on which the event occurred 110 @type layer: integer 111 @keyword task_name: Name of the task this function executes. 112 @type task_name: string 113 @param kwargs: Arbitrary keyword arguments to pass to the task 114 @type kwargs: dictionary 115 @return: True to allow other tasks to process this event. 116 @rtype: boolean 117 ''' 118 # TODO: write a nice description for execute. 119 # TODO: would it be a good idea to refactor this and move the conditional 120 # blocks for each 'added' to separate methods inside this task? 121 # TODO: check long cmdlines (call a random prog with a long line of args) 122 # TODO: check reverse/incremental search (Ctrl+R) output 123 # TODO: say the whole word (instead of just the ending) on tab-completion? 124 # TODO: say the character (ie. 'vertical bar' for '|') or common name ('pipe')? 125 # TODO: selection is bogus, fix 126 # TODO: gnome-terminal breaks long commnand lines (ex. du --apparent-size 127 # --human-readable --summarize 128 # ~/downloads/mozilla/firefox/granparadiso), find a way around. 129 130 # handling only terminal events 131 if not AccessEngineAPI.hasAccRole('terminal', por): 132 return True 133 134 # last key pressed in gterm is our current key 135 current_key = self.getLastKey() 136 137 # TODO: remove the 'else' from the action conditional loop and use this as 138 # default condition setting? 139 # propagate to chained tasks by default 140 # propagate = True 141 142 # --- Insert Events ----------------------------------------------------- # 143 if self.actions[added] == 'insert': 144 # insert events are fired by gnome-terminal when an insertion or a 145 # deletion happens. Cases to implement: 146 # 147 # 1. Terminal responding to user input should not be interrupted 148 # 2. Ignore insert event fired by delete keys 149 # 3. Route all other events to basic speech script 150 151 # 1. if the current key is a Return, the terminal is probably responding 152 # to some user input. the avalanche of events gterm sends might break the 153 # speech output. inhibit the next stop to prevent this. 154 if current_key[1] == self.ret_key: 155 AccessEngineAPI.inhibitMayStop() 156 propagate = True 157 # 2. insert event bundled in a valid deletes, ignore those 158 elif current_key[1] in self.delete_keys: 159 propagate = False 160 # 3. let basic speech handle anything else 161 else: 162 propagate = True 163 164 # --- Move Events ------------------------------------------------------- # 165 elif self.actions[added] == 'move': 166 # everything you do on gnome-terminal fires a move event, but we want to 167 # react only to a few cases: 168 # 169 # 1. Events fired by actual move keys (Up/Down/Home/End), 170 # 2. Events fired by space key, since gterm considers space to be just 171 # a move key, thus produce no announce, 172 # 3. Move events that happens before an insert, to set the actual caret 173 # position, 174 # 4. Ignore anything else. 175 176 # 1. react if this move event was fired by an actual move key 177 if current_key[1] in self.move_keys: 178 # TODO: because of the basic speech self.changed var, first move never 179 # gets announced. get around that. 180 self.last_caret = por 181 propagate = True 182 # 2. gnome-terminal only move the caret when space is pressed. do a fake 183 # space announcement then. 184 elif current_key[1] == self.space_key: 185 self.sayChar(text=' ') 186 self.last_caret = por 187 propagate = False 188 # 3. if this event is bundled with an insert event, store the caret 189 # position for future use 190 elif not current_key[1] in self.delete_keys: 191 self.last_caret = por 192 propagate = False 193 # 4. Ignore anything else 194 else: 195 propagate = False 196 197 # --- Delete Events ----------------------------------------------------- # 198 elif self.actions[added] == 'delete': 199 # delete events are fired in both inserts and deletes, but reaction to it 200 # should happen only when delete keys are pressed. Cases: 201 # 202 # 1. Handle event fired by delete key press, 203 # 2. Ignore event bundled on insert events. 204 205 # 1. delete event fired by a delete key, let's handle it 206 if current_key[1] in self.delete_keys: 207 # the text param on delete events contains the whole text after the 208 # deleted character. trim the text to announce only the deleted char 209 # 210 # TODO: selection delete is correctly announced? 211 text = text[:1] 212 # pass to BasicSpeechScript last_caret the last valid caret position 213 AccessEngineAPI.setScriptVar(self.tier, 'BasicSpeechScript', 'last_caret', 214 self.last_caret) 215 # update last_caret position 216 self.last_caret = por 217 # let basic speech do the rest 218 propagate = True 219 # not a valid delete event, ignore it 220 else: 221 propagate = False 222 else: 223 # propagate and let BasicSpeechScript handle anything we missed here 224 propagate = True 225 226 # if propagate is True, call BasicSpeechScript named task 'read caret'. 227 if propagate: 228 self.doTask('read caret', 'BasicSpeechScript', chain=False, por=por, text=text, 229 text_offset=text_offset, added=added, **kwargs) 230 231 # store keyboard input and last caret position for future comparisons 232 self.last_key = current_key 233 234 return propagate
235