1 '''
2 Provides review commands for traversing items, words, and characters.
3
4 @author: Peter Parente
5 @organization: IBM Corporation
6 @copyright: Copyright (c) 2005, 2007 IBM Corporation
7
8 @author: Martina Weicht
9 @author: Nicole Anacker
10 @organization: IT Science Center Ruegen gGmbH, Germany
11 @copyright: Copyright (c) 2007, 2008 ITSC Ruegen
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
20 from AccessEngine import AEScript, AccessEngineAPI
21 from AccessEngine import AEConstants
22
23 from Tools.i18n import bind, _
24
25 import logging
26 log = logging.getLogger('ReviewScript')
27
28
29 __uie__ = dict(kind='script', tier=None, all_tiers=True)
30
31
32 SKIP_ALL = 0
33 SKIP_REPORT = 1
34 SKIP_NONE = 2
35
36
37 REVIEW_OK = 0
38 REVIEW_NO_NEXT_ITEM = 1
39 REVIEW_NO_NEXT_WORD = 2
40 REVIEW_NO_NEXT_CHAR = 3
41 REVIEW_NO_PREV_ITEM = 4
42 REVIEW_NO_PREV_WORD = 5
43 REVIEW_NO_PREV_CHAR = 6
44 REVIEW_WRAP = 7
45
47 '''
48 Defines options having to do with review mode.
49
50 B{Skipping (enumeration):}
51 Determines how the review keys handle empty nodes that meet the requires for
52 the walker but may be of no interest to the user. If L{SKIP_ALL}, any item
53 with no item text is skipped and the walk continues. If L{SKIP_REPORT}, a
54 signal is sent to a special task named 'review skip' before skipping empty
55 items that are interactive and continuing the walk. If L{SKIP_ALL}, a signal
56 is sent to the same task before skipping any empty item and continuing the
57 walk. If L{SKIP_NONE}, the walk stops on all items.
58
59 B{OnlyVisible (bool):}
60 Should review keys only walk visible items or hidden items also?
61
62 B{Wrap (bool):} Should movement of the pointer wrap at item boundaries?
63 '''
65 '''
66 Create L{AEState} settings for this L{AEScript <AEScript.AEScript>}.
67 '''
68 self.newEnum('Skipping', SKIP_REPORT, _('Skip empty items'),
69 {_('Always') : SKIP_ALL, _('Never') : SKIP_NONE,
70 _('Report') : SKIP_REPORT},
71 _('Determines how the review commands react to empty items. '
72 'Always skips all empty, non-focusable items without '
73 'notification. Report skips all empty, non-focusable items '
74 'with a report they were skipped. Never disables '
75 'skipping.'))
76 self.newBool('OnlyVisible', True, _('Review visible items only?'),
77 _('When set, reviewing only includes visible items. '
78 'Otherwise, invisible items are also included.'))
79 self.newBool('Wrap', True, _('Wrap pointer across items?'),
80 _('When set, next and previous, word and character navigation'
81 ' can cross item boundaries. Otherwise, first and last,'
82 ' word and character are announced instead.'))
83
85 '''
86 Creates configuration groups from pre-defined settings.
87
88 @return: root setting group
89 @rtype: L{AEState.Setting.Group}
90 '''
91 g = self.newGroup()
92 g.append('Skipping')
93 g.append('OnlyVisible')
94 g.append('Wrap')
95 return g
96
98 '''
99 Registers L{tasks <registered_tasks>} for reviewing items, words, and
100 characters. Also registers tasks for moving pointer to por and focus to por.
101 It defines special hotkeys.
102 - I{(Alt+Shift+U)} - Moves the L{AETier.virtual_por
103 <AccessEngine.AETier.AETier.virtual_por>} to the beginning of the previous
104 item
105 - I{(Alt+Shift+I)} - Moves the L{AETier.virtual_por
106 <AccessEngine.AETier.AETier.virtual_por>} to the beginning of the current
107 item
108 - I{(Alt+Shift+O)} - Moves the L{AETier.virtual_por
109 <AccessEngine.AETier.AETier.virtual_por>} to the beginning of the next
110 item.
111 - I{(Alt+Shift+J)} - Moves the L{AETier.virtual_por
112 <AccessEngine.AETier.AETier.virtual_por>} to the beginning of the previous
113 word.
114 - I{(Alt+Shift+K)} - Moves the L{AETier.virtual_por
115 <AccessEngine.AETier.AETier.virtual_por>} to the beginning of the current
116 word.
117 - I{(Alt+Shift+L)} - Moves the L{AETier.virtual_por
118 <AccessEngine.AETier.AETier.virtual_por>} to the beginning of the next
119 word.
120 - I{(Alt+Shift+M)} - Moves the L{AETier.virtual_por
121 <AccessEngine.AETier.AETier.virtual_por>} to the previous character.
122 - I{(Alt+Shift+,)} - Does nothing. The L{AETier.virtual_por
123 <AccessEngine.AETier.AETier.virtual_por>} is already referring to the
124 current character.
125 - I{(Alt+Shift+.)} - Moves the L{AETier.virtual_por
126 <AccessEngine.AETier.AETier.virtual_por>} to the next character.
127
128 @cvar STATE: L{AEState} object that holds user setting values.
129 @type STATE: L{AEState}
130 @ivar peek: Should this review task instance actually affect the pointer
131 L{AEPor} and allow announcements of its changed state or simply give a
132 preview of where it will wind up for programmatic purposes?
133 @type peek: boolean
134 '''
135 STATE = ReviewScriptState
136
138 '''
139 Registers L{tasks <AEScript.registered_tasks>} that can be
140 mapped to L{AEInput.Gesture}s. All tasks are registered twice, once as
141 preview and once as not. The base class for all review classes will take
142 care of adding the word 'peek' to those tasks with the peek flag set.
143 '''
144
145 self.registerTask('review previous item', self.previousItem)
146
147 self.registerTask('review current item', self.currentItem)
148
149 self.registerTask('review next item', self.nextItem)
150
151
152 self.registerTask('review previous word', self.previousWord)
153
154 self.registerTask('review current word', self.currentWord)
155
156 self.registerTask('review next word', self.nextWord)
157
158
159 self.registerTask('review previous char', self.previousChar)
160
161 self.registerTask('review current char', self.currentChar)
162
163 self.registerTask('review next char', self.nextChar)
164
165 self.registerTask('focus to por', self.focusToPOR)
166 self.registerTask('pointer to por', self.pointerToPOR)
167 self.registerTask('mouse to por', self.mouseToPOR)
168
169 self.registerTask('review skip report', self.skipItem)
170 self.registerTask('review should skip', self.shouldSkip)
171
172
173 kbd = AccessEngineAPI.getInputDevice(None, 'keyboard')
174 AccessEngineAPI.addInputModifiers(self, kbd, kbd.AEK_ALT_L,
175 kbd.AEK_SHIFT_L, kbd.AEK_ALT_R,
176 kbd.AEK_SHIFT_R, kbd.AEK_CAPS_LOCK)
177
178
179
180
181 pairs = [[kbd.AEK_ALT_L, kbd.AEK_SHIFT_L], [kbd.AEK_ALT_R, kbd.AEK_SHIFT_R]]
182
183
184 for pair in pairs:
185
186 self.registerCommand(kbd, 'review previous item',
187 _('review previous item'), False, pair+[kbd.AEK_U])
188 self.registerCommand(kbd, 'review current item',
189 _('review current item'), False, pair+[kbd.AEK_I])
190 self.registerCommand(kbd, 'review next item',
191 _('review next item'), False, pair+[kbd.AEK_O])
192 self.registerCommand(kbd, 'review previous word',
193 _('review previous word'), False, pair+[kbd.AEK_J])
194 self.registerCommand(kbd, 'review current word',
195 _('review current word'), False, pair+[kbd.AEK_K])
196 self.registerCommand(kbd, 'review next word',
197 _('review next word'), False, pair+[kbd.AEK_L])
198 self.registerCommand(kbd, 'review previous char',
199 _('review previous char'), False, pair+[kbd.AEK_M])
200 self.registerCommand(kbd, 'review current char',
201 _('review current char'), False, pair+[kbd.AEK_COMMA])
202 self.registerCommand(kbd, 'review next char',
203 _('review next char'), False, pair+[kbd.AEK_PERIOD])
204
205 self.registerCommand(kbd, 'focus to por',
206 _('focus to por'), False, pair+[kbd.AEK_P])
207
208 self.registerCommand(kbd, 'pointer to por',
209 _('pointer to por'), False, pair+[kbd.AEK_SEMICOLON])
210
211 self.registerCommand(kbd, 'mouse to por',
212 _('mouse to por'), False, pair+[kbd.AEK_SLASH])
213
214
215 AccessEngineAPI.registerConstants(globals(), 'REVIEW_NO_NEXT_CHAR',
216 'REVIEW_NO_NEXT_ITEM',
217 'REVIEW_NO_NEXT_WORD', 'REVIEW_NO_PREV_CHAR',
218 'REVIEW_NO_PREV_WORD', 'REVIEW_NO_PREV_ITEM',
219 'REVIEW_OK', 'REVIEW_WRAP', 'SKIP_ALL',
220 'SKIP_REPORT', 'SKIP_NONE')
221
223 '''
224 Gets if the task_name has the string 'peek'.
225
226 @param task_name: Name of the given task.
227 @type task_name: string
228 @return: C{True} if the task_name has the string 'peek'.
229 @rtype: boolean
230 '''
231 return 'peek' in task_name
232
234 '''
235 Method to account for peeking. If L{peek} is C{True}, stores the new
236 L{AEPor} as a temporary task value under the name 'peek' instead of updating
237 the actual L{AETier.virtual_por <AccessEngine.AETier.AETier.virtual_por>}.
238
239 @param por: Point of regard for the related accessible
240 @type por: L{AEPor}
241 @param peek: C{True} if the task_name has the string 'peek' or C{None}.
242 @type peek: boolean
243 '''
244 if peek:
245 self.setTempData('peek', por)
246 else:
247 self.setVirtualPOR(por)
248
250 '''
251 Provides the localized name of this L{AEScript <AEScript.AEScript>}.
252
253 @return: Human readable name of this script.
254 @rtype: string
255 '''
256 return _('Review mode')
257
259 '''
260 Describe what this L{AEScript <AEScript.AEScript>} do.
261
262 @return: Human readable translated description of this script.
263 @rtype: string
264 '''
265 return _('Defines commands for reviewing applications by items, words, '
266 'and characters as well as moving the pointer and focus.')
267
268
269
270
271
273 '''
274 Moves the L{AETier.virtual_por <AccessEngine.AETier.AETier.virtual_por>} to
275 the beginning of the previous item.
276
277 Chain values:
278 - review (integer): L{REVIEW_NO_PREV_ITEM} or L{REVIEW_OK}
279 - has skipped (boolean): C{True} if at least one item has been skipped
280 - will skip (boolean): C{True} if at least one item will be skipped
281 - peek: L{AEPor} of previous item, if L{peek} is C{True}
282
283 @param onlyvisible: Shoud the B{"OnlyVisible"} user setting overrides?
284 @type onlyvisible: boolean
285 @param kwargs: Arbitrary keyword arguments to pass to the task
286 @type kwargs: dictionary
287 '''
288
289 if onlyvisible is None:
290 onlyvisible = self.state.OnlyVisible
291
292
293 skipped = self.getTempData('has skipped')
294 self.setTempData('has skipped', skipped or False)
295
296
297 for por in AccessEngineAPI.iterPrevItems(self.getVirtualPOR(), wrap=True,
298 only_visible=onlyvisible):
299
300 self.doTask('review should skip', peek=self._hasPeek(kwargs['task_name']),
301 **kwargs)
302 if not self.getTempData('should skip'):
303
304 self.setTempData('review', REVIEW_OK)
305
306 self.moveToPOR(por, self._hasPeek(kwargs['task_name']))
307 return
308
309 self.setTempData('review', REVIEW_NO_PREV_ITEM)
310
312 '''
313 Moves the L{AETier.virtual_por <AccessEngine.AETier.AETier.virtual_por>} to
314 the beginning of the current item.
315
316 Chain values:
317 - review (integer): L{REVIEW_OK}
318 - has skipped (boolean): C{False}
319 - will skip (boolean): C{False}
320 - peek: L{AEPor} of current item (if L{peek} is C{True})
321
322 @param kwargs: Arbitrary keyword arguments to pass to the task
323 @type kwargs: dictionary
324 '''
325 por = AccessEngineAPI.getCurrItem(self.getVirtualPOR())
326 self.moveToPOR(por, self._hasPeek(kwargs['task_name']))
327 self.setTempData('review', REVIEW_OK)
328 self.setTempData('has skipped', False)
329
330 - def nextItem(self, onlyvisible=None, **kwargs):
331 '''
332 Moves the L{AETier.virtual_por <AccessEngine.AETier.AETier.virtual_por>} to
333 the beginning of the next item.
334
335 Chain values:
336 - review (integer): L{REVIEW_NO_NEXT_ITEM} or L{REVIEW_OK}
337 - has skipped (boolean): C{True} if at least one item has been skipped
338 - will skip (boolean): C{True} if at least one item will be skipped
339 - peek: L{AEPor} of next item, if L{peek} is C{True}
340
341 @param onlyvisible: Shoud the B{"OnlyVisible"} user setting overrides?
342 @type onlyvisible: boolean
343 @param kwargs: Arbitrary keyword arguments to pass to the task
344 @type kwargs: dictionary
345 '''
346
347 if onlyvisible is None:
348 onlyvisible = self.state.OnlyVisible
349
350
351 skipped = self.getTempData('has skipped')
352 self.setTempData('has skipped', skipped or False)
353
354 for por in AccessEngineAPI.iterNextItems(self.getVirtualPOR(), wrap=True,
355 only_visible=onlyvisible):
356
357 kwargs['por'] = por
358 self.doTask('review should skip', peek=self._hasPeek(kwargs['task_name']),
359 **kwargs)
360 if not self.getTempData('should skip'):
361
362 self.setTempData('review', REVIEW_OK)
363
364 self.moveToPOR(por, self._hasPeek(kwargs['task_name']))
365 return
366
367 self.setTempData('review', REVIEW_NO_NEXT_ITEM)
368
370 '''
371 Moves the L{AETier.virtual_por <AccessEngine.AETier.AETier.virtual_por>} to
372 the beginning of the previous word.
373
374 Chain values:
375 - review (integer): L{REVIEW_NO_PREV_WORD}, L{REVIEW_NO_PREV_ITEM},
376 L{REVIEW_OK}, L{REVIEW_WRAP}
377 - peek: L{AEPor} of previous word if L{peek} is C{True}
378
379 @param kwargs: Arbitrary keyword arguments to pass to the task
380 @type kwargs: dictionary
381 '''
382
383 por = AccessEngineAPI.getPrevWord(self.getVirtualPOR())
384 if por is not None:
385
386 self.moveToPOR(por, self._hasPeek(kwargs['task_name']))
387 self.setTempData('review', REVIEW_OK)
388 return
389 if not self.state.Wrap:
390
391
392 self.setTempData('review', REVIEW_NO_PREV_WORD)
393 return
394
395 por = AccessEngineAPI.getPrevItem(self.getVirtualPOR(), wrap=True,
396 only_visible=self.state.OnlyVisible)
397 if por is None:
398
399 self.setTempData('review', REVIEW_NO_PREV_ITEM)
400 return
401 por = AccessEngineAPI.getLastWord(por)
402
403 if por is None:
404 raise Task.PORError
405
406 self.moveToPOR(por, self._hasPeek(kwargs['task_name']))
407
408 self.setTempData('review', REVIEW_WRAP)
409
411 '''
412 Moves the L{AETier.virtual_por <AccessEngine.AETier.AETier.virtual_por>} to
413 the beginning of the current word.
414
415 Chain values:
416 - review (integer): L{REVIEW_OK}
417 - peek: L{AEPor} of current word if L{peek} is C{True}
418
419 @param kwargs: Arbitrary keyword arguments to pass to the task
420 @type kwargs: dictionary
421 '''
422 por = AccessEngineAPI.getCurrWord(self.getVirtualPOR())
423 self.moveToPOR(por, self._hasPeek(kwargs['task_name']))
424 self.setTempData('review', REVIEW_OK)
425
427 '''
428 Moves the L{AETier.virtual_por <AccessEngine.AETier.AETier.virtual_por>} to
429 the beginning of the next word.
430
431 Chain values:
432 - review (integer): L{REVIEW_NO_NEXT_WORD}, L{REVIEW_NO_NEXT_ITEM},
433 L{REVIEW_OK}, L{REVIEW_WRAP}
434 - peek: L{AEPor} of next word if L{peek} is C{True}
435
436 @param kwargs: Arbitrary keyword arguments to pass to the task
437 @type kwargs: dictionary
438 '''
439
440 por = AccessEngineAPI.getNextWord(self.getVirtualPOR())
441 if por is not None:
442
443 self.moveToPOR(por, self._hasPeek(kwargs['task_name']))
444 self.setTempData('review', REVIEW_OK)
445 return
446 if not self.state.Wrap:
447
448
449 self.setTempData('review', REVIEW_NO_NEXT_WORD)
450 return
451
452 por = AccessEngineAPI.getNextItem(self.getVirtualPOR(), wrap=True,
453 only_visible=self.state.OnlyVisible)
454 if por is None:
455
456 self.setTempData('review', REVIEW_NO_NEXT_ITEM)
457 return
458
459 self.moveToPOR(por, self._hasPeek(kwargs['task_name']))
460
461 self.setTempData('review', REVIEW_WRAP)
462
464 '''
465 Moves the L{AETier.virtual_por <AccessEngine.AETier.AETier.virtual_por>} to
466 the previous character.
467
468 Chain values:
469 - review (integer): L{REVIEW_NO_PREV_CHAR}, L{REVIEW_NO_PREV_ITEM},
470 L{REVIEW_OK}, L{REVIEW_WRAP}
471 - peek: L{AEPor} of previous character if L{peek} is C{True}
472
473 @param kwargs: Arbitrary keyword arguments to pass to the task
474 @type kwargs: dictionary
475 '''
476 AccessEngineAPI.stopNow(self, cap='audio', role='output', **kwargs)
477
478 por = AccessEngineAPI.getPrevChar(self.getVirtualPOR())
479 if por is not None:
480
481 self.moveToPOR(por, self._hasPeek(kwargs['task_name']))
482 self.setTempData('review', REVIEW_OK)
483 return
484 if not self.state.Wrap:
485
486
487 self.setTempData('review', REVIEW_NO_PREV_CHAR)
488 return
489
490 por = AccessEngineAPI.getPrevItem(self.getVirtualPOR(), wrap=True,
491 only_visible=self.state.OnlyVisible)
492 if por is None:
493
494 self.setTempData('review', REVIEW_NO_PREV_ITEM)
495 return
496
497 por = AccessEngineAPI.getLastChar(por)
498
499 if por is None:
500 raise Task.PORError
501
502 self.moveToPOR(por, self._hasPeek(kwargs['task_name']))
503
504 self.setTempData('review', REVIEW_WRAP)
505
507 '''
508 The L{AETier.virtual_por <AccessEngine.AETier.AETier.virtual_por>} is
509 already referring to the current character. This task only exists so others
510 can link to it.
511
512 Chain values:
513 - review (integer): L{REVIEW_OK}
514 - peek: L{AEPor} of current character if L{peek} is C{True}
515
516 @param kwargs: Arbitrary keyword arguments to pass to the task
517 @type kwargs: dictionary
518 '''
519
520 self.moveToPOR(self.getVirtualPOR(), self._hasPeek(kwargs['task_name']))
521 self.setTempData('review', REVIEW_OK)
522
524 '''
525 Moves the L{AETier.virtual_por <AccessEngine.AETier.AETier.virtual_por>} to
526 the next character.
527
528 Chain values:
529 - review (integer): L{REVIEW_NO_NEXT_CHAR}, L{REVIEW_NO_NEXT_ITEM},
530 L{REVIEW_OK}, L{REVIEW_WRAP}
531 - peek: L{AEPor} of next character if L{peek} is C{True}
532
533 @param kwargs: Arbitrary keyword arguments to pass to the task
534 @type kwargs: dictionary
535 '''
536 AccessEngineAPI.stopNow(self, cap='audio', role='output', **kwargs)
537
538 por = AccessEngineAPI.getNextChar(self.getVirtualPOR())
539 if por is not None:
540
541 self.moveToPOR(por, self._hasPeek(kwargs['task_name']))
542 self.setTempData('review', REVIEW_OK)
543 return
544 if not self.state.Wrap:
545
546
547 self.setTempData('review', REVIEW_NO_NEXT_CHAR)
548 return
549
550 por = AccessEngineAPI.getNextItem(self.getVirtualPOR(), wrap=True,
551 only_visible=self.state.OnlyVisible)
552 if por is None:
553
554 self.setTempData('review', REVIEW_NO_NEXT_ITEM)
555 return
556
557 self.moveToPOR(por, self._hasPeek(kwargs['task_name']))
558
559 self.setTempData('review', REVIEW_WRAP)
560
562 '''
563 Attempts to focus, select, and move the caret to the L{AETier.virtual_por
564 <AccessEngine.AETier.AETier.virtual_por>} by default or the given L{AEPor}
565 if not None.
566
567 @param por: Point of regard for the related accessible
568 @type por: L{AEPor}
569 @param kwargs: Arbitrary keyword arguments to pass to the task
570 @type kwargs: dictionary
571 '''
572 AccessEngineAPI.stopNow(self, cap='audio', role='output', **kwargs)
573 AccessEngineAPI.setAccPOR(self.getVirtualPOR())
574
576 '''
577 Moves the L{AETier.virtual_por <AccessEngine.AETier.AETier.virtual_por>} to
578 the location of the last focus, selector, or caret event by default or the
579 given L{AEPor} if not None.
580
581 @param por: Point of regard for the related accessible
582 @type por: L{AEPor}
583 @param kwargs: Arbitrary keyword arguments to pass to the task
584 @type kwargs: dictionary
585 '''
586 if por is None:
587 self.moveToPOR(AccessEngineAPI.getAccFocus(),
588 self._hasPeek(kwargs['task_name']))
589 else:
590 self.moveToPOR(por, self._hasPeek(kwargs['task_name']))
591 self.setTempData('review', REVIEW_OK)
592
594 '''
595 Move the mouse pointer to the location of the L{AETier.virtual_por
596 <AccessEngine.AETier.AETier.virtual_por>} by default or the given L{AEPor}
597 if not None.
598
599 @param por: Point of regard for the related accessible
600 @type por: L{AEPor}
601 @param kwargs: Arbitrary keyword arguments to pass to the task
602 @type kwargs: dictionary
603 '''
604
605 AccessEngineAPI.mouseEventPOR(AEConstants.EVENT_SYNTHMOUSE_ABS,
606 self.getVirtualPOR())
607
609 '''
610 Does nothing. Exists so that other scripts can register tasks to respond to
611 skipped items.
612
613 @param kwargs: Arbitrary keyword arguments to pass to the task
614 @type kwargs: dictionary
615 '''
616 pass
617
619 '''
620 Determines if the current L{AEPor} should be skipped or not based on the
621 user setting for Skipping. Executes a task named 'review skip report' when
622 Skipping is set to L{SKIP_REPORT} or L{SKIP_ALL}.
623
624 @param peek: When set, do not 'review skip report' since we are looking to
625 preview whether or not an item will be skipped. We do not want chained
626 tasks to execute and announce any information in this case.
627 @type peek: boolean
628 @param por: Point of regard to test
629 @type por: L{AEPor}
630 @param kwargs: Arbitrary keyword arguments to pass to the task
631 @type kwargs: dictionary
632 '''
633 text = AccessEngineAPI.getItemText(por)
634 if text.strip() or AccessEngineAPI.hasOneAccState(por, 'focusable',
635 'editable'):
636
637 self.setTempData('should skip', False)
638 return
639
640 if self.state.Skipping == SKIP_ALL:
641 self.setTempData('should skip', True)
642 elif self.state.Skipping == SKIP_NONE:
643 self.setTempData('should skip', False)
644 elif self.state.Skipping == SKIP_REPORT:
645 if not peek:
646
647 self.doTask('review skip report', por = por, **kwargs)
648
649 self.setTempData('has skipped', True)
650 else:
651
652 self.setTempData('will skip', True)
653 self.setTempData('should skip', True)
654