[orca] Fix for bug #543157 - It is not always possible to distinguish on-screen text from Orca's "system" m



commit dbc8d2c388d721c30506bc5cdca97719b228e349
Author: Joanmarie Diggs <joanmarie diggs gmail com>
Date:   Sat Aug 14 22:05:10 2010 -0400

    Fix for bug #543157 - It is not always possible to distinguish on-screen text from Orca's "system" messages.
    
    Also fixes bug #412656 - Refactor speech generators to provide ACSS per string.

 src/orca/default.py                                |   44 ++-
 src/orca/orca-setup.ui                             |    3 +
 src/orca/orca.py                                   |    2 +-
 src/orca/orca_console_prefs.py                     |    4 +-
 src/orca/orca_gui_prefs.py                         |   54 +++-
 .../scripts/apps/evolution/speech_generator.py     |    5 +
 .../scripts/apps/gcalctool/speech_generator.py     |   11 +-
 src/orca/scripts/apps/soffice/speech_generator.py  |   29 ++
 .../scripts/toolkits/Gecko/speech_generator.py     |   29 ++-
 .../J2SE-access-bridge/speech_generator.py         |    5 +-
 src/orca/settings.py                               |    4 +-
 src/orca/speech.py                                 |    7 +-
 src/orca/speech_generator.py                       |  296 ++++++++++++++++++--
 13 files changed, 424 insertions(+), 69 deletions(-)
---
diff --git a/src/orca/default.py b/src/orca/default.py
index 83eab99..6655ad6 100644
--- a/src/orca/default.py
+++ b/src/orca/default.py
@@ -1921,6 +1921,7 @@ class Script(script.Script):
 
         obj = orca_state.locusOfFocus
         self.updateBraille(obj)
+        voice = self.voices[settings.DEFAULT_VOICE]
 
         frame, dialog = self.utilities.frameAndDialog(obj)
         if frame:
@@ -1932,19 +1933,20 @@ class Script(script.Script):
             #
             msg = self.speechGenerator.generateStatusBar(frame)
             if msg:
-                self.presentMessage(msg)
+                self.presentMessage(msg, voice=voice)
 
         window = dialog or frame
         if window:
             msg = self.speechGenerator.generateDefaultButton(window)
             if msg:
-                self.presentMessage(msg)
+                self.presentMessage(msg, voice=voice)
 
     def presentTitle(self, inputEvent):
         """Speaks and brailles the title of the window with focus."""
 
-        self.presentMessage(self.speechGenerator.generateTitle(
-                orca_state.locusOfFocus))
+        title = self.speechGenerator.generateTitle(orca_state.locusOfFocus)
+        for (string, voice) in title:
+            self.presentMessage(string, voice=voice)
 
     def readCharAttributes(self, inputEvent=None):
         """Reads the attributes associated with the current text character.
@@ -3295,18 +3297,19 @@ class Script(script.Script):
                     announceState = True
 
             if announceState:
+                voice = self.voices.get(settings.SYSTEM_VOICE)
                 if event.detail1:
                     # Translators: this object is now selected.
                     # Let the user know this.
                     #
                     #
-                    speech.speak(C_("text", "selected"), None, False)
+                    speech.speak(C_("text", "selected"), voice, False)
                 else:
                     # Translators: this object is now unselected.
                     # Let the user know this.
                     #
                     #
-                    speech.speak(C_("text", "unselected"), None, False)
+                    speech.speak(C_("text", "unselected"), voice, False)
                 return
 
         if event.type.startswith("object:state-changed:focused"):
@@ -5277,17 +5280,18 @@ class Script(script.Script):
         except:
             debug.printException(debug.LEVEL_FINEST)
 
+        voice = self.voices.get(settings.SYSTEM_VOICE)
         if self.utilities.isTextSelected(obj, startOffset, endOffset):
             # Translators: when the user selects (highlights) text in
             # a document, Orca lets them know this.
             #
-            speech.speak(C_("text", "selected"), None, False)
+            speech.speak(C_("text", "selected"), voice, False)
         elif len(text.getText(startOffset, endOffset)):
             # Translators: when the user unselects
             # (unhighlights) text in a document, Orca lets
             # them know this.
             #
-            speech.speak(C_("text", "unselected"), None, False)
+            speech.speak(C_("text", "unselected"), voice, False)
 
         self._saveLastTextSelections(text)
 
@@ -5355,8 +5359,7 @@ class Script(script.Script):
     #                                                                          #
     ############################################################################
 
-    @staticmethod
-    def presentMessage(fullMessage, briefMessage=None):
+    def presentMessage(self, fullMessage, briefMessage=None, voice=None):
         """Convenience method to speak a message and 'flash' it in braille.
 
         Arguments:
@@ -5368,6 +5371,8 @@ class Script(script.Script):
           brief. Note that providing no briefMessage will result in the full
           message being used for either. Callers wishing to present nothing as
           the briefMessage should set briefMessage to an empty string.
+        - voice: The voice to use when speaking this message. By default, the
+          "system" voice will be used.
         """
 
         if not fullMessage:
@@ -5382,7 +5387,8 @@ class Script(script.Script):
             else:
                 message = fullMessage
             if message:
-                speech.speak(message)
+                voice = voice or self.voices.get(settings.SYSTEM_VOICE)
+                speech.speak(message, voice)
 
         if (settings.enableBraille or settings.enableBrailleMonitor) \
            and settings.enableFlashMessages:
@@ -5396,6 +5402,7 @@ class Script(script.Script):
             if isinstance(message[0], list):
                 message = message[0]
             if isinstance(message, list):
+                message = filter(lambda i: isinstance(i, str), message)
                 message = " ".join(message)
 
             if settings.flashIsPersistent:
@@ -5724,12 +5731,19 @@ class Script(script.Script):
     #                                                                      #
     ########################################################################
 
-    @staticmethod
-    def speakMessage(string, interrupt=True):
+    def speakMessage(self, string, voice=None, interrupt=True):
         """Method to speak a single string. Scripts should use this
-        method rather than calling speech.speak directly."""
+        method rather than calling speech.speak directly.
+
+        - string: The string to be spoken.
+        - voice: The voice to use. By default, the "system" voice will
+          be used.
+        - interrupt: If True, any current speech should be interrupted
+          prior to speaking the new text.
+        """
 
-        speech.speak(string, interrupt=interrupt)
+        voice = voice or self.voices.get(settings.SYSTEM_VOICE)
+        speech.speak(string, voice, interrupt)
 
     @staticmethod
     def presentItemsInSpeech(items):
diff --git a/src/orca/orca-setup.ui b/src/orca/orca-setup.ui
index ccb9957..c5b8429 100644
--- a/src/orca/orca-setup.ui
+++ b/src/orca/orca-setup.ui
@@ -113,6 +113,9 @@
       <row>
         <col id="0" translatable="yes">Hyperlink</col>
       </row>
+      <row>
+        <col id="0" translatable="yes">System</col>
+      </row>
     </data>
   </object>
   <object class="GtkListStore" id="model2">
diff --git a/src/orca/orca.py b/src/orca/orca.py
index 8a139e2..330bc5a 100644
--- a/src/orca/orca.py
+++ b/src/orca/orca.py
@@ -2100,7 +2100,7 @@ def main():
 
     try:
         message = _("Welcome to Orca.")
-        speech.speak(message)
+        speech.speak(message, settings.voices.get(settings.SYSTEM_VOICE))
         braille.displayMessage(message)
     except:
         debug.printException(debug.LEVEL_SEVERE)
diff --git a/src/orca/orca_console_prefs.py b/src/orca/orca_console_prefs.py
index b6839d2..c70d150 100644
--- a/src/orca/orca_console_prefs.py
+++ b/src/orca/orca_console_prefs.py
@@ -308,11 +308,13 @@ def setupSpeech(prefsDict):
     defaultACSS[acss.ACSS.AVERAGE_PITCH] = 5
     uppercaseACSS = acss.ACSS({acss.ACSS.AVERAGE_PITCH : 6})
     hyperlinkACSS = acss.ACSS({})
+    systemACSS = acss.ACSS({})
 
     voices = {
         settings.DEFAULT_VOICE   : defaultACSS,
         settings.UPPERCASE_VOICE : uppercaseACSS,
-        settings.HYPERLINK_VOICE : hyperlinkACSS
+        settings.HYPERLINK_VOICE : hyperlinkACSS,
+        settings.SYSTEM_VOICE    : systemACSS
     }
 
     prefsDict["enableSpeech"] = True
diff --git a/src/orca/orca_gui_prefs.py b/src/orca/orca_gui_prefs.py
index d1cf63a..1132b35 100644
--- a/src/orca/orca_gui_prefs.py
+++ b/src/orca/orca_gui_prefs.py
@@ -75,7 +75,7 @@ from orca_i18n import C_ # to provide qualified translatable strings
 
 # Must match the order of voice types in the GtkBuilder file.
 #
-(DEFAULT, UPPERCASE, HYPERLINK) = range(3)
+(DEFAULT, UPPERCASE, HYPERLINK, SYSTEM) = range(4)
 
 # Must match the order that the timeFormatCombo is populated.
 #
@@ -168,6 +168,7 @@ class OrcaSetupGUI(orca_gtkbuilder.GtkBuilderWrapper):
         self.speechSystemsChoice = None
         self.speechSystemsChoices = None
         self.speechSystemsModel = None
+        self.systemVoice = None
         self.uppercaseVoice = None
         self.window = None
         self.workingFactories = None
@@ -469,7 +470,7 @@ class OrcaSetupGUI(orca_gtkbuilder.GtkBuilderWrapper):
         """Return the ACSS value for the the given voice type.
 
         Arguments:
-        - voiceType: one of DEFAULT, UPPERCASE, HYPERLINK
+        - voiceType: one of DEFAULT, UPPERCASE, HYPERLINK, SYSTEM
 
         Returns the voice dictionary for the given voice type.
         """
@@ -480,6 +481,8 @@ class OrcaSetupGUI(orca_gtkbuilder.GtkBuilderWrapper):
             voiceACSS = self.uppercaseVoice
         elif voiceType == HYPERLINK:
             voiceACSS = self.hyperlinkVoice
+        elif voiceType == SYSTEM:
+            voiceACSS = self.systemVoice
         else:
             voiceACSS = self.defaultVoice
 
@@ -502,7 +505,7 @@ class OrcaSetupGUI(orca_gtkbuilder.GtkBuilderWrapper):
            for the given voice type.
 
         Arguments:
-        - voiceType: one of DEFAULT, UPPERCASE, HYPERLINK
+        - voiceType: one of DEFAULT, UPPERCASE, HYPERLINK, SYSTEM
         - key: the key to look for in the voice dictionary.
         - useDefault: if True, and the key isn't found for the given voice
                       type, the look for it in the default voice dictionary
@@ -525,6 +528,12 @@ class OrcaSetupGUI(orca_gtkbuilder.GtkBuilderWrapper):
                 if not useDefault:
                     return None
                 voice = self.defaultVoice
+        elif voiceType == SYSTEM:
+            voice = self.systemVoice
+            if key not in voice:
+                if not useDefault:
+                    return None
+                voice = self.defaultVoice
         else:
             voice = self.defaultVoice
 
@@ -537,7 +546,7 @@ class OrcaSetupGUI(orca_gtkbuilder.GtkBuilderWrapper):
         """Gets the name of the voice family for the given voice type.
 
         Arguments:
-        - voiceType: one of DEFAULT, UPPERCASE, HYPERLINK
+        - voiceType: one of DEFAULT, UPPERCASE, HYPERLINK, SYSTEM
 
         Returns the name of the voice family for the given voice type,
         or None if not set.
@@ -555,7 +564,7 @@ class OrcaSetupGUI(orca_gtkbuilder.GtkBuilderWrapper):
         """Sets the name of the voice family for the given voice type.
 
         Arguments:
-        - voiceType: one of DEFAULT, UPPERCASE, HYPERLINK
+        - voiceType: one of DEFAULT, UPPERCASE, HYPERLINK, SYSTEM
         - name: the name of the voice family to set.
         - language: the locale of the voice family to set.
         """
@@ -580,7 +589,7 @@ class OrcaSetupGUI(orca_gtkbuilder.GtkBuilderWrapper):
         """Gets the speaking rate value for the given voice type.
 
         Arguments:
-        - voiceType: one of DEFAULT, UPPERCASE, HYPERLINK
+        - voiceType: one of DEFAULT, UPPERCASE, HYPERLINK, SYSTEM
 
         Returns the rate value for the given voice type, or None if
         not set.
@@ -592,7 +601,7 @@ class OrcaSetupGUI(orca_gtkbuilder.GtkBuilderWrapper):
         """Sets the speaking rate value for the given voice type.
 
         Arguments:
-        - voiceType: one of DEFAULT, UPPERCASE, HYPERLINK
+        - voiceType: one of DEFAULT, UPPERCASE, HYPERLINK, SYSTEM
         - value: the rate value to set.
         """
 
@@ -604,7 +613,7 @@ class OrcaSetupGUI(orca_gtkbuilder.GtkBuilderWrapper):
         """Gets the pitch value for the given voice type.
 
         Arguments:
-        - voiceType: one of DEFAULT, UPPERCASE, HYPERLINK
+        - voiceType: one of DEFAULT, UPPERCASE, HYPERLINK, SYSTEM
 
         Returns the pitch value for the given voice type, or None if
         not set.
@@ -617,7 +626,7 @@ class OrcaSetupGUI(orca_gtkbuilder.GtkBuilderWrapper):
         """Sets the pitch value for the given voice type.
 
         Arguments:
-        - voiceType: one of DEFAULT, UPPERCASE, HYPERLINK
+        - voiceType: one of DEFAULT, UPPERCASE, HYPERLINK, SYSTEM
         - value: the pitch value to set.
         """
 
@@ -629,7 +638,7 @@ class OrcaSetupGUI(orca_gtkbuilder.GtkBuilderWrapper):
         """Gets the volume (gain) value for the given voice type.
 
         Arguments:
-        - voiceType: one of DEFAULT, UPPERCASE, HYPERLINK
+        - voiceType: one of DEFAULT, UPPERCASE, HYPERLINK, SYSTEM
 
         Returns the volume (gain) value for the given voice type, or
         None if not set.
@@ -641,7 +650,7 @@ class OrcaSetupGUI(orca_gtkbuilder.GtkBuilderWrapper):
         """Sets the volume (gain) value for the given voice type.
 
         Arguments:
-        - voiceType: one of DEFAULT, UPPERCASE, HYPERLINK
+        - voiceType: one of DEFAULT, UPPERCASE, HYPERLINK, SYSTEM
         - value: the volume (gain) value to set.
         """
 
@@ -654,7 +663,7 @@ class OrcaSetupGUI(orca_gtkbuilder.GtkBuilderWrapper):
         on the given voice type.
 
         Arguments:
-        - voiceType: one of DEFAULT, UPPERCASE, HYPERLINK
+        - voiceType: one of DEFAULT, UPPERCASE, HYPERLINK, SYSTEM
         """
 
         familyName = self._getFamilyNameForVoiceType(voiceType)
@@ -752,6 +761,16 @@ class OrcaSetupGUI(orca_gtkbuilder.GtkBuilderWrapper):
         # hyperlink.
         #
         types.append(C_("VoiceType", "Hyperlink"))
+        # Translators: This refers to the voice used by Orca when
+        # presenting information which is not displayed on the screen
+        # as text, but is still being communicated by the system in
+        # some visual fashion. For instance, Orca says "misspelled"
+        # to indicate the presence of the red squiggly line found
+        # under a spelling error; Orca might say "3 of 6" when a
+        # user Tabs into a list of six items and the third item is
+        # selected. And so on.
+        #
+        types.append(C_("VoiceType", "System"))
         self.populateComboBox(comboBox, types)
         comboBox.set_active(DEFAULT)
         voiceType = comboBox.get_active()
@@ -910,9 +929,10 @@ class OrcaSetupGUI(orca_gtkbuilder.GtkBuilderWrapper):
         """
 
         voices = self.prefsDict["voices"]
-        self.defaultVoice   = acss.ACSS(voices[settings.DEFAULT_VOICE])
-        self.uppercaseVoice = acss.ACSS(voices[settings.UPPERCASE_VOICE])
-        self.hyperlinkVoice = acss.ACSS(voices[settings.HYPERLINK_VOICE])
+        self.defaultVoice   = acss.ACSS(voices.get(settings.DEFAULT_VOICE))
+        self.uppercaseVoice = acss.ACSS(voices.get(settings.UPPERCASE_VOICE))
+        self.hyperlinkVoice = acss.ACSS(voices.get(settings.HYPERLINK_VOICE))
+        self.systemVoice    = acss.ACSS(voices.get(settings.SYSTEM_VOICE))
 
         # Just a note on general naming pattern:
         #
@@ -2612,6 +2632,7 @@ class OrcaSetupGUI(orca_gtkbuilder.GtkBuilderWrapper):
             del self.defaultVoice[acss.ACSS.FAMILY]
             del self.uppercaseVoice[acss.ACSS.FAMILY]
             del self.hyperlinkVoice[acss.ACSS.FAMILY]
+            del self.systemVoice[acss.ACSS.FAMILY]
         except:
             pass
 
@@ -4199,7 +4220,8 @@ class OrcaSetupGUI(orca_gtkbuilder.GtkBuilderWrapper):
             self.prefsDict["voices"] = {
                 settings.DEFAULT_VOICE   : acss.ACSS(self.defaultVoice),
                 settings.UPPERCASE_VOICE : acss.ACSS(self.uppercaseVoice),
-                settings.HYPERLINK_VOICE : acss.ACSS(self.hyperlinkVoice)
+                settings.HYPERLINK_VOICE : acss.ACSS(self.hyperlinkVoice),
+                settings.SYSTEM_VOICE    : acss.ACSS(self.systemVoice),
             }
 
         settings.setGKSUGrabDisabled(self.disableKeyGrabPref)
diff --git a/src/orca/scripts/apps/evolution/speech_generator.py b/src/orca/scripts/apps/evolution/speech_generator.py
index f41f0c3..ebdbebd 100644
--- a/src/orca/scripts/apps/evolution/speech_generator.py
+++ b/src/orca/scripts/apps/evolution/speech_generator.py
@@ -85,6 +85,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
         # toggle columns. That's really the difference here.]]]
         #
         result = []
+        acss = self.voice(speech_generator.SYSTEM)
         try:
             parentTable = obj.parent.queryTable()
         except NotImplementedError:
@@ -129,6 +130,8 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
                         text = _("Read")
                     result.append(text)
 
+        if result:
+            result.extend(acss)
         return result
 
     def _generateUnrelatedLabels(self, obj, **args):
@@ -143,6 +146,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
                 _generateUnrelatedLabels(self, obj, **args)
 
         result = []
+        acss = self.voice(speech_generator.DEFAULT)
         labels = self._script.utilities.unrelatedLabels(obj)
         for label in labels:
             name = self._generateName(label, **args)
@@ -164,4 +168,5 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
                         #
                         name = [_("%s screen") % name]
             result.extend(name)
+            result.extend(acss)
         return result
diff --git a/src/orca/scripts/apps/gcalctool/speech_generator.py b/src/orca/scripts/apps/gcalctool/speech_generator.py
index 6e9925d..ea038ea 100644
--- a/src/orca/scripts/apps/gcalctool/speech_generator.py
+++ b/src/orca/scripts/apps/gcalctool/speech_generator.py
@@ -46,14 +46,17 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
             return speech_generator.SpeechGenerator._generateName(\
                 self, obj)
 
+        result = []
+        acss = self.voice(speech_generator.DEFAULT)
         if obj.name:
             name = obj.name
         else:
             name = self._script.utilities.displayedText(obj)
 
         if name:
-            return [name]
+            result.append(name)
         elif obj.description:
-            return [obj.description]
-        else:
-            return []
+            result.append(obj.description)
+        if result:
+            result.extend(acss)
+        return result
diff --git a/src/orca/scripts/apps/soffice/speech_generator.py b/src/orca/scripts/apps/soffice/speech_generator.py
index 7a5f470..c89e358 100644
--- a/src/orca/scripts/apps/soffice/speech_generator.py
+++ b/src/orca/scripts/apps/soffice/speech_generator.py
@@ -96,16 +96,20 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
         and an empty array will be returned if no label can be found.
         """
         result = []
+        acss = self.voice(speech_generator.DEFAULT)
         override = self.__overrideParagraph(obj, **args)
         label = self._script.utilities.displayedLabel(obj) or ""
         if not label and override:
             label = self._script.utilities.displayedLabel(obj.parent) or ""
         result.append(label.strip())
+        if result:
+            result.extend(acss)
         return result
 
     def _generateLabelOrName(self, obj, **args):
         """Gets the label or the name if the label is not preset."""
         result = []
+        acss = self.voice(speech_generator.DEFAULT)
         override = self.__overrideParagraph(obj, **args)
         # Treat a paragraph which is serving as a text entry in a dialog
         # as a text object.
@@ -125,6 +129,8 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
                 #
                 if not parentLabel and obj.name and len(obj.name):
                     result.append(obj.name)
+                if result:
+                    result.extend(acss)
         else:
             result.extend(speech_generator.SpeechGenerator._generateLabelOrName(
                 self, obj, **args))
@@ -151,6 +157,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
         label.
         """
         result = []
+        acss = self.voice(speech_generator.SYSTEM)
         if obj.description:
             # The description of some OOo paragraphs consists of the name
             # and the displayed text, with punctuation added. Try to spot
@@ -165,6 +172,8 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
                     result.append(obj.description)
                     break
 
+        if result:
+            result.extend(acss)
         return result
 
     def _generateToggleState(self, obj, **args):
@@ -196,6 +205,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
         row header(s).
         """
         result = []
+        acss = self.voice(speech_generator.DEFAULT)
         try:
             table = obj.parent.queryTable()
         except:
@@ -220,10 +230,13 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
                     text = self._script.utilities.substring(header, 0, -1)
                     if text:
                         result.append(text)
+        if result:
+            result.extend(acss)
         return result
 
     def _generateNewRowHeader(self, obj, **args):
         result = []
+        acss = self.voice(speech_generator.DEFAULT)
         # Check to see if this spread sheet cell has either a dynamic
         # row heading associated with it.
         #
@@ -253,6 +266,8 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
                     text = self._script.utilities.substring(header, 0, -1)
                     if text:
                         result.append(text)
+        if result:
+            result.extend(acss)
         return result
 
     def _generateColumnHeader(self, obj, **args):
@@ -263,6 +278,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
         column header(s).
         """
         result = []
+        acss = self.voice(speech_generator.DEFAULT)
         try:
             table = obj.parent.queryTable()
         except:
@@ -287,10 +303,13 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
                     text = self._script.utilities.substring(header, 0, -1)
                     if text:
                         result.append(text)
+        if result:
+            result.extend(acss)
         return result
 
     def _generateNewColumnHeader(self, obj, **args):
         result = []
+        acss = self.voice(speech_generator.DEFAULT)
         # Check to see if this spread sheet cell has either a dynamic
         # row heading associated with it.
         #
@@ -320,6 +339,8 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
                     text = self._script.utilities.substring(header, 0, -1)
                     if text:
                         result.append(text)
+        if result:
+            result.extend(acss)
         return result
 
     def _generateTooLong(self, obj, **args):
@@ -331,6 +352,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
         of the spread sheet cell, or None if the message fits.
         """
         result = []
+        acss = self.voice(speech_generator.SYSTEM)
         try:
             text = obj.queryText()
             objectText = \
@@ -355,10 +377,13 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
                 result = [ngettext("%d character too long",
                                    "%d characters too long",
                                    tooLongCount) % tooLongCount]
+        if result:
+            result.extend(acss)
         return result
 
     def _generateSpreadSheetCell(self, obj, **args):
         result = []
+        acss = self.voice(speech_generator.DEFAULT)
         if self._script.inputLineForCell == None:
             self._script.inputLineForCell = \
                 self._script.locateInputLine(obj)
@@ -373,6 +398,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
                     #
                     objectText = _("blank")
                 result.append(objectText)
+                result.extend(acss)
         except NotImplementedError:
             pass
 
@@ -391,6 +417,9 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
                         result.append(name)
                         break
 
+        if result:
+            result.extend(acss)
+
         tooLong = self._generateTooLong(obj, **args)
         if tooLong and len(tooLong):
             result.extend(self._generatePause(obj, **args))
diff --git a/src/orca/scripts/toolkits/Gecko/speech_generator.py b/src/orca/scripts/toolkits/Gecko/speech_generator.py
index 27ab914..bff614d 100644
--- a/src/orca/scripts/toolkits/Gecko/speech_generator.py
+++ b/src/orca/scripts/toolkits/Gecko/speech_generator.py
@@ -54,6 +54,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
 
     def _generateName(self, obj, **args):
         result = []
+        acss = self.voice(speech_generator.DEFAULT)
         role = args.get('role', obj.getRole())
         if role == pyatspi.ROLE_COMBO_BOX:
             # With Gecko, a combo box has a menu as a child.  The text being
@@ -87,12 +88,16 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
                             break
                 if child and child.name:
                     result.append(child.name)
+                if result:
+                    result.extend(acss)
+
         else:
             result.extend(speech_generator.SpeechGenerator._generateName(
                               self, obj, **args))
         if not result and role == pyatspi.ROLE_LIST_ITEM:
             result.append(self._script.utilities.expandEOCs(obj))
 
+        acss = self.voice(speech_generator.HYPERLINK)
         link = None
         if role == pyatspi.ROLE_LINK:
             link = obj
@@ -106,6 +111,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
             basename = self._script.getLinkBasename(link)
             if basename:
                 result.append(basename)
+                result.extend(acss)
 
         return result
 
@@ -132,6 +138,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
         return result
 
     def _generateLabel(self, obj, **args):
+        acss = self.voice(speech_generator.DEFAULT)
         result = speech_generator.SpeechGenerator._generateLabel(self,
                                                                  obj,
                                                                  **args)
@@ -162,6 +169,8 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
            and not self._script.inDocumentContent():
             result.append(obj.name)
 
+        if result:
+            result.extend(acss)
         return result
 
     def _generateLabelAndName(self, obj, **args):
@@ -196,6 +205,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
     def _generateRoleName(self, obj, **args):
         """Prevents some roles from being spoken."""
         result = []
+        acss = self.voice(speech_generator.DEFAULT)
         role = args.get('role', obj.getRole())
         force = args.get('force', False)
 
@@ -252,12 +262,17 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
             else:
                 result.append(rolenames.getSpeechForRoleName(obj, role))
 
+            if result:
+                result.extend(acss)
+
             if role == pyatspi.ROLE_LINK \
                and obj.childCount and obj[0].getRole() == pyatspi.ROLE_IMAGE:
                 # If this is a link with a child which is an image, we
                 # want to indicate that.
                 #
+                acss = self.voice(speech_generator.HYPERLINK)
                 result.append(rolenames.getSpeechForRoleName(obj[0]))
+                result.extend(acss)
 
         return result
 
@@ -271,6 +286,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
 
     def _generateNumberOfChildren(self, obj, **args):
         result = []
+        acss = self.voice(speech_generator.SYSTEM)
         role = args.get('role', obj.getRole())
         if role == pyatspi.ROLE_LIST:
             # Translators: this represents a list in HTML.
@@ -278,6 +294,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
             result.append(ngettext("List with %d item",
                                    "List with %d items",
                                    obj.childCount) % obj.childCount)
+            result.extend(acss)
         else:
             result.extend(
                 speech_generator.SpeechGenerator._generateNumberOfChildren(
@@ -387,17 +404,24 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
             text = self._script.utilities.displayedText(parent)
             label = self._script.utilities.displayedLabel(parent)
             newResult = []
+            acss = self.voice(speech_generator.DEFAULT)
             if text and (text != label) and len(text.strip()) \
                 and (not text.startswith("chrome://")):
+                newResult.extend(acss)
                 newResult.append(text)
             if label and len(label.strip()):
+                newResult.extend(acss)
                 newResult.append(label)
 
             # Finally add the role if it's not among the roles we don't
             # wish to speak.
             #
+            acss = self.voice(speech_generator.SYSTEM)
             if not (role in dontSpeakRoles) and len(newResult):
-                result.append(rolenames.getSpeechForRoleName(parent))
+                roleInfo = rolenames.getSpeechForRoleName(parent)
+                if roleInfo:
+                    result.extend(acss)
+                    result.append(roleInfo)
 
             # If this object is an ARIA widget with STATE_REQUIRED, add
             # that. (Note that for the most part, the ARIA widget itself
@@ -448,6 +472,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
         containing obj.
         """
         result = []
+        acss = self.voice(speech_generator.DEFAULT)
         headings, forms, tables, vlinks, uvlinks, percent = \
             self._script.getPageSummary(obj)
         if headings:
@@ -489,6 +514,8 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
                  '%d percent of document read',
                  percent) % percent)
 
+        if result:
+            result.extend(acss)
         return result
 
     def generateSpeech(self, obj, **args):
diff --git a/src/orca/scripts/toolkits/J2SE-access-bridge/speech_generator.py b/src/orca/scripts/toolkits/J2SE-access-bridge/speech_generator.py
index ef871cd..453691e 100644
--- a/src/orca/scripts/toolkits/J2SE-access-bridge/speech_generator.py
+++ b/src/orca/scripts/toolkits/J2SE-access-bridge/speech_generator.py
@@ -85,6 +85,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
         object has."""
 
         result = []
+        acss = self.voice(speech_generator.SYSTEM)
         if obj and obj.getState().contains(pyatspi.STATE_EXPANDED) \
            and obj.getRole() == pyatspi.ROLE_LABEL and obj.childCount:
             children = obj.childCount
@@ -93,6 +94,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
             #
             items = ngettext("%d item", "%d items", children) % children
             result.append(items)
+            result.extend(acss)
         else:
             result.extend(speech_generator.SpeechGenerator.\
                           _generateNumberOfChildren(self, obj, **args))
@@ -117,6 +119,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
                 self, obj, **args)
 
         result = []
+        acss = self.voice(speech_generator.SYSTEM)
         name = self._generateName(obj)
         position = -1
         index = total = 0
@@ -138,7 +141,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
             result.append(self._script.formatting.getString(
                               mode='speech', stringType='groupindex') \
                               %  {"index" : position, "total" : total})
-
+            result.extend(acss)
         return result
         
     def generateSpeech(self, obj, **args):
diff --git a/src/orca/settings.py b/src/orca/settings.py
index 74ec1e1..07abc5f 100644
--- a/src/orca/settings.py
+++ b/src/orca/settings.py
@@ -373,11 +373,13 @@ speechServerInfo        = None # None means let the factory decide.
 DEFAULT_VOICE           = "default"
 UPPERCASE_VOICE         = "uppercase"
 HYPERLINK_VOICE         = "hyperlink"
+SYSTEM_VOICE            = "system"
 
 voices = {
     DEFAULT_VOICE   : ACSS({}),
     UPPERCASE_VOICE : ACSS({ACSS.AVERAGE_PITCH : 5.6}),
-    HYPERLINK_VOICE : ACSS({})
+    HYPERLINK_VOICE : ACSS({}),
+    SYSTEM_VOICE    : ACSS({}),
 }
 
 # If True, enable speaking of speech indentation and justification.
diff --git a/src/orca/speech.py b/src/orca/speech.py
index 28798e3..1d0c28b 100644
--- a/src/orca/speech.py
+++ b/src/orca/speech.py
@@ -233,12 +233,13 @@ def speak(content, acss=None, interrupt=True):
                     continue
             elif isinstance(element, ACSS):
                 newVoice.update(element)
-                if newVoice and newVoice == activeVoice:
+                if newVoice == activeVoice:
                     continue
                 newItemsToSpeak.append(toSpeak.pop())
 
-            string = " ".join(toSpeak)
-            _speak(string, activeVoice, interrupt)
+            if toSpeak:
+                string = " ".join(toSpeak)
+                _speak(string, activeVoice, interrupt)
             activeVoice = newVoice
             toSpeak = newItemsToSpeak
 
diff --git a/src/orca/speech_generator.py b/src/orca/speech_generator.py
index 01bed32..b816bde 100644
--- a/src/orca/speech_generator.py
+++ b/src/orca/speech_generator.py
@@ -63,6 +63,22 @@ LINE_BREAK = [LineBreak()]
 #
 METHOD_PREFIX = "_generate"
 
+DEFAULT        = "default"
+UPPERCASE      = "uppercase"
+HYPERLINK      = "hyperlink"
+SYSTEM         = "system"
+STATE          = "state" # Candidate for sound
+VALUE          = "value" # Candidate for sound
+
+voiceType = {
+    DEFAULT     : settings.DEFAULT_VOICE,
+    UPPERCASE   : settings.UPPERCASE_VOICE,
+    HYPERLINK   : settings.HYPERLINK_VOICE,
+    SYSTEM      : settings.SYSTEM_VOICE,
+    STATE       : settings.SYSTEM_VOICE, # Users may prefer DEFAULT_VOICE here
+    VALUE       : settings.SYSTEM_VOICE, # Users may prefer DEFAULT_VOICE here
+}
+
 class SpeechGenerator(generator.Generator):
     """Takes accessible objects and produces a string to speak for
     those objects.  See the generateSpeech method, which is the primary
@@ -105,6 +121,63 @@ class SpeechGenerator(generator.Generator):
     #                                                                   #
     #####################################################################
 
+    def _generateName(self, obj, **args):
+        """Returns an array of strings for use by speech and braille that
+        represent the name of the object.  If the object is directly
+        displaying any text, that text will be treated as the name.
+        Otherwise, the accessible name of the object will be used.  If
+        there is no accessible name, then the description of the
+        object will be used.  This method will return an empty array
+        if nothing can be found.  [[[WDW - I wonder if we should just
+        have _generateName, _generateDescription,
+        _generateDisplayedText, etc., that don't do any fallback.
+        Then, we can allow the formatting to do the fallback (e.g.,
+        'displayedText or name or description'). [[[JD to WDW - I
+        needed a _generateDescription for whereAmI. :-) See below.
+        """
+        acss = self.voice(DEFAULT)
+        result = generator.Generator._generateName(self, obj, **args)
+        if result:
+            result.extend(acss)
+        return result
+
+    def _generateLabel(self, obj, **args):
+        """Returns the label for an object as an array of strings for use by
+        speech and braille.  The label is determined by the displayedLabel
+        method of the script utility, and an empty array will be returned if
+        no label can be found.
+        """
+        acss = self.voice(DEFAULT)
+        result = generator.Generator._generateLabel(self, obj, **args)
+        if result:
+            result.extend(acss)
+        return result
+
+    def _generateLabelOrName(self, obj, **args):
+        """Returns the label as an array of strings for speech and braille.
+        If the label cannot be found, the name will be used instead.
+        If the name cannot be found, an empty array will be returned.
+        """
+        result = []
+        acss = self.voice(DEFAULT)
+        result.extend(self._generateLabel(obj, **args))
+        if not result:
+            if obj.name and (len(obj.name)):
+                result.append(obj.name)
+                result.extend(acss)
+        return result
+
+    def _generateDescription(self, obj, **args):
+        """Returns an array of strings fo use by speech and braille that
+        represent the description of the object, if that description
+        is different from that of the name and label.
+        """
+        acss = self.voice(SYSTEM)
+        result = generator.Generator._generateDescription(self, obj, **args)
+        if result:
+            result.extend(acss)
+        return result
+
     def _generateTextRole(self, obj, **args):
         """A convenience method to prevent the pyatspi.ROLE_PARAGRAPH role
         from being spoken. In the case of a pyatspi.ROLE_PARAGRAPH
@@ -118,9 +191,11 @@ class SpeechGenerator(generator.Generator):
         override.]]]
         """
         result = []
+        acss = self.voice(SYSTEM)
         role = args.get('role', obj.getRole())
         if role != pyatspi.ROLE_PARAGRAPH:
             result.extend(self._generateRoleName(obj, **args))
+            result.extend(acss)
         return result
 
     def _generateRoleName(self, obj, **args):
@@ -131,9 +206,11 @@ class SpeechGenerator(generator.Generator):
         accessible role of the obj.
         """
         result = []
+        acss = self.voice(SYSTEM)
         role = args.get('role', obj.getRole())
         if (role != pyatspi.ROLE_UNKNOWN):
             result.append(rolenames.getSpeechForRoleName(obj, role))
+            result.extend(acss)
         return result
 
     def getRoleName(self, obj, **args):
@@ -152,11 +229,14 @@ class SpeechGenerator(generator.Generator):
         hierarchy and which are not in a label for or labelled by
         relation.
         """
-        labels = self._script.utilities.unrelatedLabels(obj)
         result = []
+        acss = self.voice(DEFAULT)
+        labels = self._script.utilities.unrelatedLabels(obj)
         for label in labels:
             name = self._generateName(label, **args)
             result.extend(name)
+        if result:
+            result.extend(acss)
         return result
 
     def _generateEmbedded(self, obj, **args):
@@ -165,12 +245,15 @@ class SpeechGenerator(generator.Generator):
         This either is the label or name of the object or the name of
         the application for the object.
         """
+        acss = self.voice(DEFAULT)
         result = self._generateLabelOrName(obj, **args)
         if not result:
             try:
                 result.append(obj.getApplication().name)
             except:
                 pass
+        if result:
+            result.extend(acss)
         return result
 
     #####################################################################
@@ -179,6 +262,42 @@ class SpeechGenerator(generator.Generator):
     #                                                                   #
     #####################################################################
 
+    def _generateCheckedState(self, obj, **args):
+        """Returns an array of strings for use by speech and braille that
+        represent the checked state of the object.  This is typically
+        for check boxes. [[[WDW - should we return an empty array if
+        we can guarantee we know this thing is not checkable?]]]
+        """
+        acss = self.voice(STATE)
+        result = generator.Generator._generateCheckedState(self, obj, **args)
+        if result:
+            result.extend(acss)
+        return result
+
+    def _generateExpandableState(self, obj, **args):
+        """Returns an array of strings for use by speech and braille that
+        represent the expanded/collapsed state of an object, such as a
+        tree node. If the object is not expandable, an empty array
+        will be returned.
+        """
+        acss = self.voice(STATE)
+        result = generator.Generator._generateExpandableState(self, obj, **args)
+        if result:
+            result.extend(acss)
+        return result
+
+    def _generateMenuItemCheckedState(self, obj, **args):
+        """Returns an array of strings for use by speech and braille that
+        represent the checked state of the menu item, only if it is
+        checked. Otherwise, and empty array will be returned.
+        """
+        acss = self.voice(STATE)
+        result = generator.Generator.\
+            _generateMenuItemCheckedState(self, obj, **args)
+        if result:
+            result.extend(acss)
+        return result
+
     def _generateMultiselectableState(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) that represent the multiselectable state of
@@ -186,6 +305,7 @@ class SpeechGenerator(generator.Generator):
         is not multiselectable, an empty array will be returned.
         """
         result = []
+        acss = self.voice(STATE)
         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.
@@ -193,6 +313,31 @@ class SpeechGenerator(generator.Generator):
             result.append(self._script.formatting.getString(
                 mode='speech',
                 stringType='multiselect'))
+            result.extend(acss)
+        return result
+
+    def _generateRadioState(self, obj, **args):
+        """Returns an array of strings for use by speech and braille that
+        represent the checked state of the object.  This is typically
+        for check boxes. [[[WDW - should we return an empty array if
+        we can guarantee we know this thing is not checkable?]]]
+        """
+        acss = self.voice(STATE)
+        result = generator.Generator._generateRadioState(self, obj, **args)
+        if result:
+            result.extend(acss)
+        return result
+
+    def _generateToggleState(self, obj, **args):
+        """Returns an array of strings for use by speech and braille that
+        represent the checked state of the object.  This is typically
+        for check boxes. [[[WDW - should we return an empty array if
+        we can guarantee we know this thing is not checkable?]]]
+        """
+        acss = self.voice(STATE)
+        result = generator.Generator._generateToggleState(self, obj, **args)
+        if result:
+            result.extend(acss)
         return result
 
     #####################################################################
@@ -207,6 +352,7 @@ class SpeechGenerator(generator.Generator):
         the link associated with obj.
         """
         result = []
+        acss = self.voice(HYPERLINK)
         # Get the URI for the link of interest and parse it. The parsed
         # URI is returned as a tuple containing six components:
         # scheme://netloc/path;parameters?query#fragment.
@@ -250,6 +396,8 @@ class SpeechGenerator(generator.Generator):
                 result.append(linkOutput)
                 if obj.childCount and obj[0].getRole() == pyatspi.ROLE_IMAGE:
                     result.extend(self._generateRoleName(obj[0]))
+        if result:
+            result.extend(acss)
         return result
 
     def _generateSiteDescription(self, obj, **args):
@@ -258,6 +406,7 @@ class SpeechGenerator(generator.Generator):
         pointed to by the URI of the link associated with obj.
         """
         result = []
+        acss = self.voice(HYPERLINK)
         link_uri = self._script.utilities.uri(obj)
         if link_uri:
             link_uri_info = urlparse.urlparse(link_uri)
@@ -297,6 +446,8 @@ class SpeechGenerator(generator.Generator):
                     # site than that of the link.
                     #
                     result.append(_("different site"))
+        if result:
+            result.extend(acss)
         return result
 
     def _generateFileSize(self, obj, **args):
@@ -306,6 +457,7 @@ class SpeechGenerator(generator.Generator):
         obj.
         """
         result = []
+        acss = self.voice(HYPERLINK)
         sizeString = ""
         uri = self._script.utilities.uri(obj)
         if not uri:
@@ -332,6 +484,8 @@ class SpeechGenerator(generator.Generator):
                 # Translators: This is the size of a file in megabytes
                 #
                 result.append(_("%.2f megabytes") % (float(size) * .000001))
+        if result:
+            result.extend(acss)
         return result
 
     #####################################################################
@@ -346,6 +500,7 @@ class SpeechGenerator(generator.Generator):
         it exists.  Otherwise, an empty array is returned.
         """
         result = []
+        acss = self.voice(DEFAULT)
         try:
             image = obj.queryImage()
         except:
@@ -353,6 +508,7 @@ class SpeechGenerator(generator.Generator):
         else:
             args['role'] = pyatspi.ROLE_IMAGE
             result.extend(self.generate(obj, **args))
+            result.extend(acss)
         return result
 
     #####################################################################
@@ -372,6 +528,7 @@ class SpeechGenerator(generator.Generator):
         previous object with focus.
         """
         result = []
+        acss = self.voice(DEFAULT)
         if obj:
             priorObj = args.get('priorObj', None)
             try:
@@ -405,6 +562,8 @@ class SpeechGenerator(generator.Generator):
                        and ((newRow != oldRow) \
                             or (obj.parent != priorParent)):
                         result = self._generateRowHeader(obj, **args)
+        if result:
+            result.extend(acss)
         return result
 
     def _generateNewColumnHeader(self, obj, **args):
@@ -418,6 +577,7 @@ class SpeechGenerator(generator.Generator):
         previous object with focus.
         """
         result = []
+        acss = self.voice(DEFAULT)
         if obj and not args.get('readingRow', False):
             priorObj = args.get('priorObj', None)
             try:
@@ -451,6 +611,8 @@ class SpeechGenerator(generator.Generator):
                        and ((newCol != oldCol) \
                             or (obj.parent != priorParent)):
                         result = self._generateColumnHeader(obj, **args)
+        if result:
+            result.extend(acss)
         return result
 
     def _generateRealTableCell(self, obj, **args):
@@ -461,6 +623,7 @@ class SpeechGenerator(generator.Generator):
         cell itself.  The string, 'blank', is added for empty cells.
         """
         result = []
+        acss = self.voice(DEFAULT)
         oldRole = self._overrideRole('REAL_ROLE_TABLE_CELL', args)
         result.extend(self.generate(obj, **args))
         self._restoreRole(oldRole, args)
@@ -470,6 +633,9 @@ class SpeechGenerator(generator.Generator):
             # user has navigated to an empty line.
             #
             result.append(_("blank"))
+            if result:
+                result.extend(acss)
+
         return result
 
     def _generateUnselectedCell(self, obj, **args):
@@ -481,7 +647,7 @@ class SpeechGenerator(generator.Generator):
         settings.py.]]]
         """
         result = []
-
+        acss = self.voice(STATE)
         # If this is an icon within an layered pane or a table cell
         # within a table or a tree table and the item is focused but not
         # selected, let the user know. See bug #486908 for more details.
@@ -517,6 +683,7 @@ class SpeechGenerator(generator.Generator):
             # selected or not.
             #
             result.append(C_("tablecell", "not selected"))
+            result.extend(acss)
 
         return result
 
@@ -525,6 +692,7 @@ class SpeechGenerator(generator.Generator):
         specifications) reflecting the column number of a cell.
         """
         result = []
+        acss = self.voice(SYSTEM)
         col = -1
         if obj.parent.getRole() == pyatspi.ROLE_TABLE_CELL:
             obj = obj.parent
@@ -541,6 +709,8 @@ class SpeechGenerator(generator.Generator):
             # Translators: this is in references to a column in a
             # table.
             result.append(_("column %d") % (col + 1))
+        if result:
+            result.extend(acss)
         return result
 
     def _generateRow(self, obj, **args):
@@ -548,6 +718,7 @@ class SpeechGenerator(generator.Generator):
         specifications) reflecting the row number of a cell.
         """
         result = []
+        acss = self.voice(SYSTEM)
         row = -1
         if obj.parent.getRole() == pyatspi.ROLE_TABLE_CELL:
             obj = obj.parent
@@ -564,6 +735,8 @@ class SpeechGenerator(generator.Generator):
             # Translators: this is in references to a row in a table.
             #
             result.append(_("row %d") % (row + 1))
+        if result:
+            result.extend(acss)
         return result
 
     def _generateColumnAndRow(self, obj, **args):
@@ -573,6 +746,7 @@ class SpeechGenerator(generator.Generator):
         and the total number of rows.
         """
         result = []
+        acss = self.voice(SYSTEM)
         if obj.parent.getRole() == pyatspi.ROLE_TABLE_CELL:
             obj = obj.parent
         parent = obj.parent
@@ -594,6 +768,8 @@ class SpeechGenerator(generator.Generator):
             result.append(_("row %(index)d of %(total)d") \
                           % {"index" : (row + 1),
                              "total" : table.nRows})
+        if result:
+            result.extend(acss)
         return result
 
     def _generateEndOfTableIndicator(self, obj, **args):
@@ -602,6 +778,7 @@ class SpeechGenerator(generator.Generator):
         in the table.
         """
         result = []
+        acss = self.voice(SYSTEM)
         if settings.speechVerbosityLevel == settings.VERBOSITY_LEVEL_VERBOSE:
             if obj.getRole() == pyatspi.ROLE_TABLE_CELL:
                 cell = obj
@@ -621,6 +798,8 @@ class SpeechGenerator(generator.Generator):
                     # he/she is in the last cell of a table in a document.
                     #
                     result.append(_("End of table"))
+        if result:
+            result.extend(acss)
         return result
 
     #####################################################################
@@ -637,6 +816,7 @@ class SpeechGenerator(generator.Generator):
         to return an empty array if this is not a terminal.]]]
         """
         result = []
+        acss = self.voice(DEFAULT)
         title = None
         frame = self._script.utilities.ancestorWithRole(
             obj, [pyatspi.ROLE_FRAME], [])
@@ -645,6 +825,8 @@ class SpeechGenerator(generator.Generator):
         if not title:
             title = self._script.utilities.displayedLabel(obj)
         result.append(title)
+        if result:
+            result.extend(acss)
         return result
 
     #####################################################################
@@ -653,6 +835,18 @@ class SpeechGenerator(generator.Generator):
     #                                                                   #
     #####################################################################
 
+    def _generateCurrentLineText(self, obj, **args):
+        """Returns an array of strings for use by speech and braille
+        that represents the current line of text, if
+        this is a text object.  [[[WDW - consider returning an empty
+        array if this is not a text object.]]]
+        """
+        acss = self.voice(DEFAULT)
+        result = generator.Generator._generateCurrentLineText(self, obj, **args)
+        if result:
+            result.extend(acss)
+        return result
+
     def _getCharacterAttributes(self,
                                 obj,
                                 text,
@@ -792,15 +986,19 @@ class SpeechGenerator(generator.Generator):
         except NotImplementedError:
             return []
 
+        result = []
+        acss = self.voice(DEFAULT)
         [line, startOffset, endOffset, selected] = \
             self._getTextInformation(obj)
 
         # The empty string seems to be messing with using 'or' in
         # formatting strings.
         #
-        if line == '':
-            return []
-        return [line]
+        if line:
+            result.append(line)
+            result.extend(acss)
+
+        return result
 
     def _generateTextContentWithAttributes(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
@@ -809,11 +1007,13 @@ class SpeechGenerator(generator.Generator):
         mixed in.  This requires _generateTextInformation to have been
         called prior to this method.
         """
+
         try:
             text = obj.queryText()
         except NotImplementedError:
             return []
 
+        acss = self.voice(DEFAULT)
         [line, startOffset, endOffset, selected] = \
             self._getTextInformation(obj)
 
@@ -842,7 +1042,9 @@ class SpeechGenerator(generator.Generator):
                 newLine += " ; "
             newLine += attribs
 
-        return [newLine]
+        result = [newline]
+        result.extend(acss)
+        return result
 
     def _generateAnyTextSelection(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
@@ -851,6 +1053,7 @@ class SpeechGenerator(generator.Generator):
         moved to settings.py.]]]
         """
         result = []
+        acss = self.voice(SYSTEM)
 
         [line, startOffset, endOffset, selected] = \
             self._getTextInformation(obj)
@@ -861,6 +1064,7 @@ class SpeechGenerator(generator.Generator):
             #
             text = C_("text", "selected")
             result.append(text)
+            result.extend(acss)
         return result
 
     def _generateAllTextSelection(self, obj, **args):
@@ -870,6 +1074,7 @@ class SpeechGenerator(generator.Generator):
         moved to settings.py.]]]
         """
         result = []
+        acss = self.voice(SYSTEM)
         try:
             textObj = obj.queryText()
         except:
@@ -884,6 +1089,7 @@ class SpeechGenerator(generator.Generator):
                     # a document, Orca lets them know this.
                     #
                     result = [C_("text", "selected")]
+                    result.extend(acss)
         return result
 
     def generateTextIndentation(self, obj, **args):
@@ -897,6 +1103,7 @@ class SpeechGenerator(generator.Generator):
         - obj: the text object.
         - line: the string to check for spaces and tabs.
         """
+        acss = self.voice(SYSTEM)
         if not settings.enableSpeechIndentation:
             return []
         line =  args.get('alreadyFocused', "")
@@ -941,9 +1148,10 @@ class SpeechGenerator(generator.Generator):
                 break
             spaceCount  = tabCount = 0
 
-        if len(utterance):
-            return [utterance]
-        return []
+        result = [utterance]
+        if result and result[0]:
+            result.extend(acss)
+        return result
 
     #####################################################################
     #                                                                   #
@@ -961,10 +1169,12 @@ class SpeechGenerator(generator.Generator):
         focus.
         """
         result = []
+        acss = self.voice(SYSTEM)
         oldLevel = self._script.utilities.nodeLevel(args.get('priorObj', None))
         newLevel = self._script.utilities.nodeLevel(obj)
         if (oldLevel != newLevel) and (newLevel >= 0):
             result.extend(self._generateNodeLevel(obj, **args))
+            result.extend(acss)
         return result
 
     #####################################################################
@@ -980,6 +1190,7 @@ class SpeechGenerator(generator.Generator):
         should consider returning an empty array if there is no value.
         """
         result = []
+        acss = self.voice(SYSTEM)
         try:
             value = obj.queryValue()
         except NotImplementedError:
@@ -995,6 +1206,8 @@ class SpeechGenerator(generator.Generator):
                                   "%d percent",
                                   percentValue) % percentValue
             result.append(percentage)
+        if result:
+            result.extend(acss)
         return result
 
     #####################################################################
@@ -1017,6 +1230,7 @@ class SpeechGenerator(generator.Generator):
         # AT-SPI method calls.]]]
         #
         result = []
+        acss = self.voice(DEFAULT)
         priorObj = args.get('priorObj', None)
         if obj and obj.getRole() == pyatspi.ROLE_RADIO_BUTTON:
             radioGroupLabel = None
@@ -1038,6 +1252,7 @@ class SpeechGenerator(generator.Generator):
             if (not inSameGroup) and radioGroupLabel:
                 result.append(self._script.utilities.\
                                   displayedText(radioGroupLabel))
+                result.extend(acss)
         return result
 
     def _generateNumberOfChildren(self, obj, **args):
@@ -1048,6 +1263,7 @@ class SpeechGenerator(generator.Generator):
         be moved to settings.py.]]]
         """
         result = []
+        acss = self.voice(SYSTEM)
         childNodes = self._script.utilities.childNodes(obj)
         children = len(childNodes)
         if children:
@@ -1056,6 +1272,7 @@ class SpeechGenerator(generator.Generator):
             #
             itemString = ngettext("%d item", "%d items", children) % children
             result.append(itemString)
+            result.extend(acss)
         return result
 
     def _generateNoShowingChildren(self, obj, **args):
@@ -1067,6 +1284,7 @@ class SpeechGenerator(generator.Generator):
         settings.py.]]]
         """
         result = []
+        acss = self.voice(SYSTEM)
         hasItems = False
         for child in obj:
             state = child.getState()
@@ -1078,6 +1296,7 @@ class SpeechGenerator(generator.Generator):
             # or table.
             #
             result.append(_("0 items"))
+            result.extend(acss)
         return result
 
     def _generateNoChildren(self, obj, **args ):
@@ -1089,11 +1308,13 @@ class SpeechGenerator(generator.Generator):
         settings.py.]]]
         """
         result = []
+        acss = self.voice(SYSTEM)
         if not obj.childCount:
             # Translators: this is the number of items in a layered pane
             # or table.
             #
             result.append(_("0 items"))
+            result.extend(acss)
         return result
 
     def _generateSelectedItemCount(self, obj, **args):
@@ -1103,6 +1324,7 @@ class SpeechGenerator(generator.Generator):
         panel or a layered pane.
         """
         result = []
+        acss = self.voice(SYSTEM)
         # TODO - JD: Is there a better way to do this other than
         # hard-coding it in?
         #
@@ -1129,13 +1351,13 @@ class SpeechGenerator(generator.Generator):
                       % {"index" : totalSelectedItems,
                          "total" : childCount}
         result.append(countString)
-
+        result.extend(acss)
         result.append(self._script.formatting.getString(
                           mode='speech',
                           stringType='iconindex') \
                       % {"index" : currentItem,
                          "total" : childCount})
-
+        result.extend(acss)
         return result
 
     def _generateSelectedItems(self, obj, **args):
@@ -1162,6 +1384,7 @@ class SpeechGenerator(generator.Generator):
         settings.py.]]]
         """
         result = []
+        acss = self.voice(SYSTEM)
         # If this application has more than one unfocused alert or
         # dialog window, then speak '<m> unfocused dialogs'
         # to let the user know.
@@ -1175,6 +1398,7 @@ class SpeechGenerator(generator.Generator):
             result.append(ngettext("%d unfocused dialog",
                             "%d unfocused dialogs",
                             alertAndDialogCount) % alertAndDialogCount)
+            result.extend(acss)
         return result
 
     def _generateAncestors(self, obj, **args):
@@ -1189,6 +1413,7 @@ class SpeechGenerator(generator.Generator):
         previous object with focus.
         """
         result = []
+        acss = self.voice(DEFAULT)
         priorObj = args.get('priorObj', None)
         requireText = args.get('requireText', True)
         commonAncestor = self._script.utilities.commonAncestor(priorObj, obj)
@@ -1209,16 +1434,21 @@ class SpeechGenerator(generator.Generator):
                                 and 'Text' in pyatspi.listInterfaces(parent))):
                         text = self._script.utilities.displayedText(parent)
                     if text and len(text.strip()):
+                        roleInfo = self._generateRoleName(parent)
+                        if roleInfo:
+                            roleInfo.reverse()
                         # Push announcement of cell to the end
                         #
                         if parent.getRole() not in [pyatspi.ROLE_TABLE_CELL,
                                                     pyatspi.ROLE_FILLER]:
-                            result.extend(self._generateRoleName(parent))
+                            result.extend(roleInfo)
+                        result.extend(acss)
                         result.append(text)
                         if parent.getRole() == pyatspi.ROLE_TABLE_CELL:
-                            result.extend(self._generateRoleName(parent))
+                            result.extend(roleInfo)
                 parent = parent.parent
-        return result.reverse() or result
+        result.reverse()
+        return result
 
     def _generateNewAncestors(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
@@ -1271,6 +1501,7 @@ class SpeechGenerator(generator.Generator):
         object in a group.
         """
         result = []
+        acss = self.voice(SYSTEM)
         position = -1
         total = -1
 
@@ -1294,7 +1525,7 @@ class SpeechGenerator(generator.Generator):
                               stringType='groupindex') \
                           % {"index" : position,
                              "total" : total})
-
+            result.extend(acss)
         return result
 
     def _generatePositionInList(self, obj, **args):
@@ -1303,6 +1534,7 @@ class SpeechGenerator(generator.Generator):
         object in a list.
         """
         result = []
+        acss = self.voice(SYSTEM)
         position = -1
         index = 0
         total = 0
@@ -1376,7 +1608,7 @@ class SpeechGenerator(generator.Generator):
                               stringType='groupindex') \
                           % {"index" : position,
                              "total" : total})
-
+            result.extend(acss)
         return result
 
     def _generateDefaultButton(self, obj, **args):
@@ -1433,6 +1665,7 @@ class SpeechGenerator(generator.Generator):
         any unfocused dialog boxes.
         """
         result = []
+        acss = self.voice(DEFAULT)
         frame, dialog = self._script.utilities.frameAndDialog(obj)
         if frame:
             result.append(self._generateLabelAndName(frame))
@@ -1444,9 +1677,12 @@ class SpeechGenerator(generator.Generator):
             # Translators: this tells the user how many unfocused
             # alert and dialog windows that this application has.
             #
-            result.append(ngettext("%d unfocused dialog",
-                                   "%d unfocused dialogs",
-                                   alertAndDialogCount) % alertAndDialogCount)
+            dialogs = []
+            dialogs.append(ngettext("%d unfocused dialog",
+                                    "%d unfocused dialogs",
+                                    alertAndDialogCount) % alertAndDialogCount)
+            dialogs.extend(acss)
+            result.append(dialogs)
         return result
 
     #####################################################################
@@ -1461,10 +1697,13 @@ class SpeechGenerator(generator.Generator):
         or an empty array if no accelerator can be found.
         """
         result = []
+        acss = self.voice(SYSTEM)
         [mnemonic, shortcut, accelerator] = \
             self._script.utilities.mnemonicShortcutAccelerator(obj)
         if accelerator:
             result.append(accelerator)
+            result.extend(acss)
+
         return result
 
     def _generateMnemonic(self, obj, **args):
@@ -1473,6 +1712,7 @@ class SpeechGenerator(generator.Generator):
         an empty array if no mnemonic can be found.
         """
         result = []
+        acss = self.voice(SYSTEM)
         if settings.enableMnemonicSpeaking or args.get('forceMnemonic', False):
             [mnemonic, shortcut, accelerator] = \
                 self._script.utilities.mnemonicShortcutAccelerator(obj)
@@ -1482,6 +1722,8 @@ class SpeechGenerator(generator.Generator):
                 mnemonic = shortcut
             if mnemonic:
                 result = [mnemonic]
+                result.extend(acss)
+
         return result
 
     #####################################################################
@@ -1499,6 +1741,7 @@ class SpeechGenerator(generator.Generator):
         'forceTutorial' attribute of the args dictionary to True.
         """
         result = []
+        acss = self.voice(SYSTEM)
         alreadyFocused = args.get('alreadyFocused', False)
         forceTutorial = args.get('forceTutorial', False)
         result.extend(self._script.tutorialGenerator.getTutorial(
@@ -1513,6 +1756,8 @@ class SpeechGenerator(generator.Generator):
                         frame,
                         alreadyFocused,
                         forceTutorial))
+        if result and result[0]:
+            result.extend(acss)
         return result
 
     #####################################################################
@@ -1527,13 +1772,12 @@ class SpeechGenerator(generator.Generator):
     def _generateLineBreak(self, obj, **args):
         return LINE_BREAK
 
-    def voice(self, key=None):
+    def voice(self, key=None, **args):
         """Returns an array containing a voice.  The key is a value
         to be used to look up the voice in the settings.py:voices
-        dictionary.
+        dictionary. Other arguments can be passed in for future
+        decision making.
         """
-        try:
-            voice = settings.voices[key]
-        except:
-            voice = settings.voices[settings.DEFAULT_VOICE]
-        return [voice]
+
+        voicename = voiceType.get(key) or voiceType.get(DEFAULT)
+        return [settings.voices.get(voicename)]



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