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

Source Code for Module GEditScript

  1  ''' 
  2  Defines a special L{AEScript <AEScript.AEScript>} for the text editor gedit. 
  3  Handle several problems, including mainly the spellcheck dialogue and the 
  4  document statistics dialogue. 
  5   
  6  @author: Andy Shi 
  7  @author: Cristobal Palmer 
  8  @author: Peter Parente 
  9  @organization: UNC Chapel Hill 
 10  @copyright: Copyright (c) 2007, Andy Shi, Cristobal Palmer 
 11   
 12  @author: Nicole Anacker 
 13  @organization: IT Science Center Ruegen gGmbH, Germany 
 14  @copyright: Copyright (c) 2007, 2008 ITSC Ruegen 
 15   
 16  @license: I{The BSD License}. 
 17  All rights reserved. This program and the accompanying materials are made  
 18  available under the terms of the BSD License which is available at 
 19  U{http://www.opensource.org/licenses/bsd-license.php} 
 20   
 21  @see: U{http://www.unc.edu/campus/policies/copyright.html} Section 5.D.2.A 
 22    for UNC copyright rules. 
 23  ''' 
 24  # import useful modules for Scripts 
 25  from AccessEngine import AEScript, AccessEngineAPI 
 26  from AccessEngine import AEConstants 
 27  from Tools.i18n import bind, _ 
 28   
 29  # metadata describing this Script 
 30  __uie__ = dict(kind='script', tier='gedit', all_tiers=False) 
 31   
32 -class GEditScript(AEScript.EventScript):
33 ''' 34 A special L{AEScript <AEScript.AEScript>} for gedit. 35 This class register tasks that handle the check spelling dialog and document 36 statistics dialog. It also defines hotkeys. 37 I{Caps-Lock+K} reads the current misspelled word in the context of the spell 38 check dialog. 39 I{Caps-Lock+I} reads the line containing the misspelled word. 40 41 @ivar context: Context of the current cursor position. 42 @type context: string 43 @ivar spelling: Is the spell check dialog active? 44 @type spelling: boolean 45 '''
46 - def init(self):
47 ''' 48 Registers L{event_tasks <AEScript.event_tasks>} to handle 49 L{focus <AEEvent.FocusChange>}, L{caret <AEEvent.CaretChange>} and 50 L{property change <AEEvent.PropertyChange>} events. Registers 51 L{tasks <AEScript.registered_tasks>} that can be mapped to 52 L{AEInput.Gesture}s. 53 ''' 54 self.context = None 55 self.spelling = False 56 57 # set the default output device 58 AccessEngineAPI.setScriptIdealOutput(self, 'audio') 59 60 # register tasks 61 self.registerEventTask('track focus', AEConstants.EVENT_TYPE_FOCUS_CHANGE, 62 tier=True, background=True) 63 self.registerEventTask('track carets', AEConstants.EVENT_TYPE_CARET_CHANGE, 64 tier=True) 65 #self.registerEventTask('status bar', AEConstants.EVENT_TYPE_PROPERTY_CHANGE, 66 #tier=True) 67 68 ##self.registerTask(CheckCurrentWord('gedit check word', tier=True)) 69 70 # run the view change handler after the basic speech handler 71 self.registerTask('read statistic', self.readStatisticDialog) 72 self.registerTask('read spelling', self.readSpellingDialog) 73 self.chainTask('read statistic', AEConstants.CHAIN_AFTER, 74 'read dialog', 'BasicSpeechScript') 75 self.chainTask('read spelling', AEConstants.CHAIN_AFTER, 76 'read view', 'BasicSpeechScript') 77 78 # register input commands 79 self.registerTask('read misspelled word', self.readMisspelledWord) 80 self.registerTask('read misspelled context', self.readMisspelledContext) 81 82 kbd = AccessEngineAPI.getInputDevice(None, 'keyboard') 83 AccessEngineAPI.addInputModifiers(self, kbd, kbd.AEK_CAPS_LOCK) 84 self.registerCommand(kbd, 'read misspelled word', 85 _('read misspelled word'), 86 False, [kbd.AEK_CAPS_LOCK, kbd.AEK_K]) 87 self.registerCommand(kbd, 'read misspelled context', 88 _('read misspelled context'), 89 False, [kbd.AEK_CAPS_LOCK, kbd.AEK_I])
90
91 - def getDescription(self):
92 ''' 93 Describe which L{AETier} this script applies to by default. 94 95 @return: Human readable translated description of this script. 96 @rtype: string 97 ''' 98 return _('Improves the usability of gedit spell check and document ' 99 'statistics dialogs.')
100
101 - def _isDocument(self, por):
102 ''' 103 Check if por is in the document. 104 105 @param por: Point of regard for the related accessible 106 @type por: L{AEPor} 107 @return: C{True} if por is in the document. 108 @rtype: boolean 109 ''' 110 return AccessEngineAPI.hasAccRole('text', por) and \ 111 AccessEngineAPI.hasAccState('multi line', por)
112 113 ##### 114 ## Statistic Dialog 115 #####
116 - def _isStatistic(self, por):
117 ''' 118 Check if por is in the statistic dialog. 119 120 @param por: Point of regard for the related accessible 121 @type por: L{AEPor} 122 @return: C{True} if por is in the statistic dialog, C{False} if not. 123 @rtype: boolean 124 ''' 125 # check if stats dialog is open by querying some well known paths 126 # [dialog | Statistik zum Dokument] 127 lblOne = AccessEngineAPI.getAccFromPath(por, 0,0,0) 128 lblTwo = AccessEngineAPI.getAccFromPath(por, 0,0,1,1,2) 129 lblThree = AccessEngineAPI.getAccFromPath(por, 0,0,1,1,3) 130 if not (AccessEngineAPI.hasAccRole('label', lblOne) and 131 AccessEngineAPI.hasAccRole('label', lblTwo) and 132 AccessEngineAPI.hasAccRole('label', lblThree)): 133 return False 134 return True
135
136 - def _sayStatistic(self, rootPor, **kwargs):
137 ''' 138 Read all information on the statistic dialog in a sensible order. 139 140 @param rootPor: Point of regard for the related accessible 141 @type rootPor: L{AEPor} 142 @param kwargs: Arbitrary keyword arguments to pass to the task 143 @type kwargs: dictionary 144 ''' 145 item_list = [] 146 #line 147 item_list.append(AccessEngineAPI.getAccFromPath(rootPor, 0,0,1,1,2)) 148 item_list.append(AccessEngineAPI.getAccFromPath(rootPor, 0,0,1,1,1)) 149 item_list.append(AccessEngineAPI.getAccFromPath(rootPor, 0,0,1,2,1)) 150 #word 151 item_list.append(AccessEngineAPI.getAccFromPath(rootPor, 0,0,1,1,3)) 152 item_list.append(AccessEngineAPI.getAccFromPath(rootPor, 0,0,1,1,4)) 153 item_list.append(AccessEngineAPI.getAccFromPath(rootPor, 0,0,1,2,2)) 154 #character with spaces 155 item_list.append(AccessEngineAPI.getAccFromPath(rootPor, 0,0,1,1,6)) 156 item_list.append(AccessEngineAPI.getAccFromPath(rootPor, 0,0,1,1,5)) 157 item_list.append(AccessEngineAPI.getAccFromPath(rootPor, 0,0,1,2,3)) 158 #character without spaces 159 item_list.append(AccessEngineAPI.getAccFromPath(rootPor, 0,0,1,1,8)) 160 item_list.append(AccessEngineAPI.getAccFromPath(rootPor, 0,0,1,1,7)) 161 item_list.append(AccessEngineAPI.getAccFromPath(rootPor, 0,0,1,2,4)) 162 #bytes 163 item_list.append(AccessEngineAPI.getAccFromPath(rootPor, 0,0,1,1,10)) 164 item_list.append(AccessEngineAPI.getAccFromPath(rootPor, 0,0,1,1,9)) 165 item_list.append(AccessEngineAPI.getAccFromPath(rootPor, 0,0,1,2,5)) 166 for i in xrange(0, len(item_list), 3): 167 AccessEngineAPI.sayLabel(self, cap='audio', role='output', 168 text=AccessEngineAPI.getAccName(item_list[i]), 169 talk=False, **kwargs) 170 AccessEngineAPI.sayItem(self, cap='audio', role='output', 171 text=AccessEngineAPI.getItemText(item_list[i+1]), 172 talk=True, **kwargs) 173 AccessEngineAPI.sayItem(self, cap='audio', role='output', 174 text=AccessEngineAPI.getItemText(item_list[i+2]), 175 talk=False, **kwargs)
176
177 - def readStatisticDialog(self, **kwargs):
178 ''' 179 Reads the documents statistics dialog. 180 181 @param kwargs: Arbitrary keyword arguments to pass to the task 182 @type kwargs: dictionary 183 184 @todo: we should say the labels as row and column headers, at least for 185 the first row 186 ''' 187 por = AccessEngineAPI.getRootAcc(kwargs['por']) 188 if self._isStatistic(por): 189 self._sayStatistic(por, **kwargs)
190 191 ##### 192 ## Spelling Dialog 193 #####
194 - def _isSpelling(self, por):
195 ''' 196 Check if por is in the spelling dialog. 197 198 @param por: Point of regard for the related accessible 199 @type por: L{AEPor} 200 @return: C{True} if por in the spelling dialog, C{False} if not. 201 @rtype: boolean 202 ''' 203 # for check spelling dialog using some well known paths 204 checkLabel = AccessEngineAPI.getAccFromPath(por, 0,0,1) 205 checkButton = AccessEngineAPI.getAccFromPath(por, 0,0,0,1) 206 checkTextField = AccessEngineAPI.getAccFromPath(por, 0,0,0,0) 207 if not (AccessEngineAPI.hasAccRole('label', checkLabel) and 208 AccessEngineAPI.hasAccRole('push button', checkButton) and 209 AccessEngineAPI.hasAccRole('text', checkTextField)): 210 # not the spell check dialog 211 self.spelling = False 212 return False 213 self.spelling = True 214 return True
215
216 - def _saySpelling(self, por):
217 ''' 218 Read the misspelled word. 219 220 @param por: Point of regard for the related accessible 221 @type por: L{AEPor} 222 ''' 223 # get the misspelled word label 224 label_por = AccessEngineAPI.getAccFromPath(por, 0,0,3) 225 label_text = AccessEngineAPI.getAccName(label_por) 226 # get the misspelled word, there is not relation to its label 227 word_por = AccessEngineAPI.getAccFromPath(por, 0,0,2) 228 word_text = AccessEngineAPI.getAccName(word_por) 229 # say the label and the word 230 AccessEngineAPI.inhibitMayStop() 231 AccessEngineAPI.say(self, cap='audio', role='output', text=label_text, 232 layer=AEConstants.LAYER_FOCUS, sem=AEConstants.SEM_LABEL) 233 AccessEngineAPI.say(self, cap='audio', role='output', text=word_text, 234 layer=AEConstants.LAYER_FOCUS, sem=AEConstants.SEM_WORD)
235
236 - def readSpellingDialog(self, **kwargs):
237 ''' 238 Reads the misspelled word in the spelling dialog when it first appears. 239 240 @param kwargs: Arbitrary keyword arguments to pass to the task 241 @type kwargs: dictionary 242 ''' 243 if kwargs['gained']: 244 if self._isSpelling(kwargs['por']): 245 self._saySpelling(kwargs['por'])
246
247 - def readMisspelledWord(self, **kwargs):
248 ''' 249 Reads the misspelled word whenever the user presses a hotkey. Because the 250 first misspelled word is not highlighted by gedit, getAccSelection cannot be 251 used to retrieve the misspelled word. The only way to get to the misspelled 252 word is by accessing the misspelled word label to explicitly extract its 253 contents. 254 255 @param kwargs: Arbitrary keyword arguments to pass to the task 256 @type kwargs: dictionary 257 ''' 258 if not self.spelling: 259 # do nothing if the spell check dialog is not active 260 return 261 # get the root por 262 root = AccessEngineAPI.getRootAcc(kwargs['por']) 263 # por to the misspelled word 264 misspelled_word_por = AccessEngineAPI.getAccFromPath(root, 0,0,2) 265 # word is in the name field, the text does not always update 266 misspelled_word_text = AccessEngineAPI.getAccName(misspelled_word_por) 267 # cycle through reading, spelling, and pronouncing the misspelled word 268 self.doTask('cycle review word', 'BasicSpeechScript', 269 text=misspelled_word_text, **kwargs)
270
271 - def readMisspelledContext(self, **kwargs):
272 ''' 273 Reads the line containing the misspelled word. Raises pitch on the 274 misspelled word if that feature is supported. 275 276 @param kwargs: Arbitrary keyword arguments to pass to the task 277 @type kwargs: dictionary 278 ''' 279 if not self.spelling: 280 # do nothing if the spell check dialog is not active 281 return 282 por = self.context 283 if por is None: 284 # if we have no context info, abort 285 return 286 # get the item text 287 text = AccessEngineAPI.getItemText(por) 288 # stop all output 289 AccessEngineAPI.stopNow(self, cap='audio', role='output', **kwargs) 290 try: 291 # see if we can modify pitch 292 sett = AccessEngineAPI.getStyleSetting(self, 'Pitch', 293 sem=AEConstants.SEM_WORD, **kwargs) 294 except Task.InvalidStyleError: 295 AccessEngineAPI.sayItem(self, cap='audio', role='output', 296 text=text, **kwargs) 297 else: 298 # say prefix 299 prefix = text[:por.char_offset] 300 if prefix.strip(): 301 AccessEngineAPI.sayItem(self, cap='audio', role='output', text=prefix, 302 talk=False, **kwargs) 303 # say word 304 # change pitch on the misspelled word 305 diff = (sett.max - sett.min)/10 306 sett.value += diff 307 word = AccessEngineAPI.getWordText(por) 308 AccessEngineAPI.sayWord(self, cap='audio', role='output', 309 text=word, talk=False, **kwargs) 310 # restore pitch 311 sett.value -= diff 312 # say suffix 313 suffix = text[por.char_offset+len(word):] 314 if suffix: 315 AccessEngineAPI.sayItem(self, cap='audio', role='output', 316 text=suffix, **kwargs) 317 return True
318 319 ##### 320 ## Events 321 #####
322 - def onFocusLost(self, **kwargs):
323 ''' 324 Keeps track of the L{AEPor} to the misspelled word. 325 326 @param kwargs: Arbitrary keyword arguments to pass to the task 327 @type kwargs: dictionary 328 ''' 329 if not self._isDocument(kwargs['por']): 330 # not the main text area 331 return 332 # store the context for later if the user requests it 333 self.context = AccessEngineAPI.getAccCaret(kwargs['por'])
334
335 - def onCaretMoved(self, **kwargs):
336 ''' 337 Keeps track of the L{AEPor} to the misspelled word. Announces it when the 338 spelling dialog is not shown. 339 340 @param kwargs: Arbitrary keyword arguments to pass to the task 341 @type kwargs: dictionary 342 ''' 343 if not self._isDocument(kwargs['por']): 344 # not the main text area 345 return 346 # store the context for later if the user requests it 347 self.context = kwargs['por'] 348 if self.spelling: 349 # say the misspelled word if the spelling dialog is already open 350 AccessEngineAPI.inhibitMayStop() 351 AccessEngineAPI.inhibitMayStop() 352 AccessEngineAPI.say(self, cap='audio', role='output', 353 text=_('misspelled word'), sem=AEConstants.SEM_LABEL, 354 layer=AEConstants.LAYER_FOCUS) 355 AccessEngineAPI.say(self, cap='audio', role='output', 356 text=AccessEngineAPI.getWordText(kwargs['por']), 357 sem=AEConstants.SEM_WORD, 358 layer=AEConstants.LAYER_FOCUS)
359
360 - def onStateChange(self, **kwargs):
361 ''' 362 Monitors state changes on the table to determine if a word is misspelled or 363 correct. 364 365 @param kwargs: Arbitrary keyword arguments to pass to the task 366 @type kwargs: dictionary 367 368 @todo: needs work, what about the case where the table doesn't change state? 369 ''' 370 # check for spelling dialog 371 if not self.spelling: 372 return 373 # get the suggestions table 374 root = AccessEngineAPI.getRootAcc(kwargs['por']) 375 table = AccessEngineAPI.getAccFromPath(root, 0,1,3,0) 376 if (table == por and name == 'sensitive'): 377 if value: 378 AccessEngineAPI.sayInfo(self, cap='audio', role='output', 379 text=_('misspelled'), **kwargs) 380 else: 381 AccessEngineAPI.sayInfo(self, cap='audio', role='output', 382 text=_('spelled correctly'), **kwargs)
383 384 #def onPropertyChange(self, **kwargs): 385 #''' 386 #Handles text being displayed in the statusbar. 387 #''' 388 #root = AccessEngineAPI.getRootAcc(kwargs['por']) 389 #por = AccessEngineAPI.getAccFromPath(root, 0,3,1,0) 390 #text = AccessEngineAPI.getAccText(por) 391 #if text != "": 392 #AccessEngineAPI.mayStop(self, cap='audio', role='output', **kwargs) 393 #AccessEngineAPI.inhibitMayStop() 394 #AccessEngineAPI.sayInfo(self, cap='audio', role='output', text=text, **kwargs) 395 396 #return True 397