1 '''
2 Defines a special L{AEScript <AEScript.AEScript>} for the Calculator GCalc.
3 Reports buttons and their hotkeys as they are pressed. Reads the equation bar
4 intelligently.
5
6 @author: Joel Feiner
7 @author: Peter Parente
8 @organization: UNC Chapel Hill
9 @copyright: Copyright (c) 2007, Joel Feiner
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 is available at
20 U{http://www.opensource.org/licenses/bsd-license.php}.
21
22 @see: U{http://www.unc.edu/campus/policies/copyright.html} Section 5.D.2.A for
23 UNC copyright rules.
24 '''
25
26 from AccessEngine import AEScript, AccessEngineAPI
27 from AccessEngine import AEConstants
28 from Tools.i18n import bind, _
29
30 import logging
31 log = logging.getLogger('GCalcScript')
32
33
34 __uie__ = dict(kind='script', tier='gcalctool', all_tiers=False)
35
37 '''
38 A special L{AEScript <AEScript.AEScript>} for handling gcalctool.
39 It defines the hotkey I{Caps-Lock+I} that reads the equation.
40
41 @ivar old_string: Copy of what was in the equation box.
42 @type old_string: string
43 @ivar cleared: Set to C{True} if the equation box was cleared.
44 @type cleared: boolean
45 @ivar startup: Set to C{True} until the first caret event happens.
46 @type startup: boolean
47 @ivar tooltip: Set to C{True} if the tooltip is showing.
48 @type tooltip: boolean
49 '''
51 '''
52 Registers L{event tasks <AEScript.event_tasks>} to handle
53 L{caret <AEEvent.CaretChange>}, L{selector <AEEvent.SelectorChange>},
54 L{state <AEEvent.StateChange>} and L{property <AEEvent.PropertyChange>}
55 events. Registers L{tasks <AEScript.registered_tasks>} that can be mapped to
56 L{AEInput.Gesture}s.
57 '''
58 AccessEngineAPI.setScriptIdealOutput(self, 'audio')
59
60 self.old_string = ""
61 self.cleared = True
62 self.startup = True
63 self.tooltip = False
64
65
66 self.registerEventTask('read caret', AEConstants.EVENT_TYPE_CARET_CHANGE,
67 tier=True)
68 self.registerEventTask('status bar', AEConstants.EVENT_TYPE_PROPERTY_CHANGE,
69 tier=True)
70 self.registerEventTask('read tooltip', AEConstants.EVENT_TYPE_STATE_CHANGE,
71 tier=True)
72
73
74 kbd = AccessEngineAPI.getInputDevice(None, 'keyboard')
75 AccessEngineAPI.addInputModifiers(self, kbd, kbd.AEK_CAPS_LOCK)
76
77 self.registerTask('read equation', self.readEquation)
78 self.registerCommand(kbd, 'read equation', _('read equation'),
79 False, [kbd.AEK_CAPS_LOCK, kbd.AEK_I])
80
81
82 self.registerTask('read button', self.readButtonName)
83 self.registerTask('stop reading', self.stopReading)
84 self.chainTask('read button', AEConstants.CHAIN_AROUND,
85 'read selector', 'BasicSpeechScript')
86 self.chainTask('stop reading', AEConstants.CHAIN_AROUND,
87 'read state', 'BasicSpeechScript')
88
90 '''
91 Describe which L{AETier} this script applies to by default.
92
93 @return: Human readable translated description of this script.
94 @rtype: string
95 '''
96 return _('Applies to gcalctool by default.')
97
98 - def _getFrameBoxText(self, por):
99 '''
100 Return the entire equation.
101
102 @param por: Point of regard for the related accessible
103 @type por: L{AEPor}
104 @return: Entire equation
105 @rtype: string
106 '''
107
108 frame = AccessEngineAPI.getRootAcc(por)
109 textBox = AccessEngineAPI.getAccFromPath(frame, 0, 1, 0, 0)
110 return AccessEngineAPI.getItemText(textBox)
111
113 '''
114 Look for a hotkey and give it back.
115
116 @param text: Text which contained a hotkey
117 @type text: string
118 @return: Hotkey
119 @rtype: string
120 '''
121 e = text.rfind(']')
122 if e > -1:
123 s = text.rfind('[')
124 if s > -1:
125
126 return text[s+1:e]
127
128 e = text.rfind(')')
129 if e > -1:
130 s = text.rfind('(')
131 if s > -1:
132 return text[s+1:e]
133
134
135
137 '''
138 The L{BasicSpeechScript.onStateChange
139 <BasicSpeechScript.BasicSpeechScript.onStateChange>}-method cuts off the
140 reading of the radiobutton details and reads the state twice. This chain
141 around to stop the L{BasicSpeechScript} and get the full detail infos: role,
142 accessible name, state and hotkey.
143
144 @param kwargs: Arbitrary keyword arguments to pass to the task
145 @type kwargs: dictionary
146 '''
147 pass
148
150 '''
151 Is invoked whenever user hits I{Caps-Lock+I}. It reads the equation
152 character by character.
153
154 @param kwargs: Arbitrary keyword arguments to pass to the task
155 @type kwargs: dictionary
156 @return: True to allow other tasks to process this event.
157 @rtype: boolean
158
159 @todo: NA: special characters like /*+-() are not translated
160 '''
161 text = self._getFrameBoxText(kwargs['por'])
162 AccessEngineAPI.stopNow(self, cap='audio', role='output', **kwargs)
163
164 sf = AccessEngineAPI.getStyleVal(self, 'SpellFormat', **kwargs)
165 AccessEngineAPI.setStyleVal(self, 'SpellFormat',
166 AEConstants.FORMAT_SPELL, **kwargs)
167 AccessEngineAPI.sayInfo(self, cap='audio', role='output',
168 text=text, **kwargs)
169 AccessEngineAPI.setStyleVal(self, 'SpellFormat', sf, **kwargs)
170 return True
171
172
173
174
202
203
204
205
207 '''
208 When a button is pressed: delete, move, insert events are fired in that
209 order. Uses the delete event to capture what the string was before the
210 new text is added by the insert.
211
212 @param text: Text before the changes
213 @type text: string
214 @param kwargs: Arbitrary keyword arguments to pass to the task
215 @type kwargs: dictionary
216 @return: True to allow other tasks to process this event.
217 @rtype: boolean
218 '''
219 self.old_string = text
220 return True
221
223 '''
224 Reads text that is added to the equation, either by keyboard or by clicking
225 buttons. Announces the current text in the display area.
226
227 @param text: Text insterted
228 @type text: string
229 @param kwargs: Arbitrary keyword arguments to pass to the task
230 @type kwargs: dictionary
231 @return: C{True} to allow other tasks to process this event.
232 @rtype: boolean
233 '''
234 if AccessEngineAPI.hasAccRole('edit bar', kwargs['por']):
235
236
237 endString = ''
238 startIndex = text.find(self.old_string)
239
240
241
242 if self.startup == True:
243 self.startup = False
244 startString = ''
245
246
247
248
249 endString = text
250 startIndex = 0
251 self.cleared = False
252 elif startIndex == -1:
253
254 if self.cleared:
255
256
257 startString = ""
258 endString = text
259 self.cleared = False
260 else:
261 if self.old_string == "0":
262
263 startString = ""
264 endString = text
265 elif text != "0":
266
267
268
269 if self.old_string.find(text) == 0:
270 startString = ""
271 endString = ""
272 else:
273 startString = ""
274 endString = text
275 else:
276
277 self.cleared = True
278 else:
279
280 startString = text[0:startIndex]
281 endString = text[(startIndex + len(self.old_string)):]
282
283 if endString != "":
284 AccessEngineAPI.mayStop(self, cap='audio', role='output', **kwargs)
285 AccessEngineAPI.inhibitMayStop()
286
287 if self.cleared == True:
288 text=_('cleared')
289 elif startString == "1/(":
290 text=_('invert')
291 elif startString == "-(":
292 text=_('negate')
293 elif endString == "Sqrt(":
294 text=_('square root')
295 elif endString == '-':
296 text=_('minus')
297 elif endString == '*':
298 text=_('times')
299 elif endString == '/':
300 text=_('divides')
301 elif endString == '%':
302 text=_('percent')
303 elif endString == ' Mod ':
304 text=_('modulo')
305 elif endString == "Int(":
306 text=_('integer')
307 elif endString == "Abs(":
308 text=_('absolute value')
309 elif endString == "Frac(":
310 text=_('fraction')
311 elif endString != "":
312 text=endString
313
314 AccessEngineAPI.sayInfo(self, cap='audio', role='output', text=text, **kwargs)
315 return True
316
318 '''
319 Handles text being displayed as tooltip.
320
321 @param kwargs: Arbitrary keyword arguments to pass to the task
322 @type kwargs: dictionary
323 @return: C{True} to allow other tasks to process this event.
324 @rtype: boolean
325 '''
326 if (kwargs['name'] == 'visible' and kwargs['value'] and
327 AccessEngineAPI.hasAccRole('tool tip', kwargs['por'])):
328 hotkey = None
329 AccessEngineAPI.mayStop(self, cap='audio', role='output', **kwargs)
330 tooltip = AccessEngineAPI.getAccName(kwargs['por'])
331 hotkey = self._findHotkey(tooltip)
332 if hotkey:
333 tooltip = tooltip[:tooltip.find(hotkey)-2]
334 AccessEngineAPI.sayInfo(self, cap='audio', role='output',
335 text=tooltip, **kwargs)
336 AccessEngineAPI.sayHotkey(self, cap='audio', role='output',
337 text = hotkey, **kwargs)
338 AccessEngineAPI.inhibitMayStop()
339 self.tooltip = True
340 else:
341 self.tooltip = False
342 return True
343
345 '''
346 Handles text being displayed in the statusbar.
347
348 @param kwargs: Arbitrary keyword arguments to pass to the task
349 @type kwargs: dictionary
350 @return: C{True} to allow other tasks to process this event.
351 @rtype: boolean
352 '''
353 if kwargs['value']:
354 AccessEngineAPI.mayStop(self, cap='audio', role='output', **kwargs)
355 AccessEngineAPI.sayInfo(self, cap='audio', role='output',
356 text=kwargs['value'], **kwargs)
357 AccessEngineAPI.inhibitMayStop()
358 return True
359