1 '''
2 Provides basic speech services and speech reports common to a screen reader.
3
4 @author: Peter Parente
5 @author: Pete Brunet
6 @author: Larry Weiss
7 @author: Brett Clippingdale
8 @organization: IBM Corporation
9 @copyright: Copyright (c) 2005, 2007 IBM Corporation
10
11 @author: Frank Zenker
12 @author: Nicole Anacker
13 @author: Martina Weicht
14 @organization: IT Science Center Ruegen gGmbH, Germany
15 @copyright: Copyright (c) 2007, 2008 ITSC Ruegen
16
17 @license: I{The BSD License}
18 All rights reserved. This program and the accompanying materials are made
19 available under the terms of the BSD license which accompanies
20 this distribution, and is available at
21 U{http://www.opensource.org/licenses/bsd-license.php}
22 '''
23 import AccessEngine
24 from AccessEngine import AEScript, AEEvent, AccessEngineAPI
25 from AccessEngine import AEConstants
26 from AccessEngine.AEPor import AEPor
27 import unicodedata, logging
28 from Tools.i18n import _
29
30
31 SPEAK_NEVER = 0
32 SPEAK_ALWAYS = 1
33 SPEAK_DIFF = 2
34 SPEAK_LINE = 3
35 SPEAK_ALL = 4
36
37 __uie__ = dict(kind='script', all_tiers=True)
38 log = logging.getLogger('BasicSpeechScript')
39
41 '''
42 User configurable settings for basic speech service and reports.
43
44 B{WordEcho (bool):} Echo words when typing?
45
46 B{CharEcho (bool):} Echo characters when typing?
47
48 B{AutoLang (bool):} Switch languages automatically?
49
50 B{CharLimit (numeric):} Set the threshold for text insertion synthesizing.
51 If text exceeds this threshold, it will be summarized.
52
53 B{RoleVerbosity (bool):} Say role information always, only when it changes,
54 or never?
55
56 B{IndexVerbosity (enum):} Say the index and size of a collection always,
57 only when it changes, or never?
58
59 B{HeaderVerbosity (enum):} Say headers always, when they change in a table,
60 or never?
61
62 B{TextVerbosity (enum):} Say the line containing the caret or read all text
63 starting with the line containing the caret?
64
65 @ivar activewincnt: Used to track the number of active windows.
66 @type last_count: integer
67 '''
69 '''
70 Create L{AEState} settings for this L{AEScript <AEScript.AEScript>}.
71 '''
72 self.newBool('WordEcho', False, _('Echo words?'),
73 _('When set, entire words are spoken when editing text.'))
74 self.newBool('CharEcho', True, _('Echo characters?'),
75 _('When set, individual characters are spoken when editing '
76 'text.'))
77 self.newBool('AutoLang', True, _('Switch languages automatically?'),
78 _('When set, the language of the speech synthesizer tries to '
79 'switch to the appropriate language and dialect '
80 'automatically based on the current locale.'))
81 self.newNumeric('CharLimit', 5000, _('Summarize insertions longer than'),
82 0, 10000, 0, _('Set the threshold (in characters) for text'
83 ' insertions. Any text that exceeds this limit will be '
84 ' summarized by its length.'))
85 self.newEnum('RoleVerbosity', SPEAK_DIFF, _('Say role'),
86 {_('Always') : SPEAK_ALWAYS,
87 _('When different') : SPEAK_DIFF,
88 _('Never') : SPEAK_NEVER},
89 _('Always say widget roles, never say widget roles, or '
90 'only say widget roles when they differ from the last '
91 'report.'))
92 self.newEnum('IndexVerbosity', SPEAK_ALWAYS, _('Say index'),
93 {_('Always') : SPEAK_ALWAYS,
94 _('When different') : SPEAK_DIFF,
95 _('Never') : SPEAK_NEVER},
96 _('Always say the index of an item and size of a collection, '
97 'never say either, or only say index and size when they '
98 'differ from the last report.'))
99 self.newEnum('HeaderVerbosity', SPEAK_DIFF, _('Say table headers'),
100 {_('Always') : SPEAK_ALWAYS,
101 _('When different') : SPEAK_DIFF,
102 _('Never') : SPEAK_NEVER},
103 _('Always say table headers, never say table headers, '
104 'or only say the headers when they differ from the last '
105 'report.'))
106 self.newEnum('TextVerbosity', SPEAK_LINE, _('Say text area'),
107 {_('All') : SPEAK_ALL,
108 _('Line') : SPEAK_LINE,
109 _('Nothing') : SPEAK_NEVER},
110 _('Read an entire multiline text area when it receives the '
111 'focus, read only the line containing the caret, or read '
112 'nothing.'))
113
114
115 self.activewincnt = 0
116
118 '''
119 Gets configurable settings for this L{AEScript <AEScript.AEScript>}.
120
121 @return: Group of all configurable settings
122 @rtype: L{AEState.Setting.Group}
123 '''
124 g = self.newGroup()
125 s = g.newGroup(_('Input Echo'))
126 s.extend(['CharEcho', 'WordEcho'])
127 s = g.newGroup(_('Verbosity'))
128 s.extend(['RoleVerbosity', 'IndexVerbosity',
129 'HeaderVerbosity', 'TextVerbosity', 'CharLimit'])
130 s = g.newGroup(_('Language'))
131 s.extend(['AutoLang'])
132 return g
133
135 '''
136 Defines a default user interface that makes SUE act like a typical screen
137 reader. It defines special hotkeys.
138 - I{(Strg)} - stops the speech.
139 - I{(Alt+Shift+Page-Up)} - increase the speech rate
140 - I{(Alt+Shift+Page-Down)} - decrease the speech rate
141 - I{(Alt+Shift+?)} - reads the current position / "Where Am I"
142 - I{(Alt+Shift+T)} - reads the top
143 - I{(Alt+Shift+Down)} - read all
144 - I{(Alt+Shift+F)} - reads the text color and text attributes
145 - I{(Alt+Shift+D)} - reads the item description
146
147 @ivar start_por: Starting point of regard for the read operation
148 @type start_por: L{AEPor}
149 @ivar continue_por: Point of regard where reading should continue
150 @type continue_por: L{AEPor}
151 @ivar caret_changed: Tracks whether the last caret move event was a caused by
152 a change (insert/delete) or just navigation
153 @type caret_changed: boolean
154
155 @ivar last_caret: Stores the previous caret L{AEPor}
156 @type last_caret: L{AEPor}
157 @ivar last_role: Stores the previous role to avoid duplicate announcements
158 @type last_role: string
159 @ivar last_level: Stores the previous level to avoid duplicate announcements
160 @type last_level: integer
161 @ivar last_container: Stores the previously announced container name
162 @type last_container: string
163 @ivar last_sel_len: Length of the selected text when the last selection event
164 was received
165 @type last_sel_len: integer
166 @ivar last_row: Index of the last row activated
167 @type last_row: integer
168 @ivar last_col: Index of the last column activated
169 @type last_col: integer
170 @ivar last_count: Was count of items in a collection announced during last
171 announcement?
172 @type last_count: integer
173 '''
174
175 STATE = BasicSpeechScriptState
176
178 '''
179 Registers L{event tasks <AEScript.event_tasks>} to handle
180 L{focus <AEEvent.FocusChange>}, L{view <AEEvent.ViewChange>},
181 L{caret <AEEvent.CaretChange>}, L{selector <AEEvent.SelectorChange>},
182 L{state <AEEvent.StateChange>}, and L{property <AEEvent.PropertyChange>}
183 change events. Registers L{tasks <AEScript.registered_tasks>} that can be
184 mapped to L{AEInput.Gesture}s.
185 '''
186 self.caret_changed = False
187
188
189 AccessEngineAPI.setScriptIdealOutput(self, 'audio')
190
191
192 self.registerEventTask('read focus', AEConstants.EVENT_TYPE_FOCUS_CHANGE)
193 self.registerEventTask('read view', AEConstants.EVENT_TYPE_VIEW_CHANGE,
194 all=True)
195 self.registerEventTask('read caret', AEConstants.EVENT_TYPE_CARET_CHANGE)
196 self.registerEventTask('read selector',
197 AEConstants.EVENT_TYPE_SELECTOR_CHANGE,
198 focus=True, tier=True)
199 self.registerEventTask('read state', AEConstants.EVENT_TYPE_STATE_CHANGE)
200 self.registerEventTask('read property',
201 AEConstants.EVENT_TYPE_PROPERTY_CHANGE)
202
203
204
205 self.registerTask('stop now', self.stopSpeech)
206 self.registerTask('increase speech rate', self.increaseRate)
207 self.registerTask('decrease speech rate', self.decreaseRate)
208 self.registerTask('read top', self.readTop)
209 self.registerTask('read description', self.readDescription)
210 self.registerTask('read new label', self.readNewLabel)
211 self.registerTask('read new role', self.readNewRole)
212 self.registerTask('read new level', self.readNewLevel)
213 self.registerTask('read new headers', self.readNewHeaders)
214 self.registerTask('read new container', self.readNewContainer)
215 self.registerTask('read item text', self.readItemText)
216 self.registerTask('read item index', self.readItemIndex)
217 self.registerTask('read item details', self.readItemDetails)
218 self.registerTask('read message', self.readMessage)
219 self.registerTask('read all', self.readAll)
220 self.registerTask('read review item', self.readReviewItem)
221 self.registerTask('read review skip', self.readReviewSkip)
222 self.registerTask('read pointer to por', self.readPointerToFocus)
223
224
225 self.registerTask('read review char', self.readReviewChar)
226 self.registerTask('pronounce review char', self.pronounceReviewChar)
227 self.registerCyclicInputTasks('cycle review char',
228 'read review char',
229 'pronounce review char')
230
231 self.registerTask('read review word', self.readReviewWord)
232 self.registerTask('spell review word', self.spellReviewWord)
233 self.registerTask('pronounce review word', self.pronounceReviewWord)
234 self.registerCyclicInputTasks('cycle review word',
235 'read review word',
236 'spell review word',
237 'pronounce review word')
238
239 self.registerTask('read text color', self.readTextColor)
240 self.registerTask('read text attributes', self.readTextAttributes)
241 self.registerCyclicInputTasks('cycle text attributes',
242 'read text color',
243 'read text attributes')
244
245 self.registerTask('where am i now', self.whereAmINow)
246 self.registerTask('where am i ancestors', self.whereAmIAncestors)
247 self.registerCyclicInputTasks('cycle where am i',
248 'where am i now',
249 'where am i ancestors')
250
251
252 self.chainTask('read review item', AEConstants.CHAIN_AFTER,
253 'review previous item', 'ReviewScript')
254 self.chainTask('read review item', AEConstants.CHAIN_AFTER,
255 'review current item', 'ReviewScript')
256 self.chainTask('read review item', AEConstants.CHAIN_AFTER,
257 'review next item', 'ReviewScript')
258
259 self.chainTask('read review word', AEConstants.CHAIN_AFTER,
260 'review previous word', 'ReviewScript')
261 self.chainTask('cycle review word', AEConstants.CHAIN_AFTER,
262 'review current word', 'ReviewScript')
263 self.chainTask('read review word', AEConstants.CHAIN_AFTER,
264 'review next word', 'ReviewScript')
265
266 self.chainTask('read review char', AEConstants.CHAIN_AFTER,
267 'review previous char', 'ReviewScript')
268 self.chainTask('cycle review char', AEConstants.CHAIN_AFTER,
269 'review current char', 'ReviewScript')
270 self.chainTask('read review char', AEConstants.CHAIN_AFTER,
271 'review next char', 'ReviewScript')
272
273 self.chainTask('read pointer to por', AEConstants.CHAIN_AFTER,
274 'pointer to por', 'ReviewScript')
275
276 self.chainTask('read review skip', AEConstants.CHAIN_AFTER,
277 'review skip report', 'ReviewScript')
278
279
280 kbd = AccessEngineAPI.getInputDevice(None, 'keyboard')
281 AccessEngineAPI.addInputModifiers(self, kbd, kbd.AEK_ALT_L,
282 kbd.AEK_SHIFT_L, kbd.AEK_ALT_R,
283 kbd.AEK_SHIFT_R, kbd.AEK_CAPS_LOCK)
284
285
286 self.registerCommand(kbd, 'stop now', _('stop now'),
287 True, [kbd.AEK_CONTROL_R])
288 self.registerCommand(kbd, 'stop now', _('stop now'),
289 True, [kbd.AEK_CONTROL_L])
290
291
292
293
294 pairs = [[kbd.AEK_ALT_L, kbd.AEK_SHIFT_L], [kbd.AEK_ALT_R, kbd.AEK_SHIFT_R]]
295
296
297 for pair in pairs:
298
299 self.registerCommand(kbd, 'increase speech rate',
300 _('increase speech rate'), False, pair+[kbd.AEK_PAGE_UP])
301 self.registerCommand(kbd, 'decrease speech rate',
302 _('decrease speech rate'), False, pair+[kbd.AEK_PAGE_DOWN])
303 self.registerCommand(kbd, 'read top',
304 _('read top'), False, pair+[kbd.AEK_T])
305 self.registerCommand(kbd, 'read description',
306 _('read description'), False, pair+[kbd.AEK_D])
307 self.registerCommand(kbd, 'read all',
308 _('read all'), False, pair+[kbd.AEK_DOWN])
309 self.registerCommand(kbd, 'cycle where am i',
310 _('cycle where am i'), False, pair+[kbd.AEK_QUESTION])
311 self.registerCommand(kbd, 'cycle text attributes',
312 _('cycle text attributes'), False, pair+[kbd.AEK_F])
313
314
315 self.resetLasts()
316
318 '''
319 Resets variables tracking the last container, caret position, tree level,
320 selection length, row/col offsets, and row/col count on
321 L{view change <AEEvent.ViewChange>}.
322
323 @param container: Last container name announced
324 @type container: string
325 '''
326 self.last_container = container
327 self.last_caret = AEPor(item_offset=0)
328 self.last_role = None
329 self.last_level = None
330 self.last_sel_len = 0
331 self.last_row = None
332 self.last_col = None
333 self.last_count = False
334
336 '''
337 Resets variables tracking some of the stateful information on a
338 L{focus change <AEEvent.FocusChange>}. Does not reset the last role or last
339 container announced.
340 '''
341 self.last_caret = AEPor(item_offset=0)
342 self.last_count = False
343 self.last_sel_len = 0
344 self.last_row = None
345 self.last_col = None
346 self.last_level = None
347
349 '''
350 Resets variables tracking whether an item is new or not for
351 L{readNewRole}, L{readNewLevel}, L{readNewLabel}, etc.
352 '''
353 self.last_role = None
354 self.last_count = False
355 self.last_sel_len = 0
356 self.last_row = None
357 self.last_col = None
358 self.last_level = None
359
361 '''
362 Provides the localized name of this L{AEScript <AEScript.AEScript>}.
363
364 @return: Human readable name of this script.
365 @rtype: string
366 '''
367 return _('Basic speech')
368
370 '''
371 Describe what this special L{AEScript} do.
372
373 @return: Human readable translated description of this script.
374 @rtype: string
375 '''
376 return _('Manages basic screen reader speech output. Requires a loaded '
377 'speech device.')
378
406
407
408
409
411 '''
412 Task to stop speech immediately, ignoring the value of the Stopping setting.
413
414 @param kwargs: Arbitrary keyword arguments to pass to the task
415 @type kwargs: dictionary
416 '''
417 AccessEngineAPI.stopAll(self)
418 try:
419 self.unregisterTimerTask('read all continued')
420 except KeyError:
421 pass
422
424 '''
425 Increase the speech rate. The maximum rate is announced when reached.
426 @see: L{decreaseRate}
427
428 @param kwargs: Arbitrary keyword arguments to pass to the task
429 @type kwargs: dictionary
430 '''
431 AccessEngineAPI.stopNow(self, cap='audio', role='output', **kwargs)
432 rate = AccessEngineAPI.getStyleSetting(self, 'Rate', **kwargs)
433
434 step = (abs(rate.max)+abs(rate.min))/30
435 if rate.value+step < rate.max:
436 rate.value += step
437 AccessEngineAPI.sayInfo(self, text=rate.value, template=_('rate %d'),
438 cap='audio', role='output', **kwargs)
439 else:
440 AccessEngineAPI.sayInfo(self, text=_('maximum rate'),
441 cap='audio', role='output', **kwargs)
442
444 '''
445 Decrease the speech rate. The minimum rate is announced when reached.
446 @see: L{increaseRate}
447
448 @param kwargs: Arbitrary keyword arguments to pass to the task
449 @type kwargs: dictionary
450 '''
451 AccessEngineAPI.stopNow(self, cap='audio', role='output', **kwargs)
452 rate = AccessEngineAPI.getStyleSetting(self, 'Rate', **kwargs)
453
454 step = (abs(rate.max)+abs(rate.min))/30
455 if rate.value-step > rate.min:
456 rate.value -= step
457 AccessEngineAPI.sayInfo(self, text=rate.value, template=_('rate %d'),
458 cap='audio', role='output', **kwargs)
459 else:
460 AccessEngineAPI.sayInfo(self, text=_('minimum rate'),
461 cap='audio', role='output', **kwargs)
462
464 '''
465 Reads the details of the item at the pointer or speaks an informational
466 message about why the item cannot be read.
467
468 @param kwargs: Arbitrary keyword arguments to pass to the task
469 @type kwargs: dictionary
470 '''
471 result = self.getTempData('review')
472 if not self.getTempData('has skipped'):
473
474
475 AccessEngineAPI.mayStop(self, cap='audio', role='output', **kwargs)
476 if result == AEConstants.REVIEW_OK:
477 kwargs['por'] = self.getVirtualPOR()
478 kwargs['text'] = AccessEngineAPI.getItemText(kwargs['por'])
479 self.doTask('read item details', **kwargs)
480 elif result == AEConstants.REVIEW_NO_NEXT_ITEM:
481 kwargs['por'] = self.getVirtualPOR()
482 kwargs['text'] =_('last item')
483 AccessEngineAPI.sayInfo(self, cap='audio', role='output', **kwargs)
484 elif result == AEConstants.REVIEW_NO_PREV_ITEM:
485 kwargs['por'] = self.getVirtualPOR()
486 kwargs['text'] = _('first item')
487 AccessEngineAPI.sayInfo(self, cap='audio', role='output', **kwargs)
488
490 '''
491 Reads a summary of an item skipped during review.
492
493 @param kwargs: Arbitrary keyword arguments to pass to the task
494 @type kwargs: dictionary
495 '''
496 if not self.getTempData('has skipped'):
497
498 AccessEngineAPI.stopNow(self, cap='audio', role='output',
499 reset=False, **kwargs)
500 self.doTask('read new role', **kwargs)
501
503 '''
504 Reads the word at the pointer or speaks an informational message about why
505 the word cannot be read.
506
507 @param text: Text to say, or C{None} to use the pointer L{AEPor} by default
508 @type text: string
509 @param kwargs: Arbitrary keyword arguments to pass to the task
510 @type kwargs: dictionary
511 '''
512 result = self.getTempData('review')
513 AccessEngineAPI.stopNow(self, cap='audio', role='output', **kwargs)
514
515
516 kwargs['por'] = self.getVirtualPOR()
517 if (result is None or result == AEConstants.REVIEW_OK or
518 result == AEConstants.REVIEW_WRAP):
519 AccessEngineAPI.sayWord(self, text=text,
520 cap='audio', role='output', **kwargs)
521 elif result == AEConstants.REVIEW_NO_NEXT_ITEM:
522 AccessEngineAPI.sayInfo(self, text=_('last item'),
523 cap='audio', role='output', **kwargs)
524 elif result == AEConstants.REVIEW_NO_PREV_ITEM:
525 AccessEngineAPI.sayInfo(self, text=_('first item'),
526 cap='audio', role='output', **kwargs)
527 elif result == AEConstants.REVIEW_NO_NEXT_WORD:
528 AccessEngineAPI.sayInfo(self, text=_('last word'),
529 cap='audio', role='output', **kwargs)
530 elif result == AEConstants.REVIEW_NO_PREV_WORD:
531 AccessEngineAPI.sayInfo(self, text=_('first word'),
532 cap='audio', role='output', **kwargs)
533
535 '''
536 Reads the character at the pointer or speaks an informational message about
537 why the item cannot be read.
538
539 @param kwargs: Arbitrary keyword arguments to pass to the task
540 @type kwargs: dictionary
541 '''
542 result = self.getTempData('review')
543 AccessEngineAPI.stopNow(self, cap='audio', role='output', **kwargs)
544 kwargs['por'] = self.getVirtualPOR()
545 if result == AEConstants.REVIEW_OK or result == AEConstants.REVIEW_WRAP:
546 AccessEngineAPI.sayChar(self, cap='audio', role='output', **kwargs)
547 elif result == AEConstants.REVIEW_NO_NEXT_ITEM:
548 AccessEngineAPI.sayInfo(self, text=_('last item'),
549 cap='audio', role='output', **kwargs)
550 elif result == AEConstants.REVIEW_NO_PREV_ITEM:
551 AccessEngineAPI.sayInfo(self, text=_('first item'),
552 cap='audio', role='output', **kwargs)
553 elif result == AEConstants.REVIEW_NO_NEXT_CHAR:
554 AccessEngineAPI.sayInfo(self, text=_('last char'),
555 cap='audio', role='output', **kwargs)
556 elif result == AEConstants.REVIEW_NO_PREV_CHAR:
557 AccessEngineAPI.sayInfo(self, text=_('first char'),
558 cap='audio', role='output', **kwargs)
559
561 '''
562 Spells the word at the current L{AEPor}. Says translated names for known
563 symbols and punctuation. Says Unicode values for unknown characters.
564
565 @param text: Text to say, or C{None} to use the pointer L{AEPor} by default
566 @type text: string
567 @param kwargs: Arbitrary keyword arguments to pass to the task
568 @type kwargs: dictionary
569 '''
570 sem = AEConstants.SEM_WORD
571
572 sf = AccessEngineAPI.getStyleSetting(self, 'SpellFormat', sem=sem, **kwargs)
573
574 old = sf.value
575 sf.value = AEConstants.FORMAT_SPELL
576
577 kwargs['por'] = self.getVirtualPOR()
578 AccessEngineAPI.stopNow(self, cap='audio', role='output', **kwargs)
579 AccessEngineAPI.sayWord(self, text=text,
580 cap='audio', role='output', **kwargs)
581
582 sf.value = old
583
585 '''
586 Spells the word at the current L{AEPor} using the phonetic alphabet and
587 names for known symbols and punctuation. Says Unicode values and
588 classifications for unknown characters.
589
590 @param text: Text to say, or C{None} to use the pointer L{AEPor} by default
591 @type text: string
592 @param kwargs: Arbitrary keyword arguments to pass to the task
593 @type kwargs: dictionary
594 '''
595 sem = AEConstants.SEM_WORD
596
597 sf = AccessEngineAPI.getStyleSetting(self, 'SpellFormat', sem=sem, **kwargs)
598
599 old = sf.value
600 sf.value = AEConstants.FORMAT_PHONETIC
601
602 kwargs['por'] = self.getVirtualPOR()
603 AccessEngineAPI.stopNow(self, cap='audio', role='output', **kwargs)
604 AccessEngineAPI.sayWord(self, text=text,
605 cap='audio', role='output', **kwargs)
606
607 sf.value = old
608
630
632 '''
633 Reports the new location of the pointer L{AEPor} when it snaps back to the
634 application focus, selection, and caret position.
635
636 @param por: Point of regard for the related accessible
637 @type por: L{AEPor}
638 @param kwargs: Arbitrary keyword arguments to pass to the task
639 @type kwargs: dictionary
640 '''
641 self.doTask('read focus', gained = True, por = self.getVirtualPOR(),
642 **kwargs)
643 self.doTask('read item details', por = por, **kwargs)
644
646 '''
647 Reports all control and container names in child-to-parent order from the
648 current L{AEPor} to the root accessible.
649
650 @param kwargs: Arbitrary keyword arguments to pass to the task
651 @type kwargs: dictionary
652 '''
653
654 self.resetLastsForNew()
655 for curr in AccessEngineAPI.iterAncestorAccs(kwargs['por']):
656
657 kwargs['por'] = curr
658 AccessEngineAPI.sayRole(self, cap='audio', role='output', **kwargs)
659 AccessEngineAPI.sayItem(self, cap='audio', role='output', **kwargs)
660
662 '''
663 Reports the details of the current L{AEPor}.
664
665 @param kwargs: Arbitrary keyword arguments to pass to the task
666 @type kwargs: dictionary
667 '''
668
669 self.resetLastsForNew()
670
671 r, i, h = (self.state.RoleVerbosity, self.state.IndexVerbosity,
672 self.state.HeaderVerbosity)
673 self.state.RoleVerbosity = SPEAK_ALWAYS
674 self.state.IndexVerbosity = SPEAK_ALWAYS
675 self.state.HeaderVerbosity = SPEAK_ALWAYS
676
677 self.doTask('read item details', **kwargs)
678 self.state.RoleVerbosity = r
679 self.state.IndexVerbosity = i
680 self.state.HeaderVerbosity = h
681
682 - def readItemText(self, text, **kwargs):
683 '''
684 Reads the text of the current item.
685
686 @param text: Text to say
687 @type text: string
688 @param kwargs: Arbitrary keyword arguments to pass to the task
689 @type kwargs: dictionary
690 '''
691
692
693 label = AccessEngineAPI.getAccLabel(kwargs['por'])
694 if (label != text and text != ''):
695 AccessEngineAPI.sayItem(self, text=text,
696 cap='audio', role='output', **kwargs)
697
699 '''
700 Speaks the row and column indices of the given L{AEPor}. Also speaks the
701 total number of items if it has not been announced previously.
702
703 @param por: Point of regard to an object possibly having an index of
704 interest
705 @type por: L{AEPor}
706 @param kwargs: Arbitrary keyword arguments to pass to the task
707 @type kwargs: dictionary
708 '''
709 iv = self.getScriptSettingVal('IndexVerbosity')
710 if iv == SPEAK_NEVER:
711
712 return
713
714
715 exts = AccessEngineAPI.getAccTableExtents(por)
716 if exts is not None:
717
718 row = AccessEngineAPI.getAccRow(por)
719 if row is None:
720
721 return
722
723 row += 1
724 rows, cols = exts
725 else:
726
727 row = AccessEngineAPI.getAccPosInSet(por)
728 rows = AccessEngineAPI.getAccSetSize(por)
729 if row is None or rows is None:
730
731 return
732 cols = 0
733
734 if cols <= 1:
735
736 if rows is not None and (iv == SPEAK_ALWAYS or not self.last_count):
737 text = (row, rows)
738 template = _('item %d of %d')
739 else:
740 text = row+1
741 template = _('item %d')
742 else:
743
744 col = AccessEngineAPI.getAccColumn(por)
745 if iv == SPEAK_ALWAYS or not self.last_count:
746 try:
747 text = (row, col+1, rows, cols)
748 except TypeError:
749
750 return
751 template = _('item %d, %d, of %d, %d')
752 else:
753 try:
754 text = (row, col+1)
755
756 except TypeError:
757
758 return
759 template = _('item %d, %d')
760
761 AccessEngineAPI.sayIndex(self, text=text, template=template, por=por,
762 cap='audio', role='output', **kwargs)
763 self.last_count = True
764
766 '''
767 Reads the row/column headers if they differ from the last ones announced.
768 Speaks the row and column headers of the given L{AEPor}. Only makes an
769 announcement if the last row/col encountered by this task or this
770 readNewHeaders task is different from the current.
771
772 @param por: Point of regard to a table cell possibly having a header
773 @type por: L{AEPor}
774 @param kwargs: Arbitrary keyword arguments to pass to the task
775 @type kwargs: dictionary
776 '''
777 hv = self.getScriptSettingVal('HeaderVerbosity')
778 if hv == SPEAK_NEVER:
779
780 return
781
782 row = AccessEngineAPI.getAccRow(por)
783 if row is not None and (hv == SPEAK_ALWAYS or row != self.last_row):
784
785 header = AccessEngineAPI.getAccRowHeader(por)
786 if header:
787 AccessEngineAPI.saySection(self, text=header, template=_('row %s'),
788 por=por, cap='audio', role='output', **kwargs)
789 self.last_row = row
790
791 col = AccessEngineAPI.getAccColumn(por)
792 if col is not None and (hv == SPEAK_ALWAYS or col != self.last_col):
793
794 header = AccessEngineAPI.getAccColumnHeader(por)
795 if header:
796 AccessEngineAPI.saySection(self, text=header, template=_('column %s'),
797 por=por, cap='audio', role='output', **kwargs)
798 self.last_col = col
799
800 - def readNewRole(self, por=None, role=None, say_role=True, **kwargs):
801 '''
802 Speaks the role of the given L{AEPor} if it is different from the last role
803 read. Depends on the RoleVerbosity setting. Does an explicit check for the
804 identity of the task invoking this one to account for double role
805 announcements from both focus and selector events.
806
807 @param por: Point of regard to the accessible whose role should be said
808 @type por: L{AEPor}
809 @param role: Translated role name to speak. Prefers using this if available
810 over fetching the string from the L{AEPor}
811 @type role: string
812 @param say_role: Recommendation based on data other than the L{last_role}
813 to take into account when the role verbosity is set to always so that
814 we don't double announce roles.
815 @type say_role: boolean
816 @param kwargs: Arbitrary keyword arguments to pass to the task
817 @type kwargs: dictionary
818 '''
819
820 val = self.getScriptSettingVal('RoleVerbosity')
821 if val == SPEAK_NEVER:
822
823 return
824 if role is None:
825
826 role = AccessEngineAPI.getAccRoleName(por)
827
828 if (val == SPEAK_ALWAYS and say_role) or self.last_role != role:
829
830
831 kwargs['text'] = role
832 AccessEngineAPI.sayRole(self, por=por,
833 cap='audio', role='output', **kwargs)
834 self.last_role = role
835
837 '''
838 Reads the tree level of a widget if it's different from the last level read.
839 Speaks the level of the given L{AEPor} only if it is different than the last
840 level spoken by this method.
841
842 @param por: Point of regard to the accessible whose role should be said
843 @type por: L{AEPor}
844 @param kwargs: Arbitrary keyword arguments to pass to the task
845 @type kwargs: dictionary
846 '''
847 level = AccessEngineAPI.getAccLevel(por)
848 if level is not None and level != self.last_level:
849
850 AccessEngineAPI.sayLevel(self, text=level+1, template=_('level %d'),
851 por=por, cap='audio', role='output', **kwargs)
852 self.last_level = level
853
855 '''
856 Read the label on a widget if it is different from the item text of the
857 widget and the last container label read.
858 Speaks the text of a new label at the given L{AEPor} only if it is different
859 than the item text and the last container announced by L{readNewContainer}.
860
861 @param por: Point of regard to an accessible that should be announced if it
862 is a new label
863 @type por: L{AEPor}
864 @param kwargs: Arbitrary keyword arguments to pass to the task
865 @type kwargs: dictionary
866 '''
867 label = AccessEngineAPI.getAccLabel(por)
868 if (label and label != self.last_container):
869 AccessEngineAPI.sayLabel(self, text=label, por=por,
870 cap='audio', role='output', **kwargs)
871
873 '''
874 Speaks the first interesting ancestor of the given L{AEPor} if it is
875 different from the last interesting ancestor announced.
876
877 @param por: Point of regard to an accessible that should be announced if it
878 is a new menu.
879 @type por: L{AEPor}
880 @param kwargs: Arbitrary keyword arguments to pass to the task
881 @type kwargs: dictionary
882 '''
883
884 root = AccessEngineAPI.getRootAcc(por)
885 for anc in AccessEngineAPI.iterAncestorAccs(por, allow_trivial=True):
886
887 if AccessEngineAPI.hasAccRole('application', anc):
888 break
889 to_check = [anc]
890
891 prev = AccessEngineAPI.getPrevPeerAcc(anc)
892 if (prev and AccessEngineAPI.hasAccRole('label', prev) and
893 not AccessEngineAPI.getAccRelations('label for', prev)):
894 to_check.append(prev)
895 last = self.last_container
896 for acc in to_check:
897 name_text = AccessEngineAPI.getAccName(acc)
898 label_text = AccessEngineAPI.getAccLabel(acc)
899 if (((name_text and last.startswith(name_text)) or
900 (label_text and last.startswith(label_text)) or
901 AccessEngineAPI.hasAccRole('menu bar', acc))):
902 return
903 if name_text and name_text.strip():
904
905 AccessEngineAPI.saySection(self, text=name_text, por=por,
906 cap='audio', role='output', **kwargs)
907 self.last_container = name_text
908 return
909 if label_text and label_text.strip():
910
911 AccessEngineAPI.saySection(self, text=label_text, por=por,
912 cap='audio', role='output', **kwargs)
913 self.last_container = label_text
914 return
915
917 '''
918 Makes an arbitrary announcement using the given semantic for styling.
919
920 @param text: Text to announce
921 @type text: string
922 @param sem: Semantic of the announcement. Defaults to info.
923 @type sem: integer
924 @param kwargs: Arbitrary keyword arguments to pass to the task
925 @type kwargs: dictionary
926 '''
927 AccessEngineAPI.say(self, cap='audio', role='output', text=text,
928 layer=kwargs['layer'], por=kwargs['por'], sem=sem)
929
930 - def readAll(self, interval=None, **kwargs):
931 '''
932 Read the remaining contents of the active view starting at the current
933 point of regard.
934
935 @param interval: Interval in seconds on which the timer fires.
936 @type interval: integer
937 @param kwargs: Arbitrary keyword arguments to pass to the task
938 @type kwargs: dictionary
939 '''
940 self.start_por = None
941 self.continue_por = None
942 AccessEngineAPI.mayStop(self, cap='audio', role='output', **kwargs)
943 self.registerTimerTask('read all continued', 1000, self._continueReadAll)
944
945 self.doTask('review current item', 'ReviewScript', **kwargs)
946 self.doTask('read all continued', **kwargs)
947
949 '''
950 Continue queuing up contents in the view on a timed interval.
951
952 @param kwargs: Arbitrary keyword arguments to pass to the task
953 @type kwargs: dictionary
954
955 @note: if index markers are supported, update the L{start_por} accordingly
956 so a stop leaves the user near the last reviewed location, or maybe this is
957 an option?
958 '''
959
960 por_text = self.start_por
961 self.start_por = self.start_por or self.getVirtualPOR()
962
963 self.layer = AEConstants.LAYER_FOCUS
964 if self.getVirtualPOR() == self.start_por:
965 temp_por = self.continue_por or self.getVirtualPOR()
966 self.setVirtualPOR(temp_por)
967
968 for i in xrange(5):
969
970
971 self.setTempData('has skipped', True)
972
973 self.doTask('review next item', 'ReviewScript',
974 chain=False, **kwargs)
975
976 self.doTask('read review item', chain=False, stop=False, **kwargs)
977 if self.getTempData('review') == AEConstants.REVIEW_NO_NEXT_ITEM:
978
979 self.unregisterTimerTask(kwargs['task_name'])
980 break
981 else:
982
983 self.unregisterTimerTask(kwargs['task_name'])
984
985 self.continue_por = self.getVirtualPOR()
986
987 self.setVirtualPOR(self.start_por)
988
990 '''
991 Read top of the active view, typically the title of the foreground window.
992
993 @param kwargs: Arbitrary keyword arguments to pass to the task
994 @type kwargs: dictionary
995 '''
996 AccessEngineAPI.stopNow(self, cap='audio', role='output', **kwargs)
997 AccessEngineAPI.sayWindow(self, cap='audio', role='output', **kwargs)
998
1000 '''
1001 Reusable method for getting default and current text attributes and
1002 merging them into one dictionary.
1003
1004 @param kwargs: Arbitrary keyword arguments to pass to the task
1005 @type kwargs: dictionary
1006 @return: Dictionary of name/value attribute pairs
1007 @rtype: dictionary
1008 '''
1009 attrs = AccessEngineAPI.getAccAllTextAttrs(kwargs['por'])
1010 dattrs = AccessEngineAPI.getAccDefTextAttrs(kwargs['por'])
1011
1012 if not attrs and not dattrs:
1013
1014 AccessEngineAPI.sayInfo(self, text=_('text attributes unavailable'),
1015 cap='audio', role='output', **kwargs)
1016 return None
1017 elif not attrs:
1018
1019 attrs = dattrs
1020 elif not dattrs:
1021
1022 pass
1023 else:
1024
1025 dattrs.update(attrs)
1026 attrs = dattrs
1027 return attrs
1028
1029 - def readTextColor(self, **kwargs):
1030 '''
1031 Read the text foreground and background color names followed by their values.
1032
1033 @param kwargs: Arbitrary keyword arguments to pass to the task
1034 @type kwargs: dictionary
1035 '''
1036 AccessEngineAPI.stopNow(self, cap='audio', role='output', **kwargs)
1037
1038 attrs = self._getAttrs(**kwargs)
1039 if attrs is None:
1040 return
1041
1042
1043 fgval = attrs.get('fg-color')
1044 fgname = AccessEngineAPI.getColorString(fgval) or _('default foreground')
1045
1046
1047 bgval = attrs.get('bg-color')
1048 bgname = AccessEngineAPI.getColorString(bgval) or _('default background')
1049
1050
1051 template = _('%s on %s.')
1052 names = template % (fgname, bgname)
1053 if bgval is None and fgval is None:
1054 AccessEngineAPI.sayColor(self, text=names,
1055 cap='audio', role='output', **kargs)
1056 elif bgval is not None and fgval is not None:
1057 AccessEngineAPI.sayColor(self, text=(fgval, bgval),
1058 template=names + ' ' + template, cap='audio',
1059 role='output', **kwargs)
1060 elif fgval is not None:
1061 AccessEngineAPI.sayColor(self, text=fgval, template=names + ' %s',
1062 cap='audio', role='output', **kwargs)
1063 elif bgval is not None:
1064 AccessEngineAPI.sayColor(self, text=bgval, template=names + ' %s',
1065 cap='audio', role='output', **kwargs)
1066
1067 - def readTextAttributes(self, **kwargs):
1068 '''
1069 Read all remaining text attribute name/value pairs sans color information.
1070
1071 @param kwargs: Arbitrary keyword arguments to pass to the task
1072 @type kwargs: dictionary
1073 '''
1074 AccessEngineAPI.stopNow(self, cap='audio', role='output', **kwargs)
1075 attrs = self._getAttrs(**kwargs)
1076 for name, value in attrs.items():
1077 if not name.endswith('color'):
1078 AccessEngineAPI.sayTextAttrs(self, text=(name, value), template='%s: %s.',
1079 cap='audio', role='output', **kwargs)
1080
1082 '''
1083 Read the description of the accessible at the current point of regard.
1084
1085 @param kwargs: Arbitrary keyword arguments to pass to the task
1086 @type kwargs: dictionary
1087 '''
1088 AccessEngineAPI.stopNow(self, cap='audio', role='output', **kwargs)
1089 AccessEngineAPI.sayDesc(self, cap='audio', role='output', **kwargs)
1090
1092 '''
1093 Reads all details of the current item.
1094
1095 @param text: Item text to use in the announcement
1096 @type text: string
1097 @param say_role: Recommendation about whether the role should be announced
1098 or not purely based on the type of event received
1099 @type say_role: boolean
1100 @param kwargs: Arbitrary keyword arguments to pass to the task
1101 @type kwargs: dictionary
1102 '''
1103 if text is None:
1104 text = AccessEngineAPI.getItemText(kwargs['por'])
1105
1106
1107 self.doTask('read new role', say_role=say_role, **kwargs)
1108
1109 self.doTask('read item text', text=text, **kwargs)
1110
1111 self.doTask('read new headers', **kwargs)
1112
1113 AccessEngineAPI.sayState(self, cap='audio', role='output', **kwargs)
1114
1115 self.doTask('read new level', **kwargs)
1116
1117 self.doTask('read item index', **kwargs)
1118
1119
1120
1121 AccessEngineAPI.sayHotkey(self, cap='audio', role='output', **kwargs)
1122 if not text:
1123
1124 AccessEngineAPI.sayValue(self, cap='audio', role='output', **kwargs)
1125
1126 AccessEngineAPI.sayValueExtents(self, template=_('%.1f to %.1f by %.1f'),
1127 cap='audio', role='output', **kwargs)
1128
1129
1130
1131
1133 '''
1134 Announces the title of the first view to be activated, but without stopping
1135 previous speech which is likely to be the SUE welcome message. Inhibits the
1136 next stop to avoid never announcing the title because of another immediate
1137 event following this one (i.e. focus).
1138
1139 @param kwargs: Arbitrary keyword arguments to pass to the task
1140 @type kwargs: dictionary
1141 @return: C{True} to allow other tasks to process this event.
1142 @rtype: boolean
1143 '''
1144
1145 if kwargs['title'] != '':
1146 AccessEngineAPI.sayWindow(self, cap='audio', role='output', **kwargs)
1147
1148 self.resetLasts(kwargs['title'])
1149
1150 self.state.activewincnt += 1
1151 return True
1152
1185
1187 '''
1188 Decrement the active window count. If no active window when make an
1189 announcement.
1190
1191 @param kwargs: Arbitrary keyword arguments to pass to the task
1192 @type kwargs: dictionary
1193 @return: C{True} to allow other tasks to process this event.
1194 @rtype: boolean
1195
1196 @todo: disabled announcement for the time being
1197 '''
1198
1199 self.state.activewincnt -= 1
1200
1201 if self.state.activewincnt == 0:
1202 self.registerTimerTask('in view timer', 1500, self.inViewTimer)
1203 return True
1204
1206 '''
1207 Executes some time after a View lost event to check for an active window.
1208 Makes an announcement if no window is active.
1209
1210 @param kwargs: Arbitrary keyword arguments to pass to the task
1211 @type kwargs: dictionary
1212 '''
1213
1214 if self.state.activewincnt == 0:
1215 AccessEngineAPI.stopNow(self, cap='audio', role='output', **kwargs)
1216 AccessEngineAPI.sayInfo(self, text=_('no active window'),
1217 cap='audio', role='output', **kwargs)
1218 self.unregisterTimerTask(kwargs['task_name'])
1219
1220
1221
1222
1224 '''
1225 Annouces the label and role of the newly focused widget. The role is output
1226 only if it is different from the last one announced.
1227
1228 @param kwargs: Arbitrary keyword arguments to pass to the task
1229 @type kwargs: dictionary
1230 @return: C{True} to allow other tasks to process this event.
1231 @rtype: boolean
1232 '''
1233 self.setVirtualPOR(kwargs['por'])
1234 AccessEngineAPI.mayStop(self, cap='audio', role='output', **kwargs)
1235
1236 self.doTask('read new container', **kwargs)
1237
1238
1239 self.doTask('read new label', **kwargs)
1240
1241 self.doTask('read new role', **kwargs)
1242
1243 AccessEngineAPI.inhibitMayStop()
1244
1245 self.resetLastsOnFocus()
1246
1247
1248 self.caret_changed = False
1249
1250
1251 return True
1252
1253
1254
1255
1257 '''
1258 Announces the text of the active item.
1259
1260 @param kwargs: Arbitrary keyword arguments to pass to the task
1261 @type kwargs: dictionary
1262 @return: C{True} to allow other tasks to process this event.
1263 @rtype: boolean
1264 '''
1265 AccessEngineAPI.mayStop(self, cap='audio', role='output', **kwargs)
1266 say_role = not isinstance(AccessEngine.AEEventManager.last_event,
1267 AEEvent.FocusChange)
1268 self.doTask('read item details', say_role=say_role, **kwargs)
1269
1270 - def onSelectorText(self, text, **kwargs):
1271 '''
1272 Announces the localized word selected or unselected followed by the total
1273 number of characters in the selection.
1274
1275 Single line text boxes fire selection events followed by a caret event.
1276 Multiline text boxes fire events in the opposite order. The
1277 L{Output.inhibitMayStop <AccessEngineAPI.Output.inhibitMayStop>} allows
1278 the selection event to complete for single line text boxes before the caret
1279 event stops it with a call to
1280 L{Output.mayStop <AccessEngineAPI.Output.mayStop>}.
1281
1282 @param kwargs: Arbitrary data given by the observer.
1283 @type kwargs: dictionary
1284 '''
1285 if AccessEngineAPI.getAccCaret(kwargs['por']) != self.tier.getPointer():
1286
1287
1288
1289 AccessEngineAPI.inhibitMayStop()
1290 n = len(text)
1291 if n >= self.last_sel_len:
1292 AccessEngineAPI.sayTextAttrs(self, text=n, template=_('selected %d'),
1293 cap='audio', role='output', **kwargs)
1294 elif self.last_sel_len != 0:
1295
1296 AccessEngineAPI.sayTextAttrs(self, text=(self.last_sel_len-n, n),\
1297 template=_('unselected %d, %d remaining'),
1298 cap='audio', role='output', **kwargs)
1299 self.last_sel_len = n
1300
1301
1302
1303
1305 '''
1306 Stores the point of regard to the caret event location. Tries to stop
1307 current output.
1308
1309 @param kwargs: Arbitrary keyword arguments to pass to the task
1310 @type kwargs: dictionary
1311 @return: C{True} to allow other tasks to process this event.
1312 @rtype: boolean
1313 '''
1314
1315 self.setVirtualPOR(kwargs['por'])
1316 rv = AEScript.EventScript.onCaretChange(self, **kwargs)
1317
1318 self.last_caret = kwargs['por']
1319 return rv
1320
1322 '''
1323 Announces the inserted text.
1324
1325 @param kwargs: Arbitrary keyword arguments to pass to the task
1326 @type kwargs: dictionary
1327 @return: C{True} to allow other tasks to process this event.
1328 @rtype: boolean
1329 '''
1330 text = kwargs['text']
1331 self.caret_changed = True
1332 if len(text) == 1:
1333 w = (self.state.WordEcho and unicodedata.category(text)[0] in ('C', 'Z'))
1334 c = self.state.CharEcho
1335 if w and c:
1336
1337
1338 AccessEngineAPI.mayStop(self, cap='audio', role='output', **kwargs)
1339 AccessEngineAPI.sayChar(self, cap='audio', role='output', **kwargs)
1340
1341 word_text= AccessEngineAPI.getWordText(kwargs['por'])
1342 if word_text.strip():
1343 AccessEngineAPI.say(self, text=word_text, layer=AEConstants.LAYER_TIER,
1344 cap='audio', role='output', por=kwargs['por'],
1345 sem=AEConstants.SEM_WORD)
1346 elif w:
1347
1348 AccessEngineAPI.mayStop(self, cap='audio', role='output', **kwargs)
1349 AccessEngineAPI.sayWord(self, cap='audio', role='output', **kwargs)
1350 elif c:
1351
1352 AccessEngineAPI.mayStop(self, cap='audio', role='output', **kwargs)
1353 AccessEngineAPI.sayChar(self, cap='audio', role='output', **kwargs)
1354 else:
1355
1356
1357 if len(text) > self.state.CharLimit:
1358 template=_('%d characters inserted')
1359 AccessEngineAPI.mayStop(self, cap='audio', role='output', **kwargs)
1360 kwargs['text'] = len(text)
1361 AccessEngineAPI.sayInfo(self, template=template,
1362 cap='audio', role='output', **kwargs)
1363 else:
1364 AccessEngineAPI.mayStop(self, cap='audio', role='output', **kwargs)
1365 AccessEngineAPI.sayItem(self, cap='audio', role='output', **kwargs)
1366 return True
1367
1369 '''
1370 Announces the text passed in the caret movement.
1371
1372 @param kwargs: Arbitrary keyword arguments to pass to the task
1373 @type kwargs: dictionary
1374 @return: C{True} to allow other tasks to process this event.
1375 @rtype: boolean
1376
1377 @note: MW: With caret movement LSR used to speak the string between the old
1378 and the new caret position. This behaviour is not consistent with braille
1379 output and confuses a lot. Therefore, this was changed so we'll always speak
1380 the character after the caret position which is what other screen reader do,
1381 too. More functionality is provided by the review keys.
1382 '''
1383
1384 if self.caret_changed:
1385 self.caret_changed = False
1386 return
1387
1388 if not kwargs['por'].isSameAcc(self.last_caret):
1389
1390
1391 tv = self.getScriptSettingVal('TextVerbosity')
1392 if tv == SPEAK_NEVER:
1393 return
1394 elif tv == SPEAK_LINE:
1395 AccessEngineAPI.mayStop(self, cap='audio', role='output', **kwargs)
1396 AccessEngineAPI.sayItem(self, cap='audio', role='output', **kwargs)
1397 if (AccessEngineAPI.hasAccState('multi line', kwargs['por']) and
1398 AccessEngineAPI.getAccTextSelectionCount(kwargs['por']) > 0):
1399
1400
1401
1402 kwargs['text'] = len(AccessEngineAPI.getAccTextSelection(kwargs['por'])[0])
1403 AccessEngineAPI.sayTextAttrs(self, template=_('selected %s'),
1404 cap='audio', role='output', **kwargs)
1405 else:
1406
1407 self.doTask('read all', **kwargs)
1408 elif not kwargs['por'].isSameItem(self.last_caret):
1409
1410 AccessEngineAPI.mayStop(self, cap='audio', role='output', **kwargs)
1411 AccessEngineAPI.sayItem(self, cap='audio', role='output', **kwargs)
1412 else:
1413
1414
1415 text = kwargs['text']
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438 if (kwargs['por'].char_offset+1) <= len(text):
1439 char = text[kwargs['por'].char_offset]
1440 AccessEngineAPI.mayStop(self, cap='audio', role='output', **kwargs)
1441 kwargs['text'] = char
1442 AccessEngineAPI.sayChar(self, cap='audio', role='output', **kwargs)
1443 self.may_stop = 0
1444 return True
1445
1447 '''
1448 Announces the deleted text. Prepends delete and backspace text to indicate
1449 which way the caret moved.
1450
1451 @param kwargs: Arbitrary keyword arguments to pass to the task
1452 @type kwargs: dictionary
1453 '''
1454 AccessEngineAPI.mayStop(self, cap='audio', role='output', **kwargs)
1455 if (self.last_caret.item_offset +
1456 self.last_caret.char_offset) > kwargs['text_offset']:
1457
1458 AccessEngineAPI.sayItem(self, template=_('backspace %s'),
1459 cap='audio', role='output', **kwargs)
1460 self.changed = True
1461 else:
1462
1463 AccessEngineAPI.sayItem(self, template=_('delete %s'),
1464 cap='audio', role='output', **kwargs)
1465 self.changed = False
1466
1468 '''
1469 Updates the last caret cache with the current L{AEPor}, but only if the
1470 temp value of 'no update' has not been set to C{True}.
1471
1472 @param por: Point of regard where the caret event occurred
1473 @type por: L{AEPor}
1474 @param text: The text inserted, deleted or the line of the caret
1475 @type text: string
1476 @param text_offset: The offset of the inserted/deleted text or the line
1477 offset when movement only
1478 @type text_offset: integer
1479 @param added: C{True} when text added, c{False} when text deleted,
1480 and C{None} when event is for caret movement only
1481 @type added: boolean
1482 @param kwargs: Arbitrary keyword arguments to pass to the task
1483 @type kwargs: dictionary
1484 '''
1485 if self.getTempData('no update'):
1486 return
1487
1488 self.last_caret = por
1489 self.caret_changed = False
1490
1491
1492
1493
1495 '''
1496 Announces state changes according to the ones of interest defined by
1497 L{View.getStateText <AccessEngineAPI.View.getStateText>}.
1498
1499 @param kwargs: Arbitrary keyword arguments to pass to the task
1500 @type kwargs: dictionary
1501 '''
1502 text = AccessEngineAPI.getStateText(kwargs['por'], kwargs['name'],
1503 kwargs['value'])
1504 if text:
1505
1506 AccessEngineAPI.mayStop(self, cap='audio', role='output', **kwargs)
1507 AccessEngineAPI.sayState(self, text=text,
1508 cap='audio', role='output', **kwargs)
1509
1510
1511
1512
1514 '''
1515 Announces value changes.
1516
1517 @param kwargs: Arbitrary keyword arguments to pass to the task
1518 @type kwargs: dictionary
1519 @return: C{True} to allow other tasks to process this event.
1520 @rtype: boolean
1521 '''
1522 if kwargs['name'] == 'value':
1523 if not AccessEngineAPI.getItemText(kwargs['por']):
1524
1525
1526 AccessEngineAPI.mayStop(self, cap='audio', role='output', **kwargs)
1527 AccessEngineAPI.sayItem(self, text=value,
1528 cap='audio', role='output', **kwargs)
1529 return True
1530
1553
1590