[orca/570658] Begin porting Gecko's speech generator



commit a3abebf03b0d72a2c15d9e6cca0798e487811185
Author: Willie Walker <william walker sun com>
Date:   Tue May 19 16:27:30 2009 -0400

    Begin porting Gecko's speech generator
---
 src/orca/scripts/toolkits/Gecko/Makefile.am        |    3 +-
 src/orca/scripts/toolkits/Gecko/formatting.py      |   64 ++
 src/orca/scripts/toolkits/Gecko/script.py          |    4 +-
 .../scripts/toolkits/Gecko/speech_generator.py     |  814 +++++---------------
 src/orca/speechgenerator.py                        |   18 +-
 5 files changed, 261 insertions(+), 642 deletions(-)

diff --git a/src/orca/scripts/toolkits/Gecko/Makefile.am b/src/orca/scripts/toolkits/Gecko/Makefile.am
index af9a261..2de2e09 100644
--- a/src/orca/scripts/toolkits/Gecko/Makefile.am
+++ b/src/orca/scripts/toolkits/Gecko/Makefile.am
@@ -1,9 +1,10 @@
 orca_pathdir=$(pyexecdir)
 
 orca_python_PYTHON = \
+	__init__.py \
 	bookmarks.py \
 	braille_generator.py \
-	__init__.py \
+	formatting.py \
 	script.py \
 	script_settings.py \
 	speech_generator.py \
diff --git a/src/orca/scripts/toolkits/Gecko/formatting.py b/src/orca/scripts/toolkits/Gecko/formatting.py
new file mode 100644
index 0000000..2fe7b04
--- /dev/null
+++ b/src/orca/scripts/toolkits/Gecko/formatting.py
@@ -0,0 +1,64 @@
+# Orca
+#
+# Copyright 2006-2009 Sun Microsystems Inc.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
+# Boston MA  02110-1301 USA.
+
+"""Custom formatting for Gecko."""
+
+__id__ = "$Id$"
+__version__   = "$Revision$"
+__date__      = "$Date$"
+__copyright__ = "Copyright (c) 2005-2009 Sun Microsystems Inc."
+__license__   = "LGPL"
+
+import pyatspi
+
+import orca.formatting
+
+formatting = {
+    'speech': {
+        pyatspi.ROLE_ALERT: {
+            'unfocused': 'expandedEOCs or (labelAndName + unrelatedLabels)'
+            },
+        pyatspi.ROLE_DOCUMENT_FRAME: {
+            'unfocused': 'name + roleName'
+            },
+        pyatspi.ROLE_LIST: {
+            'unfocused': 'labelOrName + multiselectableState + numberOfChildren'
+            },
+    }
+}
+
+class Formatting(orca.formatting.Formatting):
+
+    # pylint: disable-msg=W0142
+
+    def __init__(self, script):
+        orca.formatting.Formatting.__init__(self, script)
+        self.update(formatting)
+        # This is a copy of the default formatting, which we will
+        # use for ARIA widgets.
+        #
+        self._defaultFormatting = orca.formatting.Formatting(script)
+
+    def getFormat(self, dictType, **args):
+        # ARIA widgets get treated like regular default widgets.
+        #
+        if args.get('isAria', False):
+            return self._defaultFormatting.getFormat(dictType, **args)
+        else:
+            return orca.formatting.Formatting.getFormat(self, dictType, **args)
diff --git a/src/orca/scripts/toolkits/Gecko/script.py b/src/orca/scripts/toolkits/Gecko/script.py
index 0f688eb..92dc1af 100644
--- a/src/orca/scripts/toolkits/Gecko/script.py
+++ b/src/orca/scripts/toolkits/Gecko/script.py
@@ -5425,7 +5425,7 @@ class Script(default.Script):
                 utterance = [string]
                 if speakRole and not role in doNotSpeakRoles:
                     utterance.extend(\
-                        self.speechGenerator.getSpeechForObjectRole(obj))
+                        self.speechGenerator.getRoleName(obj))
   
             # If the object is a heading, or is contained within a heading,
             # speak that role information at the end of the object.
@@ -5442,7 +5442,7 @@ class Script(default.Script):
 
                 if heading:
                     utterance.extend(\
-                        self.speechGenerator.getSpeechForObjectRole(heading))
+                        self.speechGenerator.getRoleName(heading))
 
             for item in utterance:
                 utterances.append([item, self.getACSS(obj, item)])
diff --git a/src/orca/scripts/toolkits/Gecko/speech_generator.py b/src/orca/scripts/toolkits/Gecko/speech_generator.py
index c3c8bd9..e35cbdc 100644
--- a/src/orca/scripts/toolkits/Gecko/speech_generator.py
+++ b/src/orca/scripts/toolkits/Gecko/speech_generator.py
@@ -1,6 +1,6 @@
 # Orca
 #
-# Copyright 2005-2008 Sun Microsystems Inc.
+# Copyright 2005-2009 Sun Microsystems Inc.
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Library General Public
@@ -26,18 +26,16 @@ http://developer.mozilla.org/en/docs/Accessibility/ATSPI_Support
 __id__        = "$Id$"
 __version__   = "$Revision$"
 __date__      = "$Date$"
-__copyright__ = "Copyright (c) 2005-2008 Sun Microsystems Inc."
+__copyright__ = "Copyright (c) 2005-2009 Sun Microsystems Inc."
 __license__   = "LGPL"
 
 import pyatspi
 
 import orca.rolenames as rolenames
-import orca.settings as settings
 import orca.speechgenerator as speechgenerator
 
 from orca.orca_i18n import _
 from orca.orca_i18n import ngettext # for ngettext support
-from orca.orca_i18n import C_       # to provide qualified translatable strings
 
 ########################################################################
 #                                                                      #
@@ -49,21 +47,93 @@ class SpeechGenerator(speechgenerator.SpeechGenerator):
     """Provides a speech generator specific to Gecko.
     """
 
+    # pylint: disable-msg=W0142
+
     def __init__(self, script):
         speechgenerator.SpeechGenerator.__init__(self, script)
-        self.speechGenerators[pyatspi.ROLE_DOCUMENT_FRAME] = \
-             self._getSpeechForDocumentFrame
-        self.speechGenerators[pyatspi.ROLE_ENTRY]          = \
-             self._getSpeechForText
-        self.speechGenerators[pyatspi.ROLE_LINK]           = \
-             self._getSpeechForLink
-        self.speechGenerators[pyatspi.ROLE_LIST_ITEM]      = \
-             self._getSpeechForListItem
-        self.speechGenerators[pyatspi.ROLE_SLIDER]         = \
-             self._getSpeechForSlider
-
-    def getSpeechForObjectRole(self, obj, role=None):
+
+    def _getName(self, obj, **args):
+        result = speechgenerator.SpeechGenerator._getName(self, obj, **args)
+        role = args.get('role', obj.getRole())
+        if role == pyatspi.ROLE_LINK:
+            # Handle empty alt tags.
+            #
+            if result and len(result[0].strip()):
+                pass
+            else:
+                # If there's no text for the link, expose part of the
+                # URI to the user.
+                #
+                basename = self._script.getLinkBasename(obj)
+                if basename:
+                    result.append(basename)
+        return result
+
+    def _getLabel(self, obj, **args):
+        result = speechgenerator.SpeechGenerator._getLabel(self, obj, **args)
+        role = args.get('role', obj.getRole())
+        # We'll attempt to guess the label under some circumstances.
+        #
+        if not len(result) \
+           and role in [pyatspi.ROLE_CHECK_BOX,
+                        pyatspi.ROLE_COMBO_BOX,
+                        pyatspi.ROLE_ENTRY,
+                        pyatspi.ROLE_LIST,
+                        pyatspi.ROLE_PARAGRAPH,
+                        pyatspi.ROLE_PASSWORD_TEXT,
+                        pyatspi.ROLE_RADIO_BUTTON,
+                        pyatspi.ROLE_TEXT] \
+           and self._script.inDocumentContent() \
+           and not self._script.isAriaWidget(obj):
+            label = self._script.guessTheLabel(obj)
+            if label:
+                result.append(label)
+        return result
+
+    def _getLabelAndName(self, obj, **args):
+        result = []
+        role = args.get('role', obj.getRole())
+        # For radio buttons, the label is handled as a context and we
+        # assume we don't have to guess it.  If we need to guess it,
+        # we need to add it to utterances.
+        #
+        if role == pyatspi.ROLE_RADIO_BUTTON \
+           and self._script.getDisplayedLabel(obj):
+            pass
+        else:
+            result.extend(speechgenerator.SpeechGenerator._getLabelAndName(
+                self, obj, **args))
+        return result
+
+    def _getLabelOrName(self, obj, **args):
+        result = []
+        if obj.parent.getRole() == pyatspi.ROLE_AUTOCOMPLETE:
+            # This is the main difference between this class and the default
+            # class - we'll give this thing a name here, and we'll make it
+            # be the name of the autocomplete.
+            #
+            result.extend(self._getLabelOrName(obj.parent, **args))
+        else:
+            result.extend(speechgenerator.SpeechGenerator._getLabelOrName(
+                self, obj, **args))
+
+    def _getRoleName(self, obj, **args):
         """Prevents some roles from being spoken."""
+        result = []
+        role = args.get('role', obj.getRole())
+
+        # Saying "menu item" for a combo box can confuse users. Therefore,
+        # speak the combo box role instead.  Also, only do it if the menu
+        # item is not focused (if the menu item is focused, it means we're
+        # navigating in the combo box)
+        #
+        if not obj.getState().contains(pyatspi.STATE_FOCUSED):
+            comboBox = self._script.getAncestor(obj,
+                                                [pyatspi.ROLE_COMBO_BOX],
+                                                [pyatspi.ROLE_DOCUMENT_FRAME])
+            if comboBox:
+                return self._getRoleName(comboBox, **args)
+
         doNotSpeak = [pyatspi.ROLE_FORM,
                       pyatspi.ROLE_LABEL,
                       pyatspi.ROLE_MENU_ITEM,
@@ -77,9 +147,8 @@ class SpeechGenerator(speechgenerator.SpeechGenerator):
                 doNotSpeak.append(pyatspi.ROLE_LIST_ITEM)
                 doNotSpeak.append(pyatspi.ROLE_LIST)
 
-        utterances = []
-        if role or not obj.getRole() in doNotSpeak:
-            if obj.getRole() == pyatspi.ROLE_HEADING:
+        if not (role in doNotSpeak):
+            if role == pyatspi.ROLE_HEADING:
                 level = self._script.getHeadingLevel(obj)
                 if level:
                     # Translators: the %(level)d is in reference to a heading
@@ -87,594 +156,99 @@ class SpeechGenerator(speechgenerator.SpeechGenerator):
                     # and the %(role)s is in reference to a previously
                     # translated rolename for the heading.
                     #
-                    utterances.append(_("%(role)s level %(level)d") % {
+                    result.append(_("%(role)s level %(level)d") % {
                         'role': rolenames.getSpeechForRoleName(obj, role),
                         'level': level})
                 else:
-                    utterances.append(rolenames.getSpeechForRoleName(obj, role))
+                    result.append(rolenames.getSpeechForRoleName(obj, role))
             else:
-                utterances.append(rolenames.getSpeechForRoleName(obj, role))
-
-        return utterances
+                result.append(rolenames.getSpeechForRoleName(obj, role))
 
-    def _getSpeechForAlert(self, obj, already_focused):
-        """Gets the speech for an alert.  What we do here is first try
-        to see if the alert contains text via embedded object characters.
-        If it does, we speak that text.  If it doesn't, we defer to the
-        super class.  The prototype alert we're shooting for is the one
-        from http://bugzilla.gnome.org/show_bug.cgi?id=570551
+        # If this is a link with a child which is an image, we want
+        # to indicate that.
+        #
+        if role == pyatspi.ROLE_LINK \
+           and obj.childCount and obj[0].getRole() == pyatspi.ROLE_IMAGE:
+            result.extend(self._getRoleName(obj[0], **args))
 
-        Arguments:
-        - obj: an Accessible
-        - already_focused: False if object just received focus
+        return result
 
-        Returns a list of utterances to be spoken for the object.
-        """
+    def getRoleName(self, obj):
+        return self._getRoleName(obj)
 
+    def _getExpandedEOCs(self, obj, **args):
+        """Returns the expanded embedded object characters for an object."""
+        result = []
         text = self._script.expandEOCs(obj)
         if text:
-            return [text]
-        else:
-            return speechgenerator.SpeechGenerator.\
-                       _getSpeechForAlert(self, obj, already_focused)
-
-    def _getSpeechForDocumentFrame(self, obj, already_focused):
-        """Gets the speech for a document frame.
-
-        Arguments:
-        - obj: an Accessible
-        - already_focused: False if object just received focus
-
-        Returns a list of utterances to be spoken for the object.
-        """
-
-        utterances = []
-
-        name = obj.name
-        if name and len(name):
-            utterances.append(name)
-        utterances.extend(self.getSpeechForObjectRole(obj))
-
-        self._debugGenerator("Gecko._getSpeechForDocumentFrame",
-                             obj,
-                             already_focused,
-                             utterances)
-
-        return utterances
-
-    def _getSpeechForText(self, obj, already_focused):
-        """Gets the speech for an autocomplete box.
-
-        Arguments:
-        - obj: an Accessible
-        - already_focused: False if object just received focus
-
-        Returns a list of utterances to be spoken for the object.
-        """
-
-        # Treat ARIA widgets like default.py widgets
-        #
-        if self._script.isAriaWidget(obj):
-            return speechgenerator.SpeechGenerator.\
-                       _getSpeechForText(self, obj, already_focused)
-        
-        utterances = []
-        parent = obj.parent
-        if parent.getRole() == pyatspi.ROLE_AUTOCOMPLETE:
-            # This is the main difference between this class and the default
-            # class - we'll give this thing a name here, and we'll make it
-            # be the name of the autocomplete.
-            #
-            label = self._script.getDisplayedLabel(parent)
-            if not label or not len(label):
-                label = parent.name
-            utterances.append(label)
-        elif obj.getRole() in [pyatspi.ROLE_ENTRY,
-                               pyatspi.ROLE_PASSWORD_TEXT] \
-            and self._script.inDocumentContent():
-            # This is a form field in web content.  If we don't get a label,
-            # we'll try to guess what text on the page is functioning as
-            # the label.
+            result.append(text)
+        return result
+
+    def _getDisplayedText(self, obj, **args):
+        result = []
+        if obj.getRole() == pyatspi.ROLE_COMBO_BOX:
+            # With Gecko, a combo box has a menu as a child.  The text being
+            # displayed for the combo box can be obtained via the selected
+            # menu item.
             #
-            label = self._script.getDisplayedLabel(obj)
-            if not label or not len(label):
-                label = self._script.guessTheLabel(obj)
-            if label:
-                utterances.append(label)
-        else:
-            return speechgenerator.SpeechGenerator._getSpeechForText(
-                self, obj, already_focused)
-
-        if settings.presentReadOnlyText \
-           and self._script.isReadOnlyTextArea(obj):
-            utterances.append(settings.speechReadOnlyString)
-
-        utterances.extend(self.getSpeechForObjectRole(obj))
-
-        [text, caretOffset, startOffset] = self._script.getTextLineAtCaret(obj)
-        utterances.append(text)
-
-        self._debugGenerator("Gecko._getSpeechForText",
-                             obj,
-                             already_focused,
-                             utterances)
-
-        return utterances
-
-    def _getSpeechForComboBox(self, obj, already_focused):
-        """Get the speech for a combo box.  If the combo box already has focus,
-        then only the selection is spoken.
-
-        Arguments:
-        - obj: the combo box
-        - already_focused: False if object just received focus
-
-        Returns a list of utterances to be spoken for the object.
-        """
-
-        # Treat ARIA widgets like default.py widgets
-        #
-        if self._script.isAriaWidget(obj):
-            return speechgenerator.SpeechGenerator.\
-                       _getSpeechForComboBox(self, obj, already_focused)
-        
-        utterances = []
-
-        label = self._script.getDisplayedLabel(obj)
-        if not label:
-            if not self._script.inDocumentContent():
-                label = obj.name
-            else:
-                label = self._script.guessTheLabel(obj)
-
-        if not already_focused and label:
-            utterances.append(label)
-
-        # With Gecko, a combo box has a menu as a child.  The text being
-        # displayed for the combo box can be obtained via the selected
-        # menu item.
-        #
-        menu = None
-        for child in obj:
-            if child.getRole() == pyatspi.ROLE_MENU:
-                menu = child
-                break
-        if menu:
-            child = None
-            try:
-                # This should work...
-                #
-                child = menu.querySelection().getSelectedChild(0)
-                if not child:
-                    # It's probably a Gtk combo box.
+            menu = None
+            for child in obj:
+                if child.getRole() == pyatspi.ROLE_MENU:
+                    menu = child
+                    break
+            if menu:
+                child = None
+                try:
+                    # This should work...
                     #
-                    return speechgenerator.SpeechGenerator.\
-                        _getSpeechForComboBox(self, obj, already_focused)
-            except:
-                # But just in case, we'll fall back on this.
-                # [[[TODO - JD: Will we ever have a case where the first
-                # fails, but this will succeed???]]]
-                #
-                for item in menu:
-                    if item.getState().contains(pyatspi.STATE_SELECTED):
-                        child = item
-                        break
-            if child:
-                utterances.append(child.name)
-
-        utterances.extend(self._getSpeechForObjectAvailability(obj))
-
-        if not already_focused:
-            utterances.extend(self.getSpeechForObjectRole(obj))
-
-        self._debugGenerator("Gecko._getSpeechForComboBox",
-                             obj,
-                             already_focused,
-                             utterances)
-
-        return utterances
-
-    def _getSpeechForMenuItem(self, obj, already_focused):
-        """Get the speech for a menu item.
-
-        Arguments:
-        - obj: the menu item
-        - already_focused: False if object just received focus
-
-        Returns a list of utterances to be spoken for the object.
-        """
-
-        # Treat ARIA widgets like default.py widgets
-        #
-        if self._script.isAriaWidget(obj):
-            return speechgenerator.SpeechGenerator.\
-                       _getSpeechForMenuItem(self, obj, already_focused)
-        
-        if not self._script.inDocumentContent():
-            return speechgenerator.SpeechGenerator.\
-                       _getSpeechForMenuItem(self, obj, already_focused)
-
-        utterances = self._getSpeechForObjectName(obj)
-
-        # Saying "menu item" for a combo box can confuse users. Therefore,
-        # speak the combo box role instead.  Also, only do it if the menu
-        # item is not focused (if the menu item is focused, it means we're
-        # navigating in the combo box)
-        #
-        if not obj.getState().contains(pyatspi.STATE_FOCUSED):
-            comboBox = \
-                 self._script.getAncestor(obj,
-                                          [pyatspi.ROLE_COMBO_BOX],
-                                          [pyatspi.ROLE_DOCUMENT_FRAME])
-            if comboBox:
-                utterances.extend(self.getSpeechForObjectRole(comboBox))
-
-        self._debugGenerator("Gecko._getSpeechForMenuItem",
-                             obj,
-                             already_focused,
-                             utterances)
-
-        return utterances
-
-    def _getSpeechForListItem(self, obj, already_focused):
-        """Get the speech for a list item.
-
-        Arguments:
-        - obj: the list item
-        - already_focused: False if object just received focus
-
-        Returns a list of utterances to be spoken for the object.
-        """
-
-        # Treat ARIA widgets like default.py widgets
-        #
-
-        if self._script.isAriaWidget(obj) \
-           or not self._script.inDocumentContent(obj):
-            return speechgenerator.SpeechGenerator.\
-                       _getSpeechForListItem(self, obj, already_focused)
-        
-        if not obj.getState().contains(pyatspi.STATE_SELECTABLE):
-            return speechgenerator.SpeechGenerator.\
-                       _getDefaultSpeech(self, obj, already_focused)
-
-        utterances = self._getSpeechForObjectName(obj)
-
-        self._debugGenerator("Gecko._getSpeechForListItem",
-                             obj,
-                             already_focused,
-                             utterances)
-
-        return utterances
-
-    def _getSpeechForList(self, obj, already_focused):
-        """Get the speech for a list.
-
-        Arguments:
-        - obj: the list
-        - already_focused: False if object just received focus
-
-        Returns a list of utterances to be spoken for the object.
-        """
-
-        # Treat ARIA widgets like default.py widgets
-        #
-        if self._script.isAriaWidget(obj):
-            return speechgenerator.SpeechGenerator.\
-                       _getSpeechForList(self, obj, already_focused)
-        
-        if not obj.getState().contains(pyatspi.STATE_FOCUSABLE):
-            return speechgenerator.SpeechGenerator.\
-                       _getSpeechForList(self, obj, already_focused)
-
-        utterances = []
-
-        label = self._script.getDisplayedLabel(obj)
-        if not label:
-            if not self._script.inDocumentContent():
-                label = obj.name
-            else:
-                label = self._script.guessTheLabel(obj)
-
-        if not already_focused and label:
-            utterances.append(label)
-
-        item = None
-        selection = obj.querySelection()
-        for i in xrange(obj.childCount):
-            if selection.isChildSelected(i):
-                item = obj[i]
-                break
-        item = item or obj[0]
-        if item:
-            name = self._getSpeechForObjectName(item)
-            if name != label:
-                utterances.extend(name)
-
-        if not already_focused:
-            if obj.getState().contains(pyatspi.STATE_MULTISELECTABLE):
-                # Translators: "multi-select" refers to a web form list
-                # in which more than one item can be selected at a time.
-                #
-                utterances.append(_("multi-select"))
-
-            # Translators: this represents a list in HTML.
-            #
-            itemString = ngettext("List with %d item",
-                                  "List with %d items",
-                                  obj.childCount) % obj.childCount
-            utterances.append(itemString)
-
-        self._debugGenerator("Gecko._getSpeechForList",
-                             obj,
-                             already_focused,
-                             utterances)
-
-        return utterances
-
-    def _getSpeechForImage(self, obj, already_focused):
-        """Gets a list of utterances to be spoken for an image.
-
-        The default speech will be of the following form:
-
-        label name role availability
-
-        Arguments:
-        - obj: an Accessible
-        - already_focused: False if object just received focus
-
-        Returns a list of utterances to be spoken for the object.
-        """
-
-        # Treat ARIA widgets like default.py widgets
-        #
-        if self._script.isAriaWidget(obj):
-            return speechgenerator.SpeechGenerator.\
-                       _getSpeechForImage(self, obj, already_focused)
-        
-        utterances = []
-
-        if not already_focused:
-            label = self._getSpeechForObjectLabel(obj)
-            utterances.extend(label)
-            name = self._getSpeechForObjectName(obj)
-            if name != label:
-                utterances.extend(name)
-
-            # If there's no text for the image, expose the link to
-            # the user if the image is in a link.
-            #
-            link = self._script.getAncestor(obj,
-                                            [pyatspi.ROLE_LINK],
-                                            [pyatspi.ROLE_DOCUMENT_FRAME])
-            if link:
-                if not len(utterances):
-                    return self._getSpeechForLink(link, already_focused)
-                else:
-                    utterances.extend(self.getSpeechForObjectRole(link))
-
-            utterances.extend(self.getSpeechForObjectRole(obj))
-
-        utterances.extend(self._getSpeechForObjectAvailability(obj))
-
-        self._debugGenerator("Gecko._getSpeechForImage",
-                             obj,
-                             already_focused,
-                             utterances)
-
-        return utterances
-
-    def _getSpeechForLink(self, obj, already_focused):
-        """Gets a list of utterances to be spoken for a link.
-
-        Arguments:
-        - obj: an Accessible
-        - already_focused: False if object just received focus
-
-        Returns a list of utterances to be spoken for the object.
-        """
-
-        utterances = []
-
-        if not already_focused:
-            label = self._getSpeechForObjectLabel(obj)
-            utterances.extend(label)
-            name = self._getSpeechForObjectName(obj)
-
-            # Handle empty alt tags.
-            #
-            if name:
-                lengthOfName = len(name[0].strip())
-                if (lengthOfName > 0) and (name != label):
-                    utterances.extend(name)
-
-            # If there's no text for the link, expose part of the
-            # URI to the user.
-            #
-            if not len(utterances):
-                basename = self._script.getLinkBasename(obj)
-                if basename:
-                    utterances.append(basename)
-
-            utterances.extend(self.getSpeechForObjectRole(obj))
-
-            # If the link has a child which is an image, we want
-            # to indicate that.
-            #
-            if obj.childCount and obj[0].getRole() == pyatspi.ROLE_IMAGE:
-                utterances.extend(self.getSpeechForObjectRole(obj[0]))
-
-        utterances.extend(self._getSpeechForObjectAvailability(obj))
-
-        self._debugGenerator("Gecko._getSpeechForLink",
-                             obj,
-                             already_focused,
-                             utterances)
-
-        return utterances
-
-    def _getSpeechForTable(self, obj, already_focused):
-        """Get the speech for a table
-
-        Arguments:
-        - obj: the table
-        - already_focused: False if object just received focus
-
-        Returns a list of utterances to be spoken for the object.
-        """
-
-        # Treat ARIA widgets like default.py widgets
-        #
-        if self._script.isAriaWidget(obj):
-            return speechgenerator.SpeechGenerator.\
-                       _getSpeechForTable(self, obj, already_focused)
-        
-        # [[[TODO: JD - We should decide if we want to provide
-        # information about the table dimensions, whether or not
-        # this is a layout table versus a data table, etc.  For now,
-        # however, if it's in HTML content let's ignore it so that
-        # SayAll by sentence works. :-) ]]]
-        #
-        utterances = []
-
-        if not self._script.inDocumentContent():
-            return speechgenerator.SpeechGenerator.\
-                       _getSpeechForTable(self, obj, already_focused)
-
-        return utterances
-
-    def _getSpeechForRadioButton(self, obj, already_focused):
-        """Get the speech for a radio button.  If the button already had
-        focus, then only the state is spoken.
-
-        Arguments:
-        - obj: the radio button
-        - already_focused: False if object just received focus
-
-        Returns a list of utterances to be spoken for the object.
-        """
-
-        # Treat ARIA widgets like default.py widgets
-        #
-        if self._script.isAriaWidget(obj):
-            return speechgenerator.SpeechGenerator.\
-                       _getSpeechForRadioButton(self, obj, already_focused)
-        
-        if not self._script.inDocumentContent():
-            return speechgenerator.SpeechGenerator.\
-                       _getSpeechForRadioButton(self, obj, already_focused)
-
-        utterances = []
-        if obj.getState().contains(pyatspi.STATE_CHECKED):
-            # Translators: this is in reference to a radio button being
-            # selected or not.
-            #
-            selectionState = C_("radiobutton", "selected")
-        else:
-            # Translators: this is in reference to a radio button being
-            # selected or not.
-            #
-            selectionState = C_("radiobutton", "not selected")
-
-        if not already_focused:
-            # The label is handled as a context in default.py -- assuming we
-            # don't have to guess it.  If  we need to guess it, we need to
-            # add it to utterances.
-            #
-            label = self._script.getDisplayedLabel(obj)
-            if not label:
-                label = self._script.guessTheLabel(obj)
-                if label:
-                    utterances.append(label)
-
-            utterances.append(selectionState)
-            utterances.extend(self.getSpeechForObjectRole(obj))
-            utterances.extend(self._getSpeechForObjectAvailability(obj))
+                    child = menu.querySelection().getSelectedChild(0)
+                    if not child:
+                        # It's probably a Gtk combo box.
+                        #
+                        result = \
+                            speechgenerator.SpeechGenerator._getDisplayedText(
+                                self, obj, **args)
+                except:
+                    # But just in case, we'll fall back on this.
+                    # [[[TODO - JD: Will we ever have a case where the first
+                    # fails, but this will succeed???]]]
+                    #
+                    for item in menu:
+                        if item.getState().contains(pyatspi.STATE_SELECTED):
+                            child = item
+                            break
+                if child and child.name:
+                    result.append(child.name)
         else:
-            utterances.append(selectionState)
-
-        self._debugGenerator("Gecko._getSpeechForRadioButton",
-                             obj,
-                             already_focused,
-                             utterances)
-        return utterances
-
-    def _getSpeechForCheckBox(self, obj, already_focused):
-        """Get the speech for a check box.  If the check box already had
-        focus, then only the state is spoken.
+            result = \
+                speechgenerator.SpeechGenerator._getDisplayedText(
+                    self, obj, **args)
+        return result
 
-        Arguments:
-        - obj: the check box
-        - already_focused: False if object just received focus
+    def _getNumberOfChildren(self, obj, **args):
+        result = []
+        role = args.get('role', obj.getRole())
 
-        Returns a list of utterances to be spoken for the object.
-        """
-
-        # Treat ARIA widgets like default.py widgets
-        #
-        if self._script.isAriaWidget(obj):
-            return speechgenerator.SpeechGenerator.\
-                       _getSpeechForCheckBox(self, obj, already_focused)
-        
-        if not self._script.inDocumentContent():
-            return speechgenerator.SpeechGenerator.\
-                       _getSpeechForCheckBox(self, obj, already_focused)
-
-        utterances = []
-        state = obj.getState()
-        if state.contains(pyatspi.STATE_INDETERMINATE):
-            # Translators: this represents the state of a checkbox.
-            #
-            checkedState = _("partially checked")
-        elif state.contains(pyatspi.STATE_CHECKED):
-            # Translators: this represents the state of a checkbox.
-            #
-            checkedState = _("checked")
-        else:
-            # Translators: this represents the state of a checkbox.
+        if role == pyatspi.ROLE_LIST:
+            # Translators: this represents a list in HTML.
             #
-            checkedState = _("not checked")
-
-        # If it's not already focused, say its label.
-        #
-        if not already_focused:
-            label = self._script.getDisplayedLabel(obj)
-            if not label:
-                label = self._script.guessTheLabel(obj)
-            if label:
-                utterances.append(label)
-            utterances.extend(self.getSpeechForObjectRole(obj))
-            utterances.append(checkedState)
-            utterances.extend(self._getSpeechForObjectAvailability(obj))
+            result.append(ngettext("List with %d item",
+                                   "List with %d items",
+                                   obj.childCount) % obj.childCount)
         else:
-            utterances.append(checkedState)
+            result.extend(speechgenerator.SpeechGenerator._getNumberOfChildren(
+                self, obj, **args))
+        return result
 
-        self._debugGenerator("Gecko._getSpeechForCheckBox",
-                             obj,
-                             already_focused,
-                             utterances)
+    def _getAncestors(self, obj, **args):
+        result = []
+        priorObj = args.get('priorObj', None)
+        commonAncestor = self._script.findCommonAncestor(priorObj, obj)
 
-        return utterances
+        if obj is commonAncestor:
+            return result
 
-    def getSpeechContext(self, obj, stopAncestor=None):
-        """Get the speech that describes the names and role of
-        the container hierarchy of the object, stopping at and
-        not including the stopAncestor.
-
-        Arguments:
-        - obj: the object
-        - stopAncestor: the ancestor to stop at and not include (None
-          means include all ancestors)
-
-        Returns a list of utterances to be spoken.
-        """
-
-        utterances = []
-
-        if obj is stopAncestor:
-            return utterances
-
-        # Skip items of unknown rolenames, menu bars, labels with 
+        # Skip items of unknown rolenames, menu bars, labels with
         # children, and autocompletes.  (With autocompletes, we
         # wind up speaking the text object). Beginning with Firefox
         # 3.2, list items have names corresponding with their text.
@@ -702,7 +276,7 @@ class SpeechGenerator(speechgenerator.SpeechGenerator):
         parent = obj.parent
         while parent and (parent.parent != parent):
             role = parent.getRole()
-            if self._script.isSameObject(parent, stopAncestor) \
+            if self._script.isSameObject(parent, commonAncestor) \
                or role in stopRoles:
                 break
 
@@ -717,8 +291,8 @@ class SpeechGenerator(speechgenerator.SpeechGenerator):
                and parent.parent.getRole() == pyatspi.ROLE_COMBO_BOX:
                 parent = parent.parent
                 continue
-                
-            # Also skip the parent if its accessible text is a single 
+
+            # Also skip the parent if its accessible text is a single
             # EMBEDDED_OBJECT_CHARACTER: Script.getDisplayedText will
             # end up coming back to the child of an object for the text
             # if an object's text contains a single EOC. In addition,
@@ -743,65 +317,39 @@ class SpeechGenerator(speechgenerator.SpeechGenerator):
             #
             text = self._script.getDisplayedText(parent)
             label = self._script.getDisplayedLabel(parent)
-            newUtterances = []
+            newResult = []
             if text and (text != label) and len(text.strip()) \
                 and (not text.startswith("chrome://")):
-                newUtterances.append(text)
+                newResult.append(text)
             if label and len(label.strip()):
-                newUtterances.append(label)
+                newResult.append(label)
 
             # Finally add the role if it's not among the roles we don't
             # wish to speak.
             #
-            if not role in dontSpeakRoles and len(newUtterances):
-                utterances.append(rolenames.getSpeechForRoleName(parent))
+            if not (role in dontSpeakRoles) and len(newResult):
+                result.append(rolenames.getSpeechForRoleName(parent))
 
             # If this object is an ARIA widget with STATE_REQUIRED, add
             # that. (Note that for the most part, the ARIA widget itself
             # has this state, but in the case of a group of radio buttons,
             # it is the group which has the state).
             #
-            utterances.extend(self._getSpeechForRequiredObject(parent))
+            result.extend(self._getRequired(parent, **args))
 
-            utterances.extend(newUtterances)
+            result.extend(newResult)
 
             parent = parent.parent
 
-        utterances.reverse()
+        result.reverse()
 
-        return utterances
-            
-    def _getSpeechForSlider(self, obj, already_focused):
-        """Get the speech for a slider.  If the object already
-        had focus, just the value is spoken.
+        return result
 
-        Arguments:
-        - obj: the slider
-        - already_focused: False if object just received focus
-
-        Returns a list of utterances to be spoken for the object.
-        """
-        
-        # Let default handle non-ARIA widgets (XUL?)
-        if self._script.isAriaWidget(obj):
-            return speechgenerator.SpeechGenerator.\
-                       _getSpeechForSlider(self, obj, already_focused)
-
-        valueString = self._script.getTextForValue(obj)
-
-        if already_focused:
-            utterances = [valueString]
-        else:
-            utterances = []
-            utterances.extend(self._getSpeechForObjectLabel(obj))
-            utterances.extend(self.getSpeechForObjectRole(obj))
-            utterances.append(valueString)
-            utterances.extend(self._getSpeechForObjectAvailability(obj))
-
-        self._debugGenerator("Gecko._getSpeechForSlider",
-                             obj,
-                             already_focused,
-                             utterances)
-
-        return utterances
-                        
+    def getSpeech(self, obj, **args):
+        # ARIA widgets get treated like regular default widgets.
+        #
+        result = []
+        args['isAria'] = self._script.isAriaWidget(obj)
+        result = speechgenerator.SpeechGenerator.getSpeech(self, obj, **args)
+        del args['isAria']
+        return result
diff --git a/src/orca/speechgenerator.py b/src/orca/speechgenerator.py
index fd41bfe..22da38c 100644
--- a/src/orca/speechgenerator.py
+++ b/src/orca/speechgenerator.py
@@ -293,10 +293,18 @@ class SpeechGenerator:
                 result.append(_("collapsed"))
         return result
 
+    def _getMultiselectableState(self, obj, **args):
+        result = []
+        if obj.getState().contains(pyatspi.STATE_MULTISELECTABLE):
+            # Translators: "multi-select" refers to a web form list
+            # in which more than one item can be selected at a time.
+            #
+            result.append(_("multi-select"))
+        return result
+
     def _getMenuItemCheckedState(self, obj, **args):
         result = []
-        state = obj.getState()
-        if state.contains(pyatspi.STATE_CHECKED):
+        if obj.getState().contains(pyatspi.STATE_CHECKED):
             # Translators: this represents the state of a checked menu item.
             #
             result.append(_("checked"))
@@ -304,8 +312,7 @@ class SpeechGenerator:
 
     def _getAvailability(self, obj, **args):
         result = []
-        state = obj.getState()
-        if not state.contains(pyatspi.STATE_SENSITIVE):
+        if not obj.getState().contains(pyatspi.STATE_SENSITIVE):
             # Translators: this represents an item on the screen that has
             # been set insensitive (or grayed out).
             #
@@ -314,8 +321,7 @@ class SpeechGenerator:
 
     def _getRequired(self, obj, **args):
         result = []
-        state = obj.getState()
-        if state.contains(pyatspi.STATE_REQUIRED):
+        if obj.getState().contains(pyatspi.STATE_REQUIRED):
             result = [settings.speechRequiredStateString]
         return result
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]