Coverage Report - orca.where_am_I

ModuleCoverage %
orca.where_am_I
10%
1
# Orca
2
#
3
# Copyright 2005-2006 Sun Microsystems Inc.
4
#
5
# This library is free software; you can redistribute it and/or
6
# modify it under the terms of the GNU Library General Public
7
# License as published by the Free Software Foundation; either
8
# version 2 of the License, or (at your option) any later version.
9
#
10
# This library is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
# Library General Public License for more details.
14
#
15
# You should have received a copy of the GNU Library General Public
16
# License along with this library; if not, write to the
17
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18
# Boston, MA 02111-1307, USA.
19
20 1
"""Speaks information about the current object of interest."""
21
22 1
__id__        = "$Id: where_am_I.py 2126 2007-03-06 21:35:17Z richb $"
23 1
__version__   = "$Revision: 2126 $"
24 1
__date__      = "$Date: 2007-03-06 13:35:17 -0800 (Tue, 06 Mar 2007) $"
25 1
__copyright__ = "Copyright (c) 2005-2006 Sun Microsystems Inc."
26 1
__license__   = "LGPL"
27
28 1
import string
29
30 1
import atspi
31 1
import chnames
32 1
import debug
33 1
import default
34 1
import input_event
35 1
import orca_prefs
36 1
import orca_state
37 1
import rolenames
38 1
import settings
39 1
import speech
40 1
import speechserver
41 1
import Accessibility
42 1
import math
43 1
from orca_i18n import _ # for gettext support
44
45 1
_debugLevel = debug.LEVEL_FINEST
46 1
_appName = None
47 1
_statusBar = None
48 1
_lastAttributeString = ""
49
50 1
def whereAmI(obj, context, doubleClick, orcaKey):
51
    """
52
    Speaks information about the current object of interest, including
53
    the object itself, which window it is in, which application, which
54
    workspace, etc.
55
56
    The object of interest can vary depending upon the mode the user
57
    is using at the time. For example, in focus tracking mode, the
58
    object of interest is the object with keyboard focus. In review
59
    mode, the object of interest is the object currently being visited,
60
    whether it has keyboard focus or not.
61
    """
62
63 0
    if (not obj) or (not context):
64 0
        return False
65
66 0
    debug.println(_debugLevel,
67
        "whereAmI: \
68
       \n  context= %s \
69
       \n  label=%s \
70
       \n  name=%s \
71
       \n  role=%s \
72
       \n  mnemonics=%s \
73
       \n  parent label= %s \
74
       \n  parent name=%s \
75
       \n  parent role=%s \
76
       \n  double-click=%s \
77 0
       \n  orca-key=%s" % \
78
        (context,
79
         _getObjLabel(obj),
80
         _getObjName(obj),
81
         obj.role,
82
         orca_state.activeScript.getAcceleratorAndShortcut(obj),
83
         _getObjLabel(obj.parent),
84
         _getObjName(obj.parent),
85
         obj.parent.role,
86
         doubleClick,
87
         orcaKey))
88
89 0
    global _appName
90 0
    _appName = context[0]
91 0
    role = obj.role
92
93 0
    if orcaKey:
94
        # Handle the Orca modifier key being pressed.
95 0
        if _getAppName() == "soffice.bin":
96 0
            top = orca_state.activeScript.getTopLevel(obj)
97 0
            if top and top.name.endswith(" Calc"):
98 0
                _handleCalcOrcaKey(obj, doubleClick)
99
            else:
100 0
                _handleOrcaKey(obj, doubleClick)
101
        else:
102 0
            _handleOrcaKey(obj, doubleClick)
103
104
105 0
    elif role == rolenames.ROLE_CHECK_BOX:
106 0
        _speakCheckBox(obj, doubleClick)
107
108
109 0
    elif role == rolenames.ROLE_RADIO_BUTTON:
110 0
        _speakRadioButton(obj, doubleClick)
111
112
113 0
    elif role == rolenames.ROLE_COMBO_BOX:
114 0
        _speakComboBox(obj, doubleClick)
115
116
117 0
    elif role == rolenames.ROLE_SPIN_BUTTON:
118 0
        _speakSpinButton(obj, doubleClick)
119
120
121 0
    elif role == rolenames.ROLE_PUSH_BUTTON:
122 0
        _speakPushButton(obj, doubleClick)
123
124
125 0
    elif role == rolenames.ROLE_SLIDER:
126 0
        _speakSlider(obj, doubleClick)
127
128
129 0
    elif role == rolenames.ROLE_MENU or \
130
         role == rolenames.ROLE_MENU_ITEM or \
131
         role == rolenames.ROLE_CHECK_MENU or \
132
         role == rolenames.ROLE_CHECK_MENU_ITEM or \
133
         role == rolenames.ROLE_RADIO_MENU or \
134
         role == rolenames.ROLE_RADIO_MENU_ITEM:
135 0
        _speakMenuItem(obj, doubleClick)
136
137
138 0
    elif role == rolenames.ROLE_PAGE_TAB:
139 0
        _speakPageTab(obj, doubleClick)
140
141
142 0
    elif role == rolenames.ROLE_TEXT or \
143
         role == rolenames.ROLE_TERMINAL:
144 0
        _speakText(obj, doubleClick)
145
146
147 0
    elif role == rolenames.ROLE_TABLE_CELL:
148 0
        if _getAppName() == "soffice.bin":
149 0
            _speakCalcTableCell(obj, doubleClick)
150
        else:
151 0
            _speakTableCell(obj, doubleClick)
152
153
154 0
    elif role == rolenames.ROLE_PARAGRAPH:
155 0
        _speakParagraph(obj, doubleClick)
156
157 0
    return True
158
159
160 1
def _getAppName():
161
    """
162
    Returns the application name.
163
    """
164 0
    global _appName
165 0
    return _appName
166
167
168 1
def _speakCheckBox(obj, doubleClick):
169
    """Checkboxes present the following information
170
    (an example is 'Enable speech, checkbox checked, Alt E'):
171
    1. label
172
    2. role
173
    3. state
174
    4. mnemonic (i.e. Alt plus the underlined letter), if any
175
    """    
176 0
    utterances = []
177 0
    text = _getObjLabelAndName(obj)
178 0
    utterances.append(text)
179
180 0
    text = _("%s") % rolenames.getSpeechForRoleName(obj)
181 0
    utterances.append(text)
182
183 0
    if obj.state.count(atspi.Accessibility.STATE_CHECKED):
184 0
        text = _("checked")
185
    else:
186 0
        text = _("not checked")
187 0
        utterances.append(text)
188
189 0
    text = _("%s") % _getObjMnemonic(obj)
190 0
    utterances.append(text)
191
192 0
    debug.println(_debugLevel, "check box utterances=%s" % \
193
                  utterances)
194 0
    speech.speakUtterances(utterances)
195
196
197 1
def _speakRadioButton(obj, doubleClick):
198
    """
199
    Radio Buttons present the following information (an example is
200
    'Punctuation Level, Some, Radio button, selected, item 2 of 4, Alt M'):
201
202
    1. group name
203
    2. label
204
    3. role
205
    4. state
206
    5. relative position
207
    6. mnemonic (i.e. Alt plus the underlined letter), if any
208
    """
209 0
    utterances = []
210 0
    text = _("%s") % _getGroupLabel(obj)
211 0
    utterances.append(text)
212
213 0
    if doubleClick:
214 0
        text = _("%s") % _getPositionInGroup(obj)
215 0
        utterances.append(text)
216
217 0
    text = _("%s") % _getObjLabelAndName(obj)
218 0
    utterances.append(text)
219
220 0
    text = _("%s") % rolenames.getSpeechForRoleName(obj)
221 0
    utterances.append(text)
222
223 0
    if obj.state.count(atspi.Accessibility.STATE_CHECKED):
224 0
        text = _("checked")
225
    else:
226 0
        text = _("not checked")
227 0
    utterances.append(text)
228
229 0
    if not doubleClick:
230 0
        text = _("%s") % _getPositionInGroup(obj)
231 0
        utterances.append(text)
232
233 0
    text = _("%s") % _getObjMnemonic(obj)
234 0
    utterances.append(text)
235
236 0
    debug.println(_debugLevel, "radio button utterances=%s" % \
237
                  utterances)
238 0
    speech.speakUtterances(utterances)
239
240
241 1
def _speakComboBox(obj, doubleClick):
242
    """
243
    Comboboxes present the following information (an example is
244
    'Speech system: combo box, GNOME Speech Services, item 1 of 1,
245
    Alt S'):
246
    1. label
247
    2. role
248
    3. current value
249
    4. relative position
250
    5. mnemonic (i.e. Alt plus the underlined letter), if any
251
    """
252 0
    utterances = []
253 0
    text = _("%s") % _getObjLabel(obj)
254 0
    utterances.append(text)
255
256 0
    text = _("%s") % rolenames.getSpeechForRoleName(obj)
257 0
    utterances.append(text)
258
259 0
    if doubleClick:
260
        # child(0) is the popup list
261 0
        name = _("%s") % _getObjName(obj)
262 0
        text = _("%s") % _getPositionInList(obj.child(0), name)
263 0
        utterances.append(text)
264
265 0
        utterances.append(name)
266
    else:
267 0
        name = _("%s") % _getObjName(obj)
268 0
        utterances.append(name)
269
270
        # child(0) is the popup list
271 0
        text = _("%s") % _getPositionInList(obj.child(0), name)
272 0
        utterances.append(text)
273
274 0
    text = _("%s") % _getObjMnemonic(obj)
275 0
    utterances.append(text)
276
277 0
    debug.println(_debugLevel, "combo box utterances=%s" % \
278
                  utterances)
279 0
    speech.speakUtterances(utterances)
280
281
282 1
def _speakSpinButton(obj, doubleClick):
283
    """
284
    Spin Buttons present the following information (an example is
285
    'Scale factor: spin button, 4.00, Alt F'):
286
287
    1. label
288
    2. role
289
    3. current value
290
    4. mnemonic (i.e. Alt plus the underlined letter), if any
291
    """
292 0
    utterances = []
293 0
    text = _("%s") % _getObjLabelAndName(obj)
294 0
    utterances.append(text)
295
296 0
    text = _("%s") % rolenames.getSpeechForRoleName(obj)
297 0
    utterances.append(text)
298
299 0
    value = obj.value
300 0
    if value: 
301 0
        text = "%.1f" % value.currentValue
302 0
        utterances.append(text)
303
304 0
    text = _("%s") % _getObjMnemonic(obj)
305 0
    utterances.append(text)
306
307 0
    debug.println(_debugLevel, "spin button utterances=%s" % \
308
                  utterances)
309 0
    speech.speakUtterances(utterances)
310
311
312 1
def _speakPushButton(obj, doubleClick):
313
    """
314
    Push Buttons present the following information (an example is
315
    'Apply button, Alt A'):
316
317
    1. label
318
    2. role
319
    3. mnemonic (i.e. Alt plus the underlined letter), if any
320
    """
321 0
    utterances = []
322 0
    text = _("%s") % _getObjLabelAndName(obj)
323 0
    utterances.append(text)
324
325 0
    text = _("%s") % rolenames.getSpeechForRoleName(obj)
326 0
    utterances.append(text)
327
328 0
    text = _("%s") % _getObjMnemonic(obj)
329 0
    utterances.append(text)
330
331 0
    debug.println(_debugLevel, "push button utterances=%s" % \
332
                  utterances)
333 0
    speech.speakUtterances(utterances)
334
335
336 1
def _speakSlider(obj, doubleClick):
337
    """
338
    Sliders present the following information (examples include
339
    'Pitch slider, 5.0, 56%'; 'Volume slider, 9.0, 100%'):
340
341
    1. label
342
    2. role
343
    3. value
344
    4. percentage (if possible)
345
    5. mnemonic (i.e. Alt plus the underlined letter), if any
346
    """
347 0
    utterances = []
348 0
    text = _("%s") % _getObjLabel(obj)
349 0
    utterances.append(text)
350
351 0
    text = _("%s") % rolenames.getSpeechForRoleName(obj)
352 0
    utterances.append(text)
353
354 0
    values = _getSliderValues(obj)
355 0
    utterances.append(_("%s") % values[0])
356 0
    utterances.append(_("%s percent") % values[1])
357
358 0
    text = _("%s") % _getObjMnemonic(obj)
359 0
    utterances.append(text)
360
361 0
    debug.println(_debugLevel, "slider utterances=%s" % \
362
                  utterances)
363 0
    speech.speakUtterances(utterances)
364
365
366 1
def _speakMenuItem(obj, doubleClick):
367
    """
368
    Menu items present the following information (examples include
369
    'File menu, Open..., Control + O, item 2 of 20, O', 'File menu,
370
    Wizards Menu, item 4 of 20, W'):
371
372
    1. Name of the menu containing the item, followed by its role
373
    2. item name, followed by its role (if a menu) followed by its
374
    accelerator key, if any
375
    3. relative position
376
    4. mnemonic (i.e. Alt plus the underlined letter), if any
377
    """
378 0
    utterances = []
379 0
    text = _("%s") % _getObjLabelAndName(obj.parent)
380 0
    utterances.append(text)
381
382 0
    if doubleClick:
383
        # parent is the page tab list
384 0
        name = _("%s") % _getObjName(obj)
385 0
        text = _("%s") % _getPositionInList(obj.parent, name)
386 0
        utterances.append(text)
387
388 0
    text = _("%s") % _getObjLabelAndName(obj)
389 0
    utterances.append(text)
390
391 0
    text = _("%s") % _getObjAccelerator(obj)
392 0
    utterances.append(text)
393
394 0
    text = _("%s") % rolenames.getSpeechForRoleName(obj)
395 0
    utterances.append(text)
396
397 0
    if not doubleClick:
398
        # parent is the page tab list
399 0
        name = _("%s") % _getObjName(obj)
400 0
        text = _("%s") % _getPositionInList(obj.parent, name)
401 0
        utterances.append(text)
402
403 0
    text = _("%s") % _getObjShortcut(obj)
404 0
    utterances.append(text)
405
406 0
    debug.println(_debugLevel, "menu item utterances=%s" % \
407
                  utterances)
408 0
    speech.speakUtterances(utterances)
409
410
411 1
def _speakPageTab(obj, doubleClick):
412
    """
413
    Tabs in a Tab List present the following information (an example
414
    is 'Tab list, braille page, item 2 of 5'):
415
416
    1. role
417
    2. label + 'page'
418
    3. relative position
419
    4. mnemonic (i.e. Alt plus the underlined letter), if any
420
    """
421 0
    utterances = []
422 0
    text = _("%s") % rolenames.getSpeechForRoleName(obj)
423 0
    utterances.append(text)
424
425 0
    if doubleClick:
426 0
        text = _("%s page") % _getObjLabelAndName(obj)
427 0
        utterances.append(text)
428
429 0
        name = _("%s") % _getObjName(obj)
430 0
        text = _("%s") % _getPositionInList(obj.parent, name)
431 0
        utterances.append(text)
432
    else:
433 0
        name = _("%s") % _getObjName(obj)
434 0
        text = _("%s") % _getPositionInList(obj.parent, name)
435 0
        utterances.append(text)
436
437 0
        text = _("%s page") % _getObjLabelAndName(obj)
438 0
        utterances.append(text)
439
440 0
    text = _("%s") % _getObjMnemonic(obj)
441 0
    utterances.append(text)
442
443 0
    debug.println(_debugLevel, "page utterances=%s" % \
444
                  utterances)
445 0
    speech.speakUtterances(utterances)
446
447
448 1
def _speakText(obj, doubleClick):
449
    """
450
    Text boxes present the following information (an example is
451
    'Source display: text, blank, Alt O'):
452
453
    1. label, if any
454
    2. role
455
    3. contents
456
        A. if no text on the current line is selected, the current line
457
        B. if text is selected on the current line, that text, followed
458
        attibute information before  (bold "text")
459
        by 'selected' (single press) 
460
        C. if the current line is blank/empty, 'blank'
461
    4. mnemonic (i.e. Alt plus the underlined letter), if any
462
463
    Gaim, gedit, OpenOffice Writer and Terminal
464
    """
465 0
    utterances = []
466 0
    text = _("%s") % _getObjLabel(obj)
467 0
    utterances.append(text)
468
469 0
    text = _("%s") % rolenames.getSpeechForRoleName(obj)
470 0
    utterances.append(text)
471
472 0
    [textContents, startOffset, endOffset, selected] = \
473
                   _getTextContents(obj, doubleClick)
474 0
    if doubleClick:
475
        # Speak character attributes.
476 0
        textContents = \
477
            _insertAttributes(obj, startOffset, endOffset, textContents)
478 0
        savedStyle = settings.verbalizePunctuationStyle
479 0
        settings.verbalizePunctuationStyle = settings.PUNCTUATION_STYLE_SOME
480
481 0
    text = _("%s") % textContents
482 0
    utterances.append(text)
483 0
    debug.println(_debugLevel, "first text utterances=%s" % \
484
                  utterances)
485 0
    speech.speakUtterances(utterances)
486
487 0
    if doubleClick:
488 0
        verbalizePunctuationStyle = savedStyle
489
490 0
    utterances = []
491 0
    if selected:
492 0
        text = _("%s") % "selected"
493 0
        utterances.append(text)
494
495 0
    text = _("%s") % _getObjMnemonic(obj)
496 0
    utterances.append(text)
497
498 0
    debug.println(_debugLevel, "text utterances=%s" % \
499
                  utterances)
500 0
    speech.speakUtterances(utterances)
501
502
503 1
def _speakCalcTableCell(obj, doubleClick):
504
    """
505
    Given the nature of OpenOffice Calc, Orca should override the
506
    default KP_Enter behavior when the item with focus is a cell
507
    within Calc. In this instance, the following information should
508
    be spoken/displayed:
509
510
    1. "Cell"
511
    2. the cell coordinates
512
    3. the cell contents:
513
        A. if the cell is empty, "blank"
514
        B. if the cell is being edited AND if some text within the cell
515
        is selected, the selected text followed by "selected"
516
        C. otherwise, the full contents of the cell
517
    """
518
519 0
    utterances = []
520 0
    utterances.append(_("Cell"))
521
522 0
    table = obj.parent.table
523 0
    text = _("column %d") % (table.getColumnAtIndex(obj.index) + 1)
524 0
    utterances.append(text)
525 0
    text = _("row %d") % (table.getRowAtIndex(obj.index) + 1)
526 0
    utterances.append(text)
527
528 0
    text = obj.text.getText(0, -1)
529 0
    utterances.append(text)
530
531 0
    debug.println(_debugLevel, "calc table cell utterances=%s" % \
532
                  utterances)
533 0
    speech.speakUtterances(utterances)
534
535
536 1
def _speakTableCell(obj, doubleClick):
537
    """
538
    Tree Tables present the following information (an example is
539
    'Tree table, Mike Pedersen, item 8 of 10, tree level 2'):
540
541
    1. label, if any
542
    2. role
543
    3. current row (regardless of speak cell/row setting)
544
    4. relative position
545
    5. if expandable/collapsible: expanded/collapsed
546
    6. if applicable, the level
547
548
    Nautilus and Gaim
549
    """
550
551
    # Speak the first two items (and possibly the position)
552 0
    utterances = []
553 0
    if obj.parent.role == rolenames.ROLE_TABLE_CELL:
554 0
        obj = obj.parent
555 0
    parent = obj.parent
556
557 0
    text = _("%s") % _getObjLabel(obj)
558 0
    utterances.append(text)
559
560 0
    text = _("%s") % rolenames.getSpeechForRoleName(obj)
561 0
    utterances.append(text)
562 0
    debug.println(_debugLevel, "first table cell utterances=%s" % \
563
                  utterances)
564 0
    speech.speakUtterances(utterances)
565
566 0
    utterances = []
567 0
    if doubleClick:
568 0
        table = parent.table
569 0
        row = table.getRowAtIndex(orca_state.locusOfFocus.index)
570 0
        text = _("row %d of %d") % ((row+1), parent.table.nRows)
571 0
        utterances.append(text)
572 0
        speech.speakUtterances(utterances)
573
574
    # Speak the current row
575 0
    utterances = _getTableRow(obj)
576 0
    debug.println(_debugLevel, "second table cell utterances=%s" % \
577
                  utterances)
578 0
    speech.speakUtterances(utterances)
579
580
    # Speak the remaining items.
581 0
    utterances = []
582
583 0
    if not doubleClick:
584 0
        table = parent.table
585 0
        if not table:
586 0
            debug.println(_debugLevel, "??? parent=%s" % parent.role)
587 0
            return
588
589 0
        row = table.getRowAtIndex(orca_state.locusOfFocus.index)
590 0
        text = _("row %d of %d") % ((row+1), parent.table.nRows)
591 0
        utterances.append(text)
592
593 0
    if obj.state.count(atspi.Accessibility.STATE_EXPANDABLE):
594 0
        if obj.state.count(atspi.Accessibility.STATE_EXPANDED):
595 0
            text = _("expanded")
596
        else:
597 0
            text = _("collapsed")
598 0
            utterances.append(text)
599
600 0
    level = orca_state.activeScript.getNodeLevel(orca_state.locusOfFocus)
601 0
    if level >= 0:
602 0
        utterances.append(_("tree level %d") % (level + 1))
603
604 0
    debug.println(_debugLevel, "third table cell utterances=%s" % \
605
                  utterances)
606 0
    speech.speakUtterances(utterances)
607
608
609 1
def _speakParagraph(obj, doubleClick):
610
    """
611
    OpenOffice Calc cells have the role "paragraph" when
612
    they are being edited.
613
    """
614 0
    if _getAppName() == "soffice.bin":
615 0
        top = orca_state.activeScript.getTopLevel(obj)
616 0
        if top and top.name.endswith(" Calc"):
617 0
            _speakCalc(obj, doubleClick)
618
619 0
        elif top and top.name.endswith(" Writer"):
620 0
            _speakText(obj, doubleClick)
621
622
623
624 1
def _speakCalc(obj, doubleClick):
625
    """
626
    Speak a OpenOffice Calc cell.
627
    """
628 0
    utterances = []
629 0
    utterances.append(_("Cell"))
630
631
    # No way to get cell coordinates?
632
633 0
    [textContents, startOffset, endOffset, selected] = \
634
        _getTextContents(obj, doubleClick)
635 0
    text = _("%s") % textContents
636 0
    utterances.append(text)
637 0
    if selected:
638 0
        text = _("%s") % "selected"
639 0
        utterances.append(text)
640
641 0
    debug.println(_debugLevel, "editable table cell utterances=%s" % \
642
                  utterances)
643 0
    speech.speakUtterances(utterances)
644
645
646 1
def _getObjName(obj):
647
    """
648
    Returns the name to speak for an object.
649
    """
650 0
    text = ""
651 0
    name = orca_state.activeScript.getDisplayedText(obj)
652 0
    if not name:
653 0
        name = obj.description
654
655 0
    if name and name != "None":
656 0
        text = _("%s") % name
657
    # debug.println(_debugLevel, "%s name=<%s>" % (obj.role, text))
658 0
    return text
659
660
661 1
def _getObjLabel(obj):
662
    """
663
    Returns the label to speak for an object.
664
    """
665 0
    text = ""
666 0
    label = orca_state.activeScript.getDisplayedLabel(obj)
667
668 0
    if label and label != "None":
669 0
        text = _("%s") % label
670
    # debug.println(_debugLevel, "%s label=<%s>" % (obj.role, text))
671 0
    return text
672
673
674 1
def _getObjLabelAndName(obj):
675
    """
676
    Returns the object label plus the object name.
677
    """
678 0
    name = _getObjName(obj)
679 0
    label = _getObjLabel(obj)
680 0
    if name != label:
681 0
        text = _("%s %s") % (label, name)
682
    else:
683 0
        text = _("%s") % label
684
685
686 0
    if obj.text:
687 0
        [string, startOffset, endOffset] = obj.text.getTextAtOffset(0,
688 0
            atspi.Accessibility.TEXT_BOUNDARY_LINE_START)
689
690 0
        debug.println(_debugLevel, "%s text=<%s>" % (obj.role, string))
691
692 0
    return text
693
694
695 1
def _getGroupLabel(obj):
696
    """
697
    Returns the label for a group of components.
698
    """
699 0
    text = ""
700 0
    labelledBy = None
701
702 0
    relations = obj.relations
703 0
    for relation in relations:
704 0
        if relation.getRelationType() ==  \
705
               atspi.Accessibility.RELATION_LABELLED_BY:
706 0
            labelledBy = atspi.Accessible.makeAccessible(relation.getTarget(0))
707 0
            break
708
709 0
    if labelledBy:
710 0
        text = _getObjLabelAndName(labelledBy)
711
712
    else:
713 0
        parent = obj.parent
714 0
        while parent and (parent.parent != parent):
715 0
            if parent.role == rolenames.ROLE_PANEL:
716 0
                label = _getObjLabelAndName(parent)
717 0
                if label and label != "":
718 0
                    text = label
719 0
                    break
720 0
            parent = parent.parent
721
722 0
    return text
723
724
725 1
def _getPositionInGroup(obj):
726
    """
727
    Returns the relative position of an object in a group.
728
    """
729 0
    text = ""
730 0
    position = -1
731 0
    total = -1
732
733 0
    relations = obj.relations
734 0
    for relation in relations:
735 0
        if relation.getRelationType() == Accessibility.RELATION_MEMBER_OF:
736 0
            total = relation.getNTargets()
737 0
            for i in range(0, total):
738 0
                target = atspi.Accessible.makeAccessible(relation.getTarget(i))
739 0
                if target == obj:
740 0
                    position = total - i
741 0
                    break
742
743 0
    if position >= 0:
744 0
        text += _("item %d of %d") % (position, total)
745
746 0
    return text
747
748
749 1
def _getPositionInComboBox(obj, name):
750
    """
751
    Returns the relative position of an object in a combo box.
752
    """
753
754
    # The only child of a combo box is the popup menu
755 0
    return _getPositionInList(obj.child(0), name)
756
757
758
759 1
def _getPositionInList(obj, name):
760
    """
761
    Returns the relative position of an object in a list.
762
    """
763 0
    text = ""
764 0
    position = -1
765 0
    index = 0
766 0
    total = 0
767
768 0
    debug.println(_debugLevel, "obj=%s, count=%d, name=%s" % \
769
                  (obj.role, obj.childCount, name))
770
771 0
    for i in range(0, obj.childCount):
772 0
        next = _getObjName(obj.child(i))
773 0
        if next == "" or next == "Empty" or next == "separator":
774 0
            continue
775
776 0
        index += 1
777 0
        total += 1
778
779 0
        if next == name:
780 0
            position = index
781
782
783 0
    if position >= 0:
784 0
        text = _("item %d of %d") % (position, total)
785
786 0
    return text
787
788
789 1
def _getObjMnemonic(obj):
790
    """
791
    Returns the accellerator and/or shortcut for the object,
792
    if either exists.
793
    """
794 0
    list = orca_state.activeScript.getAcceleratorAndShortcut(obj)
795
796 0
    text = ""
797 0
    if not list[1]:
798 0
        text = _("%s") % list[0]
799
    else:
800 0
        text = _("%s %s") % (list[0], list[1])
801
802 0
    return text
803
804
805 1
def _getObjAccelerator(obj):
806
    """
807
    Returns the accelerator for the object, if it exists.
808
    """
809 0
    list = orca_state.activeScript.getAcceleratorAndShortcut(obj)
810
811 0
    text = ""
812 0
    if list[0]:
813 0
        text = _("%s") % list[0]
814
815 0
    return text
816
817
818 1
def _getObjShortcut(obj):
819
    """
820
    Returns the shortcut for the object, if it exists.
821
    """
822 0
    list = orca_state.activeScript.getAcceleratorAndShortcut(obj)
823
824 0
    text = ""
825 0
    if list[1]:
826 0
        text = _("%s") % list[1]
827
828 0
    return text
829
830
831 1
def _getSliderValues(obj):
832
    """
833
    Returns the slider's current value and percentage.
834
    """
835 0
    value = obj.value
836
837 0
    currentValue = "%.1f" % value.currentValue
838 0
    percent = value.currentValue / value.maximumValue * 100
839 0
    rounded = "%d" % round(percent, 5)
840
841 0
    debug.println(_debugLevel,
842 0
        "_getSliderValues: min=%f, cur=%f, max=%f, str=%s, percent=%s" % \
843
        (value.minimumValue, value.currentValue, value.maximumValue, \
844
         currentValue, rounded))
845
846 0
    return [currentValue, rounded]
847
848
849 1
def _getTableRow(obj):
850
    """Get the speech for a table cell row or a single table cell
851
    if settings.readTableCellRow is False.
852
853
    Arguments:
854
    - obj: the table
855
    - already_focused: False if object just received focus
856
857
    Returns a list of utterances to be spoken for the object.
858
    """
859 0
    utterances = []
860
861 0
    parent = obj.parent
862 0
    table = parent.table
863 0
    if not table:
864 0
        debug.println(_debugLevel, "??? parent=%s" % parent.role)
865 0
        return []
866
867 0
    row = parent.table.getRowAtIndex(obj.index)
868
869 0
    for i in range(0, parent.table.nColumns):
870 0
        cell = parent.table.getAccessibleAt(row, i)
871 0
        acc = atspi.Accessible.makeAccessible(cell)
872 0
        utterances.append(_getTableCell(acc))
873
874 0
    debug.println(_debugLevel, "row=<%s>" % utterances)
875 0
    return utterances
876
877
878 1
def _getTableCell(obj):
879
    """
880
    Get the speech utterances for a single table cell. 
881
    """
882
883
    # Don't speak check box cells that area not checked.
884 0
    notChecked = False
885 0
    action = obj.action
886 0
    if action:
887 0
        for i in range(0, action.nActions):
888 0
            if action.getName(i) == "toggle":
889 0
                obj.role = rolenames.ROLE_CHECK_BOX
890 0
                if not obj.state.count(atspi.Accessibility.STATE_CHECKED):
891 0
                    notChecked = True
892 0
                obj.role = rolenames.ROLE_TABLE_CELL
893 0
                break
894
895 0
    if notChecked:
896 0
        return ""
897
898 0
    descendant = orca_state.activeScript.getRealActiveDescendant(obj)
899 0
    text = orca_state.activeScript.getDisplayedText(descendant)
900
901
    # For Evolution mail header list.
902 0
    if _getAppName().startswith("evolution") and text == "Status":
903 0
        text = _("Read")
904
905 0
    debug.println(_debugLevel, "cell=<%s>" % text)
906 0
    return text
907
908
909 1
def _getCheckBox(obj):
910
    """
911
    Returns utterences for a check box.
912
    """
913 0
    utterances = []
914
915 0
    text = _getObjLabelAndName(obj)
916 0
    utterances.append(text)
917
918 0
    text = _("%s") % rolenames.getSpeechForRoleName(obj)
919 0
    utterances.append(text)
920
921 0
    if obj.state.count(atspi.Accessibility.STATE_CHECKED):
922 0
        text = _("checked")
923
    else:
924 0
        text = _("not checked")
925 0
    utterances.append(text)
926
927 0
    text = _("%s") % _getObjMnemonic(obj)
928 0
    utterances.append(text)
929
930 0
    return utterances
931
932
933 1
def _getTextContents(obj, doubleClick):
934
    """
935
    Returns utterences for text.
936
937
    A. if no text on the current line is selected, the current line
938
    B. if text is selected on the current line, that text, followed
939
    by 'selected'
940
    C. if the current line is blank/empty, 'blank'
941
    """
942 0
    textObj = obj.text
943 0
    caretOffset = textObj.caretOffset
944 0
    textContents = ""
945 0
    selected = False
946 0
    startSelOffset = -1
947 0
    endSelOffset = -1
948
949 0
    nSelections = textObj.getNSelections()
950 0
    debug.println(_debugLevel,
951 0
        "_getTextContents: caretOffset=%d, nSelections=%d" % \
952
        (caretOffset, nSelections))
953
954 0
    if nSelections:
955 0
        selected = True
956 0
        for i in range(0, nSelections):
957 0
            [startOffset, endOffset] = textObj.getSelection(i)
958
959 0
            debug.println(_debugLevel,
960 0
                "_getTextContents: selection start=%d, end=%d" % \
961
                (startOffset, endOffset))
962
963 0
            selectedText = textObj.getText(startOffset, endOffset)
964 0
            debug.println(_debugLevel,
965 0
                "_getTextContents: selected text=<%s>" % selectedText)
966
967 0
            if i > 0:
968 0
                textContents += " "
969 0
            textContents += selectedText
970
971
    else:
972
        # Get the line containing the caret
973
        #
974 0
        [line, startOffset, endOffset] = textObj.getTextAtOffset(
975
            textObj.caretOffset, atspi.Accessibility.TEXT_BOUNDARY_LINE_START)
976 0
        debug.println(_debugLevel, \
977 0
            "_getTextContents: len=%d, start=%d, end=%d, line=<%s>" % \
978
            (len(line), startOffset, endOffset, line))
979
980 0
        if len(line):
981 0
            line = orca_state.activeScript.adjustForRepeats(line)
982 0
            textContents = line
983
984
        else:
985 0
            char = textObj.getTextAtOffset(caretOffset,
986 0
                atspi.Accessibility.TEXT_BOUNDARY_CHAR)
987 0
            debug.println(_debugLevel,
988 0
                "_getTextContents: character=<%s>, start=%d, end=%d" % \
989
                (char[0], char[1], char[2]))
990
991 0
            if char[0] == "\n" and startOffset == caretOffset \
992
                   and settings.speakBlankLines:
993 0
                textContents = (_("blank"))
994
995 0
    return [textContents, startOffset, endOffset, selected]
996
997
998 1
def _insertAttributes(obj, startOffset, endOffset, line):
999
    """
1000
    Adjust line to include attribute information.
1001
    """
1002 0
    text = obj.text
1003 0
    if not text:
1004 0
        return ""
1005
1006 0
    newLine = ""
1007 0
    textOffset = startOffset
1008
1009 0
    for i in range(0, len(line)):
1010 0
        attribs =_getAttributesForChar(text, textOffset, line, i)
1011 0
        debug.println(_debugLevel,
1012 0
                      "line attribs <%s>" % (attribs))
1013 0
        if attribs:
1014 0
            newLine += " ; "
1015 0
            newLine += attribs
1016 0
            newLine += " "
1017
1018 0
        newLine += line[i]
1019 0
        textOffset += 1
1020
1021 0
    debug.println(_debugLevel, "newLine: <%s>" % (newLine))
1022 0
    return newLine
1023
1024
1025 1
def _getAttributesForChar(text, textOffset, line, lineIndex):
1026
1027 0
    global _lastAttributeString
1028 0
    keys = [ "style", "weight", "underline" ]
1029
1030 0
    attribStr = ""
1031
1032 0
    charAttributes = text.getAttributes(textOffset)
1033
1034 0
    if charAttributes[0]:
1035 0
        charDict = _stringToDictionary(charAttributes[0])
1036 0
        debug.println(_debugLevel,
1037 0
                      "charDict: %s" % (charDict))
1038
1039 0
        for i in range(0, len(keys)):
1040 0
            key = keys[i]
1041 0
            if charDict.has_key(key):
1042 0
                attribute = charDict[key]
1043 0
                if attribute:
1044
                    # If it's the 'weight' attribute and greater than 400, just
1045
                    # speak it as bold, otherwise speak the weight.
1046
                    #
1047 0
                    if key == "weight" and int(attribute) > 400:
1048 0
                        attribStr += " "
1049 0
                        attribStr += _("bold")
1050
1051 0
                    elif key == "underline":
1052 0
                        if attribute != "none":
1053 0
                            attribStr += " "
1054 0
                            attribStr += key
1055
1056 0
                    elif key == "style":
1057 0
                        if attribute != "normal":
1058 0
                            attribStr += " "
1059 0
                            attribStr += attribute
1060
                    else:
1061 0
                        attribStr += " "
1062 0
                        attribStr += (key + " " + attribute)
1063
1064 0
        debug.println(_debugLevel,
1065 0
                      "char <%s>: %s" % (line[lineIndex], attribStr))
1066
1067
    # Only return attributes for the beginning of an attribute run.
1068 0
    if attribStr != _lastAttributeString:
1069 0
        _lastAttributeString = attribStr
1070 0
        return attribStr
1071
    else:
1072 0
        return ""
1073
1074
1075 1
def _stringToDictionary(str):
1076
    """
1077
    Converts a string of text attribute tokens of the form
1078
    <key>:<value>; into a dictionary of keys and values.
1079
    Text before the colon is the key and text afterwards is the
1080
    value. If there is a final semi-colon, then it's ignored.
1081
    """
1082 0
    dictionary = {}
1083 0
    allTokens = str.split(";")
1084 0
    for i in range(0, len(allTokens)):
1085 0
        item = allTokens[i].split(":")
1086 0
        if len(item) == 2:
1087 0
            item[0] = _removeLeadingSpaces(item[0])
1088 0
            item[1] = _removeLeadingSpaces(item[1])
1089 0
            dictionary[item[0]] = item[1]
1090
1091 0
    return dictionary
1092
1093
1094 1
def _removeLeadingSpaces(str):
1095
    """
1096
    Returns a string with the leading space characters removed.
1097
    """
1098 0
    newStr = ""
1099 0
    leadingSpaces = True
1100 0
    for i in range(0, len(str)):
1101 0
        if str[i] == " ":
1102 0
            if leadingSpaces:
1103 0
                continue
1104
        else:
1105 0
            leadingSpaces = False
1106
1107 0
        newStr += str[i]
1108
1109 0
    return newStr
1110
1111 1
def _handleOrcaKey(obj, doubleClick):
1112
    """
1113
    Handle the Orca modifier key being pressed.
1114
1115
    When Insert + KP_Enter is pressed a single time, Orca will speak
1116
    and display the following information:
1117
1118
    1. The contents of the title bar of the application main window
1119
    2. If in a dialog box within an application, the contents of the
1120
    title bar of the dialog box.
1121
    3. Orca will pause briefly between these two pieces of information
1122
    so that the speech user can distinguish each.
1123
    """
1124 0
    global _statusBar
1125 0
    utterances = []
1126
1127 0
    list = _getFrameAndDialog(obj)
1128 0
    if doubleClick:
1129 0
        if list[0]:
1130 0
            _statusBar = None
1131 0
            _getStatusBar(list[0])
1132 0
            if _statusBar:
1133 0
                _speakStatusBar()
1134
    else:
1135 0
        if list[0]:
1136 0
            text = _("%s") % _getObjLabelAndName(list[0])
1137 0
            utterances.append(text)
1138 0
        if list[1]:
1139 0
            text = _("%s") % _getObjLabelAndName(list[1])
1140 0
            utterances.append(text)
1141
1142 0
        debug.println(_debugLevel, "titlebar utterances=%s" % \
1143
                      utterances)
1144 0
        speech.speakUtterances(utterances)
1145
1146
1147 1
def _handleCalcOrcaKey(obj, doubleClick):
1148
    """
1149
    Handle the Orca modifier key being pressed.
1150
1151
    Calc-Specific Handling: If Insert+KP_Enter is pressed a single time
1152
    while focus is on a cell within OpenOffice Calc, Orca will speak the
1153
    following information:
1154
1155
    1. The contents of the title bar of the application main window
1156
    2. The title of the current worksheet
1157
1158
    Note that if the application with focus is Calc, but a cell does not
1159
    have focus, the default behavior should be used.
1160
    """
1161 0
    global _statusBar
1162 0
    utterances = []
1163
1164 0
    list = _getCalcFrameAndSheet(obj)
1165 0
    if doubleClick:
1166 0
        if list[0]:
1167 0
            _statusBar = None
1168 0
            _getStatusBar(list[0])
1169 0
            if _statusBar:
1170 0
                _speakCalcStatusBar()
1171
    else:
1172 0
        if list[0]:
1173 0
            text = _("%s") % _getObjLabelAndName(list[0])
1174 0
            utterances.append(text)
1175 0
        if list[1]:
1176 0
            text = _("%s") % _getObjLabelAndName(list[1])
1177 0
            utterances.append(text)
1178
1179 0
        debug.println(_debugLevel, "Calc titlebar and sheet utterances=%s" % \
1180
                      utterances)
1181 0
        speech.speakUtterances(utterances)
1182
1183
1184 1
def _getFrameAndDialog(obj):
1185
    """
1186
    Returns the frame and (possibly) the dialog containing
1187
    the object.
1188
    """
1189 0
    list = [None, None]
1190
1191 0
    parent = obj.parent
1192 0
    while parent and (parent.parent != parent):
1193
        #debug.println(_debugLevel, "_getFrameAndDialog: parent=%s, %s" % \
1194
        #             (parent.role, _getObjLabelAndName(parent)))
1195 0
        if parent.role == rolenames.ROLE_FRAME:
1196 0
            list[0] = parent
1197 0
        if parent.role == rolenames.ROLE_DIALOG:
1198 0
            list[1] = parent
1199 0
        parent = parent.parent
1200
1201 0
    return list
1202
1203
1204 1
def _getCalcFrameAndSheet(obj):
1205
    """
1206
    Returns the Calc frame and sheet
1207
    """
1208 0
    list = [None, None]
1209
1210 0
    parent = obj.parent
1211 0
    while parent and (parent.parent != parent):
1212
        # debug.println(_debugLevel, "_getCalcFrameAndSheet: parent=%s, %s" % \
1213
        #               (parent.role, _getObjLabelAndName(parent)))
1214 0
        if parent.role == rolenames.ROLE_FRAME:
1215 0
            list[0] = parent
1216 0
        if parent.role == rolenames.ROLE_TABLE:
1217 0
            list[1] = parent
1218 0
        parent = parent.parent
1219
1220 0
    return list
1221
1222
1223 1
def _getStatusBar(obj):
1224
    """
1225
    Gets the status bar.
1226
    """
1227 0
    global _statusBar
1228 0
    if _statusBar:
1229 0
        return
1230
1231
    # debug.println(_debugLevel, "_findStatusBar: ROOT=%s, %s" % \
1232
    #               (obj.role, _getObjLabelAndName(obj)))
1233
1234 0
    managesDescendants = obj.state.count(\
1235
        atspi.Accessibility.STATE_MANAGES_DESCENDANTS)
1236 0
    if managesDescendants:
1237 0
        return
1238
1239 0
    for i in range(0, obj.childCount):
1240 0
        child = obj.child(i)
1241
        # debug.println(_debugLevel, "_findStatusBar: child=%s, %s" % \
1242
        #               (child.role, _getObjLabelAndName(child)))
1243 0
        if child.role == rolenames.ROLE_STATUSBAR:
1244 0
            _statusBar = child
1245 0
            return
1246
1247 0
        if child.childCount > 0:
1248 0
            _getStatusBar(child)
1249
1250
1251 1
def _speakStatusBar():
1252
    """
1253
    Speaks the status bar.
1254
    """
1255 0
    global _statusBar
1256 0
    if not _statusBar:
1257 0
        return
1258
1259 0
    utterances = []
1260
1261 0
    if _statusBar.childCount == 0:
1262 0
        text = _("%s") % _getObjName(_statusBar)
1263 0
        utterances.append(text)
1264
    else:
1265 0
        for i in range(0, _statusBar.childCount):
1266 0
            child = _statusBar.child(i)
1267 0
            text = _("%s") % _getObjName(child)
1268 0
            utterances.append(text)
1269
1270 0
    debug.println(_debugLevel, "statusbar utterances=%s" % \
1271
                  utterances)
1272 0
    speech.speakUtterances(utterances)
1273
1274
1275 1
def _speakCalcStatusBar():
1276
    """
1277
    Speaks the OpenOffice Calc statusbar.
1278
    """
1279 0
    global _statusBar
1280 0
    if not _statusBar:
1281 0
        return
1282
1283 0
    utterances = []
1284 0
    for i in range(0, _statusBar.childCount):
1285 0
        child = _statusBar.child(i)
1286 0
        text = _("%s") % _getObjName(child)
1287 0
        utterances.append(text)
1288
1289 0
    debug.println(_debugLevel, "Calc statusbar utterances=%s" % \
1290
                  utterances)
1291 0
    speech.speakUtterances(utterances)
1292
1293
1294