[orca] Migrate where am I code to the speech generator



commit b43cbe4246ad3001067d7ac99370af7d547f4e75
Author: Willie Walker <william walker sun com>
Date:   Wed Jun 10 12:01:05 2009 -0400

    Migrate where am I code to the speech generator
    
    This is a combined effort of Joanie (mostly Joanie) and Willie
---
 src/orca/bookmarks.py                              |    2 +-
 src/orca/braille.py                                |    4 +-
 src/orca/default.py                                |  348 +++-
 src/orca/formatting.py                             |  204 ++-
 src/orca/liveregions.py                            |   13 +-
 src/orca/mouse_review.py                           |    4 +-
 .../scripts/apps/Thunderbird/speech_generator.py   |    6 +-
 src/orca/scripts/apps/acroread.py                  |    6 +-
 src/orca/scripts/apps/evolution/Makefile.am        |    3 +-
 src/orca/scripts/apps/evolution/script.py          |  174 ++-
 .../scripts/apps/evolution/speech_generator.py     |   80 +-
 src/orca/scripts/apps/evolution/where_am_i.py      |  226 ---
 src/orca/scripts/apps/gcalctool/Makefile.am        |    3 +-
 src/orca/scripts/apps/gcalctool/script.py          |    7 -
 .../scripts/apps/gcalctool/speech_generator.py     |   10 +-
 src/orca/scripts/apps/gcalctool/where_am_i.py      |   49 -
 src/orca/scripts/apps/gedit/script.py              |    4 +-
 src/orca/scripts/apps/gnome-panel.py               |    3 +-
 src/orca/scripts/apps/gnome-terminal.py            |    2 +-
 src/orca/scripts/apps/liferea.py                   |    2 +-
 src/orca/scripts/apps/notify-osd.py                |    1 -
 src/orca/scripts/apps/pidgin/Makefile.am           |    3 +-
 src/orca/scripts/apps/pidgin/script.py             |    7 -
 src/orca/scripts/apps/pidgin/speech_generator.py   |   20 +-
 src/orca/scripts/apps/pidgin/where_am_i.py         |  162 --
 src/orca/scripts/apps/planner/speech_generator.py  |    7 +-
 .../scripts/apps/rhythmbox/speech_generator.py     |    6 +-
 src/orca/scripts/apps/soffice/Makefile.am          |    3 +-
 src/orca/scripts/apps/soffice/formatting.py        |   10 +-
 src/orca/scripts/apps/soffice/script.py            |   55 +-
 src/orca/scripts/apps/soffice/speech_generator.py  |  194 ++-
 src/orca/scripts/apps/soffice/where_am_i.py        |  286 ---
 src/orca/scripts/apps/yelp.py                      |    2 +-
 src/orca/scripts/toolkits/Gecko/Makefile.am        |    3 +-
 src/orca/scripts/toolkits/Gecko/bookmarks.py       |    2 +-
 src/orca/scripts/toolkits/Gecko/formatting.py      |   21 +-
 src/orca/scripts/toolkits/Gecko/script.py          |  124 ++-
 .../scripts/toolkits/Gecko/speech_generator.py     |  185 ++-
 src/orca/scripts/toolkits/Gecko/where_am_i.py      |  281 ---
 .../toolkits/J2SE-access-bridge/Makefile.am        |    3 +-
 .../toolkits/J2SE-access-bridge/__init__.py        |    1 -
 .../scripts/toolkits/J2SE-access-bridge/script.py  |    6 -
 .../J2SE-access-bridge/speech_generator.py         |   21 +-
 .../toolkits/J2SE-access-bridge/where_am_I.py      |   55 -
 src/orca/speech_generator.py                       |  991 +++++++++--
 src/orca/structural_navigation.py                  |    2 +-
 src/orca/tutorialgenerator.py                      |  299 ++--
 src/orca/where_am_I.py                             | 1988 +-------------------
 test/harness/runall.sh                             |    4 +-
 test/harness/utils.py                              |    6 +-
 test/keystrokes/firefox/codetalks_button.py        |    3 +-
 test/keystrokes/firefox/codetalks_list.py          |    2 +-
 test/keystrokes/firefox/codetalks_panel_text.py    |    3 +-
 test/keystrokes/firefox/codetalks_treegrid.py      |    7 +-
 test/keystrokes/firefox/dojo_button.py             |   13 +-
 test/keystrokes/firefox/dojo_checkbox.py           |    2 +-
 test/keystrokes/firefox/dojo_combo_box.py          |    5 +-
 test/keystrokes/firefox/dojo_dialog.py             |    2 +-
 test/keystrokes/firefox/dojo_spinner.py            |    2 +-
 test/keystrokes/firefox/dojo_tabcontainer.py       |    2 +-
 test/keystrokes/firefox/dojo_tree.py               |    2 +-
 test/keystrokes/firefox/find_wiki.py               |    3 +-
 test/keystrokes/firefox/html_role_combo_box.py     |    5 +-
 .../firefox/html_struct_nav_list_item.py           |   80 +-
 test/keystrokes/firefox/html_struct_nav_lists.py   |  110 +-
 test/keystrokes/firefox/line_nav_bug_577239.py     |    2 +
 test/keystrokes/firefox/line_nav_bug_577979.py     |    2 +
 test/keystrokes/firefox/link_where_am_i.py         |    8 +-
 test/keystrokes/firefox/moz_menu.py                |    2 +-
 test/keystrokes/firefox/moz_progressbar.py         |   20 +-
 test/keystrokes/firefox/moz_slider.py              |    4 +-
 test/keystrokes/firefox/moz_tabpanel.py            |    2 +-
 test/keystrokes/firefox/uiuc_button.py             |    2 +-
 test/keystrokes/firefox/uiuc_grid.py               |    6 +-
 test/keystrokes/firefox/uiuc_radiobutton.py        |    2 +-
 test/keystrokes/firefox/uiuc_slider.py             |    2 +-
 test/keystrokes/firefox/uiuc_tabpanel.py           |    2 +-
 test/keystrokes/firefox/xul_role_accel_label.py    |    3 +-
 .../keystrokes/firefox/xul_role_check_menu_item.py |    3 +-
 test/keystrokes/firefox/xul_role_combo_box.py      |    5 +-
 test/keystrokes/firefox/xul_role_page_tab.py       |    4 +-
 .../keystrokes/firefox/xul_role_radio_menu_item.py |    6 +-
 test/keystrokes/firefox/xul_role_tree.py           |    2 +-
 test/keystrokes/firefox/xul_role_tree_table.py     |    8 +-
 .../firefox/xul_where_am_i_status_bar.py           |    6 +-
 test/keystrokes/firefox/yahoo_tab_view.py          |    2 +-
 test/keystrokes/gtk-demo/role_column_header.py     |   33 +-
 test/keystrokes/gtk-demo/role_combo_box.py         |   21 +-
 test/keystrokes/gtk-demo/role_label.py             |   22 +-
 test/keystrokes/gtk-demo/role_menu.py              |    8 +-
 test/keystrokes/gtk-demo/role_page_tab.py          |    4 +-
 test/keystrokes/gtk-demo/role_radio_menu_item.py   |   16 +-
 test/keystrokes/gtk-demo/role_table.py             |    6 +-
 test/keystrokes/gtk-demo/role_text_multiline.py    |   14 +-
 .../gtk-demo/role_text_multiline_navigation.py     |    6 +-
 test/keystrokes/gtk-demo/role_toolbar.py           |    9 +-
 test/keystrokes/gtk-demo/role_tree_table.py        |   52 +-
 test/keystrokes/oocalc/bug_361167.py               |   19 +-
 test/keystrokes/oocalc/bug_363802.py               |    2 +-
 test/keystrokes/oocalc/bug_363804.py               |    2 +-
 test/keystrokes/oocalc/bug_364086.py               |    4 +-
 test/keystrokes/oocalc/bug_433398.py               |    2 -
 test/keystrokes/oocalc/bug_435852.py               |    8 +-
 test/keystrokes/oowriter/bug_364765.py             |    2 +-
 test/keystrokes/oowriter/bug_435226.py             |    9 +-
 test/keystrokes/progressbar/progress_updates.py    |   20 +-
 106 files changed, 2620 insertions(+), 3871 deletions(-)

diff --git a/src/orca/bookmarks.py b/src/orca/bookmarks.py
index 7a86bec..2f6bcd1 100644
--- a/src/orca/bookmarks.py
+++ b/src/orca/bookmarks.py
@@ -78,7 +78,7 @@ class Bookmarks:
         #
         utterances = [_('bookmark entered')]
         utterances.extend(
-            self._script.speechGenerator.getSpeech(
+            self._script.speechGenerator.generateSpeech(
                 context.getCurrentAccessible()))
         speech.speak(utterances)
 
diff --git a/src/orca/braille.py b/src/orca/braille.py
index dc1fff3..58f4e93 100644
--- a/src/orca/braille.py
+++ b/src/orca/braille.py
@@ -621,7 +621,7 @@ class Text(Region):
                 n += 1
 
         if attrIndicator:
-            enabledAttributes = script.attribsToDictionary(
+            enabledAttributes = script.attributeStringToDictionary(
                 settings.enabledBrailledTextAttributes)
 
             offset = self.lineOffset
@@ -645,7 +645,7 @@ class Text(Region):
                         regionMask[i] |= attrIndicator
 
         if selIndicator:
-            selections = script.getTextSelections(self.accessible)
+            selections = script.getAllTextSelections(self.accessible)
             for startOffset, endOffset in selections:
                 maskStart = max(startOffset - self.lineOffset, 0)
                 maskEnd = min(endOffset - self.lineOffset, stringLength)
diff --git a/src/orca/default.py b/src/orca/default.py
index 5a154cb..c341ead 100644
--- a/src/orca/default.py
+++ b/src/orca/default.py
@@ -25,7 +25,7 @@ for GTK."""
 __id__        = "$Id$"
 __version__   = "$Revision$"
 __date__      = "$Date$"
-__copyright__ = "Copyright (c) 2005-2009 Sun Microsystems Inc."
+__copyright__ = "Copyright (c) 2004-2009 Sun Microsystems Inc."
 __license__   = "LGPL"
 
 import locale
@@ -49,7 +49,6 @@ import orca_state
 import phonnames
 import pronunciation_dict
 import punctuation_settings
-import rolenames
 import script
 import settings
 import speech
@@ -190,17 +189,17 @@ class Script(script.Script):
                 #
                 _("Performs the detailed where am I operation."))
 
-        self.inputEventHandlers["getTitleHandler"] = \
+        self.inputEventHandlers["presentTitleHandler"] = \
             input_event.InputEventHandler(
-                Script.getTitle,
+                Script.presentTitle,
                 # Translators: This command will cause the window's
                 # title to be spoken.
                 #
                 _("Speaks the title bar."))
 
-        self.inputEventHandlers["getStatusBarHandler"] = \
+        self.inputEventHandlers["presentStatusBarHandler"] = \
             input_event.InputEventHandler(
-                Script.getStatusBar,
+                Script.presentStatusBar,
                 # Translators: This command will cause the window's
                 # status bar contents to be spoken.
                 #
@@ -1095,7 +1094,7 @@ class Script(script.Script):
                 "KP_Enter",
                 settings.defaultModifierMask,
                 settings.ORCA_MODIFIER_MASK,
-                self.inputEventHandlers["getTitleHandler"],
+                self.inputEventHandlers["presentTitleHandler"],
                 1))
 
         keyBindings.add(
@@ -1103,7 +1102,7 @@ class Script(script.Script):
                 "KP_Enter",
                 settings.defaultModifierMask,
                 settings.ORCA_MODIFIER_MASK,
-                self.inputEventHandlers["getStatusBarHandler"],
+                self.inputEventHandlers["presentStatusBarHandler"],
                 2))
 
         keyBindings.add(
@@ -1486,7 +1485,7 @@ class Script(script.Script):
                 "slash",
                 settings.defaultModifierMask,
                 settings.ORCA_MODIFIER_MASK,
-                self.inputEventHandlers["getTitleHandler"],
+                self.inputEventHandlers["presentTitleHandler"],
                 1))
 
         keyBindings.add(
@@ -1494,7 +1493,7 @@ class Script(script.Script):
                 "slash",
                 settings.defaultModifierMask,
                 settings.ORCA_MODIFIER_MASK,
-                self.inputEventHandlers["getStatusBarHandler"],
+                self.inputEventHandlers["presentStatusBarHandler"],
                 2))
 
         keyBindings.add(
@@ -2059,7 +2058,7 @@ class Script(script.Script):
             try:
                 orca_state.locusOfFocus.queryText()
             except NotImplementedError:
-                utterances = self.speechGenerator.getSpeech(
+                utterances = self.speechGenerator.generateSpeech(
                     orca_state.locusOfFocus)
                 utterances.extend(self.tutorialGenerator.getTutorial(
                            orca_state.locusOfFocus, False))
@@ -2519,15 +2518,26 @@ class Script(script.Script):
         #
         text = ""
         if obj.description:
-            text = obj.description
+            speechResult = brailleResult = obj.description
         else:
-            # Reuse the "where am I" algorithm.
-            text = self.whereAmI.getObjLabelAndName(obj)
-
-        debug.println(debug.LEVEL_FINEST, "presentTooltip: text='%s'" % text)
-        if text != "":
-            braille.displayMessage(text)
-            speech.speak(text)
+            # [[[TODO: WDW to JD: I see what you mean about making the
+            # _getLabelAndName method a public method.  We might consider
+            # doing that when we get to the braille refactor where we
+            # will probably make an uber generator class/module that the
+            # speech and braille generator classes can subclass.  For
+            # now, I've kind of hacked at the speech result.  The idea
+            # is that the speech result might return an audio cue and
+            # voice specification.  Of course, if it does that, then
+            # the speechResult[0] assumption will fail. :-(]]]
+            #
+            speechResult = self.whereAmI.getWhereAmI(obj, True)
+            brailleResult = speechResult[0]
+        debug.println(debug.LEVEL_FINEST,
+                      "presentTooltip: text='%s'" % speechResult)
+        if speechResult:
+            speech.speak(speechResult)
+        if brailleResult:
+            braille.displayMessage(brailleResult)
 
     def doWhereAmI(self, inputEvent, basicOnly):
         """Peforms the whereAmI operation.
@@ -2554,23 +2564,109 @@ class Script(script.Script):
 
         self.doWhereAmI(inputEvent, False)
 
-    def getTitle(self, inputEvent):
-        """Speaks the title of the window with focus.
+    def presentTitle(self, inputEvent):
+        """Speaks and brailles the title of the window with focus.
         """
 
         obj = orca_state.locusOfFocus
         self.updateBraille(obj)
+        speech.speak(self.speechGenerator.generateTitle(obj))
 
-        return self.whereAmI.speakTitle(orca_state.locusOfFocus)
-
-    def getStatusBar(self, inputEvent):
-        """Speaks the contents of the status bar of the window with focus.
+    def presentStatusBar(self, inputEvent):
+        """Speaks and brailles the contents of the status bar and/or default
+        button of the window with focus.
         """
 
         obj = orca_state.locusOfFocus
         self.updateBraille(obj)
 
-        return self.whereAmI.speakStatusBar(orca_state.locusOfFocus)
+        frame, dialog = self.findFrameAndDialog(obj)
+        if frame:
+            # In windows with lots of objects (Thunderbird, Firefox, etc.)
+            # If we wait until we've checked for both the status bar and
+            # a default button, there may be a noticable delay. Therefore,
+            # speak the status bar info immediately and then go looking
+            # for a default button.
+            #
+            speech.speak(self.speechGenerator.generateStatusBar(frame))
+        window = dialog or frame
+        if window:
+            speech.speak(self.speechGenerator.generateDefaultButton(window))
+
+    def findStatusBar(self, obj):
+        """Returns the status bar in the window which contains obj.
+        """
+
+        # There are some objects which are not worth descending.
+        #
+        skipRoles = [pyatspi.ROLE_TREE,
+                     pyatspi.ROLE_TREE_TABLE,
+                     pyatspi.ROLE_TABLE]
+
+        if obj.getState().contains(pyatspi.STATE_MANAGES_DESCENDANTS) \
+           or obj.getRole() in skipRoles:
+            return
+
+        statusBar = None
+        # The status bar is likely near the bottom of the window.
+        #
+        for i in range(obj.childCount - 1, -1, -1):
+            if obj[i].getRole() == pyatspi.ROLE_STATUS_BAR:
+                statusBar = obj[i]
+            elif not obj[i] in skipRoles:
+                statusBar = self.findStatusBar(obj[i])
+
+            if statusBar:
+                break
+
+        return statusBar
+
+    def findDefaultButton(self, obj):
+        """Returns the default button in dialog, obj.
+        """
+
+        # There are some objects which are not worth descending.
+        #
+        skipRoles = [pyatspi.ROLE_TREE,
+                     pyatspi.ROLE_TREE_TABLE,
+                     pyatspi.ROLE_TABLE]
+
+        if obj.getState().contains(pyatspi.STATE_MANAGES_DESCENDANTS) \
+           or obj.getRole() in skipRoles:
+            return
+
+        defaultButton = None
+        # The default button is likely near the bottom of the window.
+        #
+        for i in range(obj.childCount - 1, -1, -1):
+            if obj[i].getRole() == pyatspi.ROLE_PUSH_BUTTON \
+                and obj[i].getState().contains(pyatspi.STATE_IS_DEFAULT):
+                defaultButton = obj[i]
+            elif not obj[i].getRole() in skipRoles:
+                defaultButton = self.findDefaultButton(obj[i])
+
+            if defaultButton:
+                break
+
+        return defaultButton
+
+    def findFrameAndDialog(self, obj):
+        """Returns the frame and (possibly) the dialog containing
+        the object.
+        """
+
+        results = [None, None]
+
+        parent = obj.parent
+        while parent and (parent.parent != parent):
+            if parent.getRole() == pyatspi.ROLE_FRAME:
+                results[0] = parent
+            if parent.getRole() in [pyatspi.ROLE_DIALOG,
+                                    pyatspi.ROLE_FILE_CHOOSER]:
+                results[1] = parent
+            parent = parent.parent
+
+        return results
 
     def findCommonAncestor(self, a, b):
         """Finds the common ancestor between Accessible a and Accessible b.
@@ -2709,12 +2805,9 @@ class Script(script.Script):
                                 else:
                                     index += 1
 
-                        # Translators: this is the percentage value of a
-                        # progress bar.
-                        #
-                        percentage = _("%d percent.") % percentValue + " "
+                        utterances.extend(self.speechGenerator.generateSpeech(
+                            obj, alreadyFocused=True))
 
-                        utterances.append(percentage)
                         speech.speak(utterances)
 
                         self.lastProgressBarTime[obj] = currentTime
@@ -2830,7 +2923,7 @@ class Script(script.Script):
             else:
                 voice = self.voices[settings.DEFAULT_VOICE]
 
-            utterances = self.speechGenerator.getSpeech(
+            utterances = self.speechGenerator.generateSpeech(
                 newLocusOfFocus,
                 priorObj=oldLocusOfFocus)
             speech.speak(utterances, voice, not shouldNotInterrupt)
@@ -2939,8 +3032,8 @@ class Script(script.Script):
                 target = relation.getTarget(0)
                 if target == orca_state.locusOfFocus:
                     self.updateBraille(target)
-                    utterances = self.speechGenerator.getSpeech(
-                        target, already_focused=True)
+                    utterances = self.speechGenerator.generateSpeech(
+                        target, alreadyFocused=True)
                     utterances.extend(self.tutorialGenerator.getTutorial(
                                target, True))
                     speech.speak(utterances)
@@ -2958,8 +3051,8 @@ class Script(script.Script):
                     target = relation.getTarget(0)
                     if target == orca_state.locusOfFocus:
                         self.updateBraille(target)
-                        utterances = self.speechGenerator.getSpeech(
-                            target, already_focused=True)
+                        utterances = self.speechGenerator.generateSpeech(
+                            target, alreadyFocused=True)
                         utterances.extend(self.tutorialGenerator.getTutorial(
                                           target, True))
                         speech.speak(utterances)
@@ -2993,7 +3086,8 @@ class Script(script.Script):
 
         mag.magnifyAccessible(event, obj)
         self.updateBraille(obj)
-        utterances = self.speechGenerator.getSpeech(obj, already_focused=True)
+        utterances = self.speechGenerator.generateSpeech(
+                         obj, alreadyFocused=True)
         utterances.extend(self.tutorialGenerator.getTutorial(obj, True))
         speech.speak(utterances)
 
@@ -3194,7 +3288,7 @@ class Script(script.Script):
 
                 if objText.getNSelections() > 0:
                     [textContents, startOffset, endOffset] = \
-                        self.whereAmI.getTextSelection(obj)
+                        self.getSelectedText(obj)
 
                     # Now that we have the full selection, adjust based
                     # on the relation type. (see above comment)
@@ -3757,7 +3851,6 @@ class Script(script.Script):
         #
         if event.source.getRole() == pyatspi.ROLE_TOOL_TIP:
             obj = event.source
-
             if event.type.startswith("object:state-changed:showing"):
                 if event.detail1 == 1:
                     self.presentTooltip(obj)
@@ -3765,9 +3858,9 @@ class Script(script.Script):
                     and isinstance(orca_state.lastInputEvent,
                                    input_event.KeyboardEvent) \
                     and (orca_state.lastNonModifierKeyEvent.event_string \
-                                                                  == "F1"):
+                         == "F1"):
                     self.updateBraille(orca_state.locusOfFocus)
-                    utterances = self.speechGenerator.getSpeech(
+                    utterances = self.speechGenerator.generateSpeech(
                         orca_state.locusOfFocus)
                     utterances.extend(self.tutorialGenerator.getTutorial(
                                       orca_state.locusOfFocus, False))
@@ -4087,7 +4180,7 @@ class Script(script.Script):
                 pass
             else:
                 [textContents, startOffset, endOffset] = \
-                    self.whereAmI.getTextSelections(obj, True)
+                    self.getAllSelectedText(obj)
                 if textContents:
                     utterances = []
                     utterances.append(textContents)
@@ -4361,6 +4454,69 @@ class Script(script.Script):
 
         return True
 
+    def hasTextSelections(self, obj):
+        """Return an indication of whether this object has selected text.
+        Note that it's possible that this object has no selection, but is part
+        of a selected text area. Because of this, we need to check the
+        objects on either side to see if they are none zero length and
+        have text selections.
+
+        Arguments:
+        - obj: the text object to start checking for selected text.
+
+        Returns: an indication of whether this object has selected text,
+        or adjacent text objects have selected text.
+        """
+
+        currentSelected = False
+        otherSelected = False
+        text = obj.queryText()
+        nSelections = text.getNSelections()
+        if nSelections:
+            currentSelected = True
+        else:
+            otherSelected = False
+            text = obj.queryText()
+            displayedText = text.getText(0, -1)
+            if (text.caretOffset == 0) or len(displayedText) == 0:
+                current = obj
+                morePossibleSelections = True
+                while morePossibleSelections:
+                    morePossibleSelections = False
+                    for relation in current.getRelationSet():
+                        if relation.getRelationType() == \
+                               pyatspi.RELATION_FLOWS_FROM:
+                            prevObj = relation.getTarget(0)
+                            prevObjText = prevObj.queryText()
+                            if prevObjText.getNSelections() > 0:
+                                otherSelected = True
+                            else:
+                                displayedText = prevObjText.getText(0, -1)
+                                if len(displayedText) == 0:
+                                    current = prevObj
+                                    morePossibleSelections = True
+                            break
+
+                current = obj
+                morePossibleSelections = True
+                while morePossibleSelections:
+                    morePossibleSelections = False
+                    for relation in current.getRelationSet():
+                        if relation.getRelationType() == \
+                               pyatspi.RELATION_FLOWS_TO:
+                            nextObj = relation.getTarget(0)
+                            nextObjText = nextObj.queryText()
+                            if nextObjText.getNSelections() > 0:
+                                otherSelected = True
+                            else:
+                                displayedText = nextObjText.getText(0, -1)
+                                if len(displayedText) == 0:
+                                    current = nextObj
+                                    morePossibleSelections = True
+                            break
+
+        return [currentSelected, otherSelected]
+
     def reportScriptInfo(self, inputEvent=None):
         """Output useful information on the current script via speech
         and braille.  This information will be helpful to script writers.
@@ -5125,7 +5281,7 @@ class Script(script.Script):
         # the Braille display as an input device.
         #
         if not isinstance(inputEvent, input_event.BrailleEvent):
-            utterances = self.speechGenerator.getSpeech(
+            utterances = self.speechGenerator.generateSpeech(
                     context.getCurrentAccessible())
             utterances.extend(self.tutorialGenerator.getTutorial(
                     context.getCurrentAccessible(), False))
@@ -7534,7 +7690,7 @@ class Script(script.Script):
 
         texti.setCaretOffset(offset)
 
-    def attribsToDictionary(self, dict_string):
+    def attributeStringToDictionary(self, dict_string):
         """Creates a Python dict from a typical attributes list returned from
         different AT-SPI methods.
 
@@ -7621,7 +7777,105 @@ class Script(script.Script):
         else:
             return inner_container
 
-    def getTextSelections(self, acc):
+    # pylint: disable-msg=W0142
+
+    def getSelectedText(self, obj):
+        """Get the text selection for the given object.
+
+        Arguments:
+        - obj: the text object to extract the selected text from.
+
+        Returns: the selected text contents plus the start and end
+        offsets within the text.
+        """
+
+        textContents = ""
+        textObj = obj.queryText()
+        nSelections = textObj.getNSelections()
+        for i in range(0, nSelections):
+            [startOffset, endOffset] = textObj.getSelection(i)
+
+            debug.println(debug.LEVEL_FINEST,
+                "getSelectedText: selection start=%d, end=%d" % \
+                (startOffset, endOffset))
+
+            selectedText = textObj.getText(startOffset, endOffset)
+            debug.println(debug.LEVEL_FINEST,
+                "getSelectedText: selected text=<%s>" % selectedText)
+
+            if i > 0:
+                textContents += " "
+            textContents += selectedText
+
+        return [textContents, startOffset, endOffset]
+
+    def getAllSelectedText(self, obj):
+        """Get all the text applicable text selections for the given object.
+        including any previous or next text objects that also have
+        selected text and add in their text contents.
+
+        Arguments:
+        - obj: the text object to start extracting the selected text from.
+
+        Returns: all the selected text contents plus the start and end
+        offsets within the text for the given object.
+        """
+
+        textContents = ""
+        startOffset = 0
+        endOffset = 0
+        text = obj.queryText()
+        if text.getNSelections() > 0:
+            [textContents, startOffset, endOffset] = \
+                self.getSelectedText(obj)
+
+        current = obj
+        morePossibleSelections = True
+        while morePossibleSelections:
+            morePossibleSelections = False
+            for relation in current.getRelationSet():
+                if relation.getRelationType() \
+                   == pyatspi.RELATION_FLOWS_FROM:
+                    prevObj = relation.getTarget(0)
+                    prevObjText = prevObj.queryText()
+                    if prevObjText.getNSelections() > 0:
+                        [newTextContents, start, end] = \
+                            self.getSelectedText(prevObj)
+                        textContents = newTextContents + " " + textContents
+                        current = prevObj
+                        morePossibleSelections = True
+                    else:
+                        displayedText = prevObjText.getText(0, -1)
+                        if len(displayedText) == 0:
+                            current = prevObj
+                            morePossibleSelections = True
+                    break
+
+        current = obj
+        morePossibleSelections = True
+        while morePossibleSelections:
+            morePossibleSelections = False
+            for relation in current.getRelationSet():
+                if relation.getRelationType() \
+                   == pyatspi.RELATION_FLOWS_TO:
+                    nextObj = relation.getTarget(0)
+                    nextObjText = nextObj.queryText()
+                    if nextObjText.getNSelections() > 0:
+                        [newTextContents, start, end] = \
+                            self.getSelectedText(nextObj)
+                        textContents += " " + newTextContents
+                        current = nextObj
+                        morePossibleSelections = True
+                    else:
+                        displayedText = nextObjText.getText(0, -1)
+                        if len(displayedText) == 0:
+                            current = nextObj
+                            morePossibleSelections = True
+                    break
+
+        return [textContents, startOffset, endOffset]
+
+    def getAllTextSelections(self, acc):
         """Get a list of text selections in the given accessible object,
         equivelent to getNSelections()*texti.getSelection()
 
@@ -7675,11 +7929,11 @@ class Script(script.Script):
             return rv, 0, 0
 
         if get_defaults:
-            rv.update(self.attribsToDictionary(texti.getDefaultAttributes()))
+            rv.update(self.attributeStringToDictionary(texti.getDefaultAttributes()))
 
         attrib_str, start, end = texti.getAttributes(offset)
 
-        rv.update(self.attribsToDictionary(attrib_str))
+        rv.update(self.attributeStringToDictionary(attrib_str))
 
         return rv, start, end
 
diff --git a/src/orca/formatting.py b/src/orca/formatting.py
index 1c48a8a..a31a75e 100644
--- a/src/orca/formatting.py
+++ b/src/orca/formatting.py
@@ -35,15 +35,21 @@ formatting = {
     'speech': {
         'prefix': {
             'focused': '[]',
-            'unfocused': 'newAncestors + newRowHeader + newColumnHeader + newRadioButtonGroup'
+            'unfocused': 'newAncestors + newRowHeader + newColumnHeader + newRadioButtonGroup',
+            'basicWhereAmI': 'toolbar',
+            'detailedWhereAmI' : '[]'
             },
         'suffix': {
             'focused': '[]',
-            'unfocused': 'newNodeLevel + unselectedCell + tutorial'
+            'unfocused': 'newNodeLevel + unselectedCell + tutorial',
+            'basicWhereAmI': 'tutorial + description',
+            'detailedWhereAmI' : '[]'
             },
         'default': {
             'focused': '[]',
-            'unfocused': 'labelAndName + allTextSelection + roleName + availability + mnemonic + accelerator'
+            'unfocused': 'labelAndName + allTextSelection + roleName + availability + mnemonic + accelerator',
+            'basicWhereAmI': 'labelAndName + roleName',
+            'detailedWhereAmI' : '[]'
             },
         pyatspi.ROLE_ALERT: {
             'unfocused': 'labelAndName + unrelatedLabels'
@@ -53,60 +59,104 @@ formatting = {
             },
         pyatspi.ROLE_CHECK_BOX: {
             'focused': 'checkedState',
-            'unfocused': 'labelAndName + roleName + checkedState + required + availability + mnemonic + accelerator'
+            'unfocused': 'labelAndName + roleName + checkedState + required + availability + mnemonic + accelerator',
+            'basicWhereAmI': 'labelAndName + roleName + checkedState + mnemonic + accelerator + required'
             },
         pyatspi.ROLE_CHECK_MENU_ITEM: {
             'focused': 'checkedState',
-            'unfocused': 'labelAndName + roleName + checkedState + required + availability + mnemonic + accelerator'
+            'unfocused': 'labelAndName + roleName + checkedState + required + availability + mnemonic + accelerator',
+            'basicWhereAmI': 'ancestors + labelAndName + roleName + checkedState + accelerator + positionInList + mnemonic'
             },
         pyatspi.ROLE_COMBO_BOX: {
             'focused': 'name',
+            'basicWhereAmI': 'label + roleName + name + positionInList + mnemonic + accelerator'
             },
         pyatspi.ROLE_DIALOG: {
             'unfocused': 'labelAndName + unrelatedLabels'
             },
+        pyatspi.ROLE_DOCUMENT_FRAME: {
+            'basicWhereAmI': 'label + readOnly + textRole + textContent + anyTextSelection + mnemonic',
+            'detailedWhereAmI': 'label + readOnly + textRole + textContentWithAttributes + anyTextSelection + mnemonic + tutorial'
+            },
+        pyatspi.ROLE_EMBEDDED: {
+            'focused': 'embedded',
+            'unfocused': 'embedded'
+            },
         pyatspi.ROLE_ENTRY: {
             'focused': 'labelOrName + readOnly + textRole + currentLineText + allTextSelection',
-            'unfocused': 'labelOrName + readOnly + textRole + currentLineText + allTextSelection + mnemonic'
+            'unfocused': 'labelOrName + readOnly + textRole + currentLineText + allTextSelection + mnemonic',
+            'basicWhereAmI': 'label + readOnly + textRole + textContent + anyTextSelection + mnemonic',
+            'detailedWhereAmI': 'label + readOnly + textRole + textContentWithAttributes + anyTextSelection + mnemonic + tutorial'
             },
         pyatspi.ROLE_FRAME: {
             'focused': '[]',
             'unfocused': 'labelAndName + allTextSelection + roleName + unfocusedDialogCount + availability'
             },
+        pyatspi.ROLE_HEADING: {
+            'basicWhereAmI': 'label + readOnly + textRole + textContent + anyTextSelection + mnemonic',
+            'detailedWhereAmI': 'label + readOnly + textRole + textContentWithAttributes + anyTextSelection + mnemonic + tutorial'
+            },
         pyatspi.ROLE_ICON: {
             'focused': 'labelAndName + imageDescription + roleName',
-            'unfocused': 'labelAndName + imageDescription + roleName'
+            'unfocused': 'labelAndName + imageDescription + roleName',
+            'basicWhereAmI': 'parentRoleName + labelAndName + selectedItemCount',
+            'detailedWhereAmI': 'parentRoleName + labelAndName + selectedItemCount + selectedItems'
+            },
+        pyatspi.ROLE_LABEL: {
+            'basicWhereAmI': 'labelAndName + allTextSelection + roleName'
             },
         pyatspi.ROLE_LAYERED_PANE: {
             'focused': 'labelAndName + allTextSelection + roleName + availability + noShowingChildren',
-            'unfocused': 'labelAndName + allTextSelection + roleName + availability + noShowingChildren'
+            'unfocused': 'labelAndName + allTextSelection + roleName + availability + noShowingChildren',
+            'basicWhereAmI': 'labelAndName + roleName + selectedItemCount',
+            'detailedWhereAmI': 'labelAndName + roleName + selectedItemCount + selectedItems'
+            },
+        pyatspi.ROLE_LINK: {
+            'unfocused': 'labelAndName + roleName + availability',
+            'basicWhereAmI': 'linkInfo + siteDescription + fileSize'
             },
         pyatspi.ROLE_LIST_ITEM: {
             'focused': 'expandableState + availability',
-            'unfocused': 'labelAndName + allTextSelection + expandableState + availability'
+            'unfocused': 'labelAndName + allTextSelection + expandableState + availability',
+            'basicWhereAmI': 'label + roleName + name + positionInList + expandableState + (nodeLevel or nestingLevel)'
             },
         pyatspi.ROLE_MENU: {
             'focused': '[]',
-            'unfocused': 'labelAndName + allTextSelection + roleName + availability + mnemonic + accelerator'
+            'unfocused': 'labelAndName + allTextSelection + roleName + availability + mnemonic + accelerator',
+            'basicWhereAmI': '(ancestors or parentRoleName) + labelAndName + roleName +  positionInList + mnemonic'
             },
         pyatspi.ROLE_MENU_ITEM: {
             'focused': '[]',
-            'unfocused': 'labelAndName + menuItemCheckedState + availability + mnemonic + accelerator'
+            'unfocused': 'labelAndName + menuItemCheckedState + availability + mnemonic + accelerator',
+            'basicWhereAmI': 'ancestors + labelAndName + accelerator + positionInList + mnemonic'
+            },
+        pyatspi.ROLE_PAGE_TAB: {
+            'basicWhereAmI': 'parentRoleName + labelAndName + roleName + positionInList + mnemonic + accelerator'
+            },
+        pyatspi.ROLE_PARAGRAPH: {
+            'focused': 'labelOrName + readOnly + textRole + currentLineText + allTextSelection',
+            'unfocused': 'labelOrName + readOnly + textRole + currentLineText + allTextSelection + mnemonic',
+            'basicWhereAmI': 'label + readOnly + textRole + textContent + anyTextSelection + mnemonic',
+            'detailedWhereAmI': 'label + readOnly + textRole + textContentWithAttributes + anyTextSelection + mnemonic + tutorial'
             },
         pyatspi.ROLE_PASSWORD_TEXT: {
             'focused': 'labelOrName + readOnly + textRole + currentLineText + allTextSelection',
-            'unfocused': 'labelOrName + readOnly + textRole + currentLineText + allTextSelection + mnemonic'
+            'unfocused': 'labelOrName + readOnly + textRole + currentLineText + allTextSelection + mnemonic',
+            'basicWhereAmI': 'label + readOnly + textRole + textContent + anyTextSelection + mnemonic',
+            'detailedWhereAmI': 'label + readOnly + textRole + textContentWithAttributes + anyTextSelection + mnemonic + tutorial'
             },
         pyatspi.ROLE_PROGRESS_BAR: {
             'focused': 'percentage',
             'unfocused': 'labelAndName + percentage'
             },
         pyatspi.ROLE_PUSH_BUTTON: {
-            'unfocused': 'labelAndName + roleName + availability + mnemonic + accelerator'
+            'unfocused': 'labelAndName + roleName + availability + mnemonic + accelerator',
+            'basicWhereAmI': 'labelAndName + roleName + mnemonic + accelerator'
             },
         pyatspi.ROLE_RADIO_BUTTON: {
             'focused': 'radioState',
-            'unfocused': 'labelAndName + radioState + roleName + availability + mnemonic + accelerator'
+            'unfocused': 'labelAndName + radioState + roleName + availability + mnemonic + accelerator',
+            'basicWhereAmI': 'radioButtonGroup + labelAndName + roleName + radioState + positionInGroup + mnemonic + accelerator'
             },
         pyatspi.ROLE_RADIO_MENU_ITEM: {
             # OpenOffice check menu items currently have a role of "menu item"
@@ -117,7 +167,12 @@ formatting = {
             # See Orca bug #433398 for more details.
             #
             'focused': 'labelAndName + radioState + roleName + availability',
-            'unfocused': 'labelAndName + radioState + roleName + availability + mnemonic + accelerator'
+            'unfocused': 'labelAndName + radioState + roleName + availability + mnemonic + accelerator',
+            'basicWhereAmI': 'ancestors + labelAndName + roleName + radioState + accelerator + positionInList + mnemonic'
+            },
+        pyatspi.ROLE_SECTION: {
+            'basicWhereAmI': 'label + readOnly + textRole + textContent + anyTextSelection + mnemonic',
+            'detailedWhereAmI': 'label + readOnly + textRole + textContentWithAttributes + anyTextSelection + mnemonic + tutorial'
             },
         pyatspi.ROLE_SLIDER: {
             # Ignore the text on the slider.  See bug 340559
@@ -131,23 +186,29 @@ formatting = {
             # it exists and we haven't found anything yet.
             #
             'focused': 'value',
-            'unfocused': 'labelAndName + roleName + value + required + availability + mnemonic'
+            'unfocused': 'labelAndName + roleName + value + required + availability + mnemonic',
+            'basicWhereAmI': 'label + roleName + value + percentage + mnemonic + accelerator + required'
             },
         pyatspi.ROLE_SPIN_BUTTON: {
             'focused': 'name',
-            'unfocused': 'labelAndName + allTextSelection + roleName + availability + mnemonic + required'
+            'unfocused': 'labelAndName + allTextSelection + roleName + availability + mnemonic + required',
+            'basicWhereAmI': 'label + roleName + name + allTextSelection + mnemonic + accelerator + required'
             },
         pyatspi.ROLE_SPLIT_PANE: {
             'focused': 'value',
-            'unfocused': 'labelAndName + roleName + value + availability + mnemonic'
+            'unfocused': 'labelAndName + roleName + value + availability + mnemonic',
+            'basicWhereAmI' : 'labelAndName + roleName + value'
             },
         pyatspi.ROLE_TABLE: {
             'focused': 'labelAndName + allTextSelection + roleName + availability + noChildren',
-            'unfocused': 'labelAndName + allTextSelection + roleName + availability + noChildren'
+            'unfocused': 'labelAndName + allTextSelection + roleName + availability + noChildren',
+            'basicWhereAmI': 'labelAndName + allTextSelection + roleName + availability + noChildren'
             },
         pyatspi.ROLE_TABLE_CELL: {
             'focused': '(tableCell2ChildLabel + tableCell2ChildToggle) or cellCheckedState + (expandableState and (expandableState + numberOfChildren))',
-            'unfocused': 'tableCellRow'
+            'unfocused': 'tableCellRow',
+            'basicWhereAmI': 'parentRoleName + columnHeader + rowHeader + roleName + cellCheckedState + (realActiveDescendantDisplayedText or imageDescription + image) + columnAndRow + expandableState + nodeLevel',
+            'detailedWhereAmI': 'parentRoleName + columnHeader + rowHeader + roleName + cellCheckedState + (realActiveDescendantDisplayedText or imageDescription + image) + columnAndRow + tableCellRow + expandableState + nodeLevel'
             },
         'REAL_ROLE_TABLE_CELL': {
             # the real cell information
@@ -164,23 +225,24 @@ formatting = {
             },
         pyatspi.ROLE_TERMINAL: {
             'focused': 'terminal',
-            'unfocused': 'terminal'
+            'unfocused': 'terminal',
+            'basicWhereAmI': 'label + readOnly + textRole + textContent + anyTextSelection + mnemonic',
+            'detailedWhereAmI': 'label + readOnly + textRole + textContentWithAttributes + anyTextSelection + mnemonic + tutorial'
             },
         pyatspi.ROLE_TEXT: {
             'focused': 'labelOrName + readOnly + textRole + currentLineText + allTextSelection',
-            'unfocused': 'labelOrName + readOnly + textRole + currentLineText + allTextSelection + mnemonic'
+            'unfocused': 'labelOrName + readOnly + textRole + currentLineText + allTextSelection + mnemonic',
+            'basicWhereAmI': 'label + readOnly + textRole + textContent + anyTextSelection + mnemonic',
+            'detailedWhereAmI': 'label + readOnly + textRole + textContentWithAttributes + anyTextSelection + mnemonic + tutorial'
             },
         pyatspi.ROLE_TOGGLE_BUTTON: {
             'focused': 'toggleState',
-            'unfocused': 'labelAndName + roleName + toggleState + availability + mnemonic + accelerator'
-            },
-        pyatspi.ROLE_PARAGRAPH: {
-            'focused': 'labelOrName + readOnly + textRole + currentLineText + allTextSelection',
-            'unfocused': 'labelOrName + readOnly + textRole + currentLineText + allTextSelection + mnemonic'
+            'unfocused': 'labelAndName + roleName + toggleState + availability + mnemonic + accelerator',
+            'basicWhereAmI': 'labelAndName + roleName + toggleState'
             },
-        pyatspi.ROLE_EMBEDDED: {
-            'focused': 'embedded',
-            'unfocused': 'embedded'
+        pyatspi.ROLE_TOOL_TIP: {
+            'unfocused': 'labelAndName',
+            'basicWhereAmI': 'labelAndName'
             },
     }
 }
@@ -208,49 +270,59 @@ class Formatting(dict):
             else:
                 self[key] = val
 
-    def getPrefix(self, dictType, **args):
-        already_focused = args.get('already_focused', False)
-        if already_focused:
-            focusType = 'focused'
-        else:
-            focusType = 'unfocused'
-        try:
-            prefix = self[dictType]['prefix'][focusType]
-        except:
-            prefix = self[dictType]['prefix']['unfocused']
+    def getPrefix(self, **args):
+        """Get a formatting string to add on to the end of
+        formatting strings obtained by getFormat.
+
+        Arguments expected in args:
+        - mode: output mode, such as 'speech', 'braille'.
+        - formatType: the type of formatting, such as
+          'focused', 'basicWhereAmI', etc.
+        """
+        prefix = self[args['mode']]['prefix'][args['formatType']]
         return prefix
 
-    def getSuffix(self, dictType, **args):
-        already_focused = args.get('already_focused', False)
-        if already_focused:
-            focusType = 'focused'
-        else:
-            focusType = 'unfocused'
-        try:
-            suffix = self[dictType]['suffix'][focusType]
-        except:
-            suffix = self[dictType]['suffix']['unfocused']
+    def getSuffix(self, **args):
+        """Get a formatting string to add on to the end of
+        formatting strings obtained by getFormat.
+
+        Arguments expected in args:
+        - mode: output mode, such as 'speech', 'braille'.
+        - role: the role, such as pyatspi.ROLE_TEXT
+        - formatType: the type of formatting, such as
+          'focused', 'basicWhereAmI', etc.
+        """
+        suffix = self[args['mode']]['suffix'][args['formatType']]
         return suffix
 
-    def getFormat(self, dictType, **args):
-        already_focused = args.get('already_focused', False)
-        if already_focused:
-            focusType = 'focused'
-        else:
-            focusType = 'unfocused'
+    def getFormat(self, **args):
+        """Get a formatting string for the given mode and
+        formatType.
 
-        role = args.get('role', None)
+        Arguments expected in args:
+        - mode: output mode, such as 'speech', 'braille'.
+        - role: the role, such as pyatspi.ROLE_TEXT
+        - formatType: the type of formatting, such as
+          'focused', 'basicWhereAmI', etc.
+        """
         try:
-            roleDict = self[dictType][role]
-        except:
-            roleDict = self[dictType]['default']
-
-        try:
-            format = roleDict[focusType]
+            # First try to find the exact match.
+            #
+            format = self[args['mode']][args['role']][args['formatType']]
         except:
             try:
-                format = roleDict['unfocused']
+                # Failing that, fallback to the 'unfocused' formatType
+                # for the mode and role, if it exists.
+                #
+                format = self[args['mode']][args['role']]['unfocused']
             except:
-                format = self[dictType]['default'][focusType]
-
+                try:
+                    # Failing that, fallback to the default for the
+                    # formatType
+                    #
+                    format = self[args['mode']]['default'][args['formatType']]
+                except:
+                    # Failing that, just used the default 'unfocused' format
+                    #
+                    format = self[args['mode']]['default']['unfocused']
         return format
diff --git a/src/orca/liveregions.py b/src/orca/liveregions.py
index a318812..c56f8d6 100644
--- a/src/orca/liveregions.py
+++ b/src/orca/liveregions.py
@@ -346,13 +346,13 @@ class LiveRegionManager:
             # Toggle our flag
             self.monitoring = True  
 
-    def outputLiveRegionDescription(self, obj):
+    def generateLiveRegionDescription(self, obj, **args):
         """Used in conjuction with whereAmI to output description and 
         politeness of the given live region object"""
         objectid = self._getObjectId(obj)
         uri = self._script.bookmarks.getURIKey()
 
-        utterances = []
+        results = []
 
         # get the description if there is one.
         for relation in obj.getRelationSet():
@@ -367,7 +367,7 @@ class LiveRegionManager:
                     #
                     description = targetobj.queryText().getText(0, -1)
                     if description.strip() != obj.description.strip():
-                        utterances.append(description)
+                        results.append(description)
                 except NotImplemented:
                     pass
 
@@ -380,11 +380,12 @@ class LiveRegionManager:
 
         # We will only output useful information
         # 
-        if utterances or liveprioritystr != 'none':
+        if results or liveprioritystr != 'none':
             # Translators: output the politeness level
             #
-            utterances.append(_('politeness level %s') %liveprioritystr)
-            speech.speak(utterances)
+            results.append(_('politeness level %s') %liveprioritystr)
+
+        return results
 
     def matchLiveRegion(self, obj):
         """Predicate used to find a live region"""
diff --git a/src/orca/mouse_review.py b/src/orca/mouse_review.py
index 45ac68a..fd98062 100644
--- a/src/orca/mouse_review.py
+++ b/src/orca/mouse_review.py
@@ -266,8 +266,8 @@ class MouseReviewer:
                 braille.displayMessage(obj)
             else:
                 speech.speak(
-                    self._currentMouseOver.script.speechGenerator.getSpeech(
-                        obj))
+                    self._currentMouseOver.script.speechGenerator.\
+                        generateSpeech(obj))
                 self._currentMouseOver.script.updateBraille(obj)
 
     def _getZOrder(self, frame_name):
diff --git a/src/orca/scripts/apps/Thunderbird/speech_generator.py b/src/orca/scripts/apps/Thunderbird/speech_generator.py
index dc13506..dfbaf86 100644
--- a/src/orca/scripts/apps/Thunderbird/speech_generator.py
+++ b/src/orca/scripts/apps/Thunderbird/speech_generator.py
@@ -47,7 +47,7 @@ class SpeechGenerator(Gecko.SpeechGenerator):
     def __init__(self, script):
         Gecko.SpeechGenerator.__init__(self, script)
 
-    def _getColumnHeader(self, obj, **args):
+    def _generateColumnHeader(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) that represent the column header for an object
         that is in a table, if it exists.  Otherwise, an empty array
@@ -60,7 +60,7 @@ class SpeechGenerator(Gecko.SpeechGenerator):
         #
         return result
 
-    def _getUnrelatedLabels(self, obj, **args):
+    def _generateUnrelatedLabels(self, obj, **args):
         """Finds all labels not in a label for or labelled by relation.
         If this is the spell checking dialog, then there are no
         unrelated labels.  See bug #535192 for more details.
@@ -78,6 +78,6 @@ class SpeechGenerator(Gecko.SpeechGenerator):
                          pyatspi.ROLE_APPLICATION]):
             pass
         else:
-            result.extend(Gecko.SpeechGenerator._getUnrelatedLabels(
+            result.extend(Gecko.SpeechGenerator._generateUnrelatedLabels(
                               self, obj, **args))
         return result
diff --git a/src/orca/scripts/apps/acroread.py b/src/orca/scripts/apps/acroread.py
index 992ce10..b043f13 100644
--- a/src/orca/scripts/apps/acroread.py
+++ b/src/orca/scripts/apps/acroread.py
@@ -438,7 +438,7 @@ class Script(default.Script):
                 return
 
             utterances = \
-                 self.speechGenerator.getSpeech(newLocusOfFocus)
+                 self.speechGenerator.generateSpeech(newLocusOfFocus)
             speech.speak(utterances)
             brailleRegions = \
                  self.brailleGenerator.getBrailleRegions(newLocusOfFocus)
@@ -469,7 +469,7 @@ class Script(default.Script):
             # verboseness: reporting the drawing area(s) in which this link
             # is contained, speaking the periods in a table of contents, etc.
             #
-            utterances = self.speechGenerator.getSpeech(newLocusOfFocus)
+            utterances = self.speechGenerator.generateSpeech(newLocusOfFocus)
             adjustedUtterances = []
             for utterance in utterances:
                 adjustedUtterances.append(self.adjustForRepeats(utterance))
@@ -554,7 +554,7 @@ class Script(default.Script):
                 # Try to minimize chattiness in the Search panel
                 #
                 utterances = \
-                     self.speechGenerator.getSpeech(event.source)
+                     self.speechGenerator.generateSpeech(event.source)
                 speech.speak(utterances)
                 brailleRegions = \
                      self.brailleGenerator.getBrailleRegions(event.source)
diff --git a/src/orca/scripts/apps/evolution/Makefile.am b/src/orca/scripts/apps/evolution/Makefile.am
index 1ca5519..ef31b58 100644
--- a/src/orca/scripts/apps/evolution/Makefile.am
+++ b/src/orca/scripts/apps/evolution/Makefile.am
@@ -4,8 +4,7 @@ orca_python_PYTHON = \
     __init__.py \
     formatting.py \
     script.py \
-    speech_generator.py \
-    where_am_i.py
+    speech_generator.py
 
 orca_pythondir=$(pyexecdir)/orca/scripts/apps/evolution
 
diff --git a/src/orca/scripts/apps/evolution/script.py b/src/orca/scripts/apps/evolution/script.py
index df46d4f..abd6fb5 100644
--- a/src/orca/scripts/apps/evolution/script.py
+++ b/src/orca/scripts/apps/evolution/script.py
@@ -41,7 +41,6 @@ import orca.settings as settings
 
 from orca.orca_i18n import _ # for gettext support
 
-from where_am_i import WhereAmI
 from speech_generator import SpeechGenerator
 from formatting import Formatting
 ########################################################################
@@ -140,12 +139,6 @@ class Script(default.Script):
 
         return SpeechGenerator(self)
 
-    def getWhereAmI(self):
-        """Returns the "where am I" class for this script.
-        """
-
-        return WhereAmI(self)
-
     def getFormatting(self):
         """Returns the formatting strings for this script."""
         return Formatting(self)
@@ -289,7 +282,7 @@ class Script(default.Script):
                                        pyatspi.STATE_SENSITIVE)):
                                     self.updateBraille(orca_state.locusOfFocus)
                                     speech.speak(
-                                        self.speechGenerator.getSpeech(
+                                        self.speechGenerator.generateSpeech(
                                             orca_state.locusOfFocus))
             except NotImplementedError:
                 pass
@@ -342,7 +335,7 @@ class Script(default.Script):
 
         savedSpeechVerbosityLevel = settings.speechVerbosityLevel
         settings.speechVerbosityLevel = settings.VERBOSITY_LEVEL_BRIEF
-        utterances = speechGen.getSpeech(tab)
+        utterances = speechGen.generateSpeech(tab)
         speech.speak(utterances)
         settings.speechVerbosityLevel = savedSpeechVerbosityLevel
 
@@ -375,6 +368,149 @@ class Script(default.Script):
 
         return hrs + ' ' + mins + ' ' + suffix
 
+    def getAllSelectedText(self, obj):
+        """Get all the text applicable text selections for the given object.
+        If there is selected text, look to see if there are any previous
+        or next text objects that also have selected text and add in their
+        text contents.
+
+        Arguments:
+        - obj: the text object to start extracting the selected text from.
+
+        Returns: all the selected text contents plus the start and end
+        offsets within the text for the given object.
+        """
+
+        textContents = ""
+        startOffset = 0
+        endOffset = 0
+        if obj.queryText().getNSelections() > 0:
+            [textContents, startOffset, endOffset] = \
+                                            self.getSelectedText(obj)
+
+        # Unfortunately, Evolution doesn't use the FLOWS_FROM and
+        # FLOWS_TO relationships to easily allow us to get to previous
+        # and next text objects. Instead we have to move up the
+        # component hierarchy until we get to the object containing all
+        # the panels (with each line containing a single text item).
+        # We can then check in both directions to see if there is other
+        # contiguous text that is selected. We also have to jump over
+        # zero length (empty) text lines and continue checking on the
+        # other side.
+        #
+        container = obj.parent.parent
+        current = obj.parent.getIndexInParent()
+        morePossibleSelections = True
+        while morePossibleSelections:
+            morePossibleSelections = False
+            if (current-1) >= 0:
+                prevPanel = container[current-1]
+                try:
+                    prevObj = prevPanel[0]
+                    displayedText = prevObj.queryText().getText(0, -1)
+                    if len(displayedText) == 0:
+                        current -= 1
+                        morePossibleSelections = True
+                    elif prevObj.queryText().getNSelections() > 0:
+                        [newTextContents, start, end] = \
+                                     self.getSelectedText(prevObj)
+                        textContents = newTextContents + " " + textContents
+                        current -= 1
+                        morePossibleSelections = True
+                except:
+                    pass
+
+        current = obj.parent.getIndexInParent()
+        morePossibleSelections = True
+        while morePossibleSelections:
+            morePossibleSelections = False
+            if (current+1) < container.childCount:
+                nextPanel = container[current+1]
+                try:
+                    nextObj = nextPanel[0]
+                    displayedText = nextObj.queryText().getText(0, -1)
+                    if len(displayedText) == 0:
+                        current += 1
+                        morePossibleSelections = True
+                    elif nextObj.queryText().getNSelections() > 0:
+                        [newTextContents, start, end] = \
+                                     self.getSelectedText(nextObj)
+                        textContents += " " + newTextContents
+                        current += 1
+                        morePossibleSelections = True
+                except:
+                    pass
+
+        return [textContents, startOffset, endOffset]
+
+    def hasTextSelections(self, obj):
+        """Return an indication of whether this object has selected text.
+        Note that it's possible that this object has no text, but is part
+        of a selected text area. Because of this, we need to check the
+        objects on either side to see if they are none zero length and
+        have text selections.
+
+        Arguments:
+        - obj: the text object to start checking for selected text.
+
+        Returns: an indication of whether this object has selected text,
+        or adjacent text objects have selected text.
+        """
+
+        currentSelected = False
+        otherSelected = False
+        nSelections = obj.queryText().getNSelections()
+        if nSelections:
+            currentSelected = True
+        else:
+            otherSelected = False
+            displayedText = obj.queryText().getText(0, -1)
+            if len(displayedText) == 0:
+                container = obj.parent.parent
+                current = obj.parent.getIndexInParent()
+                morePossibleSelections = True
+                while morePossibleSelections:
+                    morePossibleSelections = False
+                    if (current-1) >= 0:
+                        prevPanel = container[current-1]
+                        prevObj = prevPanel[0]
+                        try:
+                            prevObjText = prevObj.queryText()
+                        except:
+                            prevObjText = None
+
+                        if prevObj and prevObjText:
+                            if prevObjText.getNSelections() > 0:
+                                otherSelected = True
+                            else:
+                                displayedText = prevObjText.getText(0, -1)
+                                if len(displayedText) == 0:
+                                    current -= 1
+                                    morePossibleSelections = True
+
+                current = obj.parent.getIndexInParent()
+                morePossibleSelections = True
+                while morePossibleSelections:
+                    morePossibleSelections = False
+                    if (current+1) < container.childCount:
+                        nextPanel = container[current+1]
+                        nextObj = nextPanel[0]
+                        try:
+                            nextObjText = nextObj.queryText()
+                        except:
+                            nextObjText = None
+
+                        if nextObj and nextObjText:
+                            if nextObjText.getNSelections() > 0:
+                                otherSelected = True
+                            else:
+                                displayedText = nextObjText.getText(0, -1)
+                                if len(displayedText) == 0:
+                                    current += 1
+                                    morePossibleSelections = True
+
+        return [currentSelected, otherSelected]
+
     def textLines(self, obj):
         """Creates a generator that can be used to iterate over each line
         of a text object, starting at the caret offset.
@@ -564,7 +700,7 @@ class Script(default.Script):
         return False
 
     def getMisspelledWordAndBody(self, suggestionsList, messagePanel):
-        """Gets the misspelled word from the spelling dialog and the 
+        """Gets the misspelled word from the spelling dialog and the
         list of words from the message body.
 
         Arguments:
@@ -947,7 +1083,7 @@ class Script(default.Script):
                             settings.speechVerbosityLevel = \
                                 settings.VERBOSITY_LEVEL_BRIEF
 
-                            utterances = speechGen.getSpeech(
+                            utterances = speechGen.generateSpeech(
                                 header,
                                 includeContext=False,
                                 priorObj=oldLocusOfFocus)
@@ -976,7 +1112,7 @@ class Script(default.Script):
                                 settings.VERBOSITY_LEVEL_BRIEF
                         settings.speechVerbosityLevel = \
                             savedSpeechVerbosityLevel
-                        utterances = speechGen.getSpeech(
+                        utterances = speechGen.generateSpeech(
                             cell,
                             includeContext=False,
                             priorObj=oldLocusOfFocus)
@@ -1070,9 +1206,9 @@ class Script(default.Script):
                           + "day view: tabbing to day with appts.")
 
             parent = event.source.parent
-            utterances = speechGen.getSpeech(parent,
-                                             includeContext=False,
-                                             priorObj=oldLocusOfFocus)
+            utterances = speechGen.generateSpeech(parent,
+                                                  includeContext=False,
+                                                  priorObj=oldLocusOfFocus)
             [brailleRegions, focusedRegion] = \
                     brailleGen.getBrailleRegions(parent)
             speech.speak(utterances)
@@ -1088,7 +1224,7 @@ class Script(default.Script):
                         appt = childTable.getAccessibleAt(row, 0)
                         extents = appt.queryComponent().getExtents(0)
                         if extents.y == apptExtents.y:
-                            utterances = speechGen.getSpeech(
+                            utterances = speechGen.generateSpeech(
                                 event.source,
                                 includeContext=False,
                                 priorObj=oldLocusOfFocus)
@@ -1152,7 +1288,7 @@ class Script(default.Script):
                     apptExtents = child.queryComponent().getExtents(0)
 
                     if extents.y == apptExtents.y:
-                        utterances = speechGen.getSpeech(
+                        utterances = speechGen.generateSpeech(
                             child,
                             includeContext=False,
                             priorObj=oldLocusOfFocus)
@@ -1300,7 +1436,7 @@ class Script(default.Script):
                                            "Home", "End", "Return", "Tab"]:
                         return
 
-                    # If the last keyboard event was a "same line" 
+                    # If the last keyboard event was a "same line"
                     # navigation key, then pass this event onto the
                     # onCaretMoved() method in the parent class for
                     # speaking. See bug #516565 for more details.
@@ -1334,7 +1470,7 @@ class Script(default.Script):
 
             if not self.pointOfReference.get('activeDescendantInfo'):
                 [badWord, allTokens] = \
-                    self.getMisspelledWordAndBody(event.source, 
+                    self.getMisspelledWordAndBody(event.source,
                                                   self.message_panel)
                 self.speakMisspeltWord(allTokens, badWord)
 
diff --git a/src/orca/scripts/apps/evolution/speech_generator.py b/src/orca/scripts/apps/evolution/speech_generator.py
index 0d71bee..6cf61b8 100644
--- a/src/orca/scripts/apps/evolution/speech_generator.py
+++ b/src/orca/scripts/apps/evolution/speech_generator.py
@@ -29,9 +29,11 @@ import pyatspi
 
 import orca.speech_generator as speech_generator
 
+from orca.orca_i18n import _ # for gettext support
+
 class SpeechGenerator(speech_generator.SpeechGenerator):
-    """Overrides _getSpeechForTableCell so that, if this is an expanded
-       table cell,  we can strip off the "0 items".
+    """Overrides _generateSpeechForTableCell so that, if this is an
+       expanded table cell, we can strip off the "0 items".
     """
 
     # pylint: disable-msg=W0142
@@ -39,7 +41,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
     def __init__(self, script):
         speech_generator.SpeechGenerator.__init__(self, script)
 
-    def _getRealTableCell(self, obj, **args):
+    def _generateRealTableCell(self, obj, **args):
         # Check that we are in a table cell in the mail message header list.
         # If we are and this table cell has an expanded state, then
         # dont speak the number of items.
@@ -54,8 +56,76 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
                 if state.contains(pyatspi.STATE_EXPANDED):
                     oldRole = self._overrideRole(
                         'ALTERNATIVE_REAL_ROLE_TABLE_CELL', args)
-                    result = self.getSpeech(obj, **args)
+                    result = self.generateSpeech(obj, **args)
                     self._restoreRole(oldRole, args)
                     return result
-        return speech_generator.SpeechGenerator._getRealTableCell(
+        return speech_generator.SpeechGenerator._generateRealTableCell(
             self, obj, **args)
+
+    def _generateTableCellRow(self, obj, **args):
+        """Orca has a feature to automatically read an entire row of a table
+        as the user arrows up/down the roles.  This leads to complexity in
+        the code.  This method is used to return an array of strings
+        (and possibly voice and audio specifications) for an entire row
+        in a table if that's what the user has requested and if the row
+        has changed.  Otherwise, it will return an array for just the
+        current cell.
+        """
+        # The only time we want to override things is if we're doing
+        # a detailed whereAmI. In that case, we want to minimize the
+        # chattiness associated with presenting the full row of the
+        # message list.
+        #
+        if args.get('formatType', 'unfocused') != 'detailedWhereAmI':
+            return speech_generator.SpeechGenerator.\
+                _generateTableCellRow(self, obj, **args)
+
+        # [[[TODO - JD: Maybe we can do something clever with a
+        # formatting string to address the headers associated with
+        # toggle columns. That's really the difference here.]]]
+        #
+        result = []
+        try:
+            parentTable = obj.parent.queryTable()
+        except NotImplementedError:
+            parentTable = None
+        if parentTable and parentTable.nColumns > 1 \
+           and not self._script.isLayoutOnly(obj.parent):
+            for i in range(0, parentTable.nColumns):
+                index = self._script.getCellIndex(obj)
+                row = parentTable.getRowAtIndex(index)
+                cell = parentTable.getAccessibleAt(row, i)
+                if not cell:
+                    continue
+                state = cell.getState()
+                if state.contains(pyatspi.STATE_SHOWING):
+                    # Don't speak check box cells that area not checked.
+                    #
+                    notChecked = False
+                    try:
+                        action = cell.queryAction()
+                    except NotImplementedError:
+                        action = None
+                    if action:
+                        for i in range(0, action.nActions):
+                            # Translators: this is the action name for
+                            # the 'toggle' action. It must be the same
+                            # string used in the *.po file for gail.
+                            #
+                            if action.getName(i) in ["toggle", _("toggle")]:
+                                if not state.contains(pyatspi.STATE_CHECKED):
+                                    notChecked = True
+                                break
+                    if notChecked:
+                        continue
+
+                    descendant = self._script.getRealActiveDescendant(cell)
+                    text = self._script.getDisplayedText(descendant)
+                    if text == "Status":
+                        # Translators: this in reference to an e-mail message
+                        # status of having been read or unread.
+                        #
+                        text = _("Read")
+                    result.append(text)
+
+        return result
diff --git a/src/orca/scripts/apps/evolution/where_am_i.py b/src/orca/scripts/apps/evolution/where_am_i.py
deleted file mode 100644
index 779783c..0000000
--- a/src/orca/scripts/apps/evolution/where_am_i.py
+++ /dev/null
@@ -1,226 +0,0 @@
-# Orca
-#
-# Copyright 2005-2008 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 script for Evolution."""
-
-__id__        = "$Id$"
-__version__   = "$Revision$"
-__date__      = "$Date$"
-__copyright__ = "Copyright (c) 2005-2008 Sun Microsystems Inc."
-__license__   = "LGPL"
-
-import pyatspi
-
-import orca.debug as debug
-import orca.where_am_I as where_am_I
-
-from orca.orca_i18n import _ # for gettext support
-
-class WhereAmI(where_am_I.WhereAmI):
-
-    def __init__(self, script):
-        """Create a new WhereAmI that will be used to speak information
-        about the current object of interest.
-        """
-
-        where_am_I.WhereAmI.__init__(self, script)
-
-    def _getTableCell(self, obj):
-        """Get the speech utterances for a single table cell.
-        """
-
-        # Don't speak check box cells that area not checked.
-        notChecked = False
-        try:
-            action = obj.queryAction()
-        except NotImplementedError:
-            action = None
-
-        if action:
-            for i in range(0, action.nActions):
-                # Translators: this is the action name for
-                # the 'toggle' action. It must be the same
-                # string used in the *.po file for gail.
-                #
-                if action.getName(i) in ["toggle", _("toggle")]:
-                    if not obj.getState().contains(pyatspi.STATE_CHECKED):
-                        notChecked = True
-                    break
-
-        if notChecked:
-            return ""
-
-        descendant = self._script.getRealActiveDescendant(obj)
-        text = self._script.getDisplayedText(descendant)
-
-        # For Evolution mail header list.
-        if text == "Status":
-            # Translators: this in reference to an e-mail message status of
-            # having been read or unread.
-            #
-            text = _("Read")
-
-        debug.println(self._debugLevel, "cell=<%s>" % text)
-
-        return text
-
-    def _hasTextSelections(self, obj):
-        """Return an indication of whether this object has selected text.
-        Note that it's possible that this object has no text, but is part
-        of a selected text area. Because of this, we need to check the
-        objects on either side to see if they are none zero length and
-        have text selections.
-
-        Arguments:
-        - obj: the text object to start checking for selected text.
-
-        Returns: an indication of whether this object has selected text,
-        or adjacent text objects have selected text.
-        """
-
-        currentSelected = False
-        otherSelected = False
-        nSelections = obj.queryText().getNSelections()
-        if nSelections:
-            currentSelected = True
-        else:
-            otherSelected = False
-            displayedText = obj.queryText().getText(0, -1)
-            if len(displayedText) == 0:
-                container = obj.parent.parent
-                current = obj.parent.getIndexInParent()
-                morePossibleSelections = True
-                while morePossibleSelections:
-                    morePossibleSelections = False
-                    if (current-1) >= 0:
-                        prevPanel = container[current-1]
-                        prevObj = prevPanel[0]
-                        try:
-                            prevObjText = prevObj.queryText()
-                        except:
-                            prevObjText = None
-
-                        if prevObj and prevObjText:
-                            if prevObjText.getNSelections() > 0:
-                                otherSelected = True
-                            else:
-                                displayedText = prevObjText.getText(0, -1)
-                                if len(displayedText) == 0:
-                                    current -= 1
-                                    morePossibleSelections = True
-
-                current = obj.parent.getIndexInParent()
-                morePossibleSelections = True
-                while morePossibleSelections:
-                    morePossibleSelections = False
-                    if (current+1) < container.childCount:
-                        nextPanel = container[current+1]
-                        nextObj = nextPanel[0]
-                        try:
-                            nextObjText = nextObj.queryText()
-                        except:
-                            nextObjText = None
-
-                        if nextObj and nextObjText:
-                            if nextObjText.getNSelections() > 0:
-                                otherSelected = True
-                            else:
-                                displayedText = nextObjText.getText(0, -1)
-                                if len(displayedText) == 0:
-                                    current += 1
-                                    morePossibleSelections = True
-
-        return [currentSelected, otherSelected]
-
-    def getTextSelections(self, obj, doubleClick):
-        """Get all the text applicable text selections for the given object.
-        If the user doubleclicked, look to see if there are any previous
-        or next text objects that also have selected text and add in their
-        text contents.
-
-        Arguments:
-        - obj: the text object to start extracting the selected text from.
-        - doubleClick: True if the user double-clicked the "where am I" key.
-
-        Returns: all the selected text contents plus the start and end
-        offsets within the text for the given object.
-        """
-
-        textContents = ""
-        startOffset = 0
-        endOffset = 0
-        if obj.queryText().getNSelections() > 0:
-            [textContents, startOffset, endOffset] = \
-                                            self.getTextSelection(obj)
-
-        if doubleClick:
-            # Unfortunately, Evolution doesn't use the FLOWS_FROM and
-            # FLOWS_TO relationships to easily allow us to get to previous
-            # and next text objects. Instead we have to move up the
-            # component hierarchy until we get to the object containing all
-            # the panels (with each line containing a single text item).
-            # We can then check in both directions to see if there is other
-            # contiguous text that is selected. We also have to jump over
-            # zero length (empty) text lines and continue checking on the
-            # other side.
-            #
-            container = obj.parent.parent
-            current = obj.parent.getIndexInParent()
-            morePossibleSelections = True
-            while morePossibleSelections:
-                morePossibleSelections = False
-                if (current-1) >= 0:
-                    prevPanel = container[current-1]
-                    try:
-                        prevObj = prevPanel[0]
-                        displayedText = prevObj.queryText().getText(0, -1)
-                        if len(displayedText) == 0:
-                            current -= 1
-                            morePossibleSelections = True
-                        elif prevObj.queryText().getNSelections() > 0:
-                            [newTextContents, start, end] = \
-                                         self.getTextSelection(prevObj)
-                            textContents = newTextContents + " " + textContents
-                            current -= 1
-                            morePossibleSelections = True
-                    except:
-                        pass
-
-            current = obj.parent.getIndexInParent()
-            morePossibleSelections = True
-            while morePossibleSelections:
-                morePossibleSelections = False
-                if (current+1) < container.childCount:
-                    nextPanel = container[current+1]
-                    try:
-                        nextObj = nextPanel[0]
-                        displayedText = nextObj.queryText().getText(0, -1)
-                        if len(displayedText) == 0:
-                            current += 1
-                            morePossibleSelections = True
-                        elif nextObj.queryText().getNSelections() > 0:
-                            [newTextContents, start, end] = \
-                                         self.getTextSelection(nextObj)
-                            textContents += " " + newTextContents
-                            current += 1
-                            morePossibleSelections = True
-                    except:
-                        pass
-
-        return [textContents, startOffset, endOffset]
diff --git a/src/orca/scripts/apps/gcalctool/Makefile.am b/src/orca/scripts/apps/gcalctool/Makefile.am
index 26dc31a..02dbea3 100644
--- a/src/orca/scripts/apps/gcalctool/Makefile.am
+++ b/src/orca/scripts/apps/gcalctool/Makefile.am
@@ -3,8 +3,7 @@ orca_pathdir=$(pyexecdir)
 orca_python_PYTHON = \
 	__init__.py \
 	script.py \
-	speech_generator.py \
-	where_am_i.py
+	speech_generator.py
 
 orca_pythondir=$(pyexecdir)/orca/scripts/apps/gcalctool
 
diff --git a/src/orca/scripts/apps/gcalctool/script.py b/src/orca/scripts/apps/gcalctool/script.py
index 4afb890..8e1aa53 100644
--- a/src/orca/scripts/apps/gcalctool/script.py
+++ b/src/orca/scripts/apps/gcalctool/script.py
@@ -33,7 +33,6 @@ import orca.speech as speech
 import pyatspi
 
 from speech_generator import SpeechGenerator
-from where_am_i import WhereAmI
 
 from orca.orca_i18n import _ # for gettext support
 
@@ -60,12 +59,6 @@ class Script(default.Script):
         self._lastSpokenContents = None
         self._resultsDisplay = None
 
-    def getWhereAmI(self):
-        """Returns the "where am I" class for this script.
-        """
-
-        return WhereAmI(self)
-
     def getSpeechGenerator(self):
         """Returns the speech generator for this script.
         """
diff --git a/src/orca/scripts/apps/gcalctool/speech_generator.py b/src/orca/scripts/apps/gcalctool/speech_generator.py
index ea2fe5f..4b027c6 100644
--- a/src/orca/scripts/apps/gcalctool/speech_generator.py
+++ b/src/orca/scripts/apps/gcalctool/speech_generator.py
@@ -22,27 +22,27 @@
 __id__        = "$Id$"
 __version__   = "$Revision$"
 __date__      = "$Date$"
-__copyright__ = "Copyright (c) 2005-2009 Sun Microsystems Inc."
+__copyright__ = "Copyright (c) 2004-2009 Sun Microsystems Inc."
 __license__   = "LGPL"
 
 import orca.speech_generator as speech_generator
 import pyatspi
 
 class SpeechGenerator(speech_generator.SpeechGenerator):
-    """Overrides _getSpeechForPushButton to handle 'unspeakable'
-    button labels displayed on the screen.
+    """Overrides _generateName to handle 'unspeakable' button labels
+    displayed on the screen.
     """
     def __init__(self, script):
         speech_generator.SpeechGenerator.__init__(self, script)
 
-    def _getName(self, obj, **args):
+    def _generateName(self, obj, **args):
         """Gives preference to the object name versus what is being
         displayed on the screen.  This helps accomodate the naming
         hints being given to us by gcalctool for it's mathematical
         operator buttons."""
 
         if obj.getRole() != pyatspi.ROLE_PUSH_BUTTON:
-            return speech_generator.SpeechGenerator._getName(\
+            return speech_generator.SpeechGenerator._generateName(\
                 self, obj)
 
         if obj.name:
diff --git a/src/orca/scripts/apps/gcalctool/where_am_i.py b/src/orca/scripts/apps/gcalctool/where_am_i.py
deleted file mode 100644
index 45bbfb9..0000000
--- a/src/orca/scripts/apps/gcalctool/where_am_i.py
+++ /dev/null
@@ -1,49 +0,0 @@
-# Orca
-#
-# Copyright 2004-2008 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.
-
-"""Provides a custom script for gcalctool."""
-
-__id__        = "$Id$"
-__version__   = "$Revision$"
-__date__      = "$Date$"
-__copyright__ = "Copyright (c) 2005-2008 Sun Microsystems Inc."
-__license__   = "LGPL"
-
-import orca.speech as speech
-import orca.where_am_I as where_am_I
-
-class WhereAmI(where_am_I.WhereAmI):
-
-    def __init__(self, script):
-        """Create a new WhereAmI that will be used to speak information
-        about the current object of interest.
-        """
-
-        where_am_I.WhereAmI.__init__(self, script)
-
-    def _speakStatusBar(self):
-        """Speaks the status bar."""
-
-        if not self._statusBar:
-            return
-
-        utterances = []
-        text = self.getObjLabelAndName(self._statusBar)
-        utterances.append(text)
-        speech.speak(utterances)
diff --git a/src/orca/scripts/apps/gedit/script.py b/src/orca/scripts/apps/gedit/script.py
index 1c3c659..aff35e7 100644
--- a/src/orca/scripts/apps/gedit/script.py
+++ b/src/orca/scripts/apps/gedit/script.py
@@ -570,8 +570,8 @@ class Script(default.Script):
                 # finding something.
                 #
                 speech.speak(_("Phrase found."))
-                utterances = self.speechGenerator.getSpeech(
-                    event.source, already_focused=True)
+                utterances = self.speechGenerator.generateSpeech(
+                    event.source, alreadyFocused=True)
                 speech.speak(utterances)
 
         # If Ctrl+G was used to repeat a find command, speak the line that
diff --git a/src/orca/scripts/apps/gnome-panel.py b/src/orca/scripts/apps/gnome-panel.py
index f34a93b..f1528b1 100644
--- a/src/orca/scripts/apps/gnome-panel.py
+++ b/src/orca/scripts/apps/gnome-panel.py
@@ -88,7 +88,8 @@ class Script(default.Script):
             if event.type.startswith("object:state-changed:showing") and \
                event.detail1 == 1:
                 braille.displayMessage(obj.name)
-                speech.speak(obj.name)
+                utterances = self.speechGenerator.generateSpeech(obj)
+                speech.speak(utterances)
 
         # If focus moves to something within a panel and focus was not
         # already in the containing panel, the panel will issue its
diff --git a/src/orca/scripts/apps/gnome-terminal.py b/src/orca/scripts/apps/gnome-terminal.py
index 8b905d0..655641f 100644
--- a/src/orca/scripts/apps/gnome-terminal.py
+++ b/src/orca/scripts/apps/gnome-terminal.py
@@ -100,7 +100,7 @@ class Script(default.Script):
                pageTab.getRole() == pyatspi.ROLE_PAGE_TAB and \
                pageTab.getState().contains(pyatspi.STATE_SENSITIVE):
                 self.updateBraille(newLocusOfFocus)
-                utterances = self.speechGenerator.getSpeech(pageTab)
+                utterances = self.speechGenerator.generateSpeech(pageTab)
                 speech.speak(utterances)
 
         default.Script.locusOfFocusChanged(self, event, 
diff --git a/src/orca/scripts/apps/liferea.py b/src/orca/scripts/apps/liferea.py
index 32a0a05..cd70f39 100644
--- a/src/orca/scripts/apps/liferea.py
+++ b/src/orca/scripts/apps/liferea.py
@@ -119,7 +119,7 @@ class Script(default.Script):
             # Here we extend the utterances with the speech generator for 
             # the object with focus (the push button).
             #
-            utterances.extend(speechGen.getSpeech(event.source))
+            utterances.extend(speechGen.generateSpeech(event.source))
 
             # Finally we speak/braille the utterances/regions.
             #
diff --git a/src/orca/scripts/apps/notify-osd.py b/src/orca/scripts/apps/notify-osd.py
index eefffe7..5177df0 100644
--- a/src/orca/scripts/apps/notify-osd.py
+++ b/src/orca/scripts/apps/notify-osd.py
@@ -28,7 +28,6 @@ __license__   = "LGPL"
 
 import orca.default as default
 import orca.speech as speech
-import pyatspi
 
 from orca.orca_i18n import _
 
diff --git a/src/orca/scripts/apps/pidgin/Makefile.am b/src/orca/scripts/apps/pidgin/Makefile.am
index 97922a4..b18096d 100644
--- a/src/orca/scripts/apps/pidgin/Makefile.am
+++ b/src/orca/scripts/apps/pidgin/Makefile.am
@@ -4,8 +4,7 @@ orca_python_PYTHON = \
 	__init__.py \
 	script.py \
 	script_settings.py \
-	speech_generator.py \
-	where_am_i.py
+	speech_generator.py
 
 orca_pythondir=$(pyexecdir)/orca/scripts/apps/pidgin
 
diff --git a/src/orca/scripts/apps/pidgin/script.py b/src/orca/scripts/apps/pidgin/script.py
index cc094c8..7ea82dd 100644
--- a/src/orca/scripts/apps/pidgin/script.py
+++ b/src/orca/scripts/apps/pidgin/script.py
@@ -48,7 +48,6 @@ import orca.speech as speech
 from orca.orca_i18n import _
 
 from speech_generator import SpeechGenerator
-from where_am_i import WhereAmI
 import script_settings
 
 ########################################################################
@@ -261,12 +260,6 @@ class Script(default.Script):
 
         return SpeechGenerator(self)
 
-    def getWhereAmI(self):
-        """Returns the 'where am I' class for this script.
-        """
-        
-        return WhereAmI(self)
-
     def getAppPreferencesGUI(self):
         """Return a GtkVBox contain the application unique configuration
         GUI items for the current application.
diff --git a/src/orca/scripts/apps/pidgin/speech_generator.py b/src/orca/scripts/apps/pidgin/speech_generator.py
index 6c91709..6d526dc 100644
--- a/src/orca/scripts/apps/pidgin/speech_generator.py
+++ b/src/orca/scripts/apps/pidgin/speech_generator.py
@@ -20,7 +20,7 @@
 __id__        = "$Id$"
 __version__   = "$Revision$"
 __date__      = "$Date$"
-__copyright__ = "Copyright (c) 2005-2009 Sun Microsystems Inc."
+__copyright__ = "Copyright (c) 2004-2009 Sun Microsystems Inc."
 __license__   = "LGPL"
 
 import orca.speech_generator as speech_generator
@@ -32,7 +32,7 @@ import orca.speech_generator as speech_generator
 ########################################################################
 
 class SpeechGenerator(speech_generator.SpeechGenerator):
-    """Overrides _getSpeechForTableCell() so that we can provide access
+    """Overrides _generateExpandableState so that we can provide access
     to the expanded/collapsed state and node count for the buddy list.
     """
 
@@ -41,7 +41,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
     def __init__(self, script):
         speech_generator.SpeechGenerator.__init__(self, script)
 
-    def _getExpandableState(self, obj, **args):
+    def _generateExpandableState(self, obj, **args):
         result = []
         if self._script.isInBuddyList(obj):
             # The Pidgin buddy list consists of two columns. The
@@ -54,19 +54,19 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
             expanderCell = obj.parent[obj.getIndexInParent() - 1]
             if expanderCell:
                 result.extend(
-                    speech_generator.SpeechGenerator._getExpandableState(
+                    speech_generator.SpeechGenerator._generateExpandableState(
                         self, expanderCell, **args))
             else:
                 result.extend(
-                    speech_generator.SpeechGenerator._getExpandableState(
+                    speech_generator.SpeechGenerator._generateExpandableState(
                         self, obj, **args))
         else:
             result.extend(
-                speech_generator.SpeechGenerator._getExpandableState(
+                speech_generator.SpeechGenerator._generateExpandableState(
                     self, obj, **args))
         return result
 
-    def _getNumberOfChildren(self, obj, **args):
+    def _generateNumberOfChildren(self, obj, **args):
         result = []
         if self._script.isInBuddyList(obj):
             # The Pidgin buddy list consists of two columns. The
@@ -79,14 +79,14 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
             expanderCell = obj.parent[obj.getIndexInParent() - 1]
             if expanderCell:
                 result.extend(
-                    speech_generator.SpeechGenerator._getNumberOfChildren(
+                    speech_generator.SpeechGenerator._generateNumberOfChildren(
                         self, expanderCell, **args))
             else:
                 result.extend(
-                    speech_generator.SpeechGenerator._getNumberOfChildren(
+                    speech_generator.SpeechGenerator._generateNumberOfChildren(
                         self, obj, **args))
         else:
             result.extend(
-                speech_generator.SpeechGenerator._getNumberOfChildren(
+                speech_generator.SpeechGenerator._generateNumberOfChildren(
                     self, obj, **args))
         return result
diff --git a/src/orca/scripts/apps/pidgin/where_am_i.py b/src/orca/scripts/apps/pidgin/where_am_i.py
deleted file mode 100644
index 9e7008f..0000000
--- a/src/orca/scripts/apps/pidgin/where_am_i.py
+++ /dev/null
@@ -1,162 +0,0 @@
-# Orca
-#
-# Copyright 2004-2008 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 script for gaim.  This provides the ability for Orca to
-monitor both the IM input and IM output text areas at the same time.
-
-The following script specific key sequences are supported:
-
-  Insert-h      -  Toggle whether we prefix chat room messages with
-                   the name of the chat room.
-  Insert-[1-9]  -  Speak and braille a previous chat room message.
-"""
-
-__id__        = "$Id$"
-__version__   = "$Revision$"
-__date__      = "$Date$"
-__copyright__ = "Copyright (c) 2005-2008 Sun Microsystems Inc."
-__license__   = "LGPL"
-
-import pyatspi
-
-import orca.debug as debug
-import orca.orca_state as orca_state
-import orca.rolenames as rolenames
-import orca.speech as speech
-import orca.where_am_I as where_am_I
-
-from orca.orca_i18n import _
-
-########################################################################
-#                                                                      #
-# Custom WhereAmI                                                      #
-#                                                                      #
-######################################################################## 
-
-class WhereAmI(where_am_I.WhereAmI):
-    """Overrides _speakTableCell() so that we can provide access
-    to the expanded/collapsed state for items in the buddy list.
-    """
-
-    def __init__(self, script):
-        where_am_I.WhereAmI.__init__(self, script)
-        self._script = script
-        
-    def _speakTableCell(self, obj, doubleClick):
-        """Tree Tables present the following information (an example is
-        'Tree table, Mike Pedersen, row 8 of 10, tree level 2'):
-
-        1. label, if any
-        2. role
-        3. current row (regardless of speak cell/row setting)
-        4. relative position
-        5. if expandable/collapsible: expanded/collapsed
-        6. if applicable, the level
-
-        """
-
-        if not self._script.isInBuddyList(obj):
-            return where_am_I.WhereAmI._speakTableCell(self, obj, doubleClick)
-
-        # Speak the first two items (and possibly the position)
-        #
-        utterances = []
-        if obj.parent.getRole() == pyatspi.ROLE_TABLE_CELL:
-            obj = obj.parent
-        parent = obj.parent
-
-        text = self._getObjLabel(obj)
-        utterances.append(text)
-
-        text = rolenames.getSpeechForRoleName(obj)
-        utterances.append(text)
-        debug.println(self._debugLevel, "first table cell utterances=%s" % \
-                      utterances)
-        speech.speak(utterances)
-
-        utterances = []
-        if doubleClick:
-            table = parent.queryTable()
-            index = self._script.getCellIndex(orca_state.locusOfFocus)
-            row = table.getRowAtIndex(index)
-            # Translators: this in reference to a row in a table.
-            #
-            text = _("row %d of %d") % ((row+1), table.nRows)
-            utterances.append(text)
-            speech.speak(utterances)
-
-        # Speak the current row
-        #
-        utterances = self._getTableRow(obj)
-        debug.println(self._debugLevel, "second table cell utterances=%s" % \
-                      utterances)
-        speech.speak(utterances)
-
-        # Speak the remaining items.
-        #
-        utterances = []
-
-        if not doubleClick:
-            try:
-                table = parent.queryTable()
-            except NotImplementedError:
-                debug.println(self._debugLevel, 
-                              "??? parent=%s" % parent.getRoleName())
-                return
-            else:
-                index = self._script.getCellIndex(orca_state.locusOfFocus)
-                row = table.getRowAtIndex(index)
-                # Translators: this in reference to a row in a table.
-                #
-                text = _("row %d of %d") % ((row+1), table.nRows)
-                utterances.append(text)
-
-        # The difference/reason for overriding:  We obtain the expanded
-        # state from the hidden object that immediately precedes obj.
-        #
-        try:
-            state = obj.parent[obj.getIndexInParent() - 1].getState()
-        except:
-            state = obj.getState()
-
-        if state.contains(pyatspi.STATE_EXPANDABLE):
-            if state.contains(pyatspi.STATE_EXPANDED):
-                # Translators: this represents the state of a node in a tree.
-                # 'expanded' means the children are showing.
-                # 'collapsed' means the children are not showing.
-                #
-                text = _("expanded")
-            else:
-                # Translators: this represents the state of a node in a tree.
-                # 'expanded' means the children are showing.
-                # 'collapsed' means the children are not showing.
-                #
-                text = _("collapsed")
-            utterances.append(text)
-
-        level = self._script.getNodeLevel(orca_state.locusOfFocus)
-        if level >= 0:
-            # Translators: this represents the depth of a node in a tree
-            # view (i.e., how many ancestors a node has).
-            #
-            utterances.append(_("tree level %d") % (level + 1))
-
-        debug.println(self._debugLevel, "third table cell utterances=%s" % \
-                      utterances)
-        speech.speak(utterances)
diff --git a/src/orca/scripts/apps/planner/speech_generator.py b/src/orca/scripts/apps/planner/speech_generator.py
index 0f31c6a..0931c44 100644
--- a/src/orca/scripts/apps/planner/speech_generator.py
+++ b/src/orca/scripts/apps/planner/speech_generator.py
@@ -38,7 +38,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
     def __init__(self, script):
         speech_generator.SpeechGenerator.__init__(self, script)
 
-    def _getLabelAndName(self, obj, **args):
+    def _generateLabelAndName(self, obj, **args):
         """Gets the label and the name if the name is different from the label.
         """
         result = []
@@ -64,7 +64,8 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
         elif handleTabButton:
             result.append(self._script.getDisplayedText(obj.parent[1]))
         else:
-            result.append(speech_generator.SpeechGenerator._getLabelAndName(
-                self, obj, **args))
+            result.append(
+                speech_generator.SpeechGenerator._generateLabelAndName(
+                    self, obj, **args))
 
         return result
diff --git a/src/orca/scripts/apps/rhythmbox/speech_generator.py b/src/orca/scripts/apps/rhythmbox/speech_generator.py
index 5279aa6..aaf3104 100644
--- a/src/orca/scripts/apps/rhythmbox/speech_generator.py
+++ b/src/orca/scripts/apps/rhythmbox/speech_generator.py
@@ -31,18 +31,18 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
 
     # pylint: disable-msg=W0142
 
-    """Overrides _getRealTableCell to correctly handle the table
+    """Overrides _generateRealTableCell to correctly handle the table
     cells in the Library table.
     """
     def __init__(self, script):
         speech_generator.SpeechGenerator.__init__(self, script)
 
-    def _getRealTableCell(self, obj, **args):
+    def _generateRealTableCell(self, obj, **args):
         # Check to see if this is a table cell from the Library table.
         # If so, it'll have five children and we are interested in the
         # penultimate one. See bug #512639 for more details.
         #
         if obj.childCount == 5:
             obj = obj[3]
-        return speech_generator.SpeechGenerator._getRealTableCell(
+        return speech_generator.SpeechGenerator._generateRealTableCell(
             self, obj, **args)
diff --git a/src/orca/scripts/apps/soffice/Makefile.am b/src/orca/scripts/apps/soffice/Makefile.am
index 5d9c71b..dab725c 100644
--- a/src/orca/scripts/apps/soffice/Makefile.am
+++ b/src/orca/scripts/apps/soffice/Makefile.am
@@ -6,8 +6,7 @@ orca_python_PYTHON = \
 	formatting.py \
 	script.py \
 	script_settings.py \
-	speech_generator.py \
-	where_am_i.py
+	speech_generator.py
 
 orca_pythondir=$(pyexecdir)/orca/scripts/apps/soffice
 
diff --git a/src/orca/scripts/apps/soffice/formatting.py b/src/orca/scripts/apps/soffice/formatting.py
index 57dc619..7b18df3 100644
--- a/src/orca/scripts/apps/soffice/formatting.py
+++ b/src/orca/scripts/apps/soffice/formatting.py
@@ -40,7 +40,9 @@ formatting = {
         #
         'suffix': {
             'focused': '[]',
-            'unfocused': 'newNodeLevel + tutorial'
+            'unfocused': 'newNodeLevel + tutorial',
+            'basicWhereAmI': 'tutorial + description',
+            'detailedWhereAmI' : '[]'
             },
         pyatspi.ROLE_COMBO_BOX: {
             'focused': 'name + availability',
@@ -53,6 +55,12 @@ formatting = {
         pyatspi.ROLE_TOGGLE_BUTTON: {
             'focused': 'labelAndName + toggleState'
             },
+        'ROLE_SPREADSHEET_CELL': {
+            # We treat spreadsheet cells differently from other table cells in
+            # whereAmI.
+            #
+            'basicWhereAmI': 'roleName + column + columnHeader + row + rowHeader + (textContent or spreadSheetCell) + anyTextSelection'
+            },
     }
 }
 
diff --git a/src/orca/scripts/apps/soffice/script.py b/src/orca/scripts/apps/soffice/script.py
index 8947888..39c5dcf 100644
--- a/src/orca/scripts/apps/soffice/script.py
+++ b/src/orca/scripts/apps/soffice/script.py
@@ -56,7 +56,6 @@ from orca.structural_navigation import StructuralNavigation
 from speech_generator import SpeechGenerator
 from braille_generator import BrailleGenerator
 from formatting import Formatting
-from where_am_i import WhereAmI
 import script_settings
 
 class Script(default.Script):
@@ -206,11 +205,6 @@ class Script(default.Script):
 
         return enabledTypes
 
-    def getWhereAmI(self):
-        """Returns the "where am I" class for this script.
-        """
-        return WhereAmI(self)
-
     def setupInputEventHandlers(self):
         """Defines InputEventHandler fields for this script that can be
         called by the key and braille bindings. In this particular case,
@@ -586,13 +580,23 @@ class Script(default.Script):
         Returns True if this is a table cell, False otherwise.
         """
 
+        cell = obj
         if not startFromTable:
             obj = obj.parent
 
         try:
             table = obj.queryTable()
         except:
-            return False
+            # There really doesn't seem to be a good way to identify
+            # when the user is editing a cell because it has a role
+            # of paragraph and no table in the ancestry. This hack is
+            # a carry-over from the whereAmI code.
+            #
+            if cell.getRole() == pyatspi.ROLE_PARAGRAPH:
+                top = self.getTopLevel(cell)
+                return (top and top.name.endswith(" Calc"))
+            else:
+                return False
         else:
             return table.nRows == 65536
 
@@ -630,6 +634,31 @@ class Script(default.Script):
 
         return True
 
+    def findFrameAndDialog(self, obj):
+        """Returns the frame and (possibly) the dialog containing
+        the object. Overridden here for presentation of the title
+        bar information: If the locusOfFocus is a spreadsheet cell,
+        1) we are not in a dialog and 2) we need to present both the
+        frame name and the sheet name. So we might as well return the
+        sheet in place of the dialog so that the default code can do
+        its thing.
+        """
+
+        if not self.isSpreadSheetCell(obj):
+            return default.Script.findFrameAndDialog(self, obj)
+
+        results = [None, None]
+
+        parent = obj.parent
+        while parent and (parent.parent != parent):
+            if parent.getRole() == pyatspi.ROLE_FRAME:
+                results[0] = parent
+            if parent.getRole() == pyatspi.ROLE_TABLE:
+                results[1] = parent
+            parent = parent.parent
+
+        return results
+
     def printHierarchy(self, root, ooi, indent="",
                        onlyShowing=True, omitManaged=True):
         """Prints the accessible hierarchy of all children
@@ -781,7 +810,7 @@ class Script(default.Script):
             return (cell != None)
 
         self.updateBraille(cell)
-        utterances = self.speechGenerator.getSpeech(cell)
+        utterances = self.speechGenerator.generateSpeech(cell)
         # [[[TODO: WDW - need to make sure assumption about utterances[0]
         # is still correct with the new speech generator stuff.]]]
         #
@@ -1507,7 +1536,7 @@ class Script(default.Script):
         # box in Calc. If so, then replace the non-existent name with a
         # simple one before falling through and calling the default
         # locusOfFocusChanged method, which in turn will result in our
-        # _getSpeechForComboBox() method being called.
+        # _generateSpeechForComboBox() method being called.
         #
         rolesList = [pyatspi.ROLE_LIST,
                      pyatspi.ROLE_COMBO_BOX,
@@ -1539,7 +1568,8 @@ class Script(default.Script):
         if self.isSpreadSheetCell(event.source, True):
             if newLocusOfFocus:
                 self.updateBraille(newLocusOfFocus)
-                utterances = self.speechGenerator.getSpeech(newLocusOfFocus)
+                utterances = \
+                    self.speechGenerator.generateSpeech(newLocusOfFocus)
                 speech.speak(utterances)
 
                 # Save the current row and column information in the table
@@ -1576,7 +1606,8 @@ class Script(default.Script):
                     for tab in child:
                         eventState = tab.getState()
                         if eventState.contains(pyatspi.STATE_SELECTED):
-                            utterances = self.speechGenerator.getSpeech(tab)
+                            utterances = \
+                                self.speechGenerator.generateSpeech(tab)
                             speech.speak(utterances)
             # Fall-thru to process the event with the default handler.
 
@@ -1876,7 +1907,7 @@ class Script(default.Script):
                 weToggledIt = wasCommand and keyString not in navKeys
 
             if weToggledIt:
-                speech.speak(self.speechGenerator.getSpeech(event.source))
+                speech.speak(self.speechGenerator.generateSpeech(event.source))
 
         # When a new paragraph receives focus, we get a caret-moved event and
         # two focus events (the first being object:state-changed:focused).
diff --git a/src/orca/scripts/apps/soffice/speech_generator.py b/src/orca/scripts/apps/soffice/speech_generator.py
index 7a3ae41..3aa63d6 100644
--- a/src/orca/scripts/apps/soffice/speech_generator.py
+++ b/src/orca/scripts/apps/soffice/speech_generator.py
@@ -54,7 +54,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
                                              [pyatspi.ROLE_APPLICATION]))
         return override
 
-    def _getRoleName(self, obj, **args):
+    def _generateRoleName(self, obj, **args):
         result = []
         role = args.get('role', obj.getRole())
         if role in [pyatspi.ROLE_PUSH_BUTTON, pyatspi.ROLE_TOGGLE_BUTTON] \
@@ -69,21 +69,41 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
             override = self.__overrideParagraph(obj, **args)
             if override:
                 oldRole = self._overrideRole(pyatspi.ROLE_TEXT, args)
-            result.extend(speech_generator.SpeechGenerator._getRoleName(
+            # Treat a paragraph which is inside of a spreadsheet cell as
+            # a spreadsheet cell.
+            #
+            elif role == 'ROLE_SPREADSHEET_CELL':
+                oldRole = self._overrideRole(pyatspi.ROLE_TABLE_CELL, args)
+                override = True
+            result.extend(speech_generator.SpeechGenerator._generateRoleName(
                           self, obj, **args))
             if override:
                 self._restoreRole(oldRole, args)
         return result
 
-    def _getTextRole(self, obj, **args):
+    def _generateTextRole(self, obj, **args):
         result = []
         role = args.get('role', obj.getRole())
         if role != pyatspi.ROLE_PARAGRAPH \
            or self.__overrideParagraph(obj, **args):
-            result.extend(self._getRoleName(obj, **args))
+            result.extend(self._generateRoleName(obj, **args))
+        return result
+
+    def _generateLabel(self, obj, **args):
+        """Returns the label for an object as an array of strings (and
+        possibly voice and audio specifications).  The label is
+        determined by the getDisplayedLabel of the script, and an
+        empty array will be returned if no label can be found.
+        """
+        result = []
+        override = self.__overrideParagraph(obj, **args)
+        label = self._script.getDisplayedLabel(obj) or ""
+        if not label and override:
+            label = self._script.getDisplayedLabel(obj.parent) or ""
+        result.append(label.strip())
         return result
 
-    def _getLabelOrName(self, obj, **args):
+    def _generateLabelOrName(self, obj, **args):
         """Gets the label or the name if the label is not preset."""
         result = []
         override = self.__overrideParagraph(obj, **args)
@@ -91,26 +111,49 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
         # as a text object.
         #
         if override:
-            result.extend(self._getLabel(obj, **args))
+            result.extend(self._generateLabel(obj, **args))
             if len(result) == 0 and obj.parent:
-                parentLabel = self._getLabel(obj.parent, **args)
+                parentLabel = self._generateLabel(obj.parent, **args)
                 # If we aren't already focused, we will have spoken the
                 # parent as part of the speech context and do not want
                 # to repeat it.
                 #
-                already_focused = args.get('already_focused', False)
-                if already_focused:
+                alreadyFocused = args.get('alreadyFocused', False)
+                if alreadyFocused:
                     result.extend(parentLabel)
                 # If we still don't have a label, look to the name.
                 #
                 if not parentLabel and obj.name and len(obj.name):
                     result.append(obj.name)
         else:
-            result.extend(speech_generator.SpeechGenerator._getLabelOrName(
+            result.extend(speech_generator.SpeechGenerator._generateLabelOrName(
                 self, obj, **args))
         return result
 
-    def _getToggleState(self, obj, **args):
+    def _generateDescription(self, obj, **args):
+        """Returns an array of strings (and possibly voice and audio
+        specifications) that represent the description of the object,
+        if that description is different from that of the name and
+        label.
+        """
+        result = []
+        if obj.description:
+            # The description of some OOo paragraphs consists of the name
+            # and the displayed text, with punctuation added. Try to spot
+            # this and, if found, ignore the description.
+            #
+            text = self._script.getDisplayedText(obj) or ""
+            desc = obj.description.replace(text, "")
+            for item in obj.name.split():
+                desc = desc.replace(item, "")
+            for char in desc.decode("UTF-8").strip():
+                if char.isalnum():
+                    result.append(obj.description)
+                    break
+
+        return result
+
+    def _generateToggleState(self, obj, **args):
         """Treat push buttons (which act like toggle buttons) and toggle
         buttons in the toolbar specially.  This is so we can have more
         natural sounding speech such as "bold on", "bold off", etc."""
@@ -127,11 +170,45 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
                 #
                 result.append(_("off"))
         elif role == pyatspi.ROLE_TOGGLE_BUTTON:
-            result.extend(speech_generator.SpeechGenerator._getToggleState(
+            result.extend(speech_generator.SpeechGenerator._generateToggleState(
                 self, obj, **args))
         return result
 
-    def _getNewRowHeader(self, obj, **args):
+    def _generateRowHeader(self, obj, **args):
+        """Returns an array of strings (and possibly voice and audio
+        specifications) that represent the row header for an object
+        that is in a table, if it exists.  Otherwise, an empty array
+        is returned. Overridden here so that we can get the dynamic
+        row header(s).
+        """
+        result = []
+        try:
+            table = obj.parent.queryTable()
+        except:
+            pass
+        else:
+            index = self._script.getCellIndex(obj)
+            rowIndex = table.getRowAtIndex(index)
+            if rowIndex >= 0 \
+               and table in self._script.dynamicRowHeaders:
+                column = self._script.dynamicRowHeaders[table]
+                header = self._script.getDynamicColumnHeaderCell(obj, column)
+                try:
+                    headerText = header.queryText()
+                except:
+                    headerText = None
+                if header.childCount > 0:
+                    for child in header:
+                        text = self._script.getText(child, 0, -1)
+                        if text:
+                            result.append(text)
+                elif headerText:
+                    text = self._script.getText(header, 0, -1)
+                    if text:
+                        result.append(text)
+        return result
+
+    def _generateNewRowHeader(self, obj, **args):
         result = []
         # Check to see if this spread sheet cell has either a dynamic
         # row heading associated with it.
@@ -164,7 +241,41 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
                         result.append(text)
         return result
 
-    def _getNewColumnHeader(self, obj, **args):
+    def _generateColumnHeader(self, obj, **args):
+        """Returns an array of strings (and possibly voice and audio
+        specifications) that represent the column header for an object
+        that is in a table, if it exists.  Otherwise, an empty array
+        is returned. Overridden here so that we can get the dynamic
+        column header(s).
+        """
+        result = []
+        try:
+            table = obj.parent.queryTable()
+        except:
+            pass
+        else:
+            index = self._script.getCellIndex(obj)
+            columnIndex = table.getColumnAtIndex(index)
+            if columnIndex >= 0 \
+               and table in self._script.dynamicColumnHeaders:
+                row = self._script.dynamicColumnHeaders[table]
+                header = self._script.getDynamicRowHeaderCell(obj, row)
+                try:
+                    headerText = header.queryText()
+                except:
+                    headerText = None
+                if header.childCount > 0:
+                    for child in header:
+                        text = self._script.getText(child, 0, -1)
+                        if text:
+                            result.append(text)
+                elif headerText:
+                    text = self._script.getText(header, 0, -1)
+                    if text:
+                        result.append(text)
+        return result
+
+    def _generateNewColumnHeader(self, obj, **args):
         result = []
         # Check to see if this spread sheet cell has either a dynamic
         # row heading associated with it.
@@ -197,7 +308,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
                         result.append(text)
         return result
 
-    def _getSpreadSheetCell(self, obj, **args):
+    def _generateSpreadSheetCell(self, obj, **args):
         result = []
         if self._script.inputLineForCell == None:
             self._script.inputLineForCell = \
@@ -206,6 +317,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
             if obj.queryText():
                 objectText = self._script.getText(obj, 0, -1)
                 if not script_settings.speakCellCoordinates \
+                   or args.get('formatType', 'unfocused') == 'basicWhereAmI' \
                    and len(objectText) == 0:
                     # Translators: this indicates an empty (blank) spread
                     # sheet cell.
@@ -215,7 +327,8 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
         except NotImplementedError:
             pass
 
-        if script_settings.speakCellCoordinates:
+        if script_settings.speakCellCoordinates \
+           and args.get('formatType', 'unfocused') != 'basicWhereAmI':
             nameList = obj.name.split()
             # We were assuming that the word for "cell" would always
             # precede the coordinates. This is not the case for all
@@ -223,26 +336,25 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
             # examine each item and choose the one which contains a
             # digit.
             #
-            for name in nameList:                    
+            for name in nameList:
                 for char in name.decode("UTF-8"):
                     if char.isdigit():
                         result.append(name)
         return result
 
-    def _getRealTableCell(self, obj, **args):
+    def _generateRealTableCell(self, obj, **args):
         """Get the speech for a table cell. If this isn't inside a
         spread sheet, just return the utterances returned by the default
         table cell speech handler.
 
         Arguments:
         - obj: the table cell
-        - already_focused: False if object just received focus
 
         Returns a list of utterances to be spoken for the object.
         """
         result = []
         if self._script.isSpreadSheetCell(obj):
-            result.extend(self._getSpreadSheetCell(obj, **args))
+            result.extend(self._generateSpreadSheetCell(obj, **args))
         else:
             # Check to see how many children this table cell has. If it's
             # just one (or none), then pass it on to the superclass to be
@@ -253,13 +365,13 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
             #
             if obj.childCount <= 1:
                 result.extend(speech_generator.SpeechGenerator.\
-                              _getRealTableCell(self, obj, **args))
+                              _generateRealTableCell(self, obj, **args))
             else:
                 for child in obj:
-                    result.extend(self._getRealTableCell(child, **args))
+                    result.extend(self._generateRealTableCell(child, **args))
         return result
 
-    def _getTableCellRow(self, obj, **args):
+    def _generateTableCellRow(self, obj, **args):
         """Get the speech for a table cell row or a single table cell
         if settings.readTableCellRow is False. If this isn't inside a
         spread sheet, just return the utterances returned by the default
@@ -267,7 +379,6 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
 
         Arguments:
         - obj: the table cell
-        - already_focused: False if object just received focus
 
         Returns a list of utterances to be spoken for the object.
         """
@@ -299,12 +410,37 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
                         showing = cell.getState().contains( \
                                       pyatspi.STATE_SHOWING)
                         if showing:
-                            result.extend(self._getRealTableCell(cell, **args))
+                            result.extend(self._generateRealTableCell(
+                                              cell, **args))
                 else:
-                    result.extend(self._getRealTableCell(obj, **args))
+                    result.extend(self._generateRealTableCell(obj, **args))
             else:
-                result.extend(self._getRealTableCell(obj, **args))
+                result.extend(self._generateRealTableCell(obj, **args))
         else:
-            result.extend(speech_generator.SpeechGenerator._getTableCellRow(
-                          self, obj, **args))
+            result.extend(
+                speech_generator.SpeechGenerator._generateTableCellRow(
+                    self, obj, **args))
+        return result
+
+    def generateSpeech(self, obj, **args):
+        result = []
+        if args.get('formatType', 'unfocused') == 'basicWhereAmI' \
+           and self._script.isSpreadSheetCell(obj):
+            oldRole = self._overrideRole('ROLE_SPREADSHEET_CELL', args)
+            # In addition, if focus is in a cell being edited, we cannot
+            # query the accessible table interface for coordinates and the
+            # like because we're temporarily in an entirely different object
+            # which is outside of the table. This makes things difficult.
+            # However, odds are that if we're doing a whereAmI in a cell
+            # which we are editing, we have some pointOfReference info
+            # we can use to guess the coordinates.
+            #
+            args['guessCoordinates'] = obj.getRole() == pyatspi.ROLE_PARAGRAPH
+            result.extend(speech_generator.SpeechGenerator.\
+                                           generateSpeech(self, obj, **args))
+            del args['guessCoordinates']
+            self._restoreRole(oldRole, args)
+        else:
+            result.extend(speech_generator.SpeechGenerator.\
+                                           generateSpeech(self, obj, **args))
         return result
diff --git a/src/orca/scripts/apps/soffice/where_am_i.py b/src/orca/scripts/apps/soffice/where_am_i.py
deleted file mode 100644
index c1b909e..0000000
--- a/src/orca/scripts/apps/soffice/where_am_i.py
+++ /dev/null
@@ -1,286 +0,0 @@
-# Orca
-#
-# Copyright 2005-2008 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 script for StarOffice and OpenOffice."""
-
-__id__        = "$Id$"
-__version__   = "$Revision$"
-__date__      = "$Date$"
-__copyright__ = "Copyright (c) 2005-2008 Sun Microsystems Inc."
-__license__   = "LGPL"
-
-import pyatspi
-
-import orca.debug as debug
-import orca.speech as speech
-import orca.where_am_I as where_am_I
-
-from orca.orca_i18n import _ # for gettext support
-from orca.orca_i18n import C_     # to provide qualified translatable strings
-
-class WhereAmI(where_am_I.WhereAmI):
-
-    def __init__(self, script):
-        """Create a new WhereAmI that will be used to speak information
-        about the current object of interest.
-        """
-
-        where_am_I.WhereAmI.__init__(self, script)
-
-    def _speakTableCell(self, obj, basicOnly):
-        """Given the nature of OpenOffice Calc, Orca should override the
-        default whereAmI behavior when the item with focus is a cell
-        within Calc. In this instance, the following information should
-        be spoken/displayed:
-
-        1. "Cell"
-        2. the cell coordinates
-        3. the cell contents:
-            A. if the cell is empty, "blank"
-            B. if the cell is being edited AND if some text within the cell
-            is selected, the selected text followed by "selected"
-            C. otherwise, the full contents of the cell
-        """
-
-        if not self._script.isSpreadSheetCell(obj):
-            return where_am_I.WhereAmI._speakTableCell(self, obj, basicOnly)
-
-        utterances = []
-        utterances.append(_("cell"))
-
-        table = obj.parent.queryTable()
-
-        # Translators: this represents the column we're
-        # on in a table.
-        #
-        index = self._script.getCellIndex(obj)
-        text = _("column %d") % (table.getColumnAtIndex(index) + 1)
-        utterances.append(text)
-
-        # Speak the dynamic column header (if any).
-        #
-        if table in self._script.dynamicColumnHeaders:
-            row = self._script.dynamicColumnHeaders[table]
-            header = self._script.getDynamicRowHeaderCell(obj, row)
-            try:
-                headerText = header.queryText()
-            except:
-                headerText = None
-
-            if header.childCount > 0:
-                for child in header:
-                    text = self._script.getText(child, 0, -1)
-                    if text:
-                        utterances.append(text)
-            elif headerText:
-                text = self._script.getText(header, 0, -1)
-                if text:
-                    utterances.append(text)
-
-        # Translators: this represents the row number of a table.
-        #
-        index = self._script.getCellIndex(obj)
-        text = _("row %d") % (table.getRowAtIndex(index) + 1)
-        utterances.append(text)
-
-        # Speak the dynamic row header (if any).
-        #
-        if table in self._script.dynamicRowHeaders:
-            column = self._script.dynamicRowHeaders[table]
-            header = self._script.getDynamicColumnHeaderCell(obj, column)
-            try:
-                headerText = header.queryText()
-            except:
-                headerText = None
-
-            if header.childCount > 0:
-                for child in header:
-                    text = self._script.getText(child, 0, -1)
-                    if text:
-                        utterances.append(text)
-            elif headerText:
-                text = self._script.getText(header, 0, -1)
-                if text:
-                    utterances.append(text)
-
-        text = obj.queryText().getText(0, -1)
-        utterances.append(text)
-
-        debug.println(self._debugLevel, "calc table cell utterances=%s" % \
-                      utterances)
-        speech.speak(utterances)
-
-    def _speakParagraph(self, obj, basicOnly):
-        """OpenOffice Calc cells have the role "paragraph" when
-        they are being edited.
-        """
-
-        top = self._script.getTopLevel(obj)
-        if top and top.name.endswith(" Calc"):
-            self._speakCalc(obj, basicOnly)
-        elif top and top.name.endswith(" Writer"):
-            self._speakText(obj, basicOnly)
-        else:
-            where_am_I.WhereAmI._speakParagraph(self, obj, basicOnly)
-
-    def _speakCalc(self, obj, basicOnly):
-        """Speak a OpenOffice Calc cell.
-        """
-
-        utterances = []
-        utterances.append(_("cell"))
-
-        # No way to get cell coordinates?
-
-        [textContents, startOffset, endOffset, selected] = \
-            self._getTextContents(obj, basicOnly)
-        text = textContents
-        utterances.append(text)
-        if selected:
-            # Translators: when the user selects (highlights) text in
-            # a document, Orca lets them know this.
-            #
-            text = C_("text", "selected")
-            utterances.append(text)
-
-        debug.println(self._debugLevel, "editable table cell utterances=%s" % \
-                      utterances)
-        speech.speak(utterances)
-
-    def _getCalcFrameAndSheet(self, obj):
-        """Returns the Calc frame and sheet
-        """
-
-        mylist = [None, None]
-
-        parent = obj.parent
-        while parent and (parent.parent != parent):
-            # debug.println(self._debugLevel,
-            #             "_getCalcFrameAndSheet: parent=%s, %s" % \
-            #             (parent.getRole(), self._getObjLabelAndName(parent)))
-            if parent.getRole() == pyatspi.ROLE_FRAME:
-                mylist[0] = parent
-            if parent.getRole() == pyatspi.ROLE_TABLE:
-                mylist[1] = parent
-            parent = parent.parent
-
-        return mylist
-
-    def _speakCalcStatusBar(self):
-        """Speaks the OpenOffice Calc statusbar.
-        """
-
-        if not self._statusBar:
-            return
-
-        utterances = []
-        for child in self._statusBar:
-            text = self._getObjName(child)
-            utterances.append(text)
-
-        debug.println(self._debugLevel, "Calc statusbar utterances=%s" % \
-                      utterances)
-        speech.speak(utterances)
-
-    def speakTitle(self, obj):
-        """Speak the title bar.
-
-        Calc-Specific Handling: 
-
-        1. The contents of the title bar of the application main window
-        2. The title of the current worksheet
-
-        Note that if the application with focus is Calc, but a cell does not
-        have focus, the default behavior should be used.
-        """
-
-        top = self._script.getTopLevel(obj)
-        if top and not top.name.endswith(" Calc"):
-            return where_am_I.WhereAmI.speakTitle(self, obj)
-
-        utterances = []
-
-        mylist = self._getCalcFrameAndSheet(obj)
-        if mylist[0]:
-            text = self.getObjLabelAndName(mylist[0])
-            utterances.append(text)
-        if mylist[1]:
-            text = self.getObjLabelAndName(mylist[1])
-            utterances.append(text)
-
-        debug.println(self._debugLevel,
-                      "Calc titlebar and sheet utterances=%s" % utterances)
-        speech.speak(utterances)
-
-    def speakStatusBar(self, obj):
-        """Speak the status bar contents.
-
-        Note that if the application with focus is Calc, but a cell does not
-        have focus, the default behavior should be used.
-        """
-
-        top = self._script.getTopLevel(obj)
-        if top and not top.name.endswith(" Calc"):
-            return where_am_I.WhereAmI.speakStatusBar(self, obj)
-
-        utterances = []
-
-        mylist = self._getCalcFrameAndSheet(obj)
-        if mylist[0]:
-            self._statusBar = None
-            self._getStatusBar(mylist[0])
-            if self._statusBar:
-                self._speakCalcStatusBar()
-
-        debug.println(self._debugLevel,
-                      "Calc status bar utterances=%s" % utterances)
-        speech.speak(utterances)
-
-    def _getObjLabel(self, obj):
-        """Returns the label to speak for an object.
-        """
-
-        label = self._script.getDisplayedLabel(obj) or ""
-        if not label and obj.getRole() == pyatspi.ROLE_PARAGRAPH \
-           and self._script.getAncestor(obj,
-                                        [pyatspi.ROLE_DIALOG],
-                                        [pyatspi.ROLE_APPLICATION]):
-            label = self._script.getDisplayedLabel(obj.parent) or ""
-
-        return label.strip()
-
-    def _speakObjDescription(self, obj):
-        """Speaks the object's description if it is not the same as
-        the object's name and displayed contents.
-        """
-
-        if not obj.description:
-            return
-
-        # The descriptions of some OOo paragraphs consists of the name and
-        # the displayed text, with punctuation added. Try to spot this and,
-        # if found, ignore the description.
-        #
-        text = self._script.getDisplayedText(obj) or ""
-        desc = obj.description.replace(text, "")
-        for item in obj.name.split():
-            desc = desc.replace(item, "")
-        for char in desc.decode("UTF-8").strip():
-            if char.isalnum():
-                return where_am_I.WhereAmI._speakObjDescription(self, obj)
diff --git a/src/orca/scripts/apps/yelp.py b/src/orca/scripts/apps/yelp.py
index d5ef25a..77a1f85 100644
--- a/src/orca/scripts/apps/yelp.py
+++ b/src/orca/scripts/apps/yelp.py
@@ -204,7 +204,7 @@ class Script(Gecko.Script):
                 self._currentFrameName = event.source.name
                 self.setCaretPosition(obj, characterOffset)
                 if obj.getState().contains(pyatspi.STATE_FOCUSED):
-                    speech.speak(self.speechGenerator.getSpeech(obj))
+                    speech.speak(self.speechGenerator.generateSpeech(obj))
                 elif not Gecko.script_settings.sayAllOnLoad:
                     self.speakContents(\
                         self.getLineContentsAtOffset(obj, characterOffset))
diff --git a/src/orca/scripts/toolkits/Gecko/Makefile.am b/src/orca/scripts/toolkits/Gecko/Makefile.am
index 2de2e09..44e9962 100644
--- a/src/orca/scripts/toolkits/Gecko/Makefile.am
+++ b/src/orca/scripts/toolkits/Gecko/Makefile.am
@@ -8,8 +8,7 @@ orca_python_PYTHON = \
 	script.py \
 	script_settings.py \
 	speech_generator.py \
-	structural_navigation.py \
-	where_am_i.py
+	structural_navigation.py
 
 orca_pythondir=$(pyexecdir)/orca/scripts/toolkits/Gecko
 
diff --git a/src/orca/scripts/toolkits/Gecko/bookmarks.py b/src/orca/scripts/toolkits/Gecko/bookmarks.py
index 5f97392..275b054 100644
--- a/src/orca/scripts/toolkits/Gecko/bookmarks.py
+++ b/src/orca/scripts/toolkits/Gecko/bookmarks.py
@@ -60,7 +60,7 @@ class GeckoBookmarks(bookmarks.Bookmarks):
         # Translators: this announces that a bookmark has been entered
         #
         utterances = [(_('entered bookmark'))]
-        utterances.extend(self._script.speechGenerator.getSpeech \
+        utterances.extend(self._script.speechGenerator.generateSpeech \
                          (obj, False))
         speech.speak(utterances)
         
diff --git a/src/orca/scripts/toolkits/Gecko/formatting.py b/src/orca/scripts/toolkits/Gecko/formatting.py
index 7f8492a..83a6493 100644
--- a/src/orca/scripts/toolkits/Gecko/formatting.py
+++ b/src/orca/scripts/toolkits/Gecko/formatting.py
@@ -35,6 +35,18 @@ import orca.formatting
 
 formatting = {
     'speech': {
+        'suffix': {
+            'focused': '[]',
+            'unfocused': 'newNodeLevel + unselectedCell + tutorial',
+            'basicWhereAmI': 'tutorial + description + liveRegionDescription',
+            'detailedWhereAmI' : '[]'
+            },
+        'default': {
+            'focused': '[]',
+            'unfocused': 'labelAndName + allTextSelection + roleName + availability + mnemonic + accelerator',
+            'basicWhereAmI': 'labelAndName + roleName',
+            'detailedWhereAmI' : 'pageSummary'
+            },
         pyatspi.ROLE_ALERT: {
             'unfocused': 'expandedEOCs or (labelAndName + unrelatedLabels)'
             },
@@ -44,9 +56,6 @@ formatting = {
         pyatspi.ROLE_DOCUMENT_FRAME: {
             'unfocused': 'name + roleName'
             },
-        pyatspi.ROLE_LINK: {
-            'unfocused': 'labelAndName + roleName + availability'
-            },
         pyatspi.ROLE_LIST: {
             'focused': 'focusedItem',
             'unfocused': 'labelOrName + focusedItem + multiselectableState + numberOfChildren'
@@ -75,10 +84,10 @@ class Formatting(orca.formatting.Formatting):
         #
         self._defaultFormatting = orca.formatting.Formatting(script)
 
-    def getFormat(self, dictType, **args):
+    def getFormat(self, **args):
         # ARIA widgets get treated like regular default widgets.
         #
         if args.get('isAria', False):
-            return self._defaultFormatting.getFormat(dictType, **args)
+            return self._defaultFormatting.getFormat(**args)
         else:
-            return orca.formatting.Formatting.getFormat(self, dictType, **args)
+            return orca.formatting.Formatting.getFormat(self, **args)
diff --git a/src/orca/scripts/toolkits/Gecko/script.py b/src/orca/scripts/toolkits/Gecko/script.py
index d541a68..abcc96f 100644
--- a/src/orca/scripts/toolkits/Gecko/script.py
+++ b/src/orca/scripts/toolkits/Gecko/script.py
@@ -65,7 +65,6 @@ import script_settings
 from braille_generator import BrailleGenerator
 from speech_generator import SpeechGenerator
 from formatting import Formatting
-from where_am_i import GeckoWhereAmI
 from bookmarks import GeckoBookmarks
 from structural_navigation import GeckoStructuralNavigation
 
@@ -286,11 +285,6 @@ class Script(default.Script):
             self.savedEnabledSpokenTextAttributes
         settings.allTextAttributes = self.savedAllTextAttributes
 
-    def getWhereAmI(self):
-        """Returns the "where am I" class for this script.
-        """
-        return GeckoWhereAmI(self)
-
     def getBookmarks(self):
         """Returns the "bookmarks" class for this script.
         """
@@ -1559,7 +1553,7 @@ class Script(default.Script):
             if settings.speechVerbosityLevel == \
                     settings.VERBOSITY_LEVEL_VERBOSE:
                 utterances.extend(
-                    self.speechGenerator.getSpeech(event.any_data))
+                    self.speechGenerator.generateSpeech(event.any_data))
             speech.speak(utterances)
 
     def onDocumentReload(self, event):
@@ -1692,7 +1686,7 @@ class Script(default.Script):
                     # http://bugzilla.gnome.org/show_bug.cgi?id=570551
                     #
                     if eventSourceRole == pyatspi.ROLE_ALERT:
-                        speech.speak(self.speechGenerator.getSpeech(
+                        speech.speak(self.speechGenerator.generateSpeech(
                                 event.source))
                         self.updateBraille(obj)
                     else:
@@ -1882,7 +1876,7 @@ class Script(default.Script):
                     self.updateBraille(obj)
 
                     if obj.getState().contains(pyatspi.STATE_FOCUSABLE):
-                        speech.speak(self.speechGenerator.getSpeech(obj))
+                        speech.speak(self.speechGenerator.generateSpeech(obj))
                     elif not script_settings.sayAllOnLoad:
                         self.speakContents(\
                             self.getLineContentsAtOffset(obj,
@@ -3874,7 +3868,7 @@ class Script(default.Script):
             #
             if end > childOffset + 1:
                 restOfText = unicodeText[offset:len(unicodeText)]
-                objects.append([obj, childOffset + 1, end, restOfText])                
+                objects.append([obj, childOffset + 1, end, restOfText])
  
         if obj.getRole() in [pyatspi.ROLE_IMAGE, pyatspi.ROLE_TABLE]:
             # Imagemaps that don't have alternative text won't implement
@@ -3942,6 +3936,112 @@ class Script(default.Script):
 
         return lineContents
 
+    def getPageSummary(self, obj):
+        """Returns the quantity of headings, forms, tables, visited links,
+        and unvisited links on the page containing obj.
+        """
+
+        if settings.useCollection:
+            try:
+                summary = self._collectionPageSummary()
+            except:
+                debug.printException(debug.LEVEL_SEVERE)
+                summary = self._iterativePageSummary(obj)
+        else:
+            summary = self._iterativePageSummary(obj)
+
+        return summary
+
+    def _collectionPageSummary(self):
+        """Uses the Collection interface to get the quantity of headings,
+        forms, tables, visited and unvisited links.
+        """
+
+        docframe = self.getDocumentFrame()
+        col = docframe.queryCollection()
+        # We will initialize these after the queryCollection() call in case
+        # Collection is not supported
+        #
+        headings = 0
+        forms = 0
+        tables = 0
+        vlinks = 0
+        uvlinks = 0
+        percentRead = None
+
+        stateset = pyatspi.StateSet()
+        roles = [pyatspi.ROLE_HEADING, pyatspi.ROLE_LINK, pyatspi.ROLE_TABLE,
+                 pyatspi.ROLE_FORM]
+        rule = col.createMatchRule(stateset.raw(), col.MATCH_NONE,
+                                   "", col.MATCH_NONE,
+                                   roles, col.MATCH_ANY,
+                                   "", col.MATCH_NONE,
+                                   False)
+
+        matches = col.getMatches(rule, col.SORT_ORDER_CANONICAL, 0, True)
+        col.freeMatchRule(rule)
+        for obj in matches:
+            role = obj.getRole()
+            if role == pyatspi.ROLE_HEADING:
+                headings += 1
+            elif role == pyatspi.ROLE_FORM:
+                forms += 1
+            elif role == pyatspi.ROLE_TABLE \
+                      and not self.isLayoutOnly(obj):
+                tables += 1
+            elif role == pyatspi.ROLE_LINK:
+                if obj.getState().contains(pyatspi.STATE_VISITED):
+                    vlinks += 1
+                else:
+                    uvlinks += 1
+
+        return [headings, forms, tables, vlinks, uvlinks, percentRead]
+
+    def _iterativePageSummary(self, obj):
+        """Reads the quantity of headings, forms, tables, visited and
+        unvisited links.
+        """
+
+        headings = 0
+        forms = 0
+        tables = 0
+        vlinks = 0
+        uvlinks = 0
+        percentRead = None
+        nodetotal = 0
+        obj_index = None
+        currentobj = obj
+
+        # Start at the first object after document frame.
+        #
+        obj = self.getDocumentFrame()[0]
+        while obj:
+            nodetotal += 1
+            if obj == currentobj:
+                obj_index = nodetotal
+            role = obj.getRole()
+            if role == pyatspi.ROLE_HEADING:
+                headings += 1
+            elif role == pyatspi.ROLE_FORM:
+                forms += 1
+            elif role == pyatspi.ROLE_TABLE \
+                      and not self.isLayoutOnly(obj):
+                tables += 1
+            elif role == pyatspi.ROLE_LINK:
+                if obj.getState().contains(pyatspi.STATE_VISITED):
+                    vlinks += 1
+                else:
+                    uvlinks += 1
+
+            obj = self.findNextObject(obj)
+
+        # Calculate the percentage of the document that has been read.
+        #
+        if obj_index:
+            percentRead = int(obj_index*100/nodetotal)
+
+        return [headings, forms, tables, vlinks, uvlinks, percentRead]
+
     def guessLabelFromLine(self, obj):
         """Attempts to guess what the label of an unlabeled form control
         might be by looking at surrounding contents from the same line.
@@ -5435,7 +5535,7 @@ class Script(default.Script):
             #
             if not len(string) \
                or role in [pyatspi.ROLE_ENTRY, pyatspi.ROLE_PASSWORD_TEXT]:
-                utterance = self.speechGenerator.getSpeech(obj)
+                utterance = self.speechGenerator.generateSpeech(obj)
             else:
                 utterance = [string]
                 if speakRole and not role in doNotSpeakRoles:
@@ -5521,7 +5621,7 @@ class Script(default.Script):
                 # characterOffset (lists).  In these latter cases, we'll just
                 # speak the entire component.
                 #
-                utterances = self.speechGenerator.getSpeech(obj)
+                utterances = self.speechGenerator.generateSpeech(obj)
                 speech.speak(utterances)
 
     ####################################################################
diff --git a/src/orca/scripts/toolkits/Gecko/speech_generator.py b/src/orca/scripts/toolkits/Gecko/speech_generator.py
index 3f08fd1..6c0dd66 100644
--- a/src/orca/scripts/toolkits/Gecko/speech_generator.py
+++ b/src/orca/scripts/toolkits/Gecko/speech_generator.py
@@ -52,7 +52,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
     def __init__(self, script):
         speech_generator.SpeechGenerator.__init__(self, script)
 
-    def _getName(self, obj, **args):
+    def _generateName(self, obj, **args):
         result = []
         role = args.get('role', obj.getRole())
         if role == pyatspi.ROLE_COMBO_BOX:
@@ -74,9 +74,8 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
                     if not child:
                         # It's probably a Gtk combo box.
                         #
-                        result = \
-                            speech_generator.SpeechGenerator._getDisplayedText(
-                                self, obj, **args)
+                        result = speech_generator.SpeechGenerator.\
+                            _generateDisplayedText(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
@@ -89,9 +88,11 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
                 if child and child.name:
                     result.append(child.name)
         else:
-            result.extend(speech_generator.SpeechGenerator._getName(self,
-                                                                    obj,
-                                                                    **args))
+            result.extend(speech_generator.SpeechGenerator._generateName(
+                              self, obj, **args))
+        if not result and role == pyatspi.ROLE_LIST_ITEM:
+            result.append(self._script.expandEOCs(obj))
+
         link = None
         if role == pyatspi.ROLE_LINK:
             link = obj
@@ -109,8 +110,32 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
 
         return result
 
-    def _getLabel(self, obj, **args):
-        result = speech_generator.SpeechGenerator._getLabel(self, obj, **args)
+    def _generateDescription(self, obj, **args):
+        """Returns an array of strings (and possibly voice and audio
+        specifications) that represent the description of the object,
+        if that description is different from that of the name and
+        label.
+        """
+        if args.get('role', obj.getRole()) == pyatspi.ROLE_LINK \
+           and obj.parent.getRole() == pyatspi.ROLE_IMAGE:
+            result = self._generateName(obj, **args)
+            # Translators: The following string is spoken to let the user
+            # know that he/she is on a link within an image map. An image
+            # map is an image/graphic which has been divided into regions.
+            # Each region can be clicked on and has an associated link.
+            # Please see http://en.wikipedia.org/wiki/Imagemap for more
+            # information and examples.
+            #
+            result.append(_("image map link"))
+        else:
+            result = speech_generator.SpeechGenerator.\
+                           _generateDescription(self, obj, **args)
+        return result
+
+    def _generateLabel(self, obj, **args):
+        result = speech_generator.SpeechGenerator._generateLabel(self,
+                                                                 obj,
+                                                                 **args)
         role = args.get('role', obj.getRole())
         # We'll attempt to guess the label under some circumstances.
         #
@@ -140,7 +165,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
 
         return result
 
-    def _getLabelAndName(self, obj, **args):
+    def _generateLabelAndName(self, obj, **args):
         result = []
         role = args.get('role', obj.getRole())
         # For radio buttons, the label is handled as a context and we
@@ -151,24 +176,25 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
            and self._script.getDisplayedLabel(obj):
             pass
         else:
-            result.extend(speech_generator.SpeechGenerator._getLabelAndName(
-                self, obj, **args))
+            result.extend(
+                speech_generator.SpeechGenerator._generateLabelAndName(
+                    self, obj, **args))
         return result
 
-    def _getLabelOrName(self, obj, **args):
+    def _generateLabelOrName(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))
+            result.extend(self._generateLabelOrName(obj.parent, **args))
         else:
-            result.extend(speech_generator.SpeechGenerator._getLabelOrName(
+            result.extend(speech_generator.SpeechGenerator._generateLabelOrName(
                 self, obj, **args))
         return result
 
-    def _getRoleName(self, obj, **args):
+    def _generateRoleName(self, obj, **args):
         """Prevents some roles from being spoken."""
         result = []
         role = args.get('role', obj.getRole())
@@ -184,7 +210,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
                                                 [pyatspi.ROLE_COMBO_BOX],
                                                 [pyatspi.ROLE_DOCUMENT_FRAME])
             if comboBox:
-                return self._getRoleName(comboBox, **args)
+                return self._generateRoleName(comboBox, **args)
 
         if not force:
             doNotSpeak = [pyatspi.ROLE_FORM,
@@ -200,7 +226,8 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
 
         if not force and self._script.inDocumentContent(obj):
             doNotSpeak.append(pyatspi.ROLE_TABLE_CELL)
-            if not self._script.isAriaWidget(obj):
+            if not self._script.isAriaWidget(obj) \
+               and args.get('formatType', 'unfocused') != 'basicWhereAmI':
                 doNotSpeak.append(pyatspi.ROLE_LIST_ITEM)
                 doNotSpeak.append(pyatspi.ROLE_LIST)
 
@@ -237,7 +264,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
 
         return result
 
-    def _getExpandedEOCs(self, obj, **args):
+    def _generateExpandedEOCs(self, obj, **args):
         """Returns the expanded embedded object characters for an object."""
         result = []
         text = self._script.expandEOCs(obj)
@@ -245,7 +272,7 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
             result.append(text)
         return result
 
-    def _getNumberOfChildren(self, obj, **args):
+    def _generateNumberOfChildren(self, obj, **args):
         result = []
         role = args.get('role', obj.getRole())
         if role == pyatspi.ROLE_LIST:
@@ -255,11 +282,12 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
                                    "List with %d items",
                                    obj.childCount) % obj.childCount)
         else:
-            result.extend(speech_generator.SpeechGenerator._getNumberOfChildren(
-                self, obj, **args))
+            result.extend(
+                speech_generator.SpeechGenerator._generateNumberOfChildren(
+                    self, obj, **args))
         return result
 
-    def _getFocusedItem(self, obj, **args):
+    def _generateFocusedItem(self, obj, **args):
         result = []
         role = args.get('role', obj.getRole())
         if role == pyatspi.ROLE_LIST:
@@ -271,12 +299,12 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
                     break
             item = item or obj[0]
             if item:
-                name = self._getName(item, **args)
-                if name and name != self._getLabel(obj, **args):
+                name = self._generateName(item, **args)
+                if name and name != self._generateLabel(obj, **args):
                     result.extend(name)
         return result
 
-    def _getAncestors(self, obj, **args):
+    def _generateAncestors(self, obj, **args):
         result = []
         priorObj = args.get('priorObj', None)
         commonAncestor = self._script.findCommonAncestor(priorObj, obj)
@@ -303,6 +331,14 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
         stopRoles = [pyatspi.ROLE_DOCUMENT_FRAME,
                      pyatspi.ROLE_INTERNAL_FRAME]
 
+        # [[[TODO - JD: Right now we're using this method to get the
+        # full context of menu items in whereAmI. It seems to work for
+        # gtk-demo, but here we're getting way too much context. So for
+        # now, add in a check. Later, look for better way.]]]
+        #
+        if args.get('formatType', 'unfocused') == 'basicWhereAmI':
+            stopRoles.append(pyatspi.ROLE_MENU_BAR)
+
         # There are some objects we want to include in the context,
         # but not add their rolenames.
         #
@@ -371,21 +407,106 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
             # has this state, but in the case of a group of radio buttons,
             # it is the group which has the state).
             #
-            result.extend(self._getRequired(parent, **args))
+            result.extend(self._generateRequired(parent, **args))
 
             result.extend(newResult)
 
+            # [[[TODO - JD: Right now we're using this method to get the
+            # full context of menu items in whereAmI. It seems to work for
+            # gtk-demo, but here we're getting way too much context. So for
+            # now, add in a check. Later, look for better way.]]]
+            #
+            if args.get('formatType', 'unfocused') == 'basicWhereAmI' \
+               and parent.getRole() == pyatspi.ROLE_COMBO_BOX:
+                break
+
             parent = parent.parent
 
         result.reverse()
 
         return result
 
-    def getSpeech(self, obj, **args):
+    def _generateDefaultButton(self, obj, **args):
+        """Returns an array of strings (and possibly voice and audio
+        specifications) that represent the default button in a dialog.
+        This method should initially be called with a top-level window.
+        """
+        if self._script.inDocumentContent(obj) \
+           and not self._script.isAriaWidget(obj):
+            return []
+
+        return speech_generator.SpeechGenerator.\
+                     _generateDefaultButton(self, obj, **args)
+
+    def _generateLiveRegionDescription(self, obj, **args):
+        """Returns an array of strings (and possibly voice and audio
+        specifications) that represent the live region.
+        """
+        return self._script.liveMngr.\
+                    generateLiveRegionDescription(obj, **args)
+
+    def _generatePageSummary(self, obj, **args):
+        """Returns an array of strings (and possibly voice and audio
+        specifications) that summarize the objects found on the page
+        containing obj.
+        """
+        result = []
+        headings, forms, tables, vlinks, uvlinks, percent = \
+            self._script.getPageSummary(obj)
+        if headings:
+            # Translators: Announces the number of headings in the
+            # web page that is currently being displayed.
+            #
+            result.append(ngettext \
+                ('%d heading', '%d headings', headings) % headings)
+        if forms:
+            # Translators: Announces the number of forms in the
+            # web page that is currently being displayed.
+            #
+            result.append(ngettext('%d form', '%d forms', forms) % forms)
+        if tables:
+            # Translators: Announces the number of non-layout tables in the
+            # web page that is currently being displayed.
+            #
+            result.append(ngettext('%d table', '%d tables', tables) % tables)
+        if vlinks:
+            # Translators: Announces the number of visited links in the
+            # web page that is currently being displayed.
+            #
+            result.append(ngettext \
+                ('%d visited link', '%d visited links', vlinks) % vlinks)
+        if uvlinks:
+            # Translators: Announces the number of unvisited links in the
+            # web page that is currently being displayed.
+            #
+            result.append(ngettext \
+                ('%d unvisited link', '%d unvisited links', uvlinks) % uvlinks)
+        if percent is not None:
+            # Translators: Announces the percentage of the document that has
+            # been read.  This is calculated by knowing the index of the
+            # current position divided by the total number of objects on the
+            # page.
+            #
+            result.append(_('%d percent of document read') % percent)
+
+        return result
+
+    def generateSpeech(self, obj, **args):
+        result = []
+        # Detailed WhereAmI should always be a page summary if we
+        # are in document content.
+        #
+        if args.get('formatType', 'unfocused') == 'detailedWhereAmI' \
+           and self._script.inDocumentContent(obj):
+            oldRole = self._overrideRole('default', args)
+            result.extend(speech_generator.SpeechGenerator.\
+                                           generateSpeech(self, obj, **args))
+            self._restoreRole(oldRole, args)
         # ARIA widgets get treated like regular default widgets.
         #
-        result = []
-        args['isAria'] = self._script.isAriaWidget(obj)
-        result = speech_generator.SpeechGenerator.getSpeech(self, obj, **args)
-        del args['isAria']
+        else:
+            args['isAria'] = self._script.isAriaWidget(obj)
+            result.extend(speech_generator.SpeechGenerator.\
+                                           generateSpeech(self, obj, **args))
+            del args['isAria']
         return result
diff --git a/src/orca/scripts/toolkits/Gecko/where_am_i.py b/src/orca/scripts/toolkits/Gecko/where_am_i.py
deleted file mode 100644
index 8051465..0000000
--- a/src/orca/scripts/toolkits/Gecko/where_am_i.py
+++ /dev/null
@@ -1,281 +0,0 @@
-# Orca
-#
-# 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
-# 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 script for Gecko toolkit.
-Please refer to the following URL for more information on the AT-SPI
-implementation in Gecko:
-http://developer.mozilla.org/en/docs/Accessibility/ATSPI_Support
-"""
-
-__id__        = "$Id$"
-__version__   = "$Revision$"
-__date__      = "$Date$"
-__copyright__ = "Copyright (c) 2005-2009 Sun Microsystems Inc."
-__license__   = "LGPL"
-
-import pyatspi
-
-import orca.debug as debug
-import orca.orca_state as orca_state
-import orca.settings as settings
-import orca.speech as speech
-import orca.where_am_I as where_am_I
-
-from orca.orca_i18n import _
-from orca.orca_i18n import ngettext # for ngettext support
-
-########################################################################
-#                                                                      #
-# Custom WhereAmI                                                      #
-#                                                                      #
-########################################################################
-
-class GeckoWhereAmI(where_am_I.WhereAmI):
-    def __init__(self, script):
-        """Gecko specific WhereAmI that will be used to speak information
-        about the current object of interest and will provide additional Gecko
-        specific information.
-        """
-        where_am_I.WhereAmI.__init__(self, script)
-        self._script = script
-
-    def whereAmI(self, obj, basicOnly):
-        """Calls the base class method for basic information and Gecko
-        specific presentation methods for detailed/custom information.
-        """
-        if basicOnly or not self._script.inDocumentContent(obj):
-            where_am_I.WhereAmI.whereAmI(self, obj, basicOnly)
-            self._script.liveMngr.outputLiveRegionDescription(obj)
-        else:
-            if settings.useCollection:
-                try:
-                    self._collectionPageSummary()
-                except:
-                    debug.printException(debug.LEVEL_SEVERE)
-                    self._iterativePageSummary(obj)
-            else:
-                self._iterativePageSummary(obj)
-
-    def _speakDefaultButton(self, obj):
-        """Speaks the default button in a dialog.
-
-        Arguments:
-        - obj: the dialog box for which the default button should be obtained
-        """
-
-        if not (self._script.inDocumentContent(orca_state.locusOfFocus) \
-                and not self._script.isAriaWidget(orca_state.locusOfFocus)):
-            where_am_I.WhereAmI._speakDefaultButton(self, obj)
-
-    # pylint: disable-msg=W0142
-
-    def _getSpeechForRoleName(self, obj, **args):
-        """Returns the rolename to be spoken for the object. Overridden
-        here because there are times when we do not want the speech
-        generator returning a role to speak (e.g. navigating within
-        a document), but other times when we would (e.g. during a
-        whereAmI).
-        """
-        role = args.get('role', None)
-        objRole = obj.getRole()
-        if not role and objRole in [pyatspi.ROLE_DOCUMENT_FRAME,
-                                    pyatspi.ROLE_FORM,
-                                    pyatspi.ROLE_LIST_ITEM,
-                                    pyatspi.ROLE_LIST,
-                                    pyatspi.ROLE_PARAGRAPH,
-                                    pyatspi.ROLE_SECTION,
-                                    pyatspi.ROLE_TABLE_CELL]:
-            role = objRole
-        if role:
-            args['role'] = role
-        return where_am_I.WhereAmI._getSpeechForRoleName(self, obj, **args)
-
-    def _getObjName(self, obj):
-        """Returns the name to speak for an object.
-        """
-
-        text = ""
-        name = self._script.getDisplayedText(obj)
-        if not name:
-            name = obj.description
-            if not name and obj.getRole() == pyatspi.ROLE_LIST_ITEM:
-                name = self._script.expandEOCs(obj)
-
-        if name and name != "None":
-            text = name.strip()
-        debug.println(self._debugLevel, "%s name=<%s>" % (obj.getRole(), text))
-
-        return text
-
-    def _speakObjDescription(self, obj):
-        """Speaks the object's description if it is not the same as the
-        object's name or label. Overridden here because Gecko tacks on
-        the tag associated with an imagemap in the object's description.
-        We don't want to speak that information as-is.
-
-        Arguments:
-        - obj: the accessible whose description we might wish to speak
-        """
-
-        if not (obj.getRole() == pyatspi.ROLE_LINK \
-                and obj.parent.getRole() == pyatspi.ROLE_IMAGE):
-            where_am_I.WhereAmI._speakObjDescription(self, obj)
-        else:
-            name = self._getObjName(obj)
-            if name:
-                speech.speak(name)
-            # Translators: The following string is spoken to let the user
-            # know that he/she is on a link within an image map. An image
-            # map is an image/graphic which has been divided into regions.
-            # Each region can be clicked on and has an associated link.
-            # Please see http://en.wikipedia.org/wiki/Imagemap for more
-            # information and examples.
-            #
-            speech.speak(_("image map link"))
-
-    def _collectionPageSummary(self):
-        """Uses the Collection interface to get the quantity of headings,
-        forms, tables, visited and unvisited links.
-        """
-        docframe = self._script.getDocumentFrame()
-        col = docframe.queryCollection()
-        # We will initialize these after the queryCollection() call in case
-        # Collection is not supported
-        headings = 0
-        forms = 0
-        tables = 0
-        vlinks = 0
-        uvlinks = 0
-
-        stateset = pyatspi.StateSet()
-        roles = [pyatspi.ROLE_HEADING, pyatspi.ROLE_LINK, pyatspi.ROLE_TABLE,
-                 pyatspi.ROLE_FORM]
-        rule = col.createMatchRule(stateset.raw(), col.MATCH_NONE,
-                                   "", col.MATCH_NONE,
-                                   roles, col.MATCH_ANY,
-                                   "", col.MATCH_NONE,
-                                   False)
-
-        matches = col.getMatches(rule, col.SORT_ORDER_CANONICAL, 0, True)
-
-        col.freeMatchRule(rule)
-        for obj in matches:
-            role = obj.getRole()
-            if role == pyatspi.ROLE_HEADING:
-                headings += 1
-            elif role == pyatspi.ROLE_FORM:
-                forms += 1
-            elif role == pyatspi.ROLE_TABLE \
-                      and not self._script.isLayoutOnly(obj):
-                tables += 1
-            elif role == pyatspi.ROLE_LINK:
-                if obj.getState().contains(pyatspi.STATE_VISITED):
-                    vlinks += 1
-                else:
-                    uvlinks += 1
-
-        self._outputPageSummary(headings, forms, tables, vlinks, uvlinks, None)
-
-    def _iterativePageSummary(self, obj):
-        """Reads the quantity of headings, forms, tables, visited and
-        unvisited links.
-        """
-        headings = 0
-        forms = 0
-        tables = 0
-        vlinks = 0
-        uvlinks = 0
-        nodetotal = 0
-        obj_index = None
-        currentobj = obj
-
-        # start at the first object after document frame
-        obj = self._script.getDocumentFrame()[0]
-        while obj:
-            nodetotal += 1
-            if obj == currentobj:
-                obj_index = nodetotal
-            role = obj.getRole()
-            if role == pyatspi.ROLE_HEADING:
-                headings += 1
-            elif role == pyatspi.ROLE_FORM:
-                forms += 1
-            elif role == pyatspi.ROLE_TABLE \
-                      and not self._script.isLayoutOnly(obj):
-                tables += 1
-            elif role == pyatspi.ROLE_LINK:
-                if obj.getState().contains(pyatspi.STATE_VISITED):
-                    vlinks += 1
-                else:
-                    uvlinks += 1
-
-            obj = self._script.findNextObject(obj)
-
-        # Calculate the percentage of the document that has been read.
-        if obj_index:
-            percentread = int(obj_index*100/nodetotal)
-        else:
-            percentread = None
-
-        self._outputPageSummary(headings, forms, tables,
-                               vlinks, uvlinks, percentread)
-
-    def _outputPageSummary(self, headings, forms, tables,
-                                 vlinks, uvlinks, percent):
-
-        utterances = []
-        if headings:
-            # Translators: Announces the number of headings in the
-            # web page that is currently being displayed.
-            #
-            utterances.append(ngettext \
-                 ('%d heading', '%d headings', headings) %headings)
-        if forms:
-            # Translators: Announces the number of forms in the
-            # web page that is currently being displayed.
-            #
-            utterances.append(ngettext \
-                 ('%d form', '%d forms', forms) %forms)
-        if tables:
-            # Translators: Announces the number of non-layout tables in the
-            # web page that is currently being displayed.
-            #
-            utterances.append(ngettext \
-                 ('%d table', '%d tables', tables) %tables)
-        if vlinks:
-            # Translators: Announces the number of visited links in the
-            # web page that is currently being displayed.
-            #
-            utterances.append(ngettext \
-                 ('%d visited link', '%d visited links', vlinks) %vlinks)
-        if uvlinks:
-            # Translators: Announces the number of unvisited links in the
-            # web page that is currently being displayed.
-            #
-            utterances.append(ngettext \
-                 ('%d unvisited link', '%d unvisited links', uvlinks) %uvlinks)
-        if percent is not None:
-            # Translators: Announces the percentage of the document that has
-            # been read.  This is calculated by knowing the index of the
-            # current position divided by the total number of objects on the
-            # page.
-            #
-            utterances.append(_('%d percent of document read') %percent)
-
-        speech.speak(utterances)
diff --git a/src/orca/scripts/toolkits/J2SE-access-bridge/Makefile.am b/src/orca/scripts/toolkits/J2SE-access-bridge/Makefile.am
index a322361..c2025fa 100644
--- a/src/orca/scripts/toolkits/J2SE-access-bridge/Makefile.am
+++ b/src/orca/scripts/toolkits/J2SE-access-bridge/Makefile.am
@@ -5,8 +5,7 @@ orca_python_PYTHON = \
 	braillegenerator.py \
 	formatting.py \
 	script.py \
-	speech_generator.py \
-	where_am_I.py
+	speech_generator.py
 
 orca_pythondir=$(pyexecdir)/orca/scripts/toolkits/J2SE-access-bridge
 
diff --git a/src/orca/scripts/toolkits/J2SE-access-bridge/__init__.py b/src/orca/scripts/toolkits/J2SE-access-bridge/__init__.py
index 325b44a..c9714a9 100644
--- a/src/orca/scripts/toolkits/J2SE-access-bridge/__init__.py
+++ b/src/orca/scripts/toolkits/J2SE-access-bridge/__init__.py
@@ -1,4 +1,3 @@
 from script import Script
 from speech_generator import SpeechGenerator
 from braillegenerator import BrailleGenerator
-from where_am_I import WhereAmI
diff --git a/src/orca/scripts/toolkits/J2SE-access-bridge/script.py b/src/orca/scripts/toolkits/J2SE-access-bridge/script.py
index 5e3a540..e41d374 100644
--- a/src/orca/scripts/toolkits/J2SE-access-bridge/script.py
+++ b/src/orca/scripts/toolkits/J2SE-access-bridge/script.py
@@ -34,7 +34,6 @@ import orca.keybindings as keybindings
 from braillegenerator import BrailleGenerator
 from speech_generator import SpeechGenerator
 from formatting import Formatting
-from where_am_I import WhereAmI
 
 ########################################################################
 #                                                                      #
@@ -52,11 +51,6 @@ class Script(default.Script):
         """
         default.Script.__init__(self, app)
 
-    def getWhereAmI(self):
-        """Returns the "where am I" class for this script.
-        """
-        return WhereAmI(self)
-
     def getBrailleGenerator(self):
         """Returns the braille generator for this script.
         """
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 e72f2f4..ef0c1a8 100644
--- a/src/orca/scripts/toolkits/J2SE-access-bridge/speech_generator.py
+++ b/src/orca/scripts/toolkits/J2SE-access-bridge/speech_generator.py
@@ -1,6 +1,6 @@
 # Orca
 #
-# Copyright 2006-2009 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
@@ -23,6 +23,8 @@ __date__      = "$Date$"
 __copyright__ = "Copyright (c) 2005-2009 Sun Microsystems Inc."
 __license__   = "LGPL"
 
+import pyatspi
+
 import orca.speech_generator as speech_generator
 
 ########################################################################
@@ -38,14 +40,27 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
     def __init__(self, script):
         speech_generator.SpeechGenerator.__init__(self, script)
 
-    def _getAncestors(self, obj, **args):
+    def _generateAncestors(self, obj, **args):
         """The Swing toolkit has labelled panels that do not implement the
         AccessibleText interface, but getDisplayedText returns a
         meaningful string that needs to be used if getDisplayedLabel
         returns None.
         """
         args['requireText'] = False
-        result = speech_generator.SpeechGenerator._getAncestors(
+        result = speech_generator.SpeechGenerator._generateAncestors(
             self, obj, **args)
         del args['requireText']
         return result
+
+    def generateSpeech(self, obj, **args):
+        result = []
+        if args.get('formatType', 'unfocused') == 'basicWhereAmI' \
+           and obj.getRole() == pyatspi.ROLE_TEXT:
+            spinbox = self._script.getAncestor(obj,
+                                               [pyatspi.ROLE_SPIN_BUTTON],
+                                               None)
+            if spinbox:
+                obj = spinbox
+        result.extend(speech_generator.SpeechGenerator.\
+                                       generateSpeech(self, obj, **args))
+        return result
diff --git a/src/orca/scripts/toolkits/J2SE-access-bridge/where_am_I.py b/src/orca/scripts/toolkits/J2SE-access-bridge/where_am_I.py
deleted file mode 100644
index 53bb989..0000000
--- a/src/orca/scripts/toolkits/J2SE-access-bridge/where_am_I.py
+++ /dev/null
@@ -1,55 +0,0 @@
-# Orca
-#
-# Copyright 2006-2008 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.
-
-__id__        = "$Id$"
-__version__   = "$Revision$"
-__date__      = "$Date$"
-__copyright__ = "Copyright (c) 2005-2008 Sun Microsystems Inc."
-__license__   = "LGPL"
-
-import pyatspi
-
-import orca.where_am_I as where_am_I
-
-########################################################################
-#                                                                      #
-# Custom WhereAmI                                                      #
-#                                                                      #
-########################################################################
-
-class WhereAmI(where_am_I.WhereAmI):
-    def __init__(self, script):
-        where_am_I.WhereAmI.__init__(self, script)
-
-    def whereAmI(self, obj, basicOnly):
-        """Calls the base class method for basic information and Java
-        specific presentation methods for detailed/custom information.
-        """
-
-        # If we're in the text area of a spin button, then we'll do the
-        # where am I for the spin button.
-        #
-        if obj and obj.getRole() == pyatspi.ROLE_TEXT:
-            spinbox = self._script.getAncestor(obj,
-                                               [pyatspi.ROLE_SPIN_BUTTON],
-                                               None)
-            if spinbox:
-                obj = spinbox
-
-        where_am_I.WhereAmI.whereAmI(self, obj, basicOnly)
diff --git a/src/orca/speech_generator.py b/src/orca/speech_generator.py
index 36c1c70..3ffedd4 100644
--- a/src/orca/speech_generator.py
+++ b/src/orca/speech_generator.py
@@ -29,12 +29,14 @@ __license__   = "LGPL"
 
 import sys
 import traceback
+import urlparse, urllib2
 
 import debug
 import orca_state
 import pyatspi
 import rolenames
 import settings
+import text_attribute_names
 
 from orca_i18n import _         # for gettext support
 from orca_i18n import ngettext  # for ngettext support
@@ -50,14 +52,18 @@ def _formatExceptionInfo(maxTBlevel=5):
     excTb = traceback.format_tb(trbk, maxTBlevel)
     return (excName, excArgs, excTb)
 
-# [[[WDW - general note -- for all the _get* methods, it would be great if
+# [[[WDW - general note -- for all the _generate* methods, it would be great if
 # we could return an empty array if we can determine the method does not
 # apply to the object.  This would allow us to reduce the number of strings
 # needed in formatting.py.]]]
 
+# The prefix to use for the individual generator methods
+#
+METHOD_PREFIX = "_generate"
+
 class SpeechGenerator:
     """Takes accessible objects and produces a string to speak for
-    those objects.  See the getSpeech method, which is the primary
+    those objects.  See the generateSpeech method, which is the primary
     entry point.  Subclasses can feel free to override/extend the
     speechGenerators instance field as they see fit."""
 
@@ -89,11 +95,17 @@ class SpeechGenerator:
         for method in \
             filter(lambda z: callable(z),
                    map(lambda y: getattr(self, y).__get__(self, self.__class__),
-                       filter(lambda x: x.startswith("_get"), dir(self)))):
-            name = method.__name__[4:]
+                       filter(lambda x: x.startswith(METHOD_PREFIX),
+                                        dir(self)))):
+            name = method.__name__[len(METHOD_PREFIX):]
             name = name[0].lower() + name[1:]
             self._methodsDict[name] = method
 
+        # Something to help us retain things we've computed while
+        # generating speech so we don't need to keep recomputing them.
+        #
+        self._valueCache = {}
+
         # Verify the formatting strings are OK.  This is only
         # for verification and does not effect the function of
         # Orca at all.
@@ -149,7 +161,7 @@ class SpeechGenerator:
     #                                                                   #
     #####################################################################
 
-    def _getName(self, obj, **args):
+    def _generateName(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) that represent the name of the object.  If the
         object is directly displaying any text, that text will be
@@ -157,10 +169,11 @@ class SpeechGenerator:
         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 _getName, _getDescription,
-        _getDisplayedText, etc., that don't do any fallback.  Then, we
+        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')
+        'displayedText or name or description'). [[[JD to WDW - I needed
+        a _generateDescription for whereAmI. :-) See below.
         """
         result = []
         name = self._script.getDisplayedText(obj)
@@ -170,7 +183,19 @@ class SpeechGenerator:
             result.append(obj.description)
         return result
 
-    def _getTextRole(self, obj, **args):
+    def _generateDescription(self, obj, **args):
+        """Returns an array of strings (and possibly voice and audio
+        specifications) that represent the description of the object,
+        if that description is different from that of the name and
+        label.
+        """
+        result = []
+        label = self._script.getDisplayedLabel(obj)
+        if obj.description and not obj.description in [obj.name, label]:
+            result.append(obj.description)
+        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
         role, an empty array will be returned. In all other cases, the
@@ -178,17 +203,17 @@ class SpeechGenerator:
         possibly voice and audio specifications).  Note that a 'role'
         attribute in args will override the accessible role of the
         obj. [[[WDW - I wonder if this should be moved to
-        _getRoleName.  Or, maybe make a 'do not speak roles' attribute
+        _generateRoleName.  Or, maybe make a 'do not speak roles' attribute
         of a speech generator that we can update and the user can
         override.]]]
         """
         result = []
         role = args.get('role', obj.getRole())
         if role != pyatspi.ROLE_PARAGRAPH:
-            result.extend(self._getRoleName(obj, **args))
+            result.extend(self._generateRoleName(obj, **args))
         return result
 
-    def _getRoleName(self, obj, **args):
+    def _generateRoleName(self, obj, **args):
         """Returns the role name for the object in an array of strings (and
         possibly voice and audio specifications), with the exception
         that the pyatspi.ROLE_UNKNOWN role will yield an empty array.
@@ -209,9 +234,9 @@ class SpeechGenerator:
         accessible role of the obj.  This is provided mostly as a
         method for scripts to call.
         """
-        return self._getRoleName(obj, **args)
+        return self._generateRoleName(obj, **args)
 
-    def _getLabel(self, obj, **args):
+    def _generateLabel(self, obj, **args):
         """Returns the label for an object as an array of strings (and
         possibly voice and audio specifications).  The label is
         determined by the getDisplayedLabel of the script, and an
@@ -223,14 +248,14 @@ class SpeechGenerator:
             result = [label]
         return result
 
-    def _getLabelAndName(self, obj, **args):
+    def _generateLabelAndName(self, obj, **args):
         """Returns the label and the name as an array of strings (and possibly
         voice and audio specifications).  The name will only be
         present if the name is different from the label.
         """
         result = []
-        label = self._getLabel(obj, **args)
-        name = self._getName(obj, **args)
+        label = self._generateLabel(obj, **args)
+        name = self._generateName(obj, **args)
         result.extend(label)
         if not len(label):
             result.extend(name)
@@ -238,20 +263,20 @@ class SpeechGenerator:
             result.extend(name)
         return result
 
-    def _getLabelOrName(self, obj, **args):
+    def _generateLabelOrName(self, obj, **args):
         """Returns the label as an array of strings (and possibly voice
         specifications).  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 = []
-        result.extend(self._getLabel(obj, **args))
+        result.extend(self._generateLabel(obj, **args))
         if not result:
             if obj.name and (len(obj.name)):
                 result.append(obj.name)
         return result
 
-    def _getUnrelatedLabels(self, obj, **args):
+    def _generateUnrelatedLabels(self, obj, **args):
         """Returns, as an array of strings (and possibly voice
         specifications), all the labels which are underneath the obj's
         hierarchy and which are not in a label for or labelled by
@@ -260,17 +285,17 @@ class SpeechGenerator:
         labels = self._script.findUnrelatedLabels(obj)
         result = []
         for label in labels:
-            name = self._getName(label, **args)
+            name = self._generateName(label, **args)
             result.extend(name)
         return result
 
-    def _getEmbedded(self, obj, **args):
+    def _generateEmbedded(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) used especially for handling embedded objects.
         This either is the label or name of the object or the name of
         the application for the object.
         """
-        result = self._getLabelOrName(obj, **args)
+        result = self._generateLabelOrName(obj, **args)
         if not result:
             try:
                 result.append(obj.getApplication().name)
@@ -284,7 +309,7 @@ class SpeechGenerator:
     #                                                                   #
     #####################################################################
 
-    def _getCheckedState(self, obj, **args):
+    def _generateCheckedState(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) that represent the checked state of the
         object.  This is typically for check boxes. [[[WDW - should we
@@ -308,13 +333,13 @@ class SpeechGenerator:
             result.append(_("not checked"))
         return result
 
-    def _getCellCheckedState(self, obj, **args):
+    def _generateCellCheckedState(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) that represent the checked state of the
         object.  This is typically for check boxes that are in a
         table. An empty array will be returned if this is not a
         checkable cell.  [[[WDW - I wonder if we can roll this into
-        _getCheckedState somehow.]]]
+        _generateCheckedState somehow.]]]
         """
         result = []
         try:
@@ -330,17 +355,17 @@ class SpeechGenerator:
                 if action.getName(i) in ["toggle", _("toggle")]:
                     oldRole = self._overrideRole(pyatspi.ROLE_CHECK_BOX,
                                             args)
-                    result.extend(self.getSpeech(obj, **args))
+                    result.extend(self.generateSpeech(obj, **args))
                     self._restoreRole(oldRole, args)
         return result
 
-    def _getRadioState(self, obj, **args):
+    def _generateRadioState(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) 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?]]] [[[WDW - I wonder if we can roll this
-        into _getCheckedState somehow and provide some sort of
+        into _generateCheckedState somehow and provide some sort of
         settings.py string to let you specify the wording to be used
         for different roles.]]]
         """
@@ -358,13 +383,13 @@ class SpeechGenerator:
             result.append(C_("radiobutton", "not selected"))
         return result
 
-    def _getToggleState(self, obj, **args):
+    def _generateToggleState(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) 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?]]] [[[WDW - I wonder if we can roll this
-        into _getCheckedState somehow and provide some sort of
+        into _generateCheckedState somehow and provide some sort of
         settings.py string to let you specify the wording to be used
         for different roles.]]]
         """
@@ -381,7 +406,7 @@ class SpeechGenerator:
             result.append(_("not pressed"))
         return result
 
-    def _getExpandableState(self, obj, **args):
+    def _generateExpandableState(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) that represent the expanded/collapsed state of
         an object, such as a tree node. If the object is not
@@ -405,7 +430,7 @@ class SpeechGenerator:
                 result.append(_("collapsed"))
         return result
 
-    def _getMultiselectableState(self, obj, **args):
+    def _generateMultiselectableState(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) that represent the multiselectable state of
         the object.  This is typically for check boxes. If the object
@@ -421,12 +446,12 @@ class SpeechGenerator:
             result.append(_("multi-select"))
         return result
 
-    def _getMenuItemCheckedState(self, obj, **args):
+    def _generateMenuItemCheckedState(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) that represent the checked state of the menu
         item, only if it is checked. Otherwise, and empty array will
         be returned.  [[[WDW - I wonder if we can roll this into
-        _getCheckedState somehow.]]]
+        _generateCheckedState somehow.]]]
         """
         result = []
         if obj.getState().contains(pyatspi.STATE_CHECKED):
@@ -435,7 +460,7 @@ class SpeechGenerator:
             result.append(_("checked"))
         return result
 
-    def _getAvailability(self, obj, **args):
+    def _generateAvailability(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) that represent the
         grayed/sensitivity/availability state of the object, but only
@@ -451,7 +476,7 @@ class SpeechGenerator:
             result.append(_("grayed"))
         return result
 
-    def _getRequired(self, obj, **args):
+    def _generateRequired(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) that represent the required state of the
         object, but only if it is required (i.e., it is in a dialog
@@ -459,11 +484,13 @@ class SpeechGenerator:
         Otherwise, and empty array will be returned.
         """
         result = []
-        if obj.getState().contains(pyatspi.STATE_REQUIRED):
+        if obj.getState().contains(pyatspi.STATE_REQUIRED) \
+           or (obj.getRole() == pyatspi.ROLE_RADIO_BUTTON \
+               and obj.parent.getState().contains(pyatspi.STATE_REQUIRED)):
             result = [settings.speechRequiredStateString]
         return result
 
-    def _getReadOnly(self, obj, **args):
+    def _generateReadOnly(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) that represent the read only state of this
         object, but only if it is read only (i.e., it is a text area
@@ -477,11 +504,149 @@ class SpeechGenerator:
 
     #####################################################################
     #                                                                   #
+    # Link information                                                  #
+    #                                                                   #
+    #####################################################################
+
+    def _generateLinkInfo(self, obj, **args):
+        """Returns an array of strings (and possibly voice and audio
+        specifications) that represent the protocol of the URI of
+        the link associated with obj.
+        """
+        result = []
+        # 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.
+        #
+        link_uri = self._script.getURI(obj)
+        if not link_uri:
+            # [[[TODO - JD: For some reason, this is failing for certain
+            # links. The current whereAmI code says, "It might be an anchor.
+            # Try to speak the text." and passes things off to whereAmI's
+            # _speakText method. That won't work in the new world order.
+            # Therefore, for now, I will hack in some code to do that
+            # work here so that the before and after end results match.]]]
+            #
+            result.extend(self._generateLabel(obj))
+            result.extend(self._generateRoleName(obj))
+            result.append(self._script.getDisplayedText(obj))
+        else:
+            link_uri_info = urlparse.urlparse(link_uri)
+            if link_uri_info[0] in ["ftp", "ftps", "file"]:
+                fileName = link_uri_info[2].split('/')
+                # Translators: this refers to a link to a file, where
+                # the first item is the protocol (ftp, ftps, or file)
+                # and the second item the name of the file being linked
+                # to.
+                #
+                result.append(_('%s link to %s') %\
+                               (link_uri_info[0], fileName[-1]))
+            else:
+                # Translators: this is the protocol of a link eg. http, mailto.
+                #
+                linkOutput = _('%s link') %link_uri_info[0]
+                text = self._script.getDisplayedText(obj)
+                if not text:
+                    # If there's no text for the link, expose part of the
+                    # URI to the user.
+                    #
+                    text = self._script.getLinkBasename(obj)
+                if text:
+                    linkOutput += " " + text
+                result.append(linkOutput)
+                if obj.childCount and obj[0].getRole() == pyatspi.ROLE_IMAGE:
+                    result.extend(self._generateRoleName(obj[0]))
+        return result
+
+    def _generateSiteDescription(self, obj, **args):
+        """Returns an array of strings (and possibly voice and audio
+        specifications) that describe the site (same or different)
+        pointed to by the URI of the link associated with obj.
+        """
+        result = []
+        link_uri = self._script.getURI(obj)
+        if link_uri:
+            link_uri_info = urlparse.urlparse(link_uri)
+        else:
+            return result
+        doc_uri = self._script.getDocumentFrameURI()
+        if doc_uri:
+            doc_uri_info = urlparse.urlparse(doc_uri)
+            if link_uri_info[1] == doc_uri_info[1]:
+                if link_uri_info[2] == doc_uri_info[2]:
+                    # Translators: this is an indication that a given
+                    # link points to an object that is on the same page.
+                    #
+                    result.append(_('same page'))
+                else:
+                    # Translators: this is an indication that a given
+                    # link points to an object that is at the same site
+                    # (but not on the same page as the link).
+                    #
+                    result.append(_('same site'))
+            else:
+                # check for different machine name on same site
+                #
+                linkdomain = link_uri_info[1].split('.')
+                docdomain = doc_uri_info[1].split('.')
+                if len(linkdomain) > 1 and docdomain > 1  \
+                    and linkdomain[-1] == docdomain[-1]  \
+                    and linkdomain[-2] == docdomain[-2]:
+                    # Translators: this is an indication that a given
+                    # link points to an object that is at the same site
+                    # (but not on the same page) as the link.
+                    #
+                    result.append(_('same site'))
+                else:
+                    # Translators: this is an indication that a given
+                    # link points to an object that is at a different
+                    # site than that of the link.
+                    #
+                    result.append(_('different site'))
+        return result
+
+    def _generateFileSize(self, obj, **args):
+        """Returns an array of strings (and possibly voice and audio
+        specifications) that represent the size (Content-length) of
+        the file pointed to by the URI of the link associated with
+        obj.
+        """
+        result = []
+        sizeString = ""
+        uri = self._script.getURI(obj)
+        if not uri:
+            return result
+        try:
+            x = urllib2.urlopen(uri)
+            try:
+                sizeString = x.info()['Content-length']
+            except KeyError:
+                pass
+        except (ValueError, urllib2.URLError, OSError):
+            pass
+        if sizeString:
+            size = int(sizeString)
+            if size < 10000:
+                # Translators: This is the size of a file in bytes
+                #
+                result.append(ngettext('%d byte', '%d bytes', size) % size)
+            elif size < 1000000:
+                # Translators: This is the size of a file in kilobytes
+                #
+                result.append(_('%.2f kilobytes') % (float(size) * .001))
+            elif size >= 1000000:
+                # Translators: This is the size of a file in megabytes
+                #
+                result.append(_('%.2f megabytes') % (float(size) * .000001))
+        return result
+
+    #####################################################################
+    #                                                                   #
     # Image information                                                 #
     #                                                                   #
     #####################################################################
 
-    def _getImageDescription(self, obj, **args ):
+    def _generateImageDescription(self, obj, **args ):
         """Returns an array of strings (and possibly voice and audio
         specifications) that represent the description of the image on
         the object, if it exists.  Otherwise, an empty array is
@@ -498,7 +663,7 @@ class SpeechGenerator:
                 result.append(description)
         return result
 
-    def _getImage(self, obj, **args):
+    def _generateImage(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) that represent the image on the the object, if
         it exists.  Otherwise, an empty array is returned.
@@ -510,7 +675,7 @@ class SpeechGenerator:
             pass
         else:
             role = pyatspi.ROLE_IMAGE
-            result.extend(self.getSpeech(obj, role=role))
+            result.extend(self.generateSpeech(obj, role=role))
         return result
 
     #####################################################################
@@ -519,7 +684,7 @@ class SpeechGenerator:
     #                                                                   #
     #####################################################################
 
-    def _getRowHeader(self, obj, **args):
+    def _generateRowHeader(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) that represent the row header for an object
         that is in a table, if it exists.  Otherwise, an empty array
@@ -553,14 +718,16 @@ class SpeechGenerator:
                 if desc and len(desc):
                     text = desc
                     if settings.speechVerbosityLevel \
-                            == settings.VERBOSITY_LEVEL_VERBOSE:
+                            == settings.VERBOSITY_LEVEL_VERBOSE \
+                       and not args.get('formatType', None) \
+                           in ['basicWhereAmI', 'detailedWhereAmI']:
                         text += " " \
                             + rolenames.rolenames[\
                             pyatspi.ROLE_ROW_HEADER].speech
                     result.append(text)
         return result
 
-    def _getNewRowHeader(self, obj, **args):
+    def _generateNewRowHeader(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) that represent the row header for an object
         that is in a table, if it exists and if it is different from
@@ -603,10 +770,10 @@ class SpeechGenerator:
                        and (index != newRow) \
                        and ((newRow != oldRow) \
                             or (obj.parent != priorParent)):
-                        result = self._getRowHeader(obj, **args)
+                        result = self._generateRowHeader(obj, **args)
         return result
 
-    def _getColumnHeader(self, obj, **args):
+    def _generateColumnHeader(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) that represent the column header for an object
         that is in a table, if it exists.  Otherwise, an empty array
@@ -640,14 +807,16 @@ class SpeechGenerator:
                 if desc and len(desc):
                     text = desc
                     if settings.speechVerbosityLevel \
-                            == settings.VERBOSITY_LEVEL_VERBOSE:
+                            == settings.VERBOSITY_LEVEL_VERBOSE \
+                       and not args.get('formatType', None) \
+                           in ['basicWhereAmI', 'detailedWhereAmI']:
                         text += " " \
                             + rolenames.rolenames[\
                             pyatspi.ROLE_COLUMN_HEADER].speech
                     result.append(text)
         return result
 
-    def _getNewColumnHeader(self, obj, **args):
+    def _generateNewColumnHeader(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) that represent the column header for an object
         that is in a table, if it exists and if it is different from
@@ -690,10 +859,10 @@ class SpeechGenerator:
                        and (index != newCol) \
                        and ((newCol != oldCol) \
                             or (obj.parent != priorParent)):
-                        result = self._getColumnHeader(obj, **args)
+                        result = self._generateColumnHeader(obj, **args)
         return result
 
-    def _getTableCell2ChildLabel(self, obj, **args):
+    def _generateTableCell2ChildLabel(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) for the label of a toggle in a table cell that
         has a special 2 child pattern that we run into.  Otherwise, an
@@ -732,10 +901,10 @@ class SpeechGenerator:
             if cellOrder:
                 for i in cellOrder:
                     if not hasToggle[i]:
-                        result.extend(self.getSpeech(obj[i], **args))
+                        result.extend(self.generateSpeech(obj[i], **args))
         return result
 
-    def _getTableCell2ChildToggle(self, obj, **args):
+    def _generateTableCell2ChildToggle(self, obj, **args):
         """Returns an array of strings (and possinly voice and audio
         specifications) for the toggle value of a toggle in a table
         cell that has a special 2 child pattern that we run into.
@@ -775,10 +944,10 @@ class SpeechGenerator:
             if cellOrder:
                 for i in cellOrder:
                     if hasToggle[i]:
-                        result.extend(self.getSpeech(obj[i], **args))
+                        result.extend(self.generateSpeech(obj[i], **args))
         return result
 
-    def _getRealTableCell(self, obj, **args):
+    def _generateRealTableCell(self, obj, **args):
         """Orca has a feature to automatically read an entire row of a table
         as the user arrows up/down the roles.  This leads to complexity in
         the code.  This method is used to return an array of strings
@@ -789,7 +958,7 @@ class SpeechGenerator:
         """
         result = []
         oldRole = self._overrideRole('REAL_ROLE_TABLE_CELL', args)
-        result.extend(self.getSpeech(obj, **args))
+        result.extend(self.generateSpeech(obj, **args))
         self._restoreRole(oldRole, args)
         if not result and settings.speakBlankLines \
            and not args.get('readingRow', False):
@@ -799,7 +968,7 @@ class SpeechGenerator:
             result = [_("blank")]
         return result
 
-    def _getTableCellRow(self, obj, **args):
+    def _generateTableCellRow(self, obj, **args):
         """Orca has a feature to automatically read an entire row of a table
         as the user arrows up/down the roles.  This leads to complexity in
         the code.  This method is used to return an array of strings
@@ -814,7 +983,8 @@ class SpeechGenerator:
             parentTable = obj.parent.queryTable()
         except NotImplementedError:
             parentTable = None
-        if settings.readTableCellRow and parentTable \
+        isDetailedWhereAmI = args.get('formatType', None) == 'detailedWhereAmI'
+        if (settings.readTableCellRow or isDetailedWhereAmI) and parentTable \
            and (not self._script.isLayoutOnly(obj.parent)):
             parent = obj.parent
             index = self._script.getCellIndex(obj)
@@ -827,7 +997,10 @@ class SpeechGenerator:
             # the same row).
             #
             speakAll = True
-            if "lastRow" in self._script.pointOfReference \
+            if isDetailedWhereAmI:
+                if parentTable.nColumns <= 1:
+                    return result
+            elif "lastRow" in self._script.pointOfReference \
                and "lastColumn" in self._script.pointOfReference:
                 pointOfReference = self._script.pointOfReference
                 speakAll = \
@@ -865,14 +1038,14 @@ class SpeechGenerator:
                                     accHeader = \
                                         parentTable.getColumnHeader(i)
                                     result.append(accHeader.name)
-                        result.extend(self._getRealTableCell(cell, **args))
+                        result.extend(self._generateRealTableCell(cell, **args))
             else:
-                result.extend(self._getRealTableCell(obj, **args))
+                result.extend(self._generateRealTableCell(obj, **args))
         else:
-            result.extend(self._getRealTableCell(obj, **args))
+            result.extend(self._generateRealTableCell(obj, **args))
         return result
 
-    def _getUnselectedCell(self, obj, **args):
+    def _generateUnselectedCell(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) if this is an icon within an layered pane or a
         table cell within a table or a tree table and the item is
@@ -921,13 +1094,85 @@ class SpeechGenerator:
 
         return result
 
+    def _generateColumn(self, obj, **args):
+        """Returns an array of strings (and possibly voice and audio
+        specifications) reflecting the column number of a cell.
+        """
+        result = []
+        col = -1
+        if obj.parent.getRole() == pyatspi.ROLE_TABLE_CELL:
+            obj = obj.parent
+        parent = obj.parent
+        try:
+            table = parent.queryTable()
+        except:
+            if args.get('guessCoordinates', False):
+                col = self._script.pointOfReference.get('lastColumn', -1)
+        else:
+            index = self._script.getCellIndex(obj)
+            col = table.getColumnAtIndex(index)
+        if col >= 0:
+            # Translators: this is in references to a column in a
+            # table.
+            result.append(_("column %d" % (col + 1)))
+        return result
+
+    def _generateRow(self, obj, **args):
+        """Returns an array of strings (and possibly voice and audio
+        specifications) reflecting the row number of a cell.
+        """
+        result = []
+        row = -1
+        if obj.parent.getRole() == pyatspi.ROLE_TABLE_CELL:
+            obj = obj.parent
+        parent = obj.parent
+        try:
+            table = parent.queryTable()
+        except:
+            if args.get('guessCoordinates', False):
+                row = self._script.pointOfReference.get('lastRow', -1)
+        else:
+            index = self._script.getCellIndex(obj)
+            row = table.getRowAtIndex(index)
+        if row >= 0:
+            # Translators: this is in references to a row in a table.
+            #
+            result.append(_("row %d" % (row + 1)))
+        return result
+
+    def _generateColumnAndRow(self, obj, **args):
+        """Returns an array of strings (and possibly voice and audio
+        specifications) reflecting the position of the cell in terms
+        of its column number, the total number of columns, its row,
+        and the total number of rows.
+        """
+        result = []
+        if obj.parent.getRole() == pyatspi.ROLE_TABLE_CELL:
+            obj = obj.parent
+        parent = obj.parent
+        try:
+            table = parent.queryTable()
+        except:
+            table = None
+        else:
+            index = self._script.getCellIndex(obj)
+            col = table.getColumnAtIndex(index)
+            row = table.getRowAtIndex(index)
+            # Translators: this is in references to a column in a
+            # table.
+            result.append(_("column %d of %d") % ((col + 1), table.nColumns))
+            # Translators: this is in reference to a row in a table.
+            #
+            result.append(_("row %d of %d") % ((row + 1), table.nRows))
+        return result
+
     #####################################################################
     #                                                                   #
     # Terminal information                                              #
     #                                                                   #
     #####################################################################
 
-    def _getTerminal(self, obj, **args):
+    def _generateTerminal(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) used especially for handling terminal objects.
         This either is the name of the frame the terminal is in or the
@@ -950,7 +1195,185 @@ class SpeechGenerator:
     #                                                                   #
     #####################################################################
 
-    def _getCurrentLineText(self, obj, **args ):
+    def _getCharacterAttributes(self,
+                                obj,
+                                text,
+                                textOffset,
+                                lineIndex,
+                                keys=["style", "weight", "underline"]):
+        """Helper function that returns a string containing the
+        given attributes from keys for the given character.
+        """
+        attribStr = ""
+
+        defaultAttributes = text.getDefaultAttributes()
+        attributesDictionary = \
+            self._script.attributeStringToDictionary(defaultAttributes)
+
+        charAttributes = text.getAttributes(textOffset)
+        if charAttributes[0]:
+            charDict = \
+                self._script.attributeStringToDictionary(charAttributes[0])
+            for key in charDict.keys():
+                attributesDictionary[key] = charDict[key]
+
+        if attributesDictionary:
+            for key in keys:
+                localizedKey = text_attribute_names.getTextAttributeName(key)
+                if key in attributesDictionary:
+                    attribute = attributesDictionary[key]
+                    localizedValue = \
+                        text_attribute_names.getTextAttributeName(attribute)
+                    if attribute:
+                        # If it's the 'weight' attribute and greater than 400,
+                        # just speak it as bold, otherwise speak the weight.
+                        #
+                        if key == "weight":
+                            if int(attribute) > 400:
+                                attribStr += " "
+                                # Translators: bold as in the font sense.
+                                #
+                                attribStr += _("bold")
+                        elif key == "underline":
+                            if attribute != "none":
+                                attribStr += " "
+                                attribStr += localizedKey
+                        elif key == "style":
+                            if attribute != "normal":
+                                attribStr += " "
+                                attribStr += localizedValue
+                        else:
+                            attribStr += " "
+                            attribStr += (localizedKey + " " + localizedValue)
+
+            # Also check to see if this is a hypertext link.
+            #
+            if self._script.getLinkIndex(obj, textOffset) >= 0:
+                attribStr += " "
+                # Translators: this indicates that this piece of
+                # text is a hypertext link.
+                #
+                attribStr += _("link")
+
+        return attribStr
+
+    def _generateTextInformation(self, obj, **args):
+        """Returns an empty array, but sets up a 'textInformation' attribute
+        in self._valueCache for other methods to use.  The information
+        is either:
+
+        A. if no text on the current line is selected, the current line
+        B. if text is selected, the selected text
+        C. if the current line is blank/empty, 'blank'
+
+        For all the above, we'll get a 'textInformation' entry in
+        self._valueCache that is the following list:
+
+        [textContents, startOffset, endOffset, selected]
+        """
+
+        textObj = obj.queryText()
+        caretOffset = textObj.caretOffset
+        textContents = ""
+        selected = False
+
+        nSelections = textObj.getNSelections()
+
+        [current, other] = self._script.hasTextSelections(obj)
+        if current or other:
+            selected = True
+            [textContents, startOffset, endOffset] = \
+                self._script.getAllSelectedText(obj)
+        else:
+            # Get the line containing the caret
+            #
+            [line, startOffset, endOffset] = textObj.getTextAtOffset(
+                textObj.caretOffset,
+                pyatspi.TEXT_BOUNDARY_LINE_START)
+            if len(line):
+                line = self._script.adjustForRepeats(line)
+                textContents = line
+            else:
+                char = textObj.getTextAtOffset(caretOffset,
+                    pyatspi.TEXT_BOUNDARY_CHAR)
+                if char[0] == "\n" and startOffset == caretOffset \
+                       and settings.speakBlankLines:
+                    # Translators: "blank" is a short word to mean the
+                    # user has navigated to an empty line.
+                    #
+                    textContents = (_("blank"))
+
+        self._valueCache['textInformation'] = \
+            [textContents, startOffset, endOffset, selected]
+
+        return []
+
+    def _generateTextContent(self, obj, **args):
+        """Returns an array of strings (and possibly voice and audio
+        specifications) containing the text content.  This requires
+        _generateTextInformation to have been called prior to this method.
+        """
+        try:
+            text = obj.queryText()
+        except NotImplementedError:
+            return []
+
+        if not self._valueCache.has_key('textInformation'):
+            self._generateTextInformation(obj, **args)
+        [line, startOffset, endOffset, selected] = \
+            self._valueCache['textInformation']
+        # The empty string seems to be messing with using 'or' in
+        # formatting strings.
+        #
+        if line == '':
+            return []
+        return [line]
+
+    def _generateTextContentWithAttributes(self, obj, **args):
+        """Returns an array of strings (and possibly voice and audio
+        specifications) containing the text content, obtained from the
+        'textInformation' value of self._valueCache, with character
+        attribute information mixed in.  This requires
+        _generateTextInformation to have been called prior to this method.
+        """
+        try:
+            text = obj.queryText()
+        except NotImplementedError:
+            return []
+
+        if not self._valueCache.has_key('textInformation'):
+            self._generateTextInformation(obj, **args)
+        [line, startOffset, endOffset, selected] = \
+            self._valueCache['textInformation']
+
+        newLine = ""
+        lastAttribs = None
+        textOffset = startOffset
+        for i in range(0, len(line)):
+            attribs = self._getCharacterAttributes(obj, text, textOffset, i)
+            if attribs and attribs != lastAttribs:
+                if newLine:
+                    newLine += " ; "
+                newLine += attribs
+                newLine += " "
+            lastAttribs = attribs
+            newLine += line[i]
+            textOffset += 1
+
+        attribs = self._getCharacterAttributes(obj,
+                                               text,
+                                               startOffset,
+                                               0,
+                                               ["paragraph-style"])
+
+        if attribs:
+            if newLine:
+                newLine += " ; "
+            newLine += attribs
+
+        return [newLine]
+
+    def _generateCurrentLineText(self, obj, **args ):
         """Returns an array of strings (and possibly voice and audio
         specifications) that represents the current line of text, if
         this is a text object.  [[[WDW - consider returning an empty
@@ -959,7 +1382,7 @@ class SpeechGenerator:
         [text, caretOffset, startOffset] = self._script.getTextLineAtCaret(obj)
         return [text]
 
-    def _getDisplayedText(self, obj, **args ):
+    def _generateDisplayedText(self, obj, **args ):
         """Returns an array of strings (and possibly voice and audio
         specifications) that represents all the text being displayed
         by the object. [[[WDW - consider returning an empty array if
@@ -967,7 +1390,28 @@ class SpeechGenerator:
         """
         return [self._script.getDisplayedText(obj)]
 
-    def _getAllTextSelection(self, obj, **args):
+    def _generateAnyTextSelection(self, obj, **args):
+        """Returns an array of strings (and possibly voice and audio
+        specifications) that says if any of the text for the entire
+        object is selected. [[[WDW - I wonder if this string should be
+        moved to settings.py.]]]
+        """
+        result = []
+
+        if not self._valueCache.has_key('textInformation'):
+            self._generateTextInformation(obj, **args)
+        [line, startOffset, endOffset, selected] = \
+            self._valueCache['textInformation']
+
+        if selected:
+            # Translators: when the user selects (highlights) text in
+            # a document, Orca lets them know this.
+            #
+            text = C_("text", "selected")
+            result.append(text)
+        return result
+
+    def _generateAllTextSelection(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) that says if all the text for the entire
         object is selected. [[[WDW - I wonder if this string should be
@@ -996,7 +1440,7 @@ class SpeechGenerator:
     #                                                                   #
     #####################################################################
 
-    def _getNodeLevel(self, obj, **args):
+    def _generateNodeLevel(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) that represents the tree node level of the
         object, or an empty array if the object is not a tree
@@ -1012,7 +1456,7 @@ class SpeechGenerator:
             result.append(_("tree level %d") % (level + 1))
         return result
 
-    def _getNewNodeLevel(self, obj, **args):
+    def _generateNewNodeLevel(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) that represents the tree node level of the
         object, or an empty array if the object is not a tree node or
@@ -1023,7 +1467,7 @@ class SpeechGenerator:
         settings.py.]]]
         """
 
-        # [[[TODO: WDW - hate duplicating code from _getNodeLevel,
+        # [[[TODO: WDW - hate duplicating code from _generateNodeLevel,
         # but don't want to call it because it will make the same
         # self._script.getNodeLevel call again.]]]
         #
@@ -1043,7 +1487,7 @@ class SpeechGenerator:
     #                                                                   #
     #####################################################################
 
-    def _getValue(self, obj, **args):
+    def _generateValue(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) that represents the value of the object.  This
         is typically the numerical value, but may also be the text
@@ -1053,7 +1497,7 @@ class SpeechGenerator:
         """
         return [self._script.getTextForValue(obj)]
 
-    def _getPercentage(self, obj, **args ):
+    def _generatePercentage(self, obj, **args ):
         """Returns an array of strings (and possibly voice and audio
         specifications) that represents the percentage value of the
         object.  This is typically for progress bars. [[[WDW - we
@@ -1071,7 +1515,7 @@ class SpeechGenerator:
                 * 100.0
             # Translators: this is the percentage value of a progress bar.
             #
-            percentage = _("%d percent.") % percentValue + " "
+            percentage = _("%d percent") % percentValue
             result.append(percentage)
         return result
 
@@ -1081,7 +1525,7 @@ class SpeechGenerator:
     #                                                                   #
     #####################################################################
 
-    def _getRadioButtonGroup(self, obj, **args):
+    def _generateRadioButtonGroup(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) that represents the radio button group label
         for the object, or an empty array if the object has no such
@@ -1099,9 +1543,19 @@ class SpeechGenerator:
                     break
             if radioGroupLabel:
                 result.append(self._script.getDisplayedText(radioGroupLabel))
+            else:
+                parent = obj.parent
+                while parent and (parent.parent != parent):
+                    if parent.getRole() in [pyatspi.ROLE_PANEL,
+                                            pyatspi.ROLE_FILLER]:
+                        label = self._generateLabelAndName(parent)
+                        if label:
+                            result.extend(label)
+                            break
+                    parent = parent.parent
         return result
 
-    def _getNewRadioButtonGroup(self, obj, **args):
+    def _generateNewRadioButtonGroup(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) that represents the radio button group label
         of the object, or an empty array if the object has no such
@@ -1110,7 +1564,7 @@ class SpeechGenerator:
         'priorObj' is typically set by Orca to be the previous object
         with focus.
         """
-        # [[[TODO: WDW - hate duplicating code from _getRadioButtonGroup
+        # [[[TODO: WDW - hate duplicating code from _generateRadioButtonGroup
         # but don't want to call it because it will make the same
         # AT-SPI method calls.]]]
         #
@@ -1137,7 +1591,7 @@ class SpeechGenerator:
                 result.append(self._script.getDisplayedText(radioGroupLabel))
         return result
 
-    def _getRealActiveDescendantDisplayedText(self, obj, **args ):
+    def _generateRealActiveDescendantDisplayedText(self, obj, **args ):
         """Objects, such as tables and trees, can represent individual cells
         via a complicated nested hierarchy.  This method returns an
         array of strings (and possibly voice and audio specifications)
@@ -1151,7 +1605,7 @@ class SpeechGenerator:
             result = [text]
         return result
 
-    def _getNumberOfChildren(self, obj, **args):
+    def _generateNumberOfChildren(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) that represents the number of children the
         object has.  [[[WDW - can we always return an empty array if
@@ -1169,7 +1623,7 @@ class SpeechGenerator:
             result.append(itemString)
         return result
 
-    def _getNoShowingChildren(self, obj, **args):
+    def _generateNoShowingChildren(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) that says if this object has no showing
         children (e.g., it's an empty table or list).  object has.
@@ -1191,7 +1645,7 @@ class SpeechGenerator:
             result.append(_("0 items"))
         return result
 
-    def _getNoChildren(self, obj, **args ):
+    def _generateNoChildren(self, obj, **args ):
         """Returns an array of strings (and possibly voice and audio
         specifications) that says if this object has no children at
         all (e.g., it's an empty table or list).  object has.  [[[WDW
@@ -1207,7 +1661,63 @@ class SpeechGenerator:
             result.append(_("0 items"))
         return result
 
-    def _getUnfocusedDialogCount(self, obj,  **args):
+    def _generateSelectedItemCount(self, obj, **args):
+        """Returns an array of strings (and possibly voice and audio
+        specifications) indicating how many items are selected in this
+        and the position of the current item. This object will be an icon
+        panel or a layered pane.
+        """
+        result = []
+        # TODO - JD: Is there a better way to do this other than
+        # hard-coding it in?
+        #
+        if args.get('role', obj.getRole()) == pyatspi.ROLE_ICON:
+            obj = obj.parent
+        childCount = obj.childCount
+        selectedItems = []
+        totalSelectedItems = 0
+        currentItem = 0
+        for child in obj:
+            state = child.getState()
+            if state.contains(pyatspi.STATE_SELECTED):
+                totalSelectedItems += 1
+                selectedItems.append(child)
+            if state.contains(pyatspi.STATE_FOCUSED):
+                currentItem = child.getIndexInParent() + 1
+        # Translators: this is a count of the number of selected icons
+        # and the count of the total number of icons within an icon panel.
+        # An example of an icon panel is the Nautilus folder view.
+        #
+        countString = ngettext("%d of %d item selected",
+                              "%d of %d items selected",
+                              childCount) % \
+                              (totalSelectedItems, childCount)
+        result.append(countString)
+        # Translators: this is a indication of the focused icon and the
+        # count of the total number of icons within an icon panel. An
+        # example of an icon panel is the Nautilus folder view.
+        #
+        itemString = _("on item %d of %d") % (currentItem, childCount)
+        result.append(itemString)
+        return result
+
+    def _generateSelectedItems(self, obj, **args):
+        """Returns an array of strings (and possibly voice and audio
+        specifications) containing the names of all the selected items.
+        This object will be an icon panel or a layered pane.
+        """
+        result = []
+        # TODO - JD: Is there a better way to do this other than
+        # hard-coding it in?
+        #
+        if args.get('role', obj.getRole()) == pyatspi.ROLE_ICON:
+            obj = obj.parent
+        for child in obj:
+            if child.getState().contains(pyatspi.STATE_SELECTED):
+                result.extend(self._generateLabelAndName(child))
+        return result
+
+    def _generateUnfocusedDialogCount(self, obj,  **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) that says how many unfocused alerts and
         dialogs are associated with the application for this object.
@@ -1230,7 +1740,7 @@ class SpeechGenerator:
                             alertAndDialogCount) % alertAndDialogCount)
         return result
 
-    def _getAncestors(self, obj, **args):
+    def _generateAncestors(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) that represent the text of the ancestors for
         the object.  This is typically used to present the context for
@@ -1266,14 +1776,14 @@ class SpeechGenerator:
                         #
                         if parent.getRole() not in [pyatspi.ROLE_TABLE_CELL,
                                                     pyatspi.ROLE_FILLER]:
-                            result.extend(self._getRoleName(parent))
+                            result.extend(self._generateRoleName(parent))
                         result.append(text)
                         if parent.getRole() == pyatspi.ROLE_TABLE_CELL:
-                            result.extend(self._getRoleName(parent))
+                            result.extend(self._generateRoleName(parent))
                 parent = parent.parent
         return result.reverse() or result
 
-    def _getNewAncestors(self, obj, **args):
+    def _generateNewAncestors(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) that represent the text of the ancestors for
         the object.  This is typically used to present the context for
@@ -1287,7 +1797,237 @@ class SpeechGenerator:
         """
         result = []
         if args.get('priorObj', None):
-            result = self._getAncestors(obj, **args)
+            result = self._generateAncestors(obj, **args)
+        return result
+
+    def _generateParentRoleName(self, obj, **args):
+        """Returns an array of strings (and possibly voice and audio
+        specifications) containing the role name of the parent of obj.
+        """
+        if args.get('role', obj.getRole()) == pyatspi.ROLE_ICON \
+           and args.get('formatType', None) \
+               in ['basicWhereAmI', 'detailedWhereAmI']:
+            # Translators: this is an alternative name for the
+            # parent object of a series of icons.
+            #
+            return [_("Icon panel")]
+        elif obj.parent.getRole() == pyatspi.ROLE_TABLE_CELL:
+            obj = obj.parent
+        return self._generateRoleName(obj.parent)
+
+    def _generateToolbar(self, obj, **args):
+        """Returns an array of strings (and possibly voice and audio
+        specifications) containing the name and role of the toolbar
+        which contains obj.
+        """
+        result = []
+        ancestor = self._script.getAncestor(obj,
+                                            [pyatspi.ROLE_TOOL_BAR],
+                                            [pyatspi.ROLE_FRAME])
+        if ancestor:
+            result.extend(self._generateLabelAndName(ancestor))
+            result.extend(self._generateRoleName(ancestor))
+        return result
+
+    def _generatePositionInGroup(self, obj, **args):
+        """Returns an array of strings (and possibly voice and audio
+        specifications) that represent the relative position of an
+        object in a group.
+        """
+        result = []
+        position = -1
+        total = -1
+
+        relations = obj.getRelationSet()
+        for relation in relations:
+            if relation.getRelationType() == pyatspi.RELATION_MEMBER_OF:
+                total = relation.getNTargets()
+                for i in range(0, total):
+                    target = relation.getTarget(i)
+                    if target == obj:
+                        position = total - i
+                        break
+
+        if position >= 0:
+            # Translators: this is an item in a list.
+            #
+            result.append(_("item %d of %d") % (position, total))
+
+        return result
+
+    def _generatePositionInList(self, obj, **args):
+        """Returns an array of strings (and possibly voice and audio
+        specifications) that represent the relative position of an
+        object in a list.
+        """
+        result = []
+        position = -1
+        index = 0
+        total = 0
+        name = self._generateName(obj)
+        # TODO - JD: There might be a better way to do this (e.g. pass
+        # roles in maybe?).
+        #
+        role = args.get('role', obj.getRole())
+        if role == pyatspi.ROLE_COMBO_BOX:
+            obj = obj[0]
+        elif role in [pyatspi.ROLE_PAGE_TAB,
+                      pyatspi.ROLE_MENU,
+                      pyatspi.ROLE_MENU_ITEM,
+                      pyatspi.ROLE_CHECK_MENU_ITEM,
+                      pyatspi.ROLE_RADIO_MENU_ITEM]:
+            obj = obj.parent
+        elif role == pyatspi.ROLE_LIST_ITEM:
+            parent = obj.parent
+            for relation in obj.getRelationSet():
+                if relation.getRelationType() == \
+                        pyatspi.RELATION_NODE_CHILD_OF:
+                    # getChildNodes assumes that we have an accessible table
+                    # interface to work with. If we don't, it will fail. So
+                    # don't set the parent until verifying the interface we
+                    # expect actually exists.
+                    #
+                    target = relation.getTarget(0)
+                    try:
+                        target.parent.queryTable()
+                    except:
+                        pass
+                    else:
+                        parent = target
+                    break
+            obj = parent
+
+        # We want to return the position relative to this hierarchical
+        # level and not the entire list.  If the object in question
+        # uses the NODE_CHILD_OF relationship, we need to use it instead
+        # of the childCount.
+        #
+        childNodes = self._script.getChildNodes(obj)
+        total = len(childNodes)
+        for i in range(0, total):
+            childName = self._generateName(childNodes[i])
+            if childName == name:
+                position = i+1
+                break
+
+        if not total:
+            for child in obj:
+                next = self._generateName(child)
+                state = child.getState()
+                if not next or next[0] in ["", "Empty", "separator"] \
+                   or not state.contains(pyatspi.STATE_VISIBLE):
+                    continue
+
+                index += 1
+                total += 1
+
+                if next == name:
+                    position = index
+
+        if position >= 0:
+            # Translators: this is an item in a list.
+            #
+            result.append(_("item %d of %d") % (position, total))
+
+        return result
+
+    def _generateNestingLevel(self, obj, **args):
+        """Returns an array of strings (and possibly voice and audio
+        specifications) that represent the nesting level of an object
+        in a list.
+        """
+        result = []
+        nestingLevel = 0
+        parent = obj.parent
+        while parent.parent.getRole() == pyatspi.ROLE_LIST:
+            nestingLevel += 1
+            parent = parent.parent
+        if nestingLevel:
+            # Translators: this represents a list item in a document.
+            # The nesting level is how 'deep' the item is (e.g., a
+            # level of 2 represents a list item inside a list that's
+            # inside another list).
+            #
+            result.append(_("Nesting level %d") % nestingLevel)
+        return result
+
+    def _generateDefaultButton(self, obj, **args):
+        """Returns an array of strings (and possibly voice and audio
+        specifications) that represent the default button in a dialog.
+        This method should initially be called with a top-level window.
+        """
+        result = []
+        button = self._script.findDefaultButton(obj)
+        if button and button.getState().contains(pyatspi.STATE_SENSITIVE):
+            name = self._generateName(button)
+            if name:
+                # Translators: The "default" button in a dialog box is the
+                # button that gets activated when Enter is pressed anywhere
+                # within that dialog box.
+                #
+                result.append(_("Default button is %s") % name[0])
+        return result
+
+    def generateDefaultButton(self, obj, **args):
+        """Returns an array of strings (and possibly voice and audio
+        specifications) that represent the default button of the window
+        containing the object.
+        """
+        # TODO/Question - JD to WDW: Do we want to add this to the
+        # formatting strings? For now, I've not done that.
+        #
+        return self._generateDefaultButton(obj, **args)
+
+    def _generateStatusBar(self, obj, **args):
+        """Returns an array of strings (and possibly voice and audio
+        specifications) that represent the status bar of a window.
+        This method should initially be called with a top-level window.
+        """
+        result = []
+        statusBar = self._script.findStatusBar(obj)
+        if statusBar:
+            name = self._generateName(statusBar)
+            if name:
+                result.extend(name)
+            else:
+                for child in statusBar:
+                    result.extend(self._generateName(child))
+        return result
+
+    def generateStatusBar(self, obj, **args):
+        """Returns an array of strings (and possibly voice and audio
+        specifications) that represent the status bar of the window
+        containing the object.
+        """
+        # TODO/Question - JD to WDW: Do we want to add this to the
+        # formatting strings? For now, I've not done that.
+        #
+        return self._generateStatusBar(obj, **args)
+
+    def generateTitle(self, obj, **args):
+        """Returns an array of strings (and possibly voice and audio
+        specifications) that represent the title of the window, obj.
+        containing the object, along with information associated with
+        any unfocused dialog boxes.
+        """
+        # TODO/Question - JD to WDW: Do we want to add this to the
+        # formatting strings? For now, I've not done that.
+        #
+        result = []
+        frame, dialog = self._script.findFrameAndDialog(obj)
+        if frame:
+            result.append(self._generateLabelAndName(frame))
+        if dialog:
+            result.append(self._generateLabelAndName(dialog))
+        alertAndDialogCount = \
+                    self._script.getUnfocusedAlertAndDialogCount(obj)
+        if alertAndDialogCount > 0:
+            # 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)
         return result
 
     #####################################################################
@@ -1296,7 +2036,7 @@ class SpeechGenerator:
     #                                                                   #
     #####################################################################
 
-    def _getAccelerator(self, obj, **args):
+    def _generateAccelerator(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) that represent the accelerator for the object,
         or an empty array if no accelerator can be found.
@@ -1311,13 +2051,13 @@ class SpeechGenerator:
             result.append(accelerator)
         return result
 
-    def _getMnemonic(self, obj, **args):
+    def _generateMnemonic(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) that represent the mnemonic for the object, or
         an empty array if no mnemonic can be found.
         """
         result = []
-        if settings.enableMnemonicSpeaking:
+        if settings.enableMnemonicSpeaking or args.get('forceMnemonic', False):
             [mnemonic, shortcut, accelerator] = self._script.getKeyBinding(obj)
             if mnemonic:
                 mnemonic = mnemonic[-1] # we just want a single character
@@ -1331,27 +2071,36 @@ class SpeechGenerator:
                 result = [mnemonic]
         return result
 
-
     #####################################################################
     #                                                                   #
     # Tutorial information                                              #
     #                                                                   #
     #####################################################################
 
-    def _getTutorial(self, obj, **args):
+    def _generateTutorial(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) that represent the tutorial for the object.
         The tutorial will only be generated if the user has requested
         tutorials, and will then be generated according to the
         tutorial generator.  A tutorial can be forced by setting the
-        'forceMessage' attribute of the args dictionary to True.
+        'forceTutorial' attribute of the args dictionary to True.
         """
-        already_focused = args.get('already_focused', False)
-        forceMessage = args.get('forceMessage', False)
-        return self._script.tutorialGenerator.getTutorial(
-            obj,
-            already_focused,
-            forceMessage)
+        result = []
+        alreadyFocused = args.get('alreadyFocused', False)
+        forceTutorial = args.get('forceTutorial', False)
+        result.extend(self._script.tutorialGenerator.getTutorial(
+                obj,
+                alreadyFocused,
+                forceTutorial))
+        if args.get('role', obj.getRole()) == pyatspi.ROLE_ICON \
+            and args.get('formatType', 'unfocused') == 'basicWhereAmI':
+            frame, dialog = self._script.findFrameAndDialog(obj)
+            if frame:
+                result.extend(self._script.tutorialGenerator.getTutorial(
+                        frame,
+                        alreadyFocused,
+                        forceTutorial))
+        return result
 
     #####################################################################
     #                                                                   #
@@ -1370,11 +2119,27 @@ class SpeechGenerator:
             voice = settings.voices[settings.DEFAULT_VOICE]
         return [voice]
 
-    def getSpeech(self, obj, **args):
+    def generateSpeech(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) that represent the complete speech for the
         object.  The speech to be generated depends highly upon the
         speech formatting strings in formatting.py.
+
+        args is a dictionary that may contain any of the following:
+        - alreadyFocused: if True, we're getting speech for an object
+          that previously had focus
+        - priorObj: if set, represents the object that had focus before
+          this object
+        - includeContext: boolean (default=True) which says whether
+          the context for an object should be included as a prefix
+          and suffix
+        - role: a role to override the object's role
+        - formatType: the type of formatting, such as
+          'focused', 'basicWhereAmI', etc.
+        - forceMnemonic: boolean (default=False) which says if we
+          should ignore the settings.enableMnemonicSpeaking setting
+        - forceTutorial: boolean (default=False) which says if we
+          should force a tutorial to be spoken or not
         """
         result = []
         methods = {}
@@ -1396,26 +2161,32 @@ class SpeechGenerator:
             # and get its results, placing them in the globals for the
             # the call to eval.
             #
-            format = self._script.formatting.getFormat('speech',
-                                                       **args)
+            args['mode'] = 'speech'
+            if not args.get('formatType', None):
+                if args.get('alreadyFocused', False):
+                    args['formatType'] = 'focused'
+                else:
+                    args['formatType'] = 'unfocused'
+
+            format = self._script.formatting.getFormat(**args)
 
             # Add in the speech context if this is the first time
             # we've been called.
             #
             if not args.get('recursing', False):
+                self._valueCache = {}
                 if args.get('includeContext', True):
-                    prefix = self._script.formatting.getPrefix('speech',
-                                                               **args)
-                    suffix = self._script.formatting.getSuffix('speech',
-                                                               **args)
+                    prefix = self._script.formatting.getPrefix(**args)
+                    suffix = self._script.formatting.getSuffix(**args)
                     format = '%s + %s + %s' % (prefix, format, suffix)
-                debug.println(debug.LEVEL_ALL, "getSpeech for %s using '%s'" \
-                              % (repr(args), format))
                 args['recursing'] = True
                 firstTimeCalled = True
             else:
                 firstTimeCalled = False
 
+            debug.println(debug.LEVEL_ALL, "generateSpeech for %s using '%s'" \
+                          % (repr(args), format))
+
             assert(format)
             while True:
                 try:
@@ -1440,7 +2211,7 @@ class SpeechGenerator:
             debug.printException(debug.LEVEL_SEVERE)
             result = []
 
-        if firstTimeCalled:
-            debug.println(debug.LEVEL_ALL,
-                          "getSpeech generated '%s'" % repr(result))
+        debug.println(debug.LEVEL_ALL,
+                      "generateSpeech generated '%s'" % repr(result))
+
         return result
diff --git a/src/orca/structural_navigation.py b/src/orca/structural_navigation.py
index a175d25..dfa76bd 100644
--- a/src/orca/structural_navigation.py
+++ b/src/orca/structural_navigation.py
@@ -1775,7 +1775,7 @@ class StructuralNavigation:
         else:
             voice = None
 
-        utterances = self._script.speechGenerator.getSpeech(obj)
+        utterances = self._script.speechGenerator.generateSpeech(obj)
         speech.speak(utterances, voice)
 
     #########################################################################
diff --git a/src/orca/tutorialgenerator.py b/src/orca/tutorialgenerator.py
index ca42154..a5e31f9 100644
--- a/src/orca/tutorialgenerator.py
+++ b/src/orca/tutorialgenerator.py
@@ -1,6 +1,6 @@
 # Orca
 #
-# Copyright 2008 Sun Microsystems Inc.
+# Copyright 2008-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,7 +26,7 @@ as they see fit."""
 __id__        = "$Id$"
 __version__   = "$Revision$"
 __date__      = "$Date$"
-__copyright__ = "Copyright (c) 2008 Sun Microsystems Inc."
+__copyright__ = "Copyright (c) 2008-2009 Sun Microsystems Inc."
 __license__   = "LGPL"
 
 import pyatspi
@@ -36,9 +36,9 @@ import settings
 from orca_i18n import _         # for gettext support
 
 class TutorialGenerator:
-    """Takes accessible objects and produces a tutorial string to speak 
-    for those objects.  See the getTutorialString method, which is the 
-    primary entry point.  Subclasses can feel free to override/extend 
+    """Takes accessible objects and produces a tutorial string to speak
+    for those objects.  See the getTutorialString method, which is the
+    primary entry point.  Subclasses can feel free to override/extend
     the getTutorialGenerators instance field as they see fit."""
 
     def __init__(self, script):
@@ -95,18 +95,18 @@ class TutorialGenerator:
         self.tutorialGenerators[pyatspi.ROLE_MENU_ITEM]           = \
             self._getTutorialForPushButton
         self.tutorialGenerators[pyatspi.ROLE_RADIO_MENU_ITEM]     = \
-            self._getTutorialForCheckBox        
+            self._getTutorialForCheckBox
         self.tutorialGenerators[pyatspi.ROLE_SLIDER]              = \
             self._getTutorialForSlider
 
-    def _debugGenerator(self, generatorName, obj, already_focused, utterances):
-        """Prints debug.LEVEL_FINER information regarding 
+    def _debugGenerator(self, generatorName, obj, alreadyFocused, utterances):
+        """Prints debug.LEVEL_FINER information regarding
         the tutorial generator.
 
         Arguments:
         - generatorName: the name of the generator
         - obj: the object being presented
-        - already_focused: False if object just received focus
+        - alreadyFocused: False if object just received focus
         - utterances: the generated text
         """
 
@@ -117,23 +117,23 @@ class TutorialGenerator:
         debug.println(debug.LEVEL_FINER,
                       "           role            = %s" % obj.getRoleName())
         debug.println(debug.LEVEL_FINER,
-                      "           already_focused = %s" % already_focused)
+                      "           alreadyFocused  = %s" % alreadyFocused)
         debug.println(debug.LEVEL_FINER,
                       "           utterances:")
         for text in utterances:
             debug.println(debug.LEVEL_FINER,
                     "               (%s)" % text)
 
-    def _getDefaultTutorial(self, obj, already_focused, forceMessage, \
-      role=None):
+    def _getDefaultTutorial(
+        self, obj, alreadyFocused, forceTutorial, role=None):
         """The default tutorial generator returns the empty tutorial string
         because We have no associated tutorial function for the object.
-        
+
         Arguments:
         - obj: an Accessible
-        - already_focused: False if object just received focus
-        - forceMessage: used for when whereAmI really needs the tutorial string.
-        - role: A role that should be used instead of the Accessible's 
+        - alreadyFocused: False if object just received focus
+        - forceTutorial: used for when whereAmI really needs the tutorial string
+        - role: A role that should be used instead of the Accessible's
           possible role.
 
         Returns the empty list []
@@ -142,13 +142,13 @@ class TutorialGenerator:
         return []
 
 
-    def _getTutorialForCheckBox(self, obj, already_focused, forceMessage):
+    def _getTutorialForCheckBox(self, obj, alreadyFocused, forceTutorial):
         """Get the  tutorial string for a check box.
 
         Arguments:
         - obj: the check box
-        - already_focused: False if object just received focus
-        - forceMessage: used for when whereAmI really needs the tutorial string.
+        - alreadyFocused: False if object just received focus
+        - forceTutorial: used for when whereAmI really needs the tutorial string
 
         Returns a list of tutorial utterances to be spoken for the object.
         """
@@ -157,49 +157,51 @@ class TutorialGenerator:
         # Translators: this is a tip for the user on how to toggle a checkbox.
         msg = _("Press space to toggle.")
 
-        if (not already_focused and self.lastTutorial != [msg]) or forceMessage:
+        if (not alreadyFocused and self.lastTutorial != [msg]) \
+           or forceTutorial:
             utterances.append(msg)
 
         self._debugGenerator("_getTutorialForCheckBox",
                              obj,
-                             already_focused,
+                             alreadyFocused,
                              utterances)
 
         return utterances
 
-    def _getTutorialForComboBox(self, obj, already_focused, forceMessage):
+    def _getTutorialForComboBox(self, obj, alreadyFocused, forceTutorial):
         """Get the  tutorial string for a combobox.
 
         Arguments:
         - obj: the combo box
-        - already_focused: False if object just received focus
-        - forceMessage: used for when whereAmI really needs the tutorial string.
+        - alreadyFocused: False if object just received focus
+        - forceTutorial: used for when whereAmI really needs the tutorial string
 
         Returns a list of tutorial utterances to be spoken for the object.
         """
 
         utterances = []
-        # Translators: this is a tip for the user on how to interact 
+        # Translators: this is a tip for the user on how to interact
         # with a combobox.
         msg = _("Press space to expand, and use up and down to select an item.")
 
-        if (not already_focused and self.lastTutorial != [msg]) or forceMessage:
+        if (not alreadyFocused and self.lastTutorial != [msg]) \
+           or forceTutorial:
             utterances.append(msg)
 
         self._debugGenerator("_getTutorialForComboBox",
                              obj,
-                             already_focused,
+                             alreadyFocused,
                              utterances)
 
         return utterances
 
-    def _getTutorialForFrame(self, obj, already_focused, forceMessage):
+    def _getTutorialForFrame(self, obj, alreadyFocused, forceTutorial):
         """Get the  tutorial string for a frame.
 
         Arguments:
         - obj: the frame
-        - already_focused: False if object just received focus
-        - forceMessage: used for when whereAmI really needs the tutorial string.
+        - alreadyFocused: False if object just received focus
+        - forceTutorial: used for when whereAmI really needs the tutorial string
 
         Returns a list of tutorial utterances to be spoken for the object.
         """
@@ -222,45 +224,45 @@ class TutorialGenerator:
 
         self._debugGenerator("_getTutorialForFrame",
                              obj,
-                             already_focused,
+                             alreadyFocused,
                              utterances)
 
         return utterances
 
-    def _getTutorialForIcon(self, obj, already_focused, forceMessage):
+    def _getTutorialForIcon(self, obj, alreadyFocused, forceTutorial):
         """Get the  tutorial string for an icon.
 
         Arguments:
         - obj: the icon
-        - already_focused: False if object just received focus
-        - forceMessage: used for when whereAmI really needs the tutorial string.
+        - alreadyFocused: False if object just received focus
+        - forceTutorial: used for when whereAmI really needs the tutorial string
 
         Returns a list of tutorial utterances to be spoken for the object.
         """
 
         if obj.parent.getRole() == pyatspi.ROLE_LAYERED_PANE:
             utterances = self._getTutorialForLayeredPane(obj.parent,
-                                                         already_focused,
-                                                         forceMessage)
+                                                         alreadyFocused,
+                                                         forceTutorial)
         else:
             utterances = self._getDefaultTutorial(obj,
-                                                  already_focused,
-                                                  forceMessage)
+                                                  alreadyFocused,
+                                                  forceTutorial)
 
         self._debugGenerator("_getTutorialForIcon",
                              obj,
-                             already_focused,
+                             alreadyFocused,
                              utterances)
 
         return utterances
 
-    def _getTutorialForLayeredPane(self, obj, already_focused, forceMessage):
+    def _getTutorialForLayeredPane(self, obj, alreadyFocused, forceTutorial):
         """Get the  tutorial string for a layered pane.
 
         Arguments:
         - obj: the layered pane
-        - already_focused: False if object just received focus
-        - forceMessage: used for when whereAmI really needs the tutorial string.
+        - alreadyFocused: False if object just received focus
+        - forceTutorial: used for when whereAmI really needs the tutorial string
 
         Returns a list of tutorial utterances to be spoken for the object.
         """
@@ -270,13 +272,13 @@ class TutorialGenerator:
         if not name and obj.description:
             name = obj.description
 
-        # Translators: this gives tips on how to navigate items in a 
+        # Translators: this gives tips on how to navigate items in a
         # layered pane.
         msg = _("To move to items, use either " \
                 "the arrow keys or type ahead searching.")
         utterances.append(msg)
- 
-        # Translators: this is the tutorial string for when first landing 
+
+        # Translators: this is the tutorial string for when first landing
         # on the desktop, describing how to access the system menus.
         desktopMsg = _("To get to the system menus press the alt+f1 key.")
 
@@ -285,52 +287,53 @@ class TutorialGenerator:
         if 'nautilus' in scriptName and obj == sibling:
             utterances.append(desktopMsg)
 
-        if (not already_focused and self.lastTutorial != utterances) \
-            or forceMessage:
+        if (not alreadyFocused and self.lastTutorial != utterances) \
+            or forceTutorial:
             pass
         else:
             utterances = []
 
         self._debugGenerator("_getTutorialForLayeredPane",
                              obj,
-                             already_focused,
+                             alreadyFocused,
                              utterances)
 
         return utterances
 
-    def _getTutorialForList(self, obj, already_focused, forceMessage):
+    def _getTutorialForList(self, obj, alreadyFocused, forceTutorial):
         """Get the  tutorial string for a list.
 
         Arguments:
         - obj: the list
-        - already_focused: False if object just received focus
-        - forceMessage: used for when whereAmI really needs the tutorial string.
+        - alreadyFocused: False if object just received focus
+        - forceTutorial: used for when whereAmI really needs the tutorial string
 
         Returns a list of tutorial utterances to be spoken for the object.
         """
 
         utterances = []
-        
+
         # Translators: this is the tutorial string when navigating lists.
         msg = _("Use up and down to select an item.")
-        
-        if (not already_focused and self.lastTutorial != [msg]) or forceMessage:
+
+        if (not alreadyFocused and self.lastTutorial != [msg]) \
+           or forceTutorial:
             utterances.append(msg)
 
         self._debugGenerator("_getTutorialForList",
                              obj,
-                             already_focused,
+                             alreadyFocused,
                              utterances)
 
         return utterances
-    
-    def _getTutorialForListItem(self, obj, already_focused, forceMessage):
+
+    def _getTutorialForListItem(self, obj, alreadyFocused, forceTutorial):
         """Get the  tutorial string for a listItem.
 
         Arguments:
         - obj: the listitem
-        - already_focused: False if object just received focus
-        - forceMessage: used for when whereAmI really needs the tutorial string.
+        - alreadyFocused: False if object just received focus
+        - forceTutorial: used for when whereAmI really needs the tutorial string
 
         Returns a list of tutorial utterances to be spoken for the object.
         """
@@ -338,94 +341,94 @@ class TutorialGenerator:
         utterances = []
 
         # Translators: this represents the state of a node in a tree.
-	# 'expanded' means the children are showing.
+        # 'expanded' means the children are showing.
         # 'collapsed' means the children are not showing.
         # this string informs the user how to collapse the node.
         expandedMsg = _("To collapse, press shift plus left.")
-        
+
         # Translators: this represents the state of a node in a tree.
         # 'expanded' means the children are showing.
         # 'collapsed' means the children are not showing.
         # this string informs the user how to expand the node.
         collapsedMsg = _("To expand, press shift plus right.")
-        
-        
+
+
         # If already in focus then the tree probably collapsed or expanded
         state = obj.getState()
         if state.contains(pyatspi.STATE_EXPANDABLE):
             if state.contains(pyatspi.STATE_EXPANDED):
-                if (self.lastTutorial != [expandedMsg]) or forceMessage:
+                if (self.lastTutorial != [expandedMsg]) or forceTutorial:
                     utterances.append(expandedMsg)
             else:
-                if (self.lastTutorial != [collapsedMsg]) or forceMessage:
+                if (self.lastTutorial != [collapsedMsg]) or forceTutorial:
                     utterances.append(collapsedMsg)
-                
+
         self._debugGenerator("_getTutorialForListItem",
                              obj,
-                             already_focused,
+                             alreadyFocused,
                              utterances)
         return utterances
 
-    def _getTutorialForText(self, obj, already_focused, forceMessage):
+    def _getTutorialForText(self, obj, alreadyFocused, forceTutorial):
         """Get the tutorial string for a text object.
 
         Arguments:
         - obj: the text component
-        - already_focused: False if object just received focus
-        - forceMessage: used for when whereAmI really needs the tutorial string.
+        - alreadyFocused: False if object just received focus
+        - forceTutorial: used for when whereAmI really needs the tutorial string
 
         Returns a list of tutorial utterances to be spoken for the object.
         """
         utterances = []
-        # Translators: This is the tutorial string for when landing 
+        # Translators: This is the tutorial string for when landing
         # on text fields.
         msg = _("Type in text.")
 
-        if (not already_focused or forceMessage) and \
+        if (not alreadyFocused or forceTutorial) and \
            not self._script.isReadOnlyTextArea(obj):
             utterances.append(msg)
 
         self._debugGenerator("_getTutorialForText",
                              obj,
-                             already_focused,
+                             alreadyFocused,
                              utterances)
 
         return utterances
 
-    def _getTutorialForPageTab(self, obj, already_focused, forceMessage):
+    def _getTutorialForPageTab(self, obj, alreadyFocused, forceTutorial):
         """Get the tutorial string for a page tab.
 
         Arguments:
         - obj: the page tab
-        - already_focused: False if object just received focus
-        - forceMessage: used for when whereAmI really needs the tutorial string.
+        - alreadyFocused: False if object just received focus
+        - forceTutorial: used for when whereAmI really needs the tutorial string
 
         Returns a list of tutorial utterances to be spoken for the object.
         """
 
         utterances = []
-        # Translators: this is the tutorial string for landing 
-        # on a page tab, we are informing the 
+        # Translators: this is the tutorial string for landing
+        # on a page tab, we are informing the
         # user how to navigate these.
         msg = _("Use left and right to view other tabs.")
-        
-        if (self.lastTutorial != [msg]) or forceMessage:
+
+        if (self.lastTutorial != [msg]) or forceTutorial:
             utterances.append(msg)
 
         self._debugGenerator("_getTutorialForPageTabList",
                              obj,
-                             already_focused,
+                             alreadyFocused,
                              utterances)
 
         return utterances
 
-    def _getTutorialForPushButton(self, obj, already_focused, forceMessage):
+    def _getTutorialForPushButton(self, obj, alreadyFocused, forceTutorial):
         """Get the tutorial string for a push button
 
         Arguments:
         - obj: the push button
-        - already_focused: False if object just received focus
-        - forceMessage: used for when whereAmI really needs the tutorial string.
+        - alreadyFocused: False if object just received focus
+        - forceTutorial: used for when whereAmI really needs the tutorial string
 
         Returns a list of utterances to be spoken for the object.
         """
@@ -433,52 +436,54 @@ class TutorialGenerator:
         utterances = []
         # Translators: this is the tutorial string for activating a push button.
         msg = _("To activate press space.")
-        
-        if (not already_focused and self.lastTutorial != [msg]) or forceMessage:
+
+        if (not alreadyFocused and self.lastTutorial != [msg]) \
+           or forceTutorial:
             utterances.append(msg)
 
         self._debugGenerator("_getTutorialForPushButton",
                              obj,
-                             already_focused,
+                             alreadyFocused,
                              utterances)
 
         return utterances
 
-    def _getTutorialForSpinButton(self, obj, already_focused, forceMessage):
+    def _getTutorialForSpinButton(self, obj, alreadyFocused, forceTutorial):
         """Get the tutorial string for a spin button.  If the object already has
         focus, then no tutorial is given.
 
         Arguments:
         - obj: the spin button
-        - already_focused: False if object just received focus
-        - forceMessage: used for when whereAmI really needs the tutorial string.
+        - alreadyFocused: False if object just received focus
+        - forceTutorial: used for when whereAmI really needs the tutorial string
 
         Returns a list of utterances to be spoken for the object.
         """
 
         utterances = []
-        # Translators: this is the tutorial string for when landing 
+        # Translators: this is the tutorial string for when landing
         # on a spin button.
         msg = _("Use up or down arrow to select value." \
               " Or type in the desired numerical value.")
 
-        if (not already_focused and self.lastTutorial != [msg]) or forceMessage:
+        if (not alreadyFocused and self.lastTutorial != [msg]) \
+           or forceTutorial:
             utterances.append(msg)
 
         self._debugGenerator("_getTutorialForSpinButton",
                              obj,
-                             already_focused,
+                             alreadyFocused,
                              utterances)
 
         return utterances
 
-    def _getTutorialForTableCell(self, obj, already_focused, forceMessage):
+    def _getTutorialForTableCell(self, obj, alreadyFocused, forceTutorial):
         """Get the tutorial utterances for a single table cell
 
         Arguments:
         - obj: the table
-        - already_focused: False if object just received focus
-        - forceMessage: used for when whereAmI really needs the tutorial string.
+        - alreadyFocused: False if object just received focus
+        - forceTutorial: used for when whereAmI really needs the tutorial string
 
         Returns a list of utterances to be spoken for the object.
         """
@@ -486,19 +491,19 @@ class TutorialGenerator:
         utterances = []
 
         # Translators: this represents the state of a node in a tree.
-	# 'expanded' means the children are showing.
+        # 'expanded' means the children are showing.
         # 'collapsed' means the children are not showing.
         # this string informs the user how to collapse the node.
         expandedMsg = _("To collapse, press shift plus left.")
-        
+
         # Translators: this represents the state of a node in a tree.
         # 'expanded' means the children are showing.
         # 'collapsed' means the children are not showing.
         # this string informs the user how to expand the node.
         collapsedMsg = _("To expand, press shift plus right.")
 
-        # If this table cell has 2 children and one of them has a 
-        # 'toggle' action and the other does not, then present this 
+        # If this table cell has 2 children and one of them has a
+        # 'toggle' action and the other does not, then present this
         # as a checkbox where:
         # 1) we get the checked state from the cell with the 'toggle' action
         # 2) we get the label from the other cell.
@@ -523,7 +528,7 @@ class TutorialGenerator:
                             break
 
             if hasToggle[0] and not hasToggle[1]:
-                cellOrder = [ 1, 0 ] 
+                cellOrder = [ 1, 0 ]
             elif not hasToggle[0] and hasToggle[1]:
                 cellOrder = [ 0, 1 ]
             if cellOrder:
@@ -531,12 +536,12 @@ class TutorialGenerator:
                     # Don't speak the label if just the checkbox state has
                     # changed.
                     #
-                    if already_focused and not hasToggle[i]:
+                    if alreadyFocused and not hasToggle[i]:
                         pass
                     else:
                         utterances.extend( \
                             self._getTutorialForTableCell(obj[i],
-                            already_focused, forceMessage))
+                            alreadyFocused, forceTutorial))
                 return utterances
 
         # [[[TODO: WDW - Attempt to infer the cell type.  There's a
@@ -560,39 +565,39 @@ class TutorialGenerator:
                 #
                 if action.getName(i) in ["toggle", _("toggle")]:
                     utterances = self._getTutorialForCheckBox(obj,
-                                  already_focused, forceMessage)
+                                  alreadyFocused, forceTutorial)
                     break
 
         state = obj.getState()
         if state.contains(pyatspi.STATE_EXPANDABLE):
             if state.contains(pyatspi.STATE_EXPANDED):
-                if self.lastTutorial != [expandedMsg] or forceMessage:
+                if self.lastTutorial != [expandedMsg] or forceTutorial:
                     utterances.append(expandedMsg)
             else:
-                if self.lastTutorial != [collapsedMsg] or forceMessage:
+                if self.lastTutorial != [collapsedMsg] or forceTutorial:
                     utterances.append(collapsedMsg)
 
         self._debugGenerator("_getTutorialForTableCell",
                              obj,
-                             already_focused,
+                             alreadyFocused,
                              utterances)
 
         return utterances
 
-    def _getTutorialForTableCellRow(self, obj, already_focused, forceMessage):
+    def _getTutorialForTableCellRow(self, obj, alreadyFocused, forceTutorial):
         """Get the tutorial string for the active table cell in the table row.
 
         Arguments:
         - obj: the table
-        - already_focused: False if object just received focus
-        - forceMessage: used for when whereAmI really needs the tutorial string.
+        - alreadyFocused: False if object just received focus
+        - forceTutorial: used for when whereAmI really needs the tutorial string
 
         Returns a list of utterances to be spoken for the object.
         """
 
         utterances = []
 
-        if (not already_focused):
+        if (not alreadyFocused):
             try:
                 parent_table = obj.parent.queryTable()
             except NotImplementedError:
@@ -618,28 +623,28 @@ class TutorialGenerator:
                            pointOfReference["lastColumn"] == column)
 
                 utterances.extend(self._getTutorialForTableCell(obj,
-                                        already_focused, forceMessage))
+                                        alreadyFocused, forceTutorial))
             else:
-                utterances = self._getTutorialForTableCell(obj, 
-                  already_focused, forceMessage)
+                utterances = self._getTutorialForTableCell(obj,
+                  alreadyFocused, forceTutorial)
         else:
-            utterances = self._getTutorialForTableCell(obj, already_focused, \
-              forceMessage)
+            utterances = self._getTutorialForTableCell(obj, alreadyFocused, \
+              forceTutorial)
 
         self._debugGenerator("_getTutorialForTableCellRow",
                              obj,
-                             already_focused,
+                             alreadyFocused,
                              utterances)
 
         return utterances
 
-    def _getTutorialForRadioButton(self, obj, already_focused, forceMessage):
+    def _getTutorialForRadioButton(self, obj, alreadyFocused, forceTutorial):
         """Get the tutorial string for a radio button.
 
         Arguments:
         - obj: the radio button
-        - already_focused: False if object just received focus
-        - forceMessage: used for when whereAmI really needs the tutorial string.
+        - alreadyFocused: False if object just received focus
+        - forceTutorial: used for when whereAmI really needs the tutorial string
 
         Returns a list of utterances to be spoken for the object.
         """
@@ -648,22 +653,23 @@ class TutorialGenerator:
         # Translators: this is a tip for the user, how to navigate radiobuttons.
         msg = _("Use arrow keys to change.")
 
-        if (not already_focused and self.lastTutorial != [msg]) or forceMessage:
+        if (not alreadyFocused and self.lastTutorial != [msg]) \
+           or forceTutorial:
             utterances.append(msg)
 
         self._debugGenerator("_getTutorialForRadioButton",
                              obj,
-                             already_focused,
+                             alreadyFocused,
                              utterances)
         return utterances
 
-    def _getTutorialForMenu(self, obj, already_focused, forceMessage):
+    def _getTutorialForMenu(self, obj, alreadyFocused, forceTutorial):
         """Get the tutorial string for a menu.
 
         Arguments:
         - obj: the menu
-        - already_focused: False if object just received focus
-        - forceMessage: used for when whereAmI really needs the tutorial string.
+        - alreadyFocused: False if object just received focus
+        - forceTutorial: used for when whereAmI really needs the tutorial string
 
         Returns a list of utterances to be spoken for the object.
         """
@@ -673,85 +679,86 @@ class TutorialGenerator:
         mainMenuMsg = _("To navigate, press left or right arrow. " \
                        "To move through items press up or down arrow.")
 
-        # Translators: this is a tip for the user, how to 
+        # Translators: this is a tip for the user, how to
         # navigate into sub menues.
-        subMenuMsg = _("To enter sub menu, press right arrow.")        
-        
+        subMenuMsg = _("To enter sub menu, press right arrow.")
+
         # Checking if we are a submenu,
         # we can't rely on our parent being just a menu.
         if obj.parent.name != "" and obj.parent.__class__ == obj.__class__:
-            if (self.lastTutorial != [subMenuMsg]) or forceMessage:
+            if (self.lastTutorial != [subMenuMsg]) or forceTutorial:
                 utterances.append(subMenuMsg)
         else:
-            if (self.lastTutorial != [mainMenuMsg]) or forceMessage:
+            if (self.lastTutorial != [mainMenuMsg]) or forceTutorial:
                 utterances.append(mainMenuMsg)
 
         self._debugGenerator("_getTutorialForMenu",
                              obj,
-                             already_focused,
+                             alreadyFocused,
                              utterances)
         return utterances
 
-    def _getTutorialForSlider(self, obj, already_focused, forceMessage):
+    def _getTutorialForSlider(self, obj, alreadyFocused, forceTutorial):
         """Get the tutorial string for a slider.  If the object already has
         focus, then no tutorial is given.
 
         Arguments:
         - obj: the slider
-        - already_focused: False if object just received focus
-        - forceMessage: used for when whereAmI really needs the tutorial string.
+        - alreadyFocused: False if object just received focus
+        - forceTutorial: used for when whereAmI really needs the tutorial string
 
         Returns a list of utterances to be spoken for the object.
         """
 
         utterances = []
-        # Translators: this is the tutorial string for when landing 
+        # Translators: this is the tutorial string for when landing
         # on a slider.
         msg = _("To decrease press left arrow, to increase press right arrow." \
           " To go to minimum press home, and for maximum press end.")
-              
-        if (not already_focused and self.lastTutorial != [msg]) or forceMessage:
+
+        if (not alreadyFocused and self.lastTutorial != [msg]) \
+           or forceTutorial:
             utterances.append(msg)
 
         self._debugGenerator("_getTutorialForSlider",
                              obj,
-                             already_focused,
+                             alreadyFocused,
                              utterances)
 
         return utterances
 
-    def getTutorial(self, obj, already_focused, forceMessage = False):
+    def getTutorial(self, obj, alreadyFocused, forceTutorial=False):
         """Get the tutorial for an Accessible object.  This will look
-        first to the specific tutorial generators and if this 
+        first to the specific tutorial generators and if this
         does not exist then return the empty tutorial.
         This method is the primary method
         that external callers of this class should use.
 
         Arguments:
         - obj: the object
-        - already_focused: False if object just received focus
-        - forceMessage: used for when whereAmI really needs the tutorial string.
+        - alreadyFocused: False if object just received focus
+        - forceTutorial: used for when whereAmI really needs the tutorial string
 
         Returns a list of utterances to be spoken.
         """
 
         if not settings.enableTutorialMessages:
             return []
-        
+
         role = obj.getRole()
         if role in self.tutorialGenerators:
             generator = self.tutorialGenerators[role]
         else:
             generator = self._getDefaultTutorial
-        msg = generator(obj, already_focused, forceMessage)
+        msg = generator(obj, alreadyFocused, forceTutorial)
         utterances = [" ".join(msg)]
         if msg:
             self.lastTutorial = msg
-        if forceMessage:
+        if forceTutorial:
             self.lastTutorial = ""
 
         self._debugGenerator("getTutorial",
                              obj,
-                             already_focused,
+                             alreadyFocused,
                              utterances)
         return utterances
diff --git a/src/orca/where_am_I.py b/src/orca/where_am_I.py
index 835f6a2..28f5aae 100644
--- a/src/orca/where_am_I.py
+++ b/src/orca/where_am_I.py
@@ -27,15 +27,11 @@ __license__   = "LGPL"
 
 import pyatspi
 import debug
-import orca_state
-import settings
 import speech
-import text_attribute_names
-import urlparse, urllib2
 
-from orca_i18n import _ # for gettext support
-from orca_i18n import ngettext  # for ngettext support
-from orca_i18n import C_ # to provide qualified translatable strings
+# [[[TODO: WDW - need to handle the old _speakText functionality that changes
+# settings.verbalizePunctuationStyle = settings.PUNCTUATION_STYLE_SOME
+# if we're doing an extended where am I.]]]
 
 class WhereAmI:
 
@@ -46,8 +42,6 @@ class WhereAmI:
 
         self._script = script
         self._debugLevel = debug.LEVEL_FINEST
-        self._statusBar = None
-        self._defaultButton = None
         self._lastAttributeString = ""
 
     def whereAmI(self, obj, basicOnly):
@@ -65,1962 +59,56 @@ class WhereAmI:
         if (not obj):
             return False
 
-        debug.println(self._debugLevel,
-            "whereAmI: \
-           \n  label=%s \
-           \n  name=%s \
-           \n  role=%s \
-           \n  keybinding=%s \
-           \n  parent label= %s \
-           \n  parent name=%s \
-           \n  parent role=%s \
-           \n  basicOnly=%s" % \
-            (self._getObjLabel(obj),
-             self._getObjName(obj),
-             obj.getRoleName(),
-             self._script.getKeyBinding(obj),
-             self._getObjLabel(obj.parent),
-             self._getObjName(obj.parent),
-             obj.parent.getRoleName(),
-             basicOnly))
-
         role = obj.getRole()
-
-        toolbar = self._getToolbar(obj)
-        if toolbar:
-            self._speakToolbar(toolbar)
-
-        if role == pyatspi.ROLE_CHECK_BOX:
-            self._speakCheckBox(obj, basicOnly)
-
-        elif role == pyatspi.ROLE_RADIO_BUTTON:
-            self._speakRadioButton(obj, basicOnly)
-
-        elif role == pyatspi.ROLE_COMBO_BOX:
-            self._speakComboBox(obj, basicOnly)
-
-        elif role == pyatspi.ROLE_SPIN_BUTTON:
-            self._speakSpinButton(obj, basicOnly)
-
-        elif role == pyatspi.ROLE_PUSH_BUTTON:
-            self._speakPushButton(obj, basicOnly)
-
-        elif role == pyatspi.ROLE_SLIDER:
-            self._speakSlider(obj, basicOnly)
-
-        elif role in [pyatspi.ROLE_MENU,
-                      pyatspi.ROLE_MENU_ITEM,
-                      pyatspi.ROLE_CHECK_MENU_ITEM,
-                      pyatspi.ROLE_RADIO_MENU_ITEM]:
-            self._speakMenuItem(obj, basicOnly)
-
-        elif role == pyatspi.ROLE_PAGE_TAB:
-            self._speakPageTab(obj, basicOnly)
-
-        elif role in [pyatspi.ROLE_ENTRY,
-                      pyatspi.ROLE_TEXT,
-                      pyatspi.ROLE_TERMINAL]:
+        if role in [pyatspi.ROLE_ENTRY,
+                    pyatspi.ROLE_TEXT,
+                    pyatspi.ROLE_PASSWORD_TEXT,
+                    pyatspi.ROLE_TERMINAL,
+                    pyatspi.ROLE_PARAGRAPH,
+                    pyatspi.ROLE_SECTION,
+                    pyatspi.ROLE_HEADING,
+                    pyatspi.ROLE_DOCUMENT_FRAME]:
             self._speakText(obj, basicOnly)
-
-        elif role == pyatspi.ROLE_TABLE_CELL:
-            self._speakTableCell(obj, basicOnly)
-
-        elif role == pyatspi.ROLE_LIST_ITEM:
-            self._speakListItem(obj, basicOnly)
-
-        elif role in [pyatspi.ROLE_PARAGRAPH,
-                      pyatspi.ROLE_SECTION,
-                      pyatspi.ROLE_HEADING,
-                      pyatspi.ROLE_DOCUMENT_FRAME]:
-            self._speakParagraph(obj, basicOnly)
-
-        elif role == pyatspi.ROLE_ICON:
-            self._speakIconPanel(obj, basicOnly)
-
-        elif role == pyatspi.ROLE_LINK:
-            self._speakLink(obj, basicOnly)
-
-        elif role == pyatspi.ROLE_TOGGLE_BUTTON:
-            self._speakToggleButton(obj, basicOnly)
-
-        elif role == pyatspi.ROLE_SPLIT_PANE:
-            self._speakSplitPane(obj, basicOnly)
-
-        elif role == pyatspi.ROLE_LABEL:
-            self._speakLabel(obj, basicOnly)
-
-        elif role  == pyatspi.ROLE_LAYERED_PANE:
-            self._speakLayeredPane(obj, basicOnly)
-
         else:
-            self._speakGenericObject(obj, basicOnly)
-
-        if basicOnly:
-            self._speakObjDescription(obj)
-
-        self._lastAttributeString = ""
+            speech.speak(self.getWhereAmI(obj, basicOnly))
 
         return True
 
-    def _speakCheckBox(self, obj, basicOnly):
-        """Checkboxes present the following information
-        (an example is 'Enable speech, checkbox checked, Alt E'):
-        1. label
-        2. role
-        3. state
-        4. accelerator (i.e. Alt plus the underlined letter), if any
-        5. tutorial string if enableTutorialMessages is set.
-        """
-
-        utterances = []
-        text = self.getObjLabelAndName(obj) + " " + \
-               self._getSpeechForRoleName(obj)
-        text = text + " " + self._getCheckBoxState(obj)
-        utterances.append(text)
-
-        accelerator = self._getObjAccelerator(obj)
-        utterances.append(accelerator)
-
-        text = self._getRequiredState(obj)
-        if text:
-            utterances.append(text)
-
-        getTutorial = self._script.tutorialGenerator.getTutorial
-        utterances.extend(getTutorial(obj, False, forceMessage=True))
-
-        debug.println(self._debugLevel, "check box utterances=%s" \
-                      % utterances)
-        speech.speak(utterances)
-
-    def _speakRadioButton(self, obj, basicOnly):
-        """Radio Buttons present the following information (an example is
-        'Punctuation Level, Some, Radio button, selected, item 2 of 4, Alt M'):
-
-        1. group name
-        2. label
-        3. role
-        4. state
-        5. relative position
-        6. accelerator (i.e. Alt plus the underlined letter), if any
-        7. tutorial string if enableTutorialMessages is set.
-        """
-
-        utterances = []
-        text = self._getGroupLabel(obj)
-        utterances.append(text)
-
-        text = self._getRequiredState(obj)
-        if text:
-            utterances.append(text)
-
-        text = self.getObjLabelAndName(obj) + " " + \
-               self._getSpeechForRoleName(obj)
-        utterances.append(text)
-
-        state = obj.getState()
-        if state.contains(pyatspi.STATE_CHECKED):
-            # Translators: this is in reference to a radio button being
-            # selected or not.
-            #
-            text = C_("radiobutton", "selected")
-        else:
-            # Translators: this is in reference to a radio button being
-            # selected or not.
-            #
-            text = C_("radiobutton", "not selected")
-
-        utterances.append(text)
-
-        text = self._getPositionInGroup(obj)
-        utterances.append(text)
-
-        text = self._getObjAccelerator(obj)
-        utterances.append(text)
-
-        getTutorial = self._script.tutorialGenerator.getTutorial
-        utterances.extend(getTutorial(obj, False, forceMessage=True))
-
-        debug.println(self._debugLevel, "radio button utterances=%s" % \
-                      utterances)
-        speech.speak(utterances)
-
-    def _speakComboBox(self, obj, basicOnly):
-        """Comboboxes present the following information (an example is
-        'Speech system: combo box, GNOME Speech Services, item 1 of 1,
-        Alt S'):
-        1. label
-        2. role
-        3. current value
-        4. relative position
-        5. accelerator (i.e. Alt plus the underlined letter), if any
-        6. tutorial string if enableTutorialMessages is set.
-        """
-
-        utterances = []
-        text = self._getObjLabel(obj)
-        utterances.append(text)
-
-        text = self._getSpeechForRoleName(obj)
-        utterances.append(text)
-
-        name = self._getObjName(obj)
-        utterances.append(name)
-
-        # child(0) is the popup list
-        text = self._getPositionInList(obj[0], name)
-        utterances.append(text)
-
-        accelerator = self._getObjAccelerator(obj)
-        utterances.append(accelerator)
-
-        getTutorial = self._script.tutorialGenerator.getTutorial
-        utterances.extend(getTutorial(obj, False, forceMessage=True))
-
-        debug.println(self._debugLevel, "combo box utterances=%s" % \
-                      utterances)
-        speech.speak(utterances)
-
-    def _speakSpinButton(self, obj, basicOnly):
-        """Spin Buttons present the following information (an example is
-        'Scale factor: spin button, 4.00, Alt F'):
-
-        1. label
-        2. role
-        3. current value
-        4. selected (if True).
-        5. accelerator (i.e. Alt plus the underlined letter), if any
-        6. tutorial string if enableTutorialMessages is set.
-        """
-
-        utterances = []
-        label = self._getObjLabel(obj)
-        utterances.append(label)
-
-        text = self._getSpeechForRoleName(obj)
-        utterances.append(text)
-
-        name = self._getObjName(obj)
-        if name != label:
-            utterances.append(name)
-
-        utterances.extend(self._getSpeechForAllTextSelection(obj))
-
-        text = self._getObjAccelerator(obj)
-        utterances.append(text)
-
-        text = self._getRequiredState(obj)
-        if text:
-            utterances.append(text)
-
-        getTutorial = self._script.tutorialGenerator.getTutorial
-        utterances.extend(getTutorial(obj, False, forceMessage=True))
-
-        debug.println(self._debugLevel, "spin button utterances=%s" % \
-                      utterances)
-        speech.speak(utterances)
-
-    def _speakPushButton(self, obj, basicOnly):
-        """ Push Buttons present the following information (an example is
-        'Apply button, Alt A'):
-
-        1. label
-        2. role
-        3. accelerator (i.e. Alt plus the underlined letter), if any
-        4. tutorial string if enableTutorialMessages is set.
-        """
-
-        utterances = []
-        text = self.getObjLabelAndName(obj)
-        utterances.append(text)
-
-        text = self._getSpeechForRoleName(obj)
-        utterances.append(text)
-
-        text = self._getObjAccelerator(obj)
-        utterances.append(text)
-
-        getTutorial = self._script.tutorialGenerator.getTutorial
-        utterances.extend(getTutorial(obj, False, forceMessage=True))
-
-        debug.println(self._debugLevel, "push button utterances=%s" % \
-                      utterances)
-        speech.speak(utterances)
-
-    def _speakSlider(self, obj, basicOnly):
-        """Sliders present the following information (examples include
-        'Pitch slider, 5.0, 56%'; 'Volume slider, 9.0, 100%'):
-
-        1. label
-        2. role
-        3. value
-        4. percentage (if possible)
-        5. accelerator (i.e. Alt plus the underlined letter), if any
-        6. tutorial string if enableTutorialMessages is set.
-        """
-
-        utterances = []
-        text = self._getObjLabel(obj)
-        utterances.append(text)
-
-        text = self._getSpeechForRoleName(obj)
-        utterances.append(text)
-
-        values = self._getSliderValues(obj)
-        utterances.append(values[0])
-        # Translators: this is the percentage value of a slider.
-        #
-        utterances.append(_("%s percent") % values[1])
-
-        text = self._getObjAccelerator(obj)
-        utterances.append(text)
-
-        text = self._getRequiredState(obj)
-        if text:
-            utterances.append(text)
-
-        getTutorial = self._script.tutorialGenerator.getTutorial
-        utterances.extend(getTutorial(obj, False, forceMessage=True))
-
-        debug.println(self._debugLevel, "slider utterances=%s" % \
-                      utterances)
-        speech.speak(utterances)
-
-    def _speakMenuItem(self, obj, basicOnly):
-        """Menu items present the following information (examples include
-        'File menu, Open..., Control + O, item 2 of 20, O', 'File menu,
-        Wizards Menu, item 4 of 20, W'):
-
-        1. Name of the menu containing the item, followed by its role
-        2. item name, followed by its role (if a menu) followed by its
-        accelerator key binding, if any
-        3. relative position
-        4. mnemonic (i.e. the underlined letter), if any
-        5. tutorial string if enableTutorialMessages is set.
-        """
-
-        utterances = []
-        text = self.getObjLabelAndName(obj.parent) + " " \
-               + self._getSpeechForRoleName(obj.parent, force=True)
-        utterances.append(text.strip())
-
-        text = self.getObjLabelAndName(obj)
-        utterances.append(text)
-
-        state = obj.getState()
-
-        if obj.getRole() != pyatspi.ROLE_MENU_ITEM:
-            text = self._getSpeechForRoleName(obj)
-            utterances.append(text)
-
-        if obj.getRole() == pyatspi.ROLE_CHECK_MENU_ITEM:
-            if state.contains(pyatspi.STATE_INDETERMINATE):
-                # Translators: this represents the state of a checkbox.
-                #
-                text = _("partially checked")
-            elif state.contains(pyatspi.STATE_CHECKED):
-                # Translators: this represents the state of a checkbox.
-                #
-                text = _("checked")
-            else:
-                # Translators: this represents the state of a checkbox.
-                #
-                text = _("not checked")
-            utterances.append(text)
-
-        elif obj.getRole() == pyatspi.ROLE_RADIO_MENU_ITEM:
-            if state.contains(pyatspi.STATE_CHECKED):
-                # Translators: this is in reference to a radio button being
-                # selected or not.
-                #
-                text = _("selected")
-            else:
-                # Translators: this is in reference to a radio button being
-                # selected or not.
-                #
-                text = _("not selected")
-            utterances.append(text)
-
-        text = self._getObjAccelerator(obj, False, False)
-        utterances.append(text)
-
-        name = self._getObjName(obj)
-        text = self._getPositionInList(obj.parent, name)
-        utterances.append(text)
-
-        if obj.parent \
-           and obj.parent.getRole() in [pyatspi.ROLE_MENU,
-                                        pyatspi.ROLE_MENU_BAR]:
-            text = self._getObjMnemonic(obj)
-            utterances.append(text)
-
-        getTutorial = self._script.tutorialGenerator.getTutorial
-        utterances.extend(getTutorial(obj, False, forceMessage=True))
-
-        debug.println(self._debugLevel, "menu item utterances=%s" % \
-                      utterances)
-        speech.speak(utterances)
-
-    def _speakPageTab(self, obj, basicOnly):
-        """Tabs in a Tab List present the following information (an example
-        is 'Tab list, braille page, item 2 of 5'):
-
-        1. role
-        2. label + 'page'
-        3. relative position
-        4. accelerator (i.e. Alt plus the underlined letter), if any
-        5. tutorial string if enableTutorialMessages is set.
-        """
-
-        utterances = []
-        text = self._getSpeechForRoleName(obj.parent)
-        utterances.append(text)
-
-        # Translators: "page" is the word for a page tab in a tab list.
-        #
-        text = _("%s page") % self.getObjLabelAndName(obj)
-        utterances.append(text)
-
-        name = self._getObjName(obj)
-        text = self._getPositionInList(obj.parent, name)
-        utterances.append(text)
-
-        text = self._getObjAccelerator(obj)
-        utterances.append(text)
-
-        getTutorial = self._script.tutorialGenerator.getTutorial
-        utterances.extend(getTutorial(obj, False, forceMessage=True))
-
-        debug.println(self._debugLevel, "page utterances=%s" % \
-                      utterances)
-        speech.speak(utterances)
-
     def _speakText(self, obj, basicOnly):
-        """Text boxes present the following information (an example is
-        'Source display: text, blank, Alt O'):
-
-        1. label, if any
-        2. role
-        3. contents
-            A. if no text on the current line is selected, the current line
-            B. if text is selected on the current line, that text, followed
-            attibute information before  (bold "text")
-            by 'selected' (single press)
-            C. if the current line is blank/empty, 'blank'
-        4. accelerator (i.e. Alt plus the underlined letter), if any
-        5. tutorial string if enableTutorialMessages is set.
-
-        Gaim, gedit, OpenOffice Writer and Terminal
-        """
-
+        # [[[TODO: WDW - we handle ROLE_ENTRY specially here because
+        # there is a bug in getRealActiveDescendant: it doesn't dive
+        # deep enough into the hierarchy (see comment #12 of bug
+        # #542714).  So, we'll do this nasty hack until we can feel
+        # more comfortable with mucking around with
+        # getRealActiveDescendant.]]]
+        #
         ancestor = self._script.getAncestor(obj,
                                             [pyatspi.ROLE_TABLE_CELL,
                                              pyatspi.ROLE_LIST_ITEM],
                                             [pyatspi.ROLE_FRAME])
         if ancestor and not self._script.isLayoutOnly(ancestor.parent):
-            # [[[TODO: WDW - we handle ROLE_ENTRY specially here because
-            # there is a bug in getRealActiveDescendant: it doesn't dive
-            # deep enough into the hierarchy (see comment #12 of bug
-            # #542714).  So, we'll do this nasty hack until we can feel
-            # more comfortable with mucking around with
-            # getRealActiveDescendant.]]]
-            #
             if ancestor.getRole() == pyatspi.ROLE_TABLE_CELL:
                 if obj.getRole() != pyatspi.ROLE_ENTRY:
-                    return self._speakTableCell(ancestor, basicOnly)
-            else:
-                return self._speakListItem(ancestor, basicOnly)
-
-        utterances = []
-        text = self._getObjLabel(obj)
-        utterances.append(text)
-
-        if settings.presentReadOnlyText \
-           and self._script.isReadOnlyTextArea(obj):
-            utterances.append(settings.speechReadOnlyString)
-
-        text = self._getSpeechForRoleName(obj)
-        utterances.append(text)
-
-        [textContents, startOffset, endOffset, selected] = \
-                       self._getTextContents(obj, basicOnly)
-        if not basicOnly:
-            # Speak character attributes.
-            textContents = \
-                self._insertAttributes(obj, startOffset,
-                                       endOffset, textContents)
-            savedStyle = settings.verbalizePunctuationStyle
-            settings.verbalizePunctuationStyle = settings.PUNCTUATION_STYLE_SOME
-
-        text = textContents
-        utterances.append(text)
-        debug.println(self._debugLevel, "first text utterances=%s" % \
-                      utterances)
-
-        speech.speak(utterances)
-
-        if not basicOnly:
-            settings.verbalizePunctuationStyle = savedStyle
-
-        utterances = []
-        if selected:
-            # Translators: when the user selects (highlights) text in
-            # a document, Orca lets them know this.
-            #
-            text = C_("text", "selected")
-            utterances.append(text)
-
-        text = self._getObjAccelerator(obj)
-        utterances.append(text)
-
-        getTutorial = self._script.tutorialGenerator.getTutorial
-        utterances.extend(getTutorial(obj, False, forceMessage=True))
-
-        debug.println(self._debugLevel, "text utterances=%s" % \
-                      utterances)
-        speech.speak(utterances)
-
-    def _speakTableCell(self, obj, basicOnly):
-        """Tree Tables present the following information (an example is
-        'Tree table, Mike Pedersen, item 8 of 10, tree level 2'):
-
-        1. parent's role
-        2. column header of object
-        3. row header of object
-        4. object's role
-        5. object's contents, if there are multiple columns
-        6. relative position
-        7. current row (regardless of speak cell/row setting), if
-           performing a detailed whereAmI.
-        8. if expandable/collapsible: expanded/collapsed
-        9. if applicable, the level
-        10. tutorial string if enableTutorialMessages is set.
-
-        Nautilus and Gaim
-        """
-
-        utterances = []
-        if obj.parent.getRole() == pyatspi.ROLE_TABLE_CELL:
-            obj = obj.parent
-        parent = obj.parent
-
-        text = self._getSpeechForRoleName(parent)
-        utterances.append(text)
-
-        try:
-            table = parent.queryTable()
-        except:
-            table = None
-            nColumns = 0
-        else:
-            nColumns = table.nColumns
-            index = self._script.getCellIndex(obj)
-            column = table.getColumnAtIndex(index)
-            header = table.getColumnHeader(column)
-            if header:
-                text = self._getObjName(header)
-                utterances.append(text)
-
-            row = table.getRowAtIndex(index)
-            header = table.getRowHeader(row)
-            if header:
-                text = self._getObjName(header)
-                utterances.append(text)
-
-        text = self._getSpeechForRoleName(obj)
-        utterances.append(text)
-
-        if nColumns:
-            text = self._getTableCell(obj)
-            utterances.append(text)
-
-        text = self._getRequiredState(obj)
-        if text:
-            utterances.append(text)
-
-        debug.println(self._debugLevel, "first table cell utterances=%s" % \
-                      utterances)
-        speech.speak(utterances)
-
-        utterances = []
-        if table:
-            # Translators: this is in references to a column in a
-            # table.
-            text = _("column %d of %d") % ((column + 1), table.nColumns)
-            utterances.append(text)
-
-            # Translators: this is in reference to a row in a table.
-            #
-            text = _("row %d of %d") % ((row + 1), table.nRows)
-            utterances.append(text)
-            speech.speak(utterances)
-
-            # Speak the current row if performing a "detailed" whereAmI.
-            #
-            if not basicOnly:
-                utterances = self._getTableRow(obj)
-                debug.println(self._debugLevel, \
-                              "second table cell utterances=%s" % \
-                              utterances)
-                speech.speak(utterances)
-
-        utterances = []
-        state = obj.getState()
-        if state.contains(pyatspi.STATE_EXPANDABLE):
-            if state.contains(pyatspi.STATE_EXPANDED):
-                # Translators: this represents the state of a node in a tree.
-                # 'expanded' means the children are showing.
-                # 'collapsed' means the children are not showing.
-                #
-                text = _("expanded")
-            else:
-                # Translators: this represents the state of a node in a tree.
-                # 'expanded' means the children are showing.
-                # 'collapsed' means the children are not showing.
-                #
-                text = _("collapsed")
-            utterances.append(text)
-
-        level = self._script.getNodeLevel(orca_state.locusOfFocus)
-        if level >= 0:
-            # Translators: this represents the depth of a node in a tree
-            # view (i.e., how many ancestors a node has).
-            #
-            utterances.append(_("tree level %d") % (level + 1))
-
-        getTutorial = self._script.tutorialGenerator.getTutorial
-        utterances.extend(getTutorial(obj, False, forceMessage=True))
-
-        debug.println(self._debugLevel, "third table cell utterances=%s" % \
-                      utterances)
-        speech.speak(utterances)
-
-    def _speakListItem(self, obj, basicOnly):
-        """List items should be treated like tree cells:
-
-        1. label, if any
-        2. role
-        3. name
-        4. relative position
-        5. if expandable/collapsible: expanded/collapsed
-        6. if applicable, the level
-        7. tutorial string if enableTutorialMessages is set.
-        """
-
-        utterances = []
-
-        text = self._getObjLabel(obj)
-        if text:
-            utterances.append(text)
-
-        text = self._getSpeechForRoleName(obj, force=True)
-        utterances.append(text)
-
-        text = self._getObjName(obj)
-        utterances.append(text)
-
-        parent = obj.parent
-        relationset = obj.getRelationSet()
-        for relation in relationset:
-            if relation.getRelationType() == pyatspi.RELATION_NODE_CHILD_OF:
-                parent = relation.getTarget(0)
-                break
-
-        name = self._getObjName(obj)
-        text = self._getPositionInList(parent, name)
-        utterances.append(text)
-
-        state = obj.getState()
-        if state.contains(pyatspi.STATE_EXPANDABLE):
-            if state.contains(pyatspi.STATE_EXPANDED):
-                # Translators: this represents the state of a node in a tree.
-                # 'expanded' means the children are showing.
-                # 'collapsed' means the children are not showing.
-                #
-                text = _("expanded")
-            else:
-                # Translators: this represents the state of a node in a tree.
-                # 'expanded' means the children are showing.
-                # 'collapsed' means the children are not showing.
-                #
-                text = _("collapsed")
-            utterances.append(text)
-
-        level = self._script.getNodeLevel(orca_state.locusOfFocus)
-        if level >= 0:
-            # Translators: this represents the depth of a node in a tree
-            # view (i.e., how many ancestors a node has).
-            #
-            utterances.append(_("tree level %d") % (level + 1))
-        else:
-            nestingLevel = 0
-            parent = obj.parent
-            while parent.parent.getRole() == pyatspi.ROLE_LIST:
-                nestingLevel += 1
-                parent = parent.parent
-            if nestingLevel:
-                # Translators: this represents a list item in a document.
-                # The nesting level is how 'deep' the item is (e.g., a
-                # level of 2 represents a list item inside a list that's
-                # inside another list).
-                #
-                utterances.append(_("Nesting level %d") % nestingLevel)
-
-        getTutorial = self._script.tutorialGenerator.getTutorial
-        utterances.extend(getTutorial(obj, False, forceMessage=True))
-
-        debug.println(self._debugLevel, "list item utterances=%s" % \
-                      utterances)
-        speech.speak(utterances)
-
-    def _speakParagraph(self, obj, basicOnly):
-        """Speak a paragraph object.
-        """
-
-        self._speakText(obj, basicOnly)
-
-    def _speakIconPanel(self, obj, basicOnly):
-        """Speak the contents of the pane containing this icon. The
-        1. Number of icons in the pane is spoken.
-        2. The total number of selected icons.
-        3. The name of each of the selected icons.
-        4. tutorial string if enableTutorialMessages is set.
-
-        Arguments:
-        - obj: the icon object that currently has focus.
-        """
-
-        utterances = []
-        panel = obj.parent
-        childCount = panel.childCount
-
-        utterances.append(_("Icon panel"))
-        utterances.append(self.getObjLabelAndName(obj))
-        utterances.extend(self._getSelectedItemCount(panel, basicOnly))
-
-        # get our tutorial.
-        getTutorial = self._script.tutorialGenerator.getTutorial
-        utterances.extend(getTutorial(obj, False, forceMessage=True))
-        # get the frames tutorial.
-        [frame, dialogue] = self._getFrameAndDialog(obj)
-        utterances.extend(getTutorial(frame, False, forceMessage=True))
-
-        speech.speak(utterances)
-
-    def _speakLink(self, obj, basicOnly):
-        """Speaks information about a link including protocol, domain
-        comparisons and size of file if possible.
-        Also tutorial string if enableTutorialMessages is set.
-
-        Arguments:
-        - obj: the icon object that currently has focus.
-        - basicOnly: True if the user is performing a standard/basic whereAmI.
-        """
-
-        # get the URI for the link of interest and parse it.
-        # parsed URI is returned as a tuple containing six components:
-        # scheme://netloc/path;parameters?query#fragment.
-        link_uri = self._script.getURI(obj)
-        if link_uri:
-            link_uri_info = urlparse.urlparse(link_uri)
-        else:
-            # It might be an anchor.  Try to speak the text.
-            #
-            return self._speakText(obj, basicOnly)
-
-        # Try to get the URI of the active document and parse it
-        doc_uri = self._script.getDocumentFrameURI()
-        if doc_uri:
-            doc_uri_info = urlparse.urlparse(doc_uri)
-        else:
-            doc_uri_info = None
-
-        # initialize our three outputs.  Output may change below for some
-        # protocols.
-        # Translators: this is the protocol of a link eg. http, mailto.
-        #
-        linkoutput = _('%s link') %link_uri_info[0]
-        text = self._script.getDisplayedText(obj)
-        if text:
-            linkoutput += " " + text
-        else:
-            # If there's no text for the link, expose part of the
-            # URI to the user.
-            #
-            basename = self._script.getLinkBasename(obj)
-            if basename:
-                linkoutput += " " + basename
-
-        # 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:
-            linkoutput += " " + self._getSpeechForRoleName(obj[0])
-
-        domainoutput = ''
-        sizeoutput = ''
-
-        # get size and other protocol specific information
-        if link_uri_info[0] == 'ftp' or \
-           link_uri_info[0] == 'ftps' or \
-           link_uri_info[0] == 'file':
-            # change link output message to include filename
-            filename = link_uri_info[2].split('/')
-            linkoutput = _('%s link to %s') %(link_uri_info[0], filename[-1])
-            sizestr = self.__extractSize(link_uri)
-            sizeoutput = self.__formatSizeOutput(sizestr)
-
-        # determine location differences if doc uri info is available
-        if doc_uri_info:
-            if link_uri_info[1] == doc_uri_info[1]:
-                if link_uri_info[2] == doc_uri_info[2]:
-                    # Translators: this is the domain relationship of a given
-                    # link to the current page.  eg. same page, same site.
-                    domainoutput = _('same page')
-                else:
-                    domainoutput = _('same site')
-            else:
-                # check for different machine name on same site
-                linkdomain = link_uri_info[1].split('.')
-                docdomain = doc_uri_info[1].split('.')
-                if len(linkdomain) > 1 and docdomain > 1  \
-                    and linkdomain[-1] == docdomain[-1]  \
-                    and linkdomain[-2] == docdomain[-2]:
-                    domainoutput = _('same site')
-                else:
-                    domainoutput = _('different site')
-
-        utterances = [linkoutput, domainoutput, sizeoutput]
-        getTutorial = self._script.tutorialGenerator.getTutorial
-        utterances.extend(getTutorial(obj, False, forceMessage=True))
-
-        speech.speak(utterances)
-
-    def _speakToggleButton(self, obj, basicOnly):
-        """Speak toggle button information:
-           1. Name/Label
-           2. Role
-           3. State (pressed/not pressed)
-           4. tutorial string if enableTutorialMessages is set.
-        """
-
-        utterances = []
-        text = self.getObjLabelAndName(obj)
-        utterances.append(text)
-
-        text = self._getSpeechForRoleName(obj)
-        utterances.append(text)
-
-        if obj.getState().contains(pyatspi.STATE_CHECKED):
-            # Translators: the state of a toggle button.
-            #
-            checkedState = _("pressed")
-        else:
-            # Translators: the state of a toggle button.
-            #
-            checkedState = _("not pressed")
-        utterances.append(checkedState)
-
-        getTutorial = self._script.tutorialGenerator.getTutorial
-        utterances.extend(getTutorial(obj, False, forceMessage=True))
-
-        speech.speak(utterances)
-
-    def _speakSplitPane(self, obj, basicOnly):
-        """Speak split pane information:
-           1. Name/Label
-           2. Role
-           3. Value
-           4. tutorial string if enableTutorialMessages is set.
-        """
-
-        utterances = []
-        text = self.getObjLabelAndName(obj)
-        utterances.append(text)
-
-        text = self._getSpeechForRoleName(obj)
-        utterances.append(text)
-
-        valueString = self._script.getTextForValue(obj)
-        utterances.append(valueString)
-
-        getTutorial = self._script.tutorialGenerator.getTutorial
-        utterances.extend(getTutorial(obj, False, forceMessage=True))
-
-        speech.speak(utterances)
-
-    def _speakLabel(self, obj, basicOnly):
-        """Speak label information:
-           1. Name/Label
-           2. selected (if True).
-           3. Role
-           4. tutorial string if enableTutorialMessages is set.
-        """
-
-        utterances = []
-        text = self.getObjLabelAndName(obj)
-        utterances.append(text)
-
-        utterances.extend(self._getSpeechForAllTextSelection(obj))
-
-        text = self._getSpeechForRoleName(obj)
-        utterances.append(text)
-
-        getTutorial = self._script.tutorialGenerator.getTutorial
-        utterances.extend(getTutorial(obj, False, forceMessage=True))
-
-        speech.speak(utterances)
-
-
-    def _speakLayeredPane(self, obj, basicOnly):
-        """Speak layered pane information:
-           1. Name/Label
-           2. Role
-           3. Number of selected items and total number of items.
-           4. tutorial string if enableTutorialMessages is set.
-        """
-
-        utterances = []
-        text = self.getObjLabelAndName(obj)
-        utterances.append(text)
-
-        text = self._getSpeechForRoleName(obj)
-        utterances.append(text)
-        utterances.extend(self._getSelectedItemCount(obj, basicOnly))
-
-        getTutorial = self._script.tutorialGenerator.getTutorial
-        utterances.extend(getTutorial(obj, False, forceMessage=True))
-
-        speech.speak(utterances)
-
-
-    def _getSpeechForAllTextSelection(self, obj):
-        """Check if this object has text associated with it and it's
-        completely selected.
-
-        Arguments:
-        - obj: the object being presented
-        """
-
-        utterance = []
-        try:
-            textObj = obj.queryText()
-        except:
-            pass
-        else:
-            noOfSelections = textObj.getNSelections()
-            if noOfSelections == 1:
-                [string, startOffset, endOffset] = \
-                   textObj.getTextAtOffset(0, pyatspi.TEXT_BOUNDARY_LINE_START)
-                if startOffset == 0 and endOffset == len(string):
-                    # Translators: when the user selects (highlights) text in
-                    # a document, Orca lets them know this.
-                    #
-                    utterance = [C_("text", "selected")]
-
-        return utterance
-
-    def _getSelectedItemCount(self, obj, basicOnly):
-        """Return an utterance indicating how many items are selected in this
-        object, the current item, and (if a detailed whereAmI), the names of
-        all the selected items. This object will be an icon panel or a
-        layered pane.
-
-        Arguments:
-        - obj: the object being presented
-        """
-
-        childCount = obj.childCount
-        selectedItems = []
-        totalSelectedItems = 0
-        currentItem = 0
-        for child in obj:
-            state = child.getState()
-            if state.contains(pyatspi.STATE_SELECTED):
-                totalSelectedItems += 1
-                selectedItems.append(child)
-            if state.contains(pyatspi.STATE_FOCUSED):
-                currentItem = child.getIndexInParent() + 1
-
-        utterances = []
-        # Translators: this is a count of the number of selected icons
-        # and the count of the total number of icons within an icon panel.
-        # An example of an icon panel is the Nautilus folder view.
-        #
-        countString = ngettext("%d of %d item selected",
-                              "%d of %d items selected",
-                              childCount) % \
-                              (totalSelectedItems, childCount)
-        utterances.append(countString)
-
-        # Translators: this is a indication of the focused icon and the
-        # count of the total number of icons within an icon panel. An
-        # example of an icon panel is the Nautilus folder view.
-        #
-        itemString = _("on item %d of %d") % (currentItem, childCount)
-        utterances.append(itemString)
-
-        if not basicOnly:
-            for i in range(0, len(selectedItems)):
-                utterances.append(self.getObjLabelAndName(selectedItems[i]))
-
-        return utterances
-
-    def __extractSize(self, uri):
-        """Get the http header for a given uri and try to extract the size
-        (Content-length).
-        """
-        try:
-            x = urllib2.urlopen(uri)
-            try:
-                return x.info()['Content-length']
-            except KeyError:
-                return None
-        except (ValueError, urllib2.URLError, OSError):
-            return None
-
-    def __formatSizeOutput(self, sizestr):
-        """Format the size output announcement.  Changes wording based on
-        size.
-        """
-        # sanity check
-        if sizestr is None or sizestr == '':
-            return ''
-        size = int(sizestr)
-        if size < 10000:
-            # Translators: This is the size of a file in bytes
-            #
-            return ngettext('%d byte', '%d bytes', size) % size
-        elif size < 1000000:
-            # Translators: This is the size of a file in kilobytes
-            #
-            return _('%.2f kilobytes') % (float(size) * .001)
-        elif size >= 1000000:
-            # Translators: This is the size of a file in megabytes
-            #
-            return _('%.2f megabytes') % (float(size) * .000001)
-
-    def _speakGenericObject(self, obj, basicOnly):
-        """Speak a generic object; one not specifically handled by
-        other methods.
-        """
-
-        utterances = []
-        text = self.getObjLabelAndName(obj)
-        utterances.append(text)
-
-        text = self._getSpeechForRoleName(obj)
-        utterances.append(text)
-
-        getTutorial = self._script.tutorialGenerator.getTutorial
-        utterances.extend(getTutorial(obj, False, forceMessage=True))
-
-        speech.speak(utterances)
-
-
-    def _getObjName(self, obj):
-        """Returns the name to speak for an object.
-        """
-
-        text = ""
-        name = self._script.getDisplayedText(obj)
-        if not name:
-            name = obj.description
-
-        if name and name != "None":
-            text = name.strip()
-        debug.println(self._debugLevel, "%s name=<%s>" % (obj.getRole(), text))
-
-        return text
-
-    def _getObjLabel(self, obj):
-        """Returns the label to speak for an object.
-        """
-
-        text = ""
-        label = self._script.getDisplayedLabel(obj)
-
-        if label and label != "None":
-            text = label.strip()
-
-        debug.println(self._debugLevel, "%s label=<%s>" % (obj.getRole(), text))
-
-        return text
-
-    def getObjLabelAndName(self, obj):
-        """Returns the object label plus the object name.
-        """
-
-        name = self._getObjName(obj)
-        label = self._getObjLabel(obj)
-
-        if name != label:
-            text = label + " " + name
-        else:
-            text = label
-
-        try:
-            textObj = obj.queryText()
-        except NotImplementedError:
-            pass
-        else:
-            [string, startOffset, endOffset] = textObj.getTextAtOffset(0,
-                pyatspi.TEXT_BOUNDARY_LINE_START)
-
-            debug.println(self._debugLevel, "%s text=<%s>" % \
-                            (obj.getRoleName(), string))
-
-        return text.strip()
-
-    # pylint: disable-msg=W0142
-
-    def _getSpeechForRoleName(self, obj, **args):
-        """Returns the rolename to be spoken for the object.
-        """
-
-        try:
-            result = self._script.speechGenerator.getRoleName(obj, **args)
-            if result:
-                result = result[0]
-            else:
-                result = ""
-        except:
-            debug.printException(debug.LEVEL_WARNING)
-            result = ""
-
-        return result
-
-    def _getGroupLabel(self, obj):
-        """Returns the label for a group of components.
-        """
-
-        text = ""
-        labelledBy = None
-
-        relations = obj.getRelationSet()
-        for relation in relations:
-            if relation.getRelationType() ==  pyatspi.RELATION_LABELLED_BY:
-                labelledBy = relation.getTarget(0)
-                break
-
-        if labelledBy:
-            text = self.getObjLabelAndName(labelledBy)
-        else:
-            parent = obj.parent
-            while parent and (parent.parent != parent):
-                if parent.getRole() in [pyatspi.ROLE_PANEL,
-                                        pyatspi.ROLE_FILLER]:
-                    label = self.getObjLabelAndName(parent)
-                    if label and label != "":
-                        text = label
-                        break
-                parent = parent.parent
-
-        return text
-
-    def _getPositionInGroup(self, obj):
-        """Returns the relative position of an object in a group.
-        """
-
-        text = ""
-        position = -1
-        total = -1
-
-        relations = obj.getRelationSet()
-        for relation in relations:
-            if relation.getRelationType() == pyatspi.RELATION_MEMBER_OF:
-                total = relation.getNTargets()
-                for i in range(0, total):
-                    target = relation.getTarget(i)
-                    if target == obj:
-                        position = total - i
-                        break
-
-        if position >= 0:
-            # Translators: this is an item in a list.
-            #
-            text += _("item %d of %d") % (position, total)
-
-        return text
-
-    def _getPositionInList(self, obj, name):
-        """Returns the relative position of an object in a list.
-        """
-
-        text = ""
-        position = -1
-        index = 0
-        total = 0
-
-        # We want to return the position relative to this hierarchical
-        # level and not the entire list.  If the object in question
-        # uses the NODE_CHILD_OF relationship, we need to use it instead
-        # of the childCount.
-        #
-        childNodes = self._script.getChildNodes(obj)
-        total = len(childNodes)
-        for i in range(0, total):
-            childName = self._getObjName(childNodes[i])
-            if childName == name:
-                position = i+1
-                break
-
-        if not total:
-            for child in obj:
-                next = self._getObjName(child)
-                state = child.getState()
-                if next in ["", "Empty", "separator"] \
-                   or not state.contains(pyatspi.STATE_VISIBLE):
-                    continue
-
-                index += 1
-                total += 1
-
-                if next == name:
-                    position = index
-
-
-        if position >= 0:
-            # Translators: this is an item in a list.
-            #
-            text = _("item %d of %d") % (position, total)
-
-        return text
-
-    def _getObjAccelerator(self,
-                           obj,
-                           fallbackToMnemonic=True,
-                           fallbackToFullPath=True):
-        """Returns the accelerator for the object, if it exists.
-        """
-
-        # We'll try the real accelerator first, but fallback to the
-        # mnemonic if there is no accelerator.  This is done because
-        # some implementations, such as the Java platform, will
-        # only give us the mnemonic sometimes.
-        #
-        results = self._script.getKeyBinding(obj)
-        if results[2]:
-            return results[2]
-
-        mnemonic = results[0]
-        if mnemonic and fallbackToMnemonic:
-            return mnemonic
-
-        if fallbackToFullPath:
-            return results[1]
-
-        return ""
-
-    def _getObjMnemonic(self, obj):
-        """Returns the mnemonic (a letter) for the object, if it exists.
-        """
-        results = self._script.getKeyBinding(obj)
-        return results[0][-1:]
-
-    def _getSliderValues(self, obj):
-        """Returns the slider's current value and percentage.
-        """
-
-        value = obj.queryValue()
-
-        currentValue = "%.1f" % value.currentValue
-        percent = value.currentValue / value.maximumValue * 100
-        rounded = "%d" % round(percent, 5)
-
-        debug.println(self._debugLevel,
-            "_getSliderValues: min=%f, cur=%f, max=%f, str=%s, percent=%s" % \
-            (value.minimumValue, value.currentValue, value.maximumValue, \
-             currentValue, rounded))
-
-        return [currentValue, rounded]
-
-    def _getTableRow(self, obj):
-        """Get the speech for a table cell row or a single table cell
-        if settings.readTableCellRow is False.
-        Also return a tutorial string if enableTutorialMessages is set.
-
-        Arguments:
-        - obj: the table
-        - already_focused: False if object just received focus
-
-        Returns a list of utterances to be spoken for the object.
-        """
-
-        utterances = []
-
-        parent = obj.parent
-        try:
-            table = parent.queryTable()
-        except NotImplementedError:
-            debug.println(self._debugLevel, "??? parent=%s" % parent.getRole())
-            return []
-        else:
-            index = self._script.getCellIndex(obj)
-            row = table.getRowAtIndex(index)
-            for i in range(0, table.nColumns):
-                acc = table.getAccessibleAt(row, i)
-                utterances.append(self._getTableCell(acc))
-
-            debug.println(self._debugLevel, "row=<%s>" % utterances)
-
-            getTutorial = self._script.tutorialGenerator.getTutorial
-            utterances.extend(getTutorial(obj, False, forceMessage=True))
-            return utterances
-
-    def _getTableCell(self, obj):
-        """Get the speech utterances for a single table cell.
-        """
-
-        isToggle = False
-        try:
-            action = obj.queryAction()
-        except NotImplementedError:
-            pass
-        else:
-            for i in range(0, action.nActions):
-                # Translators: this is the action name for
-                # the 'toggle' action. It must be the same
-                # string used in the *.po file for gail.
-                #
-                if action.getName(i) in ["toggle", _("toggle")]:
-                    isToggle = True
-                    break
-
-        if isToggle:
-            text = self._getSpeechForRoleName(obj, role=pyatspi.ROLE_CHECK_BOX)
-            text = text + " " + self._getCheckBoxState(obj)
-        else:
-            descendant = self._script.getRealActiveDescendant(obj)
-            text = self._script.getDisplayedText(descendant)
-
-        debug.println(self._debugLevel, "cell=<%s>" % text)
-        return text
-
-    def _getCheckBoxState(self, obj):
-        """Get the state of a checkbox/toggle-able table cell.
-        """
-
-        isToggle = (obj.getRole() == pyatspi.ROLE_CHECK_BOX)
-        if not isToggle:
-            try:
-                action = obj.queryAction()
-            except NotImplementedError:
-                pass
-            else:
-                for i in range(0, action.nActions):
-                    # Translators: this is the action name for
-                    # the 'toggle' action. It must be the same
-                    # string used in the *.po file for gail.
-                    #
-                    if action.getName(i) in ["toggle", _("toggle")]:
-                        isToggle = True
-                        break
-        if not isToggle:
-            return ""
-
-        state = obj.getState()
-        if state.contains(pyatspi.STATE_INDETERMINATE):
-            # Translators: this represents the state of a checkbox.
-            #
-            text = _("partially checked")
-        elif state.contains(pyatspi.STATE_CHECKED):
-            # Translators: this represents the state of a checkbox.
-            #
-            text = _("checked")
-        else:
-            # Translators: this represents the state of a checkbox.
-            #
-            text = _("not checked")
-
-        return text
-
-    def getTextSelection(self, obj):
-        """Get the text selection for the given object.
-
-        Arguments:
-        - obj: the text object to extract the selected text from.
-
-        Returns: the selected text contents plus the start and end
-        offsets within the text.
-        """
-
-        textContents = ""
-        textObj = obj.queryText()
-        nSelections = textObj.getNSelections()
-        for i in range(0, nSelections):
-            [startOffset, endOffset] = textObj.getSelection(i)
-
-            debug.println(self._debugLevel,
-                "getTextSelection: selection start=%d, end=%d" % \
-                (startOffset, endOffset))
-
-            selectedText = textObj.getText(startOffset, endOffset)
-            debug.println(self._debugLevel,
-                "getTextSelection: selected text=<%s>" % selectedText)
-
-            if i > 0:
-                textContents += " "
-            textContents += selectedText
-
-        return [textContents, startOffset, endOffset]
-
-    def getTextSelections(self, obj, basicOnly):
-        """Get all the text applicable text selections for the given object.
-        If the user is doing a detailed whereAmI, look to see if there are
-        any previous or next text objects that also have selected text and
-        add in their text contents.
-
-        Arguments:
-        - obj: the text object to start extracting the selected text from.
-        - basicOnly: True if the user is performing a standard/basic whereAmI.
-
-        Returns: all the selected text contents plus the start and end
-        offsets within the text for the given object.
-        """
-
-        textContents = ""
-        startOffset = 0
-        endOffset = 0
-        text = obj.queryText()
-        if text.getNSelections() > 0:
-            [textContents, startOffset, endOffset] = \
-                                            self.getTextSelection(obj)
-
-        if not basicOnly:
-            current = obj
-            morePossibleSelections = True
-            while morePossibleSelections:
-                morePossibleSelections = False
-                for relation in current.getRelationSet():
-                    if relation.getRelationType() == \
-                           pyatspi.RELATION_FLOWS_FROM:
-                        prevObj = relation.getTarget(0)
-                        prevObjText = prevObj.queryText()
-                        if prevObjText.getNSelections() > 0:
-                            [newTextContents, start, end] = \
-                                         self.getTextSelection(prevObj)
-                            textContents = newTextContents + " " + textContents
-                            current = prevObj
-                            morePossibleSelections = True
-                        else:
-                            displayedText = prevObjText.getText(0, -1)
-                            if len(displayedText) == 0:
-                                current = prevObj
-                                morePossibleSelections = True
-                        break
-
-            current = obj
-            morePossibleSelections = True
-            while morePossibleSelections:
-                morePossibleSelections = False
-                for relation in current.getRelationSet():
-                    if relation.getRelationType() == \
-                           pyatspi.RELATION_FLOWS_TO:
-                        nextObj = relation.getTarget(0)
-                        nextObjText = nextObj.queryText()
-                        if nextObjText.getNSelections() > 0:
-                            [newTextContents, start, end] = \
-                                         self.getTextSelection(nextObj)
-                            textContents += " " + newTextContents
-                            current = nextObj
-                            morePossibleSelections = True
-                        else:
-                            displayedText = nextObjText.getText(0, -1)
-                            if len(displayedText) == 0:
-                                current = nextObj
-                                morePossibleSelections = True
-                        break
-
-        else:
-            # We're only interested in the text selected on this line.
-            #
-            [line, lineStart, lineEnd] = \
-                   text.getTextAtOffset(text.caretOffset,
-                                        pyatspi.TEXT_BOUNDARY_LINE_START)
-            if lineStart != endOffset:
-                startOffset = max(startOffset, lineStart)
-                endOffset = min(endOffset, lineEnd)
-                textContents = line[startOffset - lineStart:
-                                    endOffset - lineStart]
-
-        return [textContents, startOffset, endOffset]
-
-    def _hasTextSelections(self, obj):
-        """Return an indication of whether this object has selected text.
-        Note that it's possible that this object has no text, but is part
-        of a selected text area. Because of this, we need to check the
-        objects on either side to see if they are none zero length and
-        have text selections.
-
-        Arguments:
-        - obj: the text object to start checking for selected text.
-
-        Returns: an indication of whether this object has selected text,
-        or adjacent text objects have selected text.
-        """
-
-        currentSelected = False
-        otherSelected = False
-        text = obj.queryText()
-        nSelections = text.getNSelections()
-        if nSelections:
-            currentSelected = True
-        else:
-            otherSelected = False
-            text = obj.queryText()
-            displayedText = text.getText(0, -1)
-            if len(displayedText) == 0:
-                current = obj
-                morePossibleSelections = True
-                while morePossibleSelections:
-                    morePossibleSelections = False
-                    for relation in current.getRelationSet():
-                        if relation.getRelationType() == \
-                               pyatspi.RELATION_FLOWS_FROM:
-                            prevObj = relation.getTarget(0)
-                            prevObjText = prevObj.queryText()
-                            if prevObjText.getNSelections() > 0:
-                                otherSelected = True
-                            else:
-                                displayedText = prevObjText.getText(0, -1)
-                                if len(displayedText) == 0:
-                                    current = prevObj
-                                    morePossibleSelections = True
-                            break
-
-                current = obj
-                morePossibleSelections = True
-                while morePossibleSelections:
-                    morePossibleSelections = False
-                    for relation in current.getRelationSet():
-                        if relation.getRelationType() == \
-                               pyatspi.RELATION_FLOWS_TO:
-                            nextObj = relation.getTarget(0)
-                            nextObjText = nextObj.queryText()
-                            if nextObjText.getNSelections() > 0:
-                                otherSelected = True
-                            else:
-                                displayedText = nextObjText.getText(0, -1)
-                                if len(displayedText) == 0:
-                                    current = nextObj
-                                    morePossibleSelections = True
-                            break
-
-        return [currentSelected, otherSelected]
-
-    def _getTextContents(self, obj, basicOnly):
-        """Returns utterences for text.
-
-        A. if no text on the current line is selected, the current line
-        B. if text is selected on the current line, that text, followed
-        by 'selected'
-        C. if the current line is blank/empty, 'blank'
-        """
-
-        textObj = obj.queryText()
-        caretOffset = textObj.caretOffset
-        textContents = ""
-        selected = False
-
-        nSelections = textObj.getNSelections()
-        debug.println(self._debugLevel,
-            "_getTextContents: caretOffset=%d, nSelections=%d" % \
-            (caretOffset, nSelections))
-
-        [current, other] = self._hasTextSelections(obj)
-        if (not basicOnly and (current or other)) or \
-           (basicOnly and current):
-            selected = True
-            [textContents, startOffset, endOffset] = \
-                                  self.getTextSelections(obj, basicOnly)
-        else:
-            # Get the line containing the caret
-            #
-            [line, startOffset, endOffset] = textObj.getTextAtOffset(
-                textObj.caretOffset,
-                pyatspi.TEXT_BOUNDARY_LINE_START)
-            debug.println(self._debugLevel, \
-                "_getTextContents: len=%d, start=%d, end=%d, line=<%s>" % \
-                (len(line), startOffset, endOffset, line))
-
-            if len(line):
-                line = self._script.adjustForRepeats(line)
-                textContents = line
+                    speech.speak(self.getWhereAmI(ancestor, basicOnly))
+                    return
             else:
-                char = textObj.getTextAtOffset(caretOffset,
-                    pyatspi.TEXT_BOUNDARY_CHAR)
-                debug.println(self._debugLevel,
-                    "_getTextContents: character=<%s>, start=%d, end=%d" % \
-                    (char[0], char[1], char[2]))
-
-                if char[0] == "\n" and startOffset == caretOffset \
-                       and settings.speakBlankLines:
-                    # Translators: "blank" is a short word to mean the
-                    # user has navigated to an empty line.
-                    #
-                    textContents = (_("blank"))
-
-        return [textContents, startOffset, endOffset, selected]
-
-    def _insertAttributes(self, obj, startOffset, endOffset, line):
-        """Adjust line to include attribute information.
-        """
-
-        try:
-            text = obj.queryText()
-        except NotImplementedError:
-            return ""
-
-        newLine = ""
-        textOffset = startOffset
-
-        for i in range(0, len(line)):
-            attribs = self._getAttributesForChar(obj, text, textOffset, line, i)
-            debug.println(self._debugLevel,
-                          "line attribs <%s>" % (attribs))
-            if attribs:
-                if newLine:
-                    newLine += " ; "
-                newLine += attribs
-                newLine += " "
-
-            newLine += line[i]
-            textOffset += 1
-
-        attribs = self._getAttributesForChar(obj,
-                                             text,
-                                             startOffset,
-                                             line,
-                                             0,
-                                             ["paragraph-style"])
-        if attribs:
-            if newLine:
-                newLine += " ; "
-            newLine += attribs
-
-        debug.println(self._debugLevel, "newLine: <%s>" % (newLine))
-
-        return newLine
-
-    def _getAttributesForChar(self,
-                              obj,
-                              text,
-                              textOffset,
-                              line,
-                              lineIndex,
-                              keys=["style", "weight", "underline"]):
-
-        attribStr = ""
-
-        defaultAttributes = text.getDefaultAttributes()
-        attributesDictionary = self._stringToDictionary(defaultAttributes)
-
-        charAttributes = text.getAttributes(textOffset)
-        if charAttributes[0]:
-            charDict = self._stringToDictionary(charAttributes[0])
-            for key in charDict.keys():
-                attributesDictionary[key] = charDict[key]
-
-        if attributesDictionary:
-            for key in keys:
-                localizedKey = text_attribute_names.getTextAttributeName(key)
-                if key in attributesDictionary:
-                    attribute = attributesDictionary[key]
-                    localizedValue = \
-                        text_attribute_names.getTextAttributeName(attribute)
-                    if attribute:
-                        # If it's the 'weight' attribute and greater than 400,
-                        # just speak it as bold, otherwise speak the weight.
-                        #
-                        if key == "weight":
-                            if int(attribute) > 400:
-                                attribStr += " "
-                                # Translators: bold as in the font sense.
-                                #
-                                attribStr += _("bold")
-                        elif key == "underline":
-                            if attribute != "none":
-                                attribStr += " "
-                                attribStr += localizedKey
-                        elif key == "style":
-                            if attribute != "normal":
-                                attribStr += " "
-                                attribStr += localizedValue
-                        else:
-                            attribStr += " "
-                            attribStr += (localizedKey + " " + localizedValue)
-
-            # Also check to see if this is a hypertext link.
-            #
-            if self._script.getLinkIndex(obj, textOffset) >= 0:
-                attribStr += " "
-                # Translators: this indicates that this piece of
-                # text is a hypertext link.
-                #
-                attribStr += _("link")
-
-            if line:
-                debug.println(self._debugLevel,
-                              "char <%s>: %s" % (line[lineIndex], attribStr))
-
-        # Only return attributes for the beginning of an attribute run.
-        if attribStr != self._lastAttributeString:
-            self._lastAttributeString = attribStr
-            return attribStr
-        else:
-            return ""
-
-    def _stringToDictionary(self, tokenString):
-        """Converts a string of text attribute tokens of the form
-        <key>:<value>; into a dictionary of keys and values.
-        Text before the colon is the key and text afterwards is the
-        value. If there is a final semi-colon, then it's ignored.
-        """
-
-        dictionary = {}
-        allTokens = tokenString.split(";")
-        for token in allTokens:
-            item = token.split(":")
-            if len(item) == 2:
-                item[0] = item[0].lstrip()
-                item[1] = item[1].lstrip()
-                dictionary[item[0]] = item[1]
-
-        return dictionary
-
-    def speakTitle(self, obj):
-        """Orca will speak the following information:
-
-        1. The contents of the title bar of the application main window
-        2. If in a dialog box within an application, the contents of the
-        title bar of the dialog box.
-        3. '<n> unfocused dialogs' if this application has more than
-           one unfocused alert or dialog window.
-        4. Orca will pause briefly between these two pieces of information
-        so that the speech user can distinguish each.
-        """
-
-        utterances = []
-
-        results = self._getFrameAndDialog(obj)
-
-        if results[0]:
-            text = self.getObjLabelAndName(results[0])
-            utterances.append(text)
-        if results[1]:
-            text = self.getObjLabelAndName(results[1])
-            utterances.append(text)
-
-        alertAndDialogCount = \
-                    self._script.getUnfocusedAlertAndDialogCount(obj)
-        if alertAndDialogCount > 0:
-            # Translators: this tells the user how many unfocused
-            # alert and dialog windows that this application has.
-            #
-            line = ngettext("%d unfocused dialog",
-                            "%d unfocused dialogs",
-                            alertAndDialogCount) % alertAndDialogCount
-            utterances.append(line)
-
-        debug.println(self._debugLevel, "titlebar utterances=%s" % \
-                      utterances)
-        speech.speak(utterances)
-
-    def speakStatusBar(self, obj):
-        """Speak the contents of the status bar of the window with focus.
-        """
-
-        utterances = []
-
-        results = self._getFrameAndDialog(obj)
-
-        if results[0]:
-            self._statusBar = None
-            self._getStatusBar(results[0])
-            if self._statusBar:
-                self._speakStatusBar()
-        window = results[1] or results[0]
-        if window:
-            self._speakDefaultButton(window)
-
-        debug.println(self._debugLevel, "status bar utterances=%s" % \
-                      utterances)
-        speech.speak(utterances)
-
-    def _getFrameAndDialog(self, obj):
-        """Returns the frame and (possibly) the dialog containing
-        the object.
-        """
-
-        results = [None, None]
-
-        parent = obj.parent
-        while parent and (parent.parent != parent):
-            #debug.println(self._debugLevel,
-            #              "_getFrameAndDialog: parent=%s, %s" % \
-            #              (parent.getRole(), self.getObjLabelAndName(parent)))
-            if parent.getRole() == pyatspi.ROLE_FRAME:
-                results[0] = parent
-            if parent.getRole() in [pyatspi.ROLE_DIALOG,
-                                    pyatspi.ROLE_FILE_CHOOSER]:
-                results[1] = parent
-            parent = parent.parent
-
-        return results
-
-    def _getStatusBar(self, obj):
-        """Gets the status bar.
-        """
-        if self._statusBar:
-            return
-
-        # debug.println(self._debugLevel, "_findStatusBar: ROOT=%s, %s" % \
-        #               (obj.getRole(), self.getObjLabelAndName(obj)))
-        state = obj.getState()
-        managesDescendants = state.contains(pyatspi.STATE_MANAGES_DESCENDANTS)
-        if managesDescendants:
-            return
-
-        for child in obj:
-            # debug.println(self._debugLevel,
-            #               "_findStatusBar: child=%s, %s" % \
-            #               (child.getRole(), self.getObjLabelAndName(child)))
-            if child.getRole() == pyatspi.ROLE_STATUS_BAR:
-                self._statusBar = child
+                speech.speak(self.getWhereAmI(ancestor, basicOnly))
                 return
+        speech.speak(self.getWhereAmI(obj, basicOnly))
 
-            if child.childCount > 0:
-                self._getStatusBar(child)
-
-    def _speakStatusBar(self):
-        """Speaks the status bar.
-        """
-
-        if not self._statusBar:
-            return
-
-        utterances = []
-
-        text = self._getObjName(self._statusBar)
-        if text:
-            utterances.append(text)
-        elif self._statusBar.childCount:
-            for child in self._statusBar:
-                text = self._getObjName(child)
-                utterances.append(text)
-
-        debug.println(self._debugLevel, "statusbar utterances=%s" % \
-                      utterances)
-        speech.speak(utterances)
-
-    def _getDefaultButton(self, obj):
-        """Gets the default button in a dialog.
-
-        Arguments:
-        - obj: the dialog box for which the default button should be obtained
+    def getWhereAmI(self, obj, basicOnly):
+        """Returns an array of strings (and possibly voice and audio
+        specifications) that represent the complete speech for the
+        object.  The speech to be generated depends highly upon the
+        speech formatting strings in formatting.py.
         """
-
-        defaultButton = None
-        for child in obj:
-            # debug.println(self._debugLevel,
-            #               "_getDefaultButton: child=%s, %s" % \
-            #               (child.getRole(), self.getObjLabelAndName(child)))
-            state = child.getState()
-            if child.getRole() == pyatspi.ROLE_PUSH_BUTTON \
-                and state.contains(pyatspi.STATE_IS_DEFAULT):
-                defaultButton = child
-            else:
-                defaultButton = self._getDefaultButton(child)
-
-            if defaultButton:
-                break
-
-        return defaultButton
-
-    def _speakDefaultButton(self, obj):
-        """Speaks the default button in a dialog.
-
-        Arguments:
-        - obj: the dialog box for which the default button should be obtained
-        """
-
-        defaultButton = self._getDefaultButton(obj)
-
-        if not defaultButton:
-            return
-
-        state = defaultButton.getState()
-        if not state.contains(pyatspi.STATE_SENSITIVE):
-            return
-
-        utterances = []
-
-        # Translators: The "default" button in a dialog box is the button
-        # that gets activated when Enter is pressed anywhere within that
-        # dialog box.
-        #
-        text = _("Default button is %s") % \
-               self._getObjName(defaultButton)
-        utterances.append(text)
-
-        debug.println(self._debugLevel, "default button utterances=%s" % \
-                      utterances)
-        speech.speak(utterances)
-
-    def _speakObjDescription(self, obj):
-        """Speaks the object's description if it is not the same as
-        the object's name or label.
-        """
-
-        description = obj.description
-        name = self._getObjName(obj)
-        label = self._getObjLabel(obj)
-
-        if description and not description in [name, label]:
-            speech.speak(description)
-
-    def _getToolbar(self, obj):
-        """Returns the toolbar containing the object or None if the object
-        is not contained in a toolbar.
-        """
-
-        parent = obj.parent
-        while parent and (parent.parent != parent):
-            if parent.getRole() == pyatspi.ROLE_TOOL_BAR:
-                return parent
-            parent = parent.parent
-
-        return None
-
-    def _speakToolbar(self, obj):
-        """Speaks the label and/or name of a toolbar, followed by its role.
-        """
-
-        utterances = []
-        text = self.getObjLabelAndName(obj) + " " + \
-               self._getSpeechForRoleName(obj)
-        utterances.append(text.strip())
-
-        debug.println(self._debugLevel, "toolbar utterances=%s" \
-                      % utterances)
-        speech.speak(utterances)
-
-    def _getRequiredState(self, obj):
-        """Returns a string describing the required state of the given
-        object.
-
-        Arguments:
-        - obj: the Accessible object
-        """
-
-        if not settings.presentRequiredState:
-            return ""
-
-        if obj.getRole() == pyatspi.ROLE_RADIO_BUTTON:
-            obj = obj.parent
-
-        state = obj.getState()
-        if state.contains(pyatspi.STATE_REQUIRED):
-            return settings.speechRequiredStateString
-        else:
-            return ""
+        if basicOnly:
+            formatType = 'basicWhereAmI'
+        else:
+            formatType = 'detailedWhereAmI'
+        return self._script.speechGenerator.generateSpeech(
+                   obj,
+                   alreadyFocused=True,
+                   formatType=formatType,
+                   forceMnemonic=True,
+                   forceTutorial=True)
diff --git a/test/harness/runall.sh b/test/harness/runall.sh
index df8dbf0..1bd564e 100755
--- a/test/harness/runall.sh
+++ b/test/harness/runall.sh
@@ -167,7 +167,7 @@ do
       #
       mkdir -p ./tmp/$application
       cd ./tmp/$application
-      for testFile in `find $testDir -type f -name "*.py" | sort`; do
+      for testFile in `find $testDir -xtype f -name "*.py" | sort`; do
         echo ========================================
         echo Running $testFile
         if [ "$found" -gt 0 ]
@@ -213,7 +213,7 @@ do
             sed "/speech.speakUtterances utterance='Desktop frame'/,/speech.speakUtterances utterance='Icon View layered pane'/ d" $newResultsFile.speech > $currentdir/$outputdir/$newResultsFile.speech
             mv $newResultsFile.speech $currentdir/$outputdir/$newResultsFile.speech.unfiltered
             mv $newResultsFile.debug $currentdir/$outputdir
-            rm -f *
+            rm -rf *
         fi
 
         echo Finished running $testFile.
diff --git a/test/harness/utils.py b/test/harness/utils.py
index e867c95..20cde2e 100644
--- a/test/harness/utils.py
+++ b/test/harness/utils.py
@@ -200,15 +200,15 @@ class AssertPresentationAction(AtomicAction):
             diffs = list(d.compare(self._expectedResults, results))
             print >> myErr, '\n'.join(list(diffs))
         except:
-            print "(ERROR COMPUTING DIFFERENCES!!!)"
+            print >> myErr, "(ERROR COMPUTING DIFFERENCES!!!)"
             for i in range(0, max(len(results), len(self._expectedResults))):
                 try:
-                    print "  EXPECTED: %s" \
+                    print >> myErr, "  EXPECTED: %s" \
                           % self._expectedResults[i].decode("UTF-8", "replace")
                 except:
                     pass
                 try:
-                    print "  ACTUAL:   %s" \
+                    print >> myErr, "  ACTUAL:   %s" \
                           % results[i].decode("UTF-8", "replace")
                 except:
                     pass
diff --git a/test/keystrokes/firefox/codetalks_button.py b/test/keystrokes/firefox/codetalks_button.py
index bad17ca..1efa4c0 100644
--- a/test/keystrokes/firefox/codetalks_button.py
+++ b/test/keystrokes/firefox/codetalks_button.py
@@ -65,8 +65,7 @@ sequence.append(utils.AssertPresentationAction(
     "basic whereamI", 
     ["BRAILLE LINE:  'Check Now Button Check to see if your order has been shipped.  $l'",
      "     VISIBLE:  'Check Now Button Check to see if', cursor=1",
-     "SPEECH OUTPUT: 'Check Now button '",
-     "SPEECH OUTPUT: 'Check to see if your order has been shipped.'"]))
+     "SPEECH OUTPUT: 'Check Now button Check to see if your order has been shipped.'"]))
 
 ########################################################################
 # Now push the button.  The following will be presented.
diff --git a/test/keystrokes/firefox/codetalks_list.py b/test/keystrokes/firefox/codetalks_list.py
index e5bf618..247a7dd 100644
--- a/test/keystrokes/firefox/codetalks_list.py
+++ b/test/keystrokes/firefox/codetalks_list.py
@@ -75,7 +75,7 @@ sequence.append(utils.AssertPresentationAction(
     "5. Down Arrow", 
     ["BRAILLE LINE:  '\$Revision: [0-9]* \$'",
      "     VISIBLE:  '\$Revision: [0-9]* \$', cursor=1",
-     "SPEECH OUTPUT: '\$Revision: [0-9]* \$'"]))
+     "SPEECH OUTPUT: '\$Revision: [0-9]* dollar'"]))
 
 ########################################################################
 # Up Arrow.
diff --git a/test/keystrokes/firefox/codetalks_panel_text.py b/test/keystrokes/firefox/codetalks_panel_text.py
index 4147302..9af2c66 100644
--- a/test/keystrokes/firefox/codetalks_panel_text.py
+++ b/test/keystrokes/firefox/codetalks_panel_text.py
@@ -59,8 +59,7 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("Down"))
 sequence.append(utils.AssertPresentationAction(
     "5. Down Arrow", 
-    ["BUG? - We are now speaking the word 'bullet' for each bullet. We probably should be. Note that I've only marked this once, but it applies throughout this test.",
-     "BRAILLE LINE:  'â?¢ left and right arrows to move from tab to tab. Panel is made visible when tab gets focus. Arrow keys do'",
+    ["BRAILLE LINE:  'â?¢ left and right arrows to move from tab to tab. Panel is made visible when tab gets focus. Arrow keys do'",
      "     VISIBLE:  'â?¢ left and right arrows to move ', cursor=1",
      "SPEECH OUTPUT: 'bullet left and right arrows to move from tab to tab. Panel is made visible when tab gets focus. Arrow keys do'"]))
 
diff --git a/test/keystrokes/firefox/codetalks_treegrid.py b/test/keystrokes/firefox/codetalks_treegrid.py
index dfd51dc..82a7b2e 100644
--- a/test/keystrokes/firefox/codetalks_treegrid.py
+++ b/test/keystrokes/firefox/codetalks_treegrid.py
@@ -88,7 +88,7 @@ sequence.append(utils.AssertPresentationAction(
     "basic whereAmI", 
     ["BRAILLE LINE:  '+A Question of Love'",
      "     VISIBLE:  '+A Question of Love', cursor=0",
-     "SPEECH OUTPUT: '+A Question of Love '"]))
+     "SPEECH OUTPUT: '+A Question of Love'"]))
 
 ########################################################################
 # Space to expand the current item.
@@ -112,7 +112,7 @@ sequence.append(utils.AssertPresentationAction(
     "basic whereAmI", 
     ["BRAILLE LINE:  '-A Question of Love'",
      "     VISIBLE:  '-A Question of Love', cursor=0",
-     "SPEECH OUTPUT: '-A Question of Love '"]))
+     "SPEECH OUTPUT: '-A Question of Love'"]))
 
 ########################################################################
 # Down Arrow to the child item.
@@ -140,8 +140,7 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("Right"))
 sequence.append(utils.AssertPresentationAction(
     "2. Right Arrow in child", 
-    ["BUG? - We're saying 'dollar' here. If the dollar sign were right against the 9, I would say this is a bug; because of the whitespace I'm not sure.",
-     "BRAILLE LINE:  'ISBN 978-3-453-40540-0 Cell Author Nora Roberts Cell Price $ 9.99 Cell'",
+    ["BRAILLE LINE:  'ISBN 978-3-453-40540-0 Cell Author Nora Roberts Cell Price $ 9.99 Cell'",
      "     VISIBLE:  'Price $ 9.99 Cell', cursor=1",
      "SPEECH OUTPUT: 'dollar 9.99'"]))
 
diff --git a/test/keystrokes/firefox/dojo_button.py b/test/keystrokes/firefox/dojo_button.py
index 83a8d95..b6830aa 100644
--- a/test/keystrokes/firefox/dojo_button.py
+++ b/test/keystrokes/firefox/dojo_button.py
@@ -48,7 +48,7 @@ sequence.append(utils.AssertPresentationAction(
     "Basic Where Am I on <button>", 
     ["BRAILLE LINE:  '<button> <input type='button'> Button       Create â?? Edit! â?? Color â?? Save â??   '",
      "     VISIBLE:  '<button> <input type='button'> B', cursor=1",
-     "SPEECH OUTPUT: '<button> button '"]))
+     "SPEECH OUTPUT: '<button> button'"]))
 
 ########################################################################
 # Tab to <input type='button'>  
@@ -72,7 +72,7 @@ sequence.append(utils.AssertPresentationAction(
     "Basic Where Am I on <input type='button'>", 
     ["BRAILLE LINE:  '<button> <input type='button'> Button       Create â?? Edit! â?? Color â?? Save â??   '",
      "     VISIBLE:  '<input type='button'> Button    ', cursor=1",
-     "SPEECH OUTPUT: '<input type='button'> button '"]))
+     "SPEECH OUTPUT: '<input type='button'> button'"]))
 
 ########################################################################
 # Tab to "Create"  
@@ -98,7 +98,7 @@ sequence.append(utils.AssertPresentationAction(
     "Basic Where Am I on Create", 
     ["BRAILLE LINE:  'Create Button'",
      "     VISIBLE:  'Create Button', cursor=1",
-     "SPEECH OUTPUT: 'Create button '"]))
+     "SPEECH OUTPUT: 'Create button'"]))
 
 # WDW - Tabbing to the Create button pops up a tooltip.  Should we present
 # it automatically?
@@ -131,7 +131,7 @@ sequence.append(utils.AssertPresentationAction(
     "Basic Where Am I on drop down menu on Create", 
     ["BRAILLE LINE:  '<button> Button <input type='button'> ButtonCreate Button â?? MenuSave Button â?? Menu'",
      "     VISIBLE:  'â?? MenuSave Button â?? Menu', cursor=1",
-     "SPEECH OUTPUT: 'â?? menu '"]))
+     "SPEECH OUTPUT: 'â?? menu'"]))
 
 ########################################################################
 # Open the drop down menu.
@@ -169,10 +169,9 @@ sequence.append(KeyComboAction("KP_Enter"))
 sequence.append(PauseAction(3000))
 sequence.append(utils.AssertPresentationAction(
     "Basic Where Am I on Create from template", 
-    ["BUG? - Why are we speaking 'item 1 of 1'?",
-     "BRAILLE LINE:  'Create from template'",
+    ["BRAILLE LINE:  'Create from template'",
      "     VISIBLE:  'Create from template', cursor=1",
-     "SPEECH OUTPUT: 'Create from template  item 1 of 1'"]))
+     "SPEECH OUTPUT: 'Create from template item 1 of 1'"]))
 
 ########################################################################
 # Close the menu and go to the Edit! button
diff --git a/test/keystrokes/firefox/dojo_checkbox.py b/test/keystrokes/firefox/dojo_checkbox.py
index 3759b6c..e0eef80 100644
--- a/test/keystrokes/firefox/dojo_checkbox.py
+++ b/test/keystrokes/firefox/dojo_checkbox.py
@@ -131,7 +131,7 @@ sequence.append(utils.AssertPresentationAction(
     "Basic Where Am I", 
     ["BRAILLE LINE:  '< > CheckBox cb7: normal checkbox. disable Button enable Button set value to \"fish\" Button Reset value+checked Button \"onChange\" handler updates: []'",
      "     VISIBLE:  '< > CheckBox cb7: normal checkbo', cursor=1",
-     "SPEECH OUTPUT: 'cb7: normal checkbox. check box not checked '"]))
+     "SPEECH OUTPUT: 'cb7: normal checkbox. check box not checked'"]))
 
 ########################################################################
 # Close the demo
diff --git a/test/keystrokes/firefox/dojo_combo_box.py b/test/keystrokes/firefox/dojo_combo_box.py
index 39644cf..10c02cb 100644
--- a/test/keystrokes/firefox/dojo_combo_box.py
+++ b/test/keystrokes/firefox/dojo_combo_box.py
@@ -50,7 +50,7 @@ sequence.append(TypeAction("C"))
 sequence.append(PauseAction(1000))
 sequence.append(utils.AssertPresentationAction(
     "Replace existing text with a 'C'", 
-    ["BUG? - At this point, the entry expands into a combo box with three items showing. We speak them all. We should instead indicate that this object is expanded, similar to what we do with autocompletes. In addition, we're speaking 'bullet' for the initial bullet throughout the test. I think we should be, but marking it just in case.",
+    ["BUG? - At this point, the entry expands into a combo box with three items showing. We speak them all. We should instead indicate that this object is expanded, similar to what we do with autocompletes.",
      "BRAILLE LINE:  'â?? US State test 1 California $l'",
      "     VISIBLE:  'â?? US State test 1 California $l', cursor=0",
      "BRAILLE LINE:  'â?? US State test 1 C $l'",
@@ -158,8 +158,7 @@ sequence.append(utils.AssertPresentationAction(
     "Basic Where Am I - Combo box collapsed back into an entry", 
     ["BRAILLE LINE:  'â?? US State test 1 California $l'",
      "     VISIBLE:  'â?? US State test 1 California $l', cursor=0",
-     "SPEECH OUTPUT: 'US State test 1 text alifornia'",
-     "SPEECH OUTPUT: 'selected '"]))
+     "SPEECH OUTPUT: 'US State test 1 text alifornia selected'"]))
 
 ########################################################################
 # Close the demo
diff --git a/test/keystrokes/firefox/dojo_dialog.py b/test/keystrokes/firefox/dojo_dialog.py
index cf327e2..b8f1d64 100644
--- a/test/keystrokes/firefox/dojo_dialog.py
+++ b/test/keystrokes/firefox/dojo_dialog.py
@@ -87,7 +87,7 @@ sequence.append(utils.AssertPresentationAction(
     "Basic whereAmI", 
     ["BRAILLE LINE:  'First tab Page Second tab Page'",
      "     VISIBLE:  'First tab Page Second tab Page', cursor=1",
-     "SPEECH OUTPUT: 'tab list First tab page item 1 of 2 '"]))
+     "SPEECH OUTPUT: 'tab list First tab page item 1 of 2'"]))
 
 ########################################################################
 # Close the dialog, focus goes back to button.  
diff --git a/test/keystrokes/firefox/dojo_spinner.py b/test/keystrokes/firefox/dojo_spinner.py
index 7fe11a1..f4b3103 100644
--- a/test/keystrokes/firefox/dojo_spinner.py
+++ b/test/keystrokes/firefox/dojo_spinner.py
@@ -209,7 +209,7 @@ sequence.append(utils.AssertPresentationAction(
     "basic whereAmI", 
     ["BRAILLE LINE:  'Spinbox #1: 902 $l'",
      "     VISIBLE:  'Spinbox #1: 902 $l', cursor=16",
-     "SPEECH OUTPUT: 'Spinbox #1: spin button 902 '"]))
+     "SPEECH OUTPUT: 'Spinbox #1: spin button 902 required'"]))
 
 ########################################################################
 # Close the demo
diff --git a/test/keystrokes/firefox/dojo_tabcontainer.py b/test/keystrokes/firefox/dojo_tabcontainer.py
index 910d957..34336ce 100644
--- a/test/keystrokes/firefox/dojo_tabcontainer.py
+++ b/test/keystrokes/firefox/dojo_tabcontainer.py
@@ -50,7 +50,7 @@ sequence.append(utils.AssertPresentationAction(
     "basic whereAmI", 
     ["BRAILLE LINE:  'Tab 1 Page Tab 2 Page Tab 3 Page Inlined Sub TabContainer Page Sub TabContainer from href Page SplitContainer from href Page'",
      "     VISIBLE:  'Tab 1 Page Tab 2 Page Tab 3 Page', cursor=1",
-     "SPEECH OUTPUT: 'tab list Tab 1 page item 1 of 7 '"]))
+     "SPEECH OUTPUT: 'tab list Tab 1 page item 1 of 7'"]))
 
 ########################################################################
 # Use arrows to move between tabs: 'Tab 2'.  The following will be presented.
diff --git a/test/keystrokes/firefox/dojo_tree.py b/test/keystrokes/firefox/dojo_tree.py
index 68b2aad..ca435ee 100644
--- a/test/keystrokes/firefox/dojo_tree.py
+++ b/test/keystrokes/firefox/dojo_tree.py
@@ -62,7 +62,7 @@ sequence.append(utils.AssertPresentationAction(
     "basic whereAmI", 
     ["BRAILLE LINE:  'Africa ListItem'",
      "     VISIBLE:  'Africa ListItem', cursor=1",
-     "SPEECH OUTPUT: 'list item Africa  collapsed tree level 2'"]))
+     "SPEECH OUTPUT: 'list item Africa item 1 of 6 collapsed tree level 2'"]))
 
 ########################################################################
 # Use arrows to expand/collapse/navigate tree.  
diff --git a/test/keystrokes/firefox/find_wiki.py b/test/keystrokes/firefox/find_wiki.py
index ae34737..c6ad8fd 100644
--- a/test/keystrokes/firefox/find_wiki.py
+++ b/test/keystrokes/firefox/find_wiki.py
@@ -102,7 +102,8 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("Return"))
 sequence.append(utils.AssertPresentationAction(
     "7. Return",
-    ["SPEECH OUTPUT: 'Darragh � Héiligh link  has created several audio guides for Orca. This is a fantastic contribution'"]))
+    ["BUG? - Should we be doing this?",
+     "SPEECH OUTPUT: 'Darragh O ACUTE Héiligh link  has created several audio guides for Orca. This is a fantastic contribution'"]))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("Return"))
diff --git a/test/keystrokes/firefox/html_role_combo_box.py b/test/keystrokes/firefox/html_role_combo_box.py
index 0d1ae86..3e12c0b 100644
--- a/test/keystrokes/firefox/html_role_combo_box.py
+++ b/test/keystrokes/firefox/html_role_combo_box.py
@@ -246,9 +246,10 @@ sequence.append(KeyComboAction("KP_Enter"))
 sequence.append(PauseAction(3000))
 sequence.append(utils.AssertPresentationAction(
     "Basic Where Am I", 
-    ["BRAILLE LINE:  'Braille Combo'",
+    ["BUG? - The item count appears to be incorrect",
+     "BRAILLE LINE:  'Braille Combo'",
      "     VISIBLE:  'Braille Combo', cursor=1",
-     "SPEECH OUTPUT: 'combo box   '"]))
+     "SPEECH OUTPUT: 'Component combo box Braille item 1 of 1'"]))
 
 ########################################################################
 # Press Shift Tab once to return to the Version combo box.  
diff --git a/test/keystrokes/firefox/html_struct_nav_list_item.py b/test/keystrokes/firefox/html_struct_nav_list_item.py
index f4a8bb4..e524a09 100644
--- a/test/keystrokes/firefox/html_struct_nav_list_item.py
+++ b/test/keystrokes/firefox/html_struct_nav_list_item.py
@@ -35,8 +35,8 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("<Control>Home"))
 sequence.append(utils.AssertPresentationAction(
     "Top of file", 
-    ["BRAILLE LINE:  'Welcome to a List of Lists h1'",
-     "     VISIBLE:  'Welcome to a List of Lists h1', cursor=1",
+    ["BRAILLE LINE:  'Welcome to a List of Lists h1'",
+     "     VISIBLE:  'Welcome to a List of Lists h1', cursor=1",
      "SPEECH OUTPUT: 'Welcome to a List of Lists heading level 1'"]))
 
 ########################################################################
@@ -70,9 +70,9 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("l"))
 sequence.append(utils.AssertPresentationAction(
     "l to third list", 
-    ["BRAILLE LINE:  'â?¢ listing item'",
-     "     VISIBLE:  'â?¢ listing item', cursor=1",
-     "SPEECH OUTPUT: 'List with 2 items'",
+    ["BRAILLE LINE:  'â?¢ listing item'",
+     "     VISIBLE:  'â?¢ listing item', cursor=1",
+     "SPEECH OUTPUT: 'List with 2 items'",
      "SPEECH OUTPUT: 'bullet listing item'"]))
 
 ########################################################################
@@ -83,112 +83,112 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("i"))
 sequence.append(utils.AssertPresentationAction(
     "i in third list", 
-    ["BRAILLE LINE:  'â?¢ first sublevel'",
-     "     VISIBLE:  'â?¢ first sublevel', cursor=1",
+    ["BRAILLE LINE:  'â?¢ first sublevel'",
+     "     VISIBLE:  'â?¢ first sublevel', cursor=1",
      "SPEECH OUTPUT: 'bullet first sublevel'"]))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("i"))
 sequence.append(utils.AssertPresentationAction(
     "i in third list", 
-    ["BRAILLE LINE:  'â?¢ look for the bullet on'",
-     "     VISIBLE:  'â?¢ look for the bullet on', cursor=1",
+    ["BRAILLE LINE:  'â?¢ look for the bullet on'",
+     "     VISIBLE:  'â?¢ look for the bullet on', cursor=1",
      "SPEECH OUTPUT: 'bullet look for the bullet on'"]))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("i"))
 sequence.append(utils.AssertPresentationAction(
     "i in third list", 
-    ["BRAILLE LINE:  'â?¢ each sublevel'",
-     "     VISIBLE:  'â?¢ each sublevel', cursor=1",
+    ["BRAILLE LINE:  'â?¢ each sublevel'",
+     "     VISIBLE:  'â?¢ each sublevel', cursor=1",
      "SPEECH OUTPUT: 'bullet each sublevel'"]))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("i"))
 sequence.append(utils.AssertPresentationAction(
     "i in third list", 
-    ["BRAILLE LINE:  'â?¢ they should all be different, except here.'",
-     "     VISIBLE:  'â?¢ they should all be different, ', cursor=1",
+    ["BRAILLE LINE:  'â?¢ they should all be different, except here.'",
+     "     VISIBLE:  'â?¢ they should all be different, ', cursor=1",
      "SPEECH OUTPUT: 'bullet they should all be different, except here.'"]))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("i"))
 sequence.append(utils.AssertPresentationAction(
     "i in third list", 
-    ["BRAILLE LINE:  'â?¢ second sublevel'",
-     "     VISIBLE:  'â?¢ second sublevel', cursor=1",
+    ["BRAILLE LINE:  'â?¢ second sublevel'",
+     "     VISIBLE:  'â?¢ second sublevel', cursor=1",
      "SPEECH OUTPUT: 'bullet second sublevel'"]))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("i"))
 sequence.append(utils.AssertPresentationAction(
     "i in third list", 
-    ["BRAILLE LINE:  'â?¢ or you can specify a square'",
-     "     VISIBLE:  'â?¢ or you can specify a square', cursor=1",
+    ["BRAILLE LINE:  'â?¢ or you can specify a square'",
+     "     VISIBLE:  'â?¢ or you can specify a square', cursor=1",
      "SPEECH OUTPUT: 'bullet or you can specify a square'"]))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("i"))
 sequence.append(utils.AssertPresentationAction(
     "i in third list", 
-    ["BRAILLE LINE:  'â?¢ if your TYPE is circle'",
-     "     VISIBLE:  'â?¢ if your TYPE is circle', cursor=1",
+    ["BRAILLE LINE:  'â?¢ if your TYPE is circle'",
+     "     VISIBLE:  'â?¢ if your TYPE is circle', cursor=1",
      "SPEECH OUTPUT: 'bullet if your TYPE is circle'"]))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("i"))
 sequence.append(utils.AssertPresentationAction(
     "i in third list", 
-    ["BRAILLE LINE:  'â?¢ or even a disc'",
-     "     VISIBLE:  'â?¢ or even a disc', cursor=1",
+    ["BRAILLE LINE:  'â?¢ or even a disc'",
+     "     VISIBLE:  'â?¢ or even a disc', cursor=1",
      "SPEECH OUTPUT: 'bullet or even a disc'"]))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("i"))
 sequence.append(utils.AssertPresentationAction(
     "i in third list", 
-    ["BRAILLE LINE:  'â?¢ Franz Liszt'",
-     "     VISIBLE:  'â?¢ Franz Liszt', cursor=1",
+    ["BRAILLE LINE:  'â?¢ Franz Liszt'",
+     "     VISIBLE:  'â?¢ Franz Liszt', cursor=1",
      "SPEECH OUTPUT: 'bullet Franz Liszt'"]))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("i"))
 sequence.append(utils.AssertPresentationAction(
     "i in third list", 
-    ["BRAILLE LINE:  'â?¢ was a composer who was not square'",
-     "     VISIBLE:  'â?¢ was a composer who was not squ', cursor=1",
+    ["BRAILLE LINE:  'â?¢ was a composer who was not square'",
+     "     VISIBLE:  'â?¢ was a composer who was not squ', cursor=1",
      "SPEECH OUTPUT: 'bullet was a composer who was not square'"]))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("i"))
 sequence.append(utils.AssertPresentationAction(
     "i in third list", 
-    ["BRAILLE LINE:  'â?¢ would have liked the Who'",
-     "     VISIBLE:  'â?¢ would have liked the Who', cursor=1",
+    ["BRAILLE LINE:  'â?¢ would have liked the Who'",
+     "     VISIBLE:  'â?¢ would have liked the Who', cursor=1",
      "SPEECH OUTPUT: 'bullet would have liked the Who'"]))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("i"))
 sequence.append(utils.AssertPresentationAction(
     "i in third list", 
-    ["BRAILLE LINE:  'â?¢ feeling listless'",
-     "     VISIBLE:  'â?¢ feeling listless', cursor=1",
+    ["BRAILLE LINE:  'â?¢ feeling listless'",
+     "     VISIBLE:  'â?¢ feeling listless', cursor=1",
      "SPEECH OUTPUT: 'bullet feeling listless'"]))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("i"))
 sequence.append(utils.AssertPresentationAction(
     "i in third list", 
-    ["BRAILLE LINE:  'â?¢ blah, blah, blah'",
-     "     VISIBLE:  'â?¢ blah, blah, blah', cursor=1",
+    ["BRAILLE LINE:  'â?¢ blah, blah, blah'",
+     "     VISIBLE:  'â?¢ blah, blah, blah', cursor=1",
      "SPEECH OUTPUT: 'bullet blah, blah, blah'"]))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("i"))
 sequence.append(utils.AssertPresentationAction(
     "i in third list", 
-    ["BRAILLE LINE:  'â?¢ whine, whine, whine'",
-     "     VISIBLE:  'â?¢ whine, whine, whine', cursor=1",
+    ["BRAILLE LINE:  'â?¢ whine, whine, whine'",
+     "     VISIBLE:  'â?¢ whine, whine, whine', cursor=1",
      "SPEECH OUTPUT: 'bullet whine, whine, whine'"]))
 
 sequence.append(utils.StartRecordingAction())
@@ -223,25 +223,25 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("<Shift>i"))
 sequence.append(utils.AssertPresentationAction(
     "shift + i should wrap to bottom", 
-    ["BRAILLE LINE:  'â?¢ whine, whine, whine'",
-     "     VISIBLE:  'â?¢ whine, whine, whine', cursor=1",
-     "SPEECH OUTPUT: 'Wrapping to bottom.'",
+    ["BRAILLE LINE:  'â?¢ whine, whine, whine'",
+     "     VISIBLE:  'â?¢ whine, whine, whine', cursor=1",
+     "SPEECH OUTPUT: 'Wrapping to bottom.'",
      "SPEECH OUTPUT: 'bullet whine, whine, whine'"]))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("<Shift>i"))
 sequence.append(utils.AssertPresentationAction(
     "shift + i in third list", 
-    ["BRAILLE LINE:  'â?¢ blah, blah, blah'",
-     "     VISIBLE:  'â?¢ blah, blah, blah', cursor=1",
+    ["BRAILLE LINE:  'â?¢ blah, blah, blah'",
+     "     VISIBLE:  'â?¢ blah, blah, blah', cursor=1",
      "SPEECH OUTPUT: 'bullet blah, blah, blah'"]))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("<Shift>i"))
 sequence.append(utils.AssertPresentationAction(
     "shift + i in third list", 
-    ["BRAILLE LINE:  'â?¢ feeling listless'",
-     "     VISIBLE:  'â?¢ feeling listless', cursor=1",
+    ["BRAILLE LINE:  'â?¢ feeling listless'",
+     "     VISIBLE:  'â?¢ feeling listless', cursor=1",
      "SPEECH OUTPUT: 'bullet feeling listless'"]))
 
 ########################################################################
diff --git a/test/keystrokes/firefox/html_struct_nav_lists.py b/test/keystrokes/firefox/html_struct_nav_lists.py
index 58e7e5f..b686b58 100644
--- a/test/keystrokes/firefox/html_struct_nav_lists.py
+++ b/test/keystrokes/firefox/html_struct_nav_lists.py
@@ -35,8 +35,8 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("<Control>Home"))
 sequence.append(utils.AssertPresentationAction(
     "Top of file", 
-    ["BRAILLE LINE:  'Welcome to a List of Lists h1'",
-     "     VISIBLE:  'Welcome to a List of Lists h1', cursor=1",
+    ["BRAILLE LINE:  'Welcome to a List of Lists h1'",
+     "     VISIBLE:  'Welcome to a List of Lists h1', cursor=1",
      "SPEECH OUTPUT: 'Welcome to a List of Lists heading level 1'"]))
 
 ########################################################################
@@ -70,9 +70,9 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("l"))
 sequence.append(utils.AssertPresentationAction(
     "l to third list", 
-    ["BRAILLE LINE:  'â?¢ listing item'",
-     "     VISIBLE:  'â?¢ listing item', cursor=1",
-     "SPEECH OUTPUT: 'List with 2 items'",
+    ["BRAILLE LINE:  'â?¢ listing item'",
+     "     VISIBLE:  'â?¢ listing item', cursor=1",
+     "SPEECH OUTPUT: 'List with 2 items'",
      "SPEECH OUTPUT: 'bullet listing item'"]))
 
 ########################################################################
@@ -82,10 +82,10 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("l"))
 sequence.append(utils.AssertPresentationAction(
     "l to third list's first sub list", 
-    ["BRAILLE LINE:  'â?¢ first sublevel'",
-     "     VISIBLE:  'â?¢ first sublevel', cursor=1",
-     "SPEECH OUTPUT: 'List with 2 items'",
-     "SPEECH OUTPUT: 'Nesting level 1'",
+    ["BRAILLE LINE:  'â?¢ first sublevel'",
+     "     VISIBLE:  'â?¢ first sublevel', cursor=1",
+     "SPEECH OUTPUT: 'List with 2 items'",
+     "SPEECH OUTPUT: 'Nesting level 1'",
      "SPEECH OUTPUT: 'bullet first sublevel'"]))
 
 ########################################################################
@@ -96,10 +96,10 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("l"))
 sequence.append(utils.AssertPresentationAction(
     "l to third list's first sub list's first list", 
-    ["BRAILLE LINE:  'â?¢ look for the bullet on'",
-     "     VISIBLE:  'â?¢ look for the bullet on', cursor=1",
-     "SPEECH OUTPUT: 'List with 2 items'",
-     "SPEECH OUTPUT: 'Nesting level 2'",
+    ["BRAILLE LINE:  'â?¢ look for the bullet on'",
+     "     VISIBLE:  'â?¢ look for the bullet on', cursor=1",
+     "SPEECH OUTPUT: 'List with 2 items'",
+     "SPEECH OUTPUT: 'Nesting level 2'",
      "SPEECH OUTPUT: 'bullet look for the bullet on'"]))
 
 ########################################################################
@@ -110,10 +110,10 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("l"))
 sequence.append(utils.AssertPresentationAction(
     "l to third list's inner-most list", 
-    ["BRAILLE LINE:  'â?¢ each sublevel'",
-     "     VISIBLE:  'â?¢ each sublevel', cursor=1",
-     "SPEECH OUTPUT: 'List with 2 items'",
-     "SPEECH OUTPUT: 'Nesting level 3'",
+    ["BRAILLE LINE:  'â?¢ each sublevel'",
+     "     VISIBLE:  'â?¢ each sublevel', cursor=1",
+     "SPEECH OUTPUT: 'List with 2 items'",
+     "SPEECH OUTPUT: 'Nesting level 3'",
      "SPEECH OUTPUT: 'bullet each sublevel'"]))
 
 ########################################################################
@@ -124,10 +124,10 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("l"))
 sequence.append(utils.AssertPresentationAction(
     "l to next sub list in the third list", 
-    ["BRAILLE LINE:  'â?¢ if your TYPE is circle'",
-     "     VISIBLE:  'â?¢ if your TYPE is circle', cursor=1",
-     "SPEECH OUTPUT: 'List with 2 items'",
-     "SPEECH OUTPUT: 'Nesting level 2'",
+    ["BRAILLE LINE:  'â?¢ if your TYPE is circle'",
+     "     VISIBLE:  'â?¢ if your TYPE is circle', cursor=1",
+     "SPEECH OUTPUT: 'List with 2 items'",
+     "SPEECH OUTPUT: 'Nesting level 2'",
      "SPEECH OUTPUT: 'bullet if your TYPE is circle'"]))
 
 ########################################################################
@@ -137,10 +137,10 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("l"))
 sequence.append(utils.AssertPresentationAction(
     "l to next sub list in the third list", 
-    ["BRAILLE LINE:  'â?¢ was a composer who was not square'",
-     "     VISIBLE:  'â?¢ was a composer who was not squ', cursor=1",
-     "SPEECH OUTPUT: 'List with 2 items'",
-     "SPEECH OUTPUT: 'Nesting level 1'",
+    ["BRAILLE LINE:  'â?¢ was a composer who was not square'",
+     "     VISIBLE:  'â?¢ was a composer who was not squ', cursor=1",
+     "SPEECH OUTPUT: 'List with 2 items'",
+     "SPEECH OUTPUT: 'Nesting level 1'",
      "SPEECH OUTPUT: 'bullet was a composer who was not square'"]))
 
 ########################################################################
@@ -150,9 +150,9 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("l"))
 sequence.append(utils.AssertPresentationAction(
     "l to last sub list in the third list", 
-    ["BRAILLE LINE:  'â?¢ feeling listless'",
-     "     VISIBLE:  'â?¢ feeling listless', cursor=1",
-     "SPEECH OUTPUT: 'List with 3 items'",
+    ["BRAILLE LINE:  'â?¢ feeling listless'",
+     "     VISIBLE:  'â?¢ feeling listless', cursor=1",
+     "SPEECH OUTPUT: 'List with 3 items'",
      "SPEECH OUTPUT: 'bullet feeling listless'"]))
 
 ########################################################################
@@ -176,10 +176,10 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("<Shift>l"))
 sequence.append(utils.AssertPresentationAction(
     "shift + l - should wrap to bottom", 
-    ["BRAILLE LINE:  'â?¢ feeling listless'",
-     "     VISIBLE:  'â?¢ feeling listless', cursor=1",
-     "SPEECH OUTPUT: 'Wrapping to bottom.'",
-     "SPEECH OUTPUT: 'List with 3 items'",
+    ["BRAILLE LINE:  'â?¢ feeling listless'",
+     "     VISIBLE:  'â?¢ feeling listless', cursor=1",
+     "SPEECH OUTPUT: 'Wrapping to bottom.'",
+     "SPEECH OUTPUT: 'List with 3 items'",
      "SPEECH OUTPUT: 'bullet feeling listless'"]))
 
 ########################################################################
@@ -190,59 +190,59 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("<Shift>l"))
 sequence.append(utils.AssertPresentationAction(
     "shift + l", 
-    ["BRAILLE LINE:  'â?¢ was a composer who was not square'",
-     "     VISIBLE:  'â?¢ was a composer who was not squ', cursor=1",
-     "SPEECH OUTPUT: 'List with 2 items'",
-     "SPEECH OUTPUT: 'Nesting level 1'",
+    ["BRAILLE LINE:  'â?¢ was a composer who was not square'",
+     "     VISIBLE:  'â?¢ was a composer who was not squ', cursor=1",
+     "SPEECH OUTPUT: 'List with 2 items'",
+     "SPEECH OUTPUT: 'Nesting level 1'",
      "SPEECH OUTPUT: 'bullet was a composer who was not square'"]))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("<Shift>l"))
 sequence.append(utils.AssertPresentationAction(
     "shift + l", 
-     ["BRAILLE LINE:  'â?¢ if your TYPE is circle'",
-     "     VISIBLE:  'â?¢ if your TYPE is circle', cursor=1",
-     "SPEECH OUTPUT: 'List with 2 items'",
-     "SPEECH OUTPUT: 'Nesting level 2'",
+     ["BRAILLE LINE:  'â?¢ if your TYPE is circle'",
+     "     VISIBLE:  'â?¢ if your TYPE is circle', cursor=1",
+     "SPEECH OUTPUT: 'List with 2 items'",
+     "SPEECH OUTPUT: 'Nesting level 2'",
      "SPEECH OUTPUT: 'bullet if your TYPE is circle'"]))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("<Shift>l"))
 sequence.append(utils.AssertPresentationAction(
     "shift + l", 
-    ["BRAILLE LINE:  'â?¢ each sublevel'",
-     "     VISIBLE:  'â?¢ each sublevel', cursor=1",
-     "SPEECH OUTPUT: 'List with 2 items'",
-     "SPEECH OUTPUT: 'Nesting level 3'",
+    ["BRAILLE LINE:  'â?¢ each sublevel'",
+     "     VISIBLE:  'â?¢ each sublevel', cursor=1",
+     "SPEECH OUTPUT: 'List with 2 items'",
+     "SPEECH OUTPUT: 'Nesting level 3'",
      "SPEECH OUTPUT: 'bullet each sublevel'"]))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("<Shift>l"))
 sequence.append(utils.AssertPresentationAction(
     "shift + l", 
-    ["BRAILLE LINE:  'â?¢ look for the bullet on'",
-     "     VISIBLE:  'â?¢ look for the bullet on', cursor=1",
-     "SPEECH OUTPUT: 'List with 2 items'",
-     "SPEECH OUTPUT: 'Nesting level 2'",
+    ["BRAILLE LINE:  'â?¢ look for the bullet on'",
+     "     VISIBLE:  'â?¢ look for the bullet on', cursor=1",
+     "SPEECH OUTPUT: 'List with 2 items'",
+     "SPEECH OUTPUT: 'Nesting level 2'",
      "SPEECH OUTPUT: 'bullet look for the bullet on'"]))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("<Shift>l"))
 sequence.append(utils.AssertPresentationAction(
     "shift + l", 
-    ["BRAILLE LINE:  'â?¢ first sublevel'",
-     "     VISIBLE:  'â?¢ first sublevel', cursor=1",
-     "SPEECH OUTPUT: 'List with 2 items'",
-     "SPEECH OUTPUT: 'Nesting level 1'",
+    ["BRAILLE LINE:  'â?¢ first sublevel'",
+     "     VISIBLE:  'â?¢ first sublevel', cursor=1",
+     "SPEECH OUTPUT: 'List with 2 items'",
+     "SPEECH OUTPUT: 'Nesting level 1'",
      "SPEECH OUTPUT: 'bullet first sublevel'"]))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("<Shift>l"))
 sequence.append(utils.AssertPresentationAction(
     "shift + l", 
-    ["BRAILLE LINE:  'â?¢ listing item'",
-     "     VISIBLE:  'â?¢ listing item', cursor=1",
-     "SPEECH OUTPUT: 'List with 2 items'",
+    ["BRAILLE LINE:  'â?¢ listing item'",
+     "     VISIBLE:  'â?¢ listing item', cursor=1",
+     "SPEECH OUTPUT: 'List with 2 items'",
      "SPEECH OUTPUT: 'bullet listing item'"]))
 
 sequence.append(utils.StartRecordingAction())
diff --git a/test/keystrokes/firefox/line_nav_bug_577239.py b/test/keystrokes/firefox/line_nav_bug_577239.py
index fe0464b..16e2c0f 100644
--- a/test/keystrokes/firefox/line_nav_bug_577239.py
+++ b/test/keystrokes/firefox/line_nav_bug_577239.py
@@ -41,6 +41,7 @@ sequence.append(PauseAction(3000))
 sequence.append(KeyComboAction("<Alt>p"))
 sequence.append(WaitForFocus("Position cursor at start of line when navigating vertically", acc_role=pyatspi.ROLE_CHECK_BOX))
 sequence.append(KeyComboAction("<Alt>o"))
+sequence.append(PauseAction(1000))
 sequence.append(TypeAction(" "))
 sequence.append(WaitForWindowActivate(utils.firefoxFrameNames, None))
 
@@ -202,6 +203,7 @@ sequence.append(PauseAction(3000))
 sequence.append(KeyComboAction("<Alt>p"))
 sequence.append(WaitForFocus("Position cursor at start of line when navigating vertically", acc_role=pyatspi.ROLE_CHECK_BOX))
 sequence.append(KeyComboAction("<Alt>o"))
+sequence.append(PauseAction(1000))
 sequence.append(TypeAction(" "))
 sequence.append(WaitForWindowActivate(utils.firefoxFrameNames, None))
 
diff --git a/test/keystrokes/firefox/line_nav_bug_577979.py b/test/keystrokes/firefox/line_nav_bug_577979.py
index 16fe00a..5e9bb9b 100644
--- a/test/keystrokes/firefox/line_nav_bug_577979.py
+++ b/test/keystrokes/firefox/line_nav_bug_577979.py
@@ -41,6 +41,7 @@ sequence.append(PauseAction(3000))
 sequence.append(KeyComboAction("<Alt>p"))
 sequence.append(WaitForFocus("Position cursor at start of line when navigating vertically", acc_role=pyatspi.ROLE_CHECK_BOX))
 sequence.append(KeyComboAction("<Alt>o"))
+sequence.append(PauseAction(1000))
 sequence.append(TypeAction(" "))
 sequence.append(WaitForWindowActivate(utils.firefoxFrameNames, None))
 
@@ -149,6 +150,7 @@ sequence.append(PauseAction(3000))
 sequence.append(KeyComboAction("<Alt>p"))
 sequence.append(WaitForFocus("Position cursor at start of line when navigating vertically", acc_role=pyatspi.ROLE_CHECK_BOX))
 sequence.append(KeyComboAction("<Alt>o"))
+sequence.append(PauseAction(1000))
 sequence.append(TypeAction(" "))
 sequence.append(WaitForWindowActivate(utils.firefoxFrameNames, None))
 
diff --git a/test/keystrokes/firefox/link_where_am_i.py b/test/keystrokes/firefox/link_where_am_i.py
index 52c2d3f..9f9cf5c 100644
--- a/test/keystrokes/firefox/link_where_am_i.py
+++ b/test/keystrokes/firefox/link_where_am_i.py
@@ -39,7 +39,7 @@ sequence.append(utils.AssertPresentationAction(
     "Where Am I on Product summary link", 
     ["BRAILLE LINE:  '3. Product summary (designed for maintainers)'",
      "     VISIBLE:  'Product summary (designed for ma', cursor=1",
-     "SPEECH OUTPUT: 'http link Product summary different site '"]))
+     "SPEECH OUTPUT: 'http link Product summary different site'"]))
 
 ########################################################################
 # Go home tab and do a Where Am I
@@ -55,7 +55,7 @@ sequence.append(utils.AssertPresentationAction(
     "Where Am I on New bug link", 
     ["BRAILLE LINE:  'New bug · Browse · Search · Reports · Account · Admin · Help Logged In william walker sun com | Log Out'",
      "     VISIBLE:  'New bug · Browse · Search · Repo', cursor=1",
-     "SPEECH OUTPUT: 'http link New bug different site '"]))
+     "SPEECH OUTPUT: 'http link New bug different site'"]))
 
 ########################################################################
 # Shift+Tab back to the footprint
@@ -67,10 +67,10 @@ sequence.append(KeyComboAction("KP_Enter"))
 sequence.append(PauseAction(3000))
 sequence.append(utils.AssertPresentationAction(
     "Where Am I on footprint", 
-    ["BUG? - We are speaking 'link' rather than 'image'",
+    ["BUG? - Is it redundant to speak link twice?",
      "BRAILLE LINE:  'Home Image Bugzilla'",
      "     VISIBLE:  'Home Image Bugzilla', cursor=1",
-     "SPEECH OUTPUT: 'http link Home image different site '"]))
+     "SPEECH OUTPUT: 'http link Home link image different site'"]))
 
 ########################################################################
 # Move to the location bar by pressing Control+L.  When it has focus
diff --git a/test/keystrokes/firefox/moz_menu.py b/test/keystrokes/firefox/moz_menu.py
index 507fc10..5415afe 100644
--- a/test/keystrokes/firefox/moz_menu.py
+++ b/test/keystrokes/firefox/moz_menu.py
@@ -47,7 +47,7 @@ sequence.append(utils.AssertPresentationAction(
     "basic whereAmI", 
     ["BRAILLE LINE:  'Edit'",
      "     VISIBLE:  'Edit', cursor=1",
-     "SPEECH OUTPUT: 'Edit section Edit  item 1 of 1'"]))
+     "SPEECH OUTPUT: 'Edit item 1 of 1'"]))
 
 ########################################################################
 # Use arrows to navigate menu structure.
diff --git a/test/keystrokes/firefox/moz_progressbar.py b/test/keystrokes/firefox/moz_progressbar.py
index 5f15894..746b27a 100644
--- a/test/keystrokes/firefox/moz_progressbar.py
+++ b/test/keystrokes/firefox/moz_progressbar.py
@@ -141,16 +141,16 @@ sequence.append(WaitAction("object:property-change:accessible-value",
 
 sequence.append(utils.AssertPresentationAction(
     "progress output", 
-    ["SPEECH OUTPUT: '10 percent. '",
-     "SPEECH OUTPUT: '20 percent. '",
-     "SPEECH OUTPUT: '30 percent. '",
-     "SPEECH OUTPUT: '40 percent. '",
-     "SPEECH OUTPUT: '50 percent. '",
-     "SPEECH OUTPUT: '60 percent. '",
-     "SPEECH OUTPUT: '70 percent. '",
-     "SPEECH OUTPUT: '80 percent. '",
-     "SPEECH OUTPUT: '90 percent. '",
-     "SPEECH OUTPUT: '100 percent. '"]))
+    ["SPEECH OUTPUT: '10 percent'",
+     "SPEECH OUTPUT: '20 percent'",
+     "SPEECH OUTPUT: '30 percent'",
+     "SPEECH OUTPUT: '40 percent'",
+     "SPEECH OUTPUT: '50 percent'",
+     "SPEECH OUTPUT: '60 percent'",
+     "SPEECH OUTPUT: '70 percent'",
+     "SPEECH OUTPUT: '80 percent'",
+     "SPEECH OUTPUT: '90 percent'",
+     "SPEECH OUTPUT: '100 percent'"]))
 
 ########################################################################
 # Close the demo
diff --git a/test/keystrokes/firefox/moz_slider.py b/test/keystrokes/firefox/moz_slider.py
index 55d1a2b..6ca62fc 100644
--- a/test/keystrokes/firefox/moz_slider.py
+++ b/test/keystrokes/firefox/moz_slider.py
@@ -43,10 +43,10 @@ sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("KP_Enter"))
 sequence.append(PauseAction(3000))
 sequence.append(utils.AssertPresentationAction(
-    "basic whereAmI", 
+    "basic whereAmI",
     ["BRAILLE LINE:  '10% Slider'",
      "     VISIBLE:  '10% Slider', cursor=1",
-     "SPEECH OUTPUT: 'slider 10.0 10 percent '"]))
+     "SPEECH OUTPUT: 'slider 10% 10 percent'"]))
 
 ########################################################################
 # Move the slider several times.  The following will be presented for each.
diff --git a/test/keystrokes/firefox/moz_tabpanel.py b/test/keystrokes/firefox/moz_tabpanel.py
index 8a24843..301b8c8 100644
--- a/test/keystrokes/firefox/moz_tabpanel.py
+++ b/test/keystrokes/firefox/moz_tabpanel.py
@@ -40,7 +40,7 @@ sequence.append(utils.AssertPresentationAction(
     "basic whereAmI", 
     ["BRAILLE LINE:  'Tab Zero Page Tab One Page Tab Two Page Tab Three Page Tab Four Page'",
      "     VISIBLE:  'Tab Zero Page Tab One Page Tab T', cursor=1",
-     "SPEECH OUTPUT: 'tab list Tab Zero page item 1 of 5 '"]))
+     "SPEECH OUTPUT: 'tab list Tab Zero page item 1 of 5'"]))
 
 ########################################################################
 # Move to tab 2.
diff --git a/test/keystrokes/firefox/uiuc_button.py b/test/keystrokes/firefox/uiuc_button.py
index ae35b89..92529f9 100644
--- a/test/keystrokes/firefox/uiuc_button.py
+++ b/test/keystrokes/firefox/uiuc_button.py
@@ -48,7 +48,7 @@ sequence.append(utils.AssertPresentationAction(
     "basic whereamI", 
     ["BRAILLE LINE:  'Font Larger + Button Font Smaller - Button &=y Italic i ToggleButton Bold B Button'",
      "     VISIBLE:  'Font Larger + Button Font Smalle', cursor=1",
-     "SPEECH OUTPUT: 'Font Larger + button '"]))
+     "SPEECH OUTPUT: 'Font Larger + button'"]))
 
 ########################################################################
 # Now push the first button.  The following will be presented.
diff --git a/test/keystrokes/firefox/uiuc_grid.py b/test/keystrokes/firefox/uiuc_grid.py
index eff1aab..8e1841c 100644
--- a/test/keystrokes/firefox/uiuc_grid.py
+++ b/test/keystrokes/firefox/uiuc_grid.py
@@ -48,10 +48,10 @@ sequence.append(KeyComboAction("KP_Enter"))
 sequence.append(PauseAction(3000))
 sequence.append(utils.AssertPresentationAction(
     "basic whereAmI", 
-    ["BRAILLE LINE:  'Email 0 Selected CheckBox 1 Cell Read message Image Attachment Image Lowest priority Image John Smith Cell Trip to Florida Cell 2007-10-03 Cell 2K Cell'",
+    ["BUG? - This amount of verbosity in the speech is not the result of the refactor, but it would be nice to figure out why we're doing it",
+     "BRAILLE LINE:  'Email 0 Selected CheckBox 1 Cell Read message Image Attachment Image Lowest priority Image John Smith Cell Trip to Florida Cell 2007-10-03 Cell 2K Cell'",
      "     VISIBLE:  'Email 0 Selected CheckBox 1 Cell', cursor=1",
-     "SPEECH OUTPUT: '1 Read message Attachment Lowest priority From John Smith Subject Trip to Florida panel'",
-     "SPEECH OUTPUT: '1 Read message Attachment Lowest priority John Smith Trip to Florida'"]))
+     "SPEECH OUTPUT: '1 Read message Attachment Lowest priority From John Smith Subject Trip to Florida panel 1 Read message Attachment Lowest priority John Smith Trip to Florida'"]))
 
 ########################################################################
 # Move down grid
diff --git a/test/keystrokes/firefox/uiuc_radiobutton.py b/test/keystrokes/firefox/uiuc_radiobutton.py
index 372ee9c..1660b7f 100644
--- a/test/keystrokes/firefox/uiuc_radiobutton.py
+++ b/test/keystrokes/firefox/uiuc_radiobutton.py
@@ -47,7 +47,7 @@ sequence.append(utils.AssertPresentationAction(
     "basic whereamI", 
     ["BRAILLE LINE:  '& y Thai RadioButton'",
      "     VISIBLE:  '& y Thai RadioButton', cursor=1",
-     "SPEECH OUTPUT: 'Lunch Options Thai radio button not selected  '"]))
+     "SPEECH OUTPUT: 'Lunch Options Thai radio button not selected'"]))
 
 ########################################################################
 # Move to the second radio button.
diff --git a/test/keystrokes/firefox/uiuc_slider.py b/test/keystrokes/firefox/uiuc_slider.py
index 2e830f3..e1007a3 100644
--- a/test/keystrokes/firefox/uiuc_slider.py
+++ b/test/keystrokes/firefox/uiuc_slider.py
@@ -46,7 +46,7 @@ sequence.append(utils.AssertPresentationAction(
     "basic whereAmI", 
     ["BRAILLE LINE:  'Slider Control 1 50 Slider'",
      "     VISIBLE:  'Slider Control 1 50 Slider', cursor=1",
-     "SPEECH OUTPUT: 'Slider Control 1 slider 50.0 50 percent '"]))
+     "SPEECH OUTPUT: 'Slider Control 1 slider 50 50 percent'"]))
     
 ########################################################################
 # Increment slider several times
diff --git a/test/keystrokes/firefox/uiuc_tabpanel.py b/test/keystrokes/firefox/uiuc_tabpanel.py
index 7035d9b..3d769d5 100644
--- a/test/keystrokes/firefox/uiuc_tabpanel.py
+++ b/test/keystrokes/firefox/uiuc_tabpanel.py
@@ -57,7 +57,7 @@ sequence.append(utils.AssertPresentationAction(
     "basic whereAmI", 
     ["BRAILLE LINE:  'Crust Page Veges Page Carnivore Page Delivery Page'",
      "     VISIBLE:  'Carnivore Page Delivery Page', cursor=1",
-     "SPEECH OUTPUT: 'tab list Carnivore page item 3 of 4 '"]))
+     "SPEECH OUTPUT: 'tab list Carnivore page item 3 of 4'"]))
 
 ########################################################################
 # Navigate to fourth tab
diff --git a/test/keystrokes/firefox/xul_role_accel_label.py b/test/keystrokes/firefox/xul_role_accel_label.py
index 2ea4ff1..fe4bac0 100755
--- a/test/keystrokes/firefox/xul_role_accel_label.py
+++ b/test/keystrokes/firefox/xul_role_accel_label.py
@@ -70,8 +70,7 @@ sequence.append(utils.AssertPresentationAction(
     "Basic Where Am I", 
     ["BRAILLE LINE:  '" + utils.firefoxAppNames + " Application " + utils.firefoxFrameNames + " Frame ToolBar Application MenuBar Open File...\(Control O\)'",
      "     VISIBLE:  'Open File...(Control O)', cursor=1",
-     "SPEECH OUTPUT: 'tool bar'",
-     "SPEECH OUTPUT: 'File menu Open Fileâ?¦ Control O item 4 of [0-9]+ '"]))
+     "SPEECH OUTPUT: 'tool bar File menu Open Fileâ?¦ Control O item 4 of [0-9]+'"]))
 
 ########################################################################
 # Dismiss the menu by pressing Escape and wait for the location bar
diff --git a/test/keystrokes/firefox/xul_role_check_menu_item.py b/test/keystrokes/firefox/xul_role_check_menu_item.py
index 02a80f7..4ec0cb1 100755
--- a/test/keystrokes/firefox/xul_role_check_menu_item.py
+++ b/test/keystrokes/firefox/xul_role_check_menu_item.py
@@ -49,8 +49,7 @@ sequence.append(utils.AssertPresentationAction(
     "Basic Where Am I", 
     ["BRAILLE LINE:  '" + utils.firefoxAppNames + " Application " + utils.firefoxFrameNames + " Frame ToolBar Application MenuBar < > Full Screen CheckItem\(F11\)'",
      "     VISIBLE:  '< > Full Screen CheckItem(F11)', cursor=1",
-     "SPEECH OUTPUT: 'tool bar'",
-     "SPEECH OUTPUT: 'View menu Full Screen check item not checked F11 item 10 of 10 '"]))
+     "SPEECH OUTPUT: 'tool bar View menu Full Screen check item not checked F11 item 10 of 10'"]))
 
 ########################################################################
 # Dismiss the menu by pressing Escape and wait for the location bar
diff --git a/test/keystrokes/firefox/xul_role_combo_box.py b/test/keystrokes/firefox/xul_role_combo_box.py
index 3d48d94..3c0945c 100644
--- a/test/keystrokes/firefox/xul_role_combo_box.py
+++ b/test/keystrokes/firefox/xul_role_combo_box.py
@@ -159,9 +159,10 @@ sequence.append(KeyComboAction("KP_Enter"))
 sequence.append(PauseAction(3000))
 sequence.append(utils.AssertPresentationAction(
     "Basic Where Am I", 
-    ["BRAILLE LINE:  '" + utils.firefoxAppNames + " Application " + utils.firefoxAppNames + " Preferences Frame Main ScrollPane Startup Panel  ComboShow a blank pageWhen " + utils.firefoxAppNames + " starts:  Show a blank page'",
+    ["BUG? - We claim this is item 1 of 1. This is how it was before the refactor as well.",
+     "BRAILLE LINE:  '" + utils.firefoxAppNames + " Application " + utils.firefoxAppNames + " Preferences Frame Main ScrollPane Startup Panel  ComboShow a blank pageWhen " + utils.firefoxAppNames + " starts:  Show a blank page'",
      "     VISIBLE:  'Show a blank page', cursor=1",
-     "SPEECH OUTPUT: 'Show a blank page combo box Show a blank page  item 1 of 1 '"]))
+     "SPEECH OUTPUT: 'When " + utils.firefoxAppNames + " starts: combo box Show a blank page item 1 of 1'"]))
 
 ########################################################################
 # Press Shift+Tab to move back to the Main list item.
diff --git a/test/keystrokes/firefox/xul_role_page_tab.py b/test/keystrokes/firefox/xul_role_page_tab.py
index 81c1fc2..f8bf441 100755
--- a/test/keystrokes/firefox/xul_role_page_tab.py
+++ b/test/keystrokes/firefox/xul_role_page_tab.py
@@ -33,7 +33,7 @@ sequence.append(utils.AssertPresentationAction(
     "Basic Where Am I", 
     ["BRAILLE LINE:  '" + utils.firefoxAppNames + " Application Print Dialog General Page'",
      "     VISIBLE:  'General Page', cursor=1",
-     "SPEECH OUTPUT: 'tab list General page item 1 of [0-9]+ '"]))
+     "SPEECH OUTPUT: 'tab list General page item 1 of [0-9]+'"]))
 
 ########################################################################
 # Right Arrow to move to the second page tab.  
@@ -56,7 +56,7 @@ sequence.append(utils.AssertPresentationAction(
     "Basic Where Am I", 
     ["BRAILLE LINE:  '" + utils.firefoxAppNames + " Application Print Dialog Page Setup Page'",
      "     VISIBLE:  'Page Setup Page', cursor=1",
-     "SPEECH OUTPUT: 'tab list Page Setup page item 2 of [0-9]+ '"]))
+     "SPEECH OUTPUT: 'tab list Page Setup page item 2 of [0-9]+'"]))
 
 ########################################################################
 # Left Arrow to move to the first page tab.  
diff --git a/test/keystrokes/firefox/xul_role_radio_menu_item.py b/test/keystrokes/firefox/xul_role_radio_menu_item.py
index 783d63d..7848284 100755
--- a/test/keystrokes/firefox/xul_role_radio_menu_item.py
+++ b/test/keystrokes/firefox/xul_role_radio_menu_item.py
@@ -49,8 +49,7 @@ sequence.append(utils.AssertPresentationAction(
     "Basic Where Am I", 
     ["BRAILLE LINE:  '" + utils.firefoxAppNames + " Application " + utils.firefoxFrameNames + " Frame ToolBar Application MenuBar View Menu & y No Style RadioItem'",
      "     VISIBLE:  '& y No Style RadioItem', cursor=1",
-     "SPEECH OUTPUT: 'tool bar'",
-     "SPEECH OUTPUT: 'Page Style menu No Style radio menu item not selected  item 1 of 2 '"]))
+     "SPEECH OUTPUT: 'tool bar View menu Page Style menu No Style radio menu item not selected item 1 of 2'"]))
 
 ########################################################################
 # Down Arrow to the "Basic Page Style" radio menu item.
@@ -73,8 +72,7 @@ sequence.append(utils.AssertPresentationAction(
     "Basic Where Am I", 
     ["BRAILLE LINE:  '" + utils.firefoxAppNames + " Application " + utils.firefoxFrameNames + " Frame ToolBar Application MenuBar View Menu &=y Basic Page Style RadioItem'",
      "     VISIBLE:  '&=y Basic Page Style RadioItem', cursor=1",
-     "SPEECH OUTPUT: 'tool bar'",
-     "SPEECH OUTPUT: 'Page Style menu Basic Page Style radio menu item selected  item 2 of 2 '"]))
+     "SPEECH OUTPUT: 'tool bar View menu Page Style menu Basic Page Style radio menu item selected item 2 of 2'"]))
 
 ########################################################################
 # Dismiss the "Page Style" menu by pressing Escape.
diff --git a/test/keystrokes/firefox/xul_role_tree.py b/test/keystrokes/firefox/xul_role_tree.py
index d15cb3c..2bfac99 100644
--- a/test/keystrokes/firefox/xul_role_tree.py
+++ b/test/keystrokes/firefox/xul_role_tree.py
@@ -181,7 +181,7 @@ sequence.append(utils.AssertPresentationAction(
     "Tab back to tree table",
     ["BRAILLE LINE:  '" + utils.firefoxAppNames + " Application Library Frame ScrollPane TreeTable Name ColumnHeader Bookmarks Toolbar   TREE LEVEL 1'",
      "     VISIBLE:  'Bookmarks Toolbar   TREE LEVEL 1', cursor=1",
-     "SPEECH OUTPUT: 'Name column header Bookmarks Toolbar blank blank not selected'"]))
+     "SPEECH OUTPUT: 'Name column header Bookmarks Toolbar not selected'"]))
 
 ########################################################################
 # Now that the Places Manager is back to its pre-explored state,
diff --git a/test/keystrokes/firefox/xul_role_tree_table.py b/test/keystrokes/firefox/xul_role_tree_table.py
index 0fd3c88..dfb592e 100644
--- a/test/keystrokes/firefox/xul_role_tree_table.py
+++ b/test/keystrokes/firefox/xul_role_tree_table.py
@@ -51,7 +51,7 @@ sequence.append(utils.AssertPresentationAction(
     "Down Arrow in tree table",
     ["BRAILLE LINE:  '" + utils.firefoxAppNames + " Application Library Frame ScrollPane TreeTable Name ColumnHeader Bookmarks Menu   TREE LEVEL 1'",
      "     VISIBLE:  'Bookmarks Menu   TREE LEVEL 1', cursor=1",
-     "SPEECH OUTPUT: 'Bookmarks Menu blank blank'"]))
+     "SPEECH OUTPUT: 'Bookmarks Menu'"]))
 
 ########################################################################
 # Do a basic "Where Am I" via KP_Enter. 
@@ -63,9 +63,7 @@ sequence.append(utils.AssertPresentationAction(
     "Basic Where Am I", 
     ["BRAILLE LINE:  '" + utils.firefoxAppNames + " Application Library Frame ScrollPane TreeTable Name ColumnHeader Bookmarks Menu TREE LEVEL 1'",
      "     VISIBLE:  'Bookmarks Menu TREE LEVEL 1', cursor=1",
-     "SPEECH OUTPUT: 'tree table Name cell Bookmarks Menu'",
-     "SPEECH OUTPUT: 'column 1 of 3 row 2 of 3'",
-     "SPEECH OUTPUT: 'tree level 1'"]))
+     "SPEECH OUTPUT: 'tree table Name cell Bookmarks Menu column 1 of 3 row 2 of 3 tree level 1'"]))
 
 ########################################################################
 # Press Up Arrow to return to the previous item.
@@ -76,7 +74,7 @@ sequence.append(utils.AssertPresentationAction(
     "Up Arrow in tree table",
     ["BRAILLE LINE:  '" + utils.firefoxAppNames + " Application Library Frame ScrollPane TreeTable Name ColumnHeader Bookmarks Toolbar   TREE LEVEL 1'",
      "     VISIBLE:  'Bookmarks Toolbar   TREE LEVEL 1', cursor=1",
-     "SPEECH OUTPUT: 'Bookmarks Toolbar blank blank'"]))
+     "SPEECH OUTPUT: 'Bookmarks Toolbar'"]))
 
 ########################################################################
 # Press Alt F4 to close the window.
diff --git a/test/keystrokes/firefox/xul_where_am_i_status_bar.py b/test/keystrokes/firefox/xul_where_am_i_status_bar.py
index 6139c9d..82cc88f 100644
--- a/test/keystrokes/firefox/xul_where_am_i_status_bar.py
+++ b/test/keystrokes/firefox/xul_where_am_i_status_bar.py
@@ -70,7 +70,7 @@ sequence.append(utils.AssertPresentationAction(
      "BRAILLE LINE:  'Who expects the Spanish Inquisition? Button'",
      "     VISIBLE:  'Who expects the Spanish Inquisit', cursor=1",
      "SPEECH OUTPUT: 'Status Bar Regression Test - " + utils.firefoxFrameNames + "'",
-     "SPEECH OUTPUT: 'NOBODY expects the Spanish Inquisition! '"]))
+     "SPEECH OUTPUT: 'NOBODY expects the Spanish Inquisition!'"]))
 
 ########################################################################
 # Press Tab to the second push button and press it with space bar.  
@@ -105,7 +105,7 @@ sequence.append(utils.AssertPresentationAction(
      "BRAILLE LINE:  'Our chief weapon is... Button'",
      "     VISIBLE:  'Our chief weapon is... Button', cursor=1",
      "SPEECH OUTPUT: 'Status Bar Regression Test - " + utils.firefoxFrameNames + "'",
-     "SPEECH OUTPUT: 'Surprise. Surprise and fear. Fear and surprise... And ruthless efficiency... And an almost fanatical devotion to the Pope... And nice red uniforms. '"]))
+     "SPEECH OUTPUT: 'Surprise. Surprise and fear. Fear and surprise... And ruthless efficiency... And an almost fanatical devotion to the Pope... And nice red uniforms.'"]))
 
 ########################################################################
 # Press Tab to the third push button and press it with space bar.  
@@ -140,7 +140,7 @@ sequence.append(utils.AssertPresentationAction(
      "BRAILLE LINE:  'Fetch the COMFY CHAIR (AKA clear out the status bar) Button'",
      "     VISIBLE:  'Fetch the COMFY CHAIR (AKA clear', cursor=1",
      "SPEECH OUTPUT: 'Status Bar Regression Test - " + utils.firefoxFrameNames + "'",
-     "SPEECH OUTPUT: 'Done '"]))
+     "SPEECH OUTPUT: 'Done'"]))
 
 ########################################################################
 # Move to the location bar by pressing Control+L.  When it has focus
diff --git a/test/keystrokes/firefox/yahoo_tab_view.py b/test/keystrokes/firefox/yahoo_tab_view.py
index b4e2f92..dec5be1 100644
--- a/test/keystrokes/firefox/yahoo_tab_view.py
+++ b/test/keystrokes/firefox/yahoo_tab_view.py
@@ -84,7 +84,7 @@ sequence.append(utils.AssertPresentationAction(
     "basic whereAmI", 
     ["BRAILLE LINE:  'Opera Page Firefox Page Explorer Page Safari Page'",
      "     VISIBLE:  'Safari Page', cursor=1",
-     "SPEECH OUTPUT: 'tab list Safari page item 4 of 4 '"]))
+     "SPEECH OUTPUT: 'tab list Safari page item 4 of 4'"]))
 
 ########################################################################
 # Left Arrow back to the third tab
diff --git a/test/keystrokes/gtk-demo/role_column_header.py b/test/keystrokes/gtk-demo/role_column_header.py
index d54ce12..168a3b9 100644
--- a/test/keystrokes/gtk-demo/role_column_header.py
+++ b/test/keystrokes/gtk-demo/role_column_header.py
@@ -105,12 +105,24 @@ sequence.append(utils.AssertPresentationAction(
     "Normal cell basic Where Am I",
     ["BRAILLE LINE:  'gtk-demo Application GtkListStore demo Frame ScrollPane Table Severity ColumnHeader < > Fixed? 60482 Normal scrollable notebooks and hidden tabs'",
      "     VISIBLE:  'Normal scrollable notebooks and ', cursor=1",
-     "SPEECH OUTPUT: 'table Severity cell Normal'",
-     "SPEECH OUTPUT: 'column 3 of 4 row 1 of 14'"]))
+     "SPEECH OUTPUT: 'table Severity cell Normal column 3 of 4 row 1 of 14'"]))
 
 ########################################################################
 # Do a detailed "Where Am I" via KP_Enter.
 #
+# JD to WDW: This test was changed. Multiple utterances are now just
+# one. Do we care? 
+#
+# WDW to JD: No - it's OK.  If pauses are needed (which we can learn via
+# end user testing), we can add '+ ["."] +' things in the formatting
+# strings as needed.
+#
+# In addition, something I would consider a bug fix: Before we
+# didn't speak the name ("Fixed?") of the checkbox at the start of the row;
+# now we do.
+#
+# WDW to JD: I agree.
+#
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("KP_Enter"))
 sequence.append(KeyComboAction("KP_Enter"))
@@ -121,11 +133,8 @@ sequence.append(utils.AssertPresentationAction(
      "     VISIBLE:  'Normal scrollable notebooks and ', cursor=1",
      "BRAILLE LINE:  'gtk-demo Application GtkListStore demo Frame ScrollPane Table Severity ColumnHeader < > Fixed? 60482 Normal scrollable notebooks and hidden tabs'",
      "     VISIBLE:  'Normal scrollable notebooks and ', cursor=1",
-     "SPEECH OUTPUT: 'table Severity cell Normal'",
-     "SPEECH OUTPUT: 'column 3 of 4 row 1 of 14'",
-     "SPEECH OUTPUT: 'table Severity cell Normal'",
-     "SPEECH OUTPUT: 'column 3 of 4 row 1 of 14'",
-     "SPEECH OUTPUT: 'check box not checked 60482 Normal scrollable notebooks and hidden tabs'"]))
+     "SPEECH OUTPUT: 'table Severity cell Normal column 3 of 4 row 1 of 14'",
+     "SPEECH OUTPUT: 'table Severity cell Normal column 3 of 4 row 1 of 14 Fixed? check box not checked 60482 Normal scrollable notebooks and hidden tabs'"]))
 
 ########################################################################
 # Now move to the cell to the left containing the number "60482".
@@ -169,8 +178,7 @@ sequence.append(utils.AssertPresentationAction(
     "Checkbox cell basic Where Am I",
     ["BRAILLE LINE:  'gtk-demo Application GtkListStore demo Frame ScrollPane Table Fixed? ColumnHeader < > Fixed? 60482 Normal scrollable notebooks and hidden tabs'",
      "     VISIBLE:  '< > Fixed? 60482 Normal scrollab', cursor=1",
-     "SPEECH OUTPUT: 'table Fixed? cell check box not checked'",
-     "SPEECH OUTPUT: 'column 1 of 4 row 1 of 14'"]))
+     "SPEECH OUTPUT: 'table Fixed? cell check box not checked column 1 of 4 row 1 of 14'"]))
 
 ########################################################################
 # Do a detailed "Where Am I" via KP_Enter.
@@ -185,11 +193,8 @@ sequence.append(utils.AssertPresentationAction(
      "     VISIBLE:  '< > Fixed? 60482 Normal scrollab', cursor=1",
      "BRAILLE LINE:  'gtk-demo Application GtkListStore demo Frame ScrollPane Table Fixed? ColumnHeader < > Fixed? 60482 Normal scrollable notebooks and hidden tabs'",
      "     VISIBLE:  '< > Fixed? 60482 Normal scrollab', cursor=1",
-     "SPEECH OUTPUT: 'table Fixed? cell check box not checked'",
-     "SPEECH OUTPUT: 'column 1 of 4 row 1 of 14'",
-     "SPEECH OUTPUT: 'table Fixed? cell check box not checked'",
-     "SPEECH OUTPUT: 'column 1 of 4 row 1 of 14'",
-     "SPEECH OUTPUT: 'check box not checked 60482 Normal scrollable notebooks and hidden tabs'"]))
+     "SPEECH OUTPUT: 'table Fixed? cell check box not checked column 1 of 4 row 1 of 14'",
+     "SPEECH OUTPUT: 'table Fixed? cell check box not checked column 1 of 4 row 1 of 14 Fixed? check box not checked 60482 Normal scrollable notebooks and hidden tabs'"]))
  
 ########################################################################
 # Close the GtkListStore demo
diff --git a/test/keystrokes/gtk-demo/role_combo_box.py b/test/keystrokes/gtk-demo/role_combo_box.py
index e5da7de..dfc1a04 100644
--- a/test/keystrokes/gtk-demo/role_combo_box.py
+++ b/test/keystrokes/gtk-demo/role_combo_box.py
@@ -43,6 +43,13 @@ sequence.append(utils.AssertPresentationAction(
 ########################################################################
 # Do a basic "Where Am I" via KP_Enter.
 #
+# JD to WDW: This to me looks like two bug fixes:
+# 1) Lost a trailing space
+# 2) Before we were saying "menu"; I don't think we should in a combo box,
+#    even though technically there is a menu in between a combo box and
+#    the options it contains in the hierarchy.
+# Question: Should we be presenting the containing panel or not?
+#
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("KP_Enter"))
 sequence.append(PauseAction(3000))
@@ -50,7 +57,7 @@ sequence.append(utils.AssertPresentationAction(
     "Warning combo box item Where Am I",
     ["BRAILLE LINE:  'gtk-demo Application Combo boxes Frame Some stock icons Panel  ComboWarning Warning'",
      "     VISIBLE:  'Warning', cursor=1",
-     "SPEECH OUTPUT: 'menu Warning  item 1 of 5 '"]))
+     "SPEECH OUTPUT: 'Some stock icons panel Warning item 1 of 5'"]))
 
 ########################################################################
 # Now arrow down and select the "New" item.
@@ -67,6 +74,13 @@ sequence.append(utils.AssertPresentationAction(
 ########################################################################
 # Do a basic "Where Am I" via KP_Enter.
 #
+# JD to WDW: This to me looks like two bug fixes:
+# 1) Lost a trailing space
+# 2) Before we were saying "menu"; I don't think we should in a combo box,
+#    even though technically there is a menu in between a combo box and
+#    the options it contains in the hierarchy.
+# Question: Should we be presenting the containing panel or not?
+#
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("KP_Enter"))
 sequence.append(PauseAction(3000))
@@ -74,7 +88,7 @@ sequence.append(utils.AssertPresentationAction(
     "New combo box item Where Am I",
     ["BRAILLE LINE:  'gtk-demo Application Combo boxes Frame Some stock icons Panel  ComboWarning New'",
      "     VISIBLE:  'New', cursor=1",
-     "SPEECH OUTPUT: 'menu New  item 3 of 5 '"]))
+     "SPEECH OUTPUT: 'Some stock icons panel New item 3 of 5'"]))
 
 ########################################################################
 # Select the "New" entry and tab to the editable text combo box.  Skip
@@ -203,8 +217,7 @@ sequence.append(utils.AssertPresentationAction(
     "Editable text combo box with selected text Where Am I",
     ["BRAILLE LINE:  'gtk-demo Application Combo boxes Frame Editable Panel Four $l'",
      "     VISIBLE:  'Four $l', cursor=5",
-     "SPEECH OUTPUT: 'text Four'",
-     "SPEECH OUTPUT: 'selected '"]))
+     "SPEECH OUTPUT: 'text Four selected'"]))
 
 ########################################################################
 # Tab to the triangular down arrow of the editable combo box and open
diff --git a/test/keystrokes/gtk-demo/role_label.py b/test/keystrokes/gtk-demo/role_label.py
index cc7d0b6..fcf13f4 100644
--- a/test/keystrokes/gtk-demo/role_label.py
+++ b/test/keystrokes/gtk-demo/role_label.py
@@ -69,6 +69,15 @@ sequence.append(utils.AssertPresentationAction(
 ########################################################################
 # Do an extended "Where Am I" via double KP_Enter.
 #
+# JD to WDW: The difference here is that we are not repeating the speech
+# output. Personally I think that's a good thing. Is there any reason
+# we should be duplicating output in the case of a detailed/extended
+# where am i?
+#
+# WDW to JD: the first should be for the first KP_Enter (i.e., basic
+# where am I) and the second should be for the second KP_Enter (i.e.,
+# extended where am I). So, why did we end up losing one?
+#
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("KP_Enter"))
 sequence.append(KeyComboAction("KP_Enter"))
@@ -80,8 +89,6 @@ sequence.append(utils.AssertPresentationAction(
      "BRAILLE LINE:  'gtk-demo Application Information Alert number of times: $l'",
      "     VISIBLE:  'number of times: $l', cursor=17",
      "SPEECH OUTPUT: 'This message box has been popped up the following",
-     "number of times: selected label'",
-     "SPEECH OUTPUT: 'This message box has been popped up the following",
      "number of times: selected label'"]))
 
 ########################################################################
@@ -139,6 +146,15 @@ sequence.append(utils.AssertPresentationAction(
 ########################################################################
 # Do an extended "Where Am I" via double KP_Enter.
 #
+# JD to WDW: The difference here is that we are not repeating the speech
+# output. Personally I think that's a good thing. Is there any reason
+# we should be duplicating output in the case of a detailed/extended
+# where am i?
+#
+# WDW to JD: the first should be for the first KP_Enter (i.e., basic
+# where am I) and the second should be for the second KP_Enter (i.e.,
+# extended where am I). So, why did we end up losing one?
+#
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("KP_Enter"))
 sequence.append(KeyComboAction("KP_Enter"))
@@ -150,8 +166,6 @@ sequence.append(utils.AssertPresentationAction(
      "BRAILLE LINE:  'gtk-demo Application Information Alert This message box has been popped up the following $l'",
      "     VISIBLE:  'This message box has been popped', cursor=5",
      "SPEECH OUTPUT: 'This message box has been popped up the following",
-     "number of times: selected label'",
-     "SPEECH OUTPUT: 'This message box has been popped up the following",
      "number of times: selected label'"]))
 
 ########################################################################
diff --git a/test/keystrokes/gtk-demo/role_menu.py b/test/keystrokes/gtk-demo/role_menu.py
index faa8133..c2fb37d 100644
--- a/test/keystrokes/gtk-demo/role_menu.py
+++ b/test/keystrokes/gtk-demo/role_menu.py
@@ -48,7 +48,7 @@ sequence.append(utils.AssertPresentationAction(
     "File menu Where Am I",
     ["BRAILLE LINE:  'gtk-demo Application Application Window Frame File Menu'",
      "     VISIBLE:  'File Menu', cursor=1",
-     "SPEECH OUTPUT: 'menu bar File menu  item 1 of 3 f'"]))
+     "SPEECH OUTPUT: 'menu bar File menu item 1 of 3 f'"]))
 
 ########################################################################
 # Right arrow to the "Preferences" menu.
@@ -73,7 +73,7 @@ sequence.append(utils.AssertPresentationAction(
     "Preferences menu Where Am I",
     ["BRAILLE LINE:  'gtk-demo Application Application Window Frame Preferences Menu'",
      "     VISIBLE:  'Preferences Menu', cursor=1",
-     "SPEECH OUTPUT: 'menu bar Preferences menu  item 2 of 3 p'"]))
+     "SPEECH OUTPUT: 'menu bar Preferences menu item 2 of 3 p'"]))
 
 ########################################################################
 # Go down to the "Color" menu.
@@ -98,7 +98,7 @@ sequence.append(utils.AssertPresentationAction(
     "Color menu Where Am I",
     ["BRAILLE LINE:  'gtk-demo Application Application Window Frame MenuBar Color Menu'",
      "     VISIBLE:  'Color Menu', cursor=1",
-     "SPEECH OUTPUT: 'Preferences menu Color menu  item 1 of 3 c'"]))
+     "SPEECH OUTPUT: 'Preferences menu Color menu item 1 of 3 c'"]))
 
 ########################################################################
 # Go down to the "Shape" menu.
@@ -123,7 +123,7 @@ sequence.append(utils.AssertPresentationAction(
     "Shape menu Where Am I",
     ["BRAILLE LINE:  'gtk-demo Application Application Window Frame MenuBar Shape Menu'",
      "     VISIBLE:  'Shape Menu', cursor=1",
-     "SPEECH OUTPUT: 'Preferences menu Shape menu  item 2 of 3 s'"]))
+     "SPEECH OUTPUT: 'Preferences menu Shape menu item 2 of 3 s'"]))
 
 ########################################################################
 # Dismiss the menu and close the Application Window demo window
diff --git a/test/keystrokes/gtk-demo/role_page_tab.py b/test/keystrokes/gtk-demo/role_page_tab.py
index 2984beb..09b1489 100644
--- a/test/keystrokes/gtk-demo/role_page_tab.py
+++ b/test/keystrokes/gtk-demo/role_page_tab.py
@@ -56,7 +56,7 @@ sequence.append(utils.AssertPresentationAction(
     "General page tab Where Am I",
     ["BRAILLE LINE:  'gtk-demo Application Print Dialog General Page'",
      "     VISIBLE:  'General Page', cursor=1",
-     "SPEECH OUTPUT: 'tab list General page item 1 of 2 '"]))
+     "SPEECH OUTPUT: 'tab list General page item 1 of 2'"]))
 
 ########################################################################
 # Arrow Right to the "Page Setup" tab.
@@ -80,7 +80,7 @@ sequence.append(utils.AssertPresentationAction(
     "Page Setup page tab Where Am I",
     ["BRAILLE LINE:  'gtk-demo Application Print Dialog Page Setup Page'",
      "     VISIBLE:  'Page Setup Page', cursor=1",
-     "SPEECH OUTPUT: 'tab list Page Setup page item 2 of 2 '"]))
+     "SPEECH OUTPUT: 'tab list Page Setup page item 2 of 2'"]))
 
 ########################################################################
 # Close the demo
diff --git a/test/keystrokes/gtk-demo/role_radio_menu_item.py b/test/keystrokes/gtk-demo/role_radio_menu_item.py
index 2ba11ac..319c811 100644
--- a/test/keystrokes/gtk-demo/role_radio_menu_item.py
+++ b/test/keystrokes/gtk-demo/role_radio_menu_item.py
@@ -43,6 +43,12 @@ sequence.append(utils.AssertPresentationAction(
 ########################################################################
 # Do a basic "Where Am I" via KP_Enter.
 #
+# JD to WDW: I'm not sure if this is a regression or not. Now we are
+# speaking a parent menu which we were not before. I *think* that's
+# a bug fix. :-)
+#
+# WDW to JD: I agree -- it looks like a bug fix (yeah!)
+#
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("KP_Enter"))
 sequence.append(PauseAction(3000))
@@ -50,7 +56,7 @@ sequence.append(utils.AssertPresentationAction(
     "Red button Where Am I",
     ["BRAILLE LINE:  'gtk-demo Application Application Window Frame MenuBar Preferences Menu <x> Red CheckItem(Control r)'",
      "     VISIBLE:  '<x> Red CheckItem(Control r)', cursor=1",
-     "SPEECH OUTPUT: 'Color menu Red check item checked Control r item 1 of 3 r'"]))
+     "SPEECH OUTPUT: 'Preferences menu Color menu Red check item checked Control r item 1 of 3 r'"]))
 
 ########################################################################
 # Down arrow to the "Green" menu item.
@@ -68,6 +74,12 @@ sequence.append(utils.AssertPresentationAction(
 ########################################################################
 # Do a basic "Where Am I" via KP_Enter.
 #
+# JD to WDW: I'm not sure if this is a regression or not. Now we are
+# speaking a parent menu which we were not before. I *think* that's
+# a bug fix. :-)
+#
+# WDW to JD: I agree -- it looks like a bug fix (yeah!)
+#
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("KP_Enter"))
 sequence.append(PauseAction(3000))
@@ -75,7 +87,7 @@ sequence.append(utils.AssertPresentationAction(
     "Green button Where Am I",
     ["BRAILLE LINE:  'gtk-demo Application Application Window Frame MenuBar Preferences Menu < > Green CheckItem(Control g)'",
      "     VISIBLE:  '< > Green CheckItem(Control g)', cursor=1",
-     "SPEECH OUTPUT: 'Color menu Green check item not checked Control g item 2 of 3 g'"]))
+     "SPEECH OUTPUT: 'Preferences menu Color menu Green check item not checked Control g item 2 of 3 g'"]))
 
 ########################################################################
 # Dismiss the menu and close the Application Window demo window
diff --git a/test/keystrokes/gtk-demo/role_table.py b/test/keystrokes/gtk-demo/role_table.py
index d429935..f91c999 100644
--- a/test/keystrokes/gtk-demo/role_table.py
+++ b/test/keystrokes/gtk-demo/role_table.py
@@ -82,8 +82,7 @@ sequence.append(utils.AssertPresentationAction(
     "Table Where Am I",
     ["BRAILLE LINE:  'gtk-demo Application Shopping list Frame ScrollPane Table Number ColumnHeader 3 bottles of coke[ ]*'",
      "     VISIBLE:  '3 bottles of coke[ ]*', cursor=1",
-     "SPEECH OUTPUT: 'table Number cell 3'",
-     "SPEECH OUTPUT: 'column 1 of 3 row 1 of 5'"]))
+     "SPEECH OUTPUT: 'table Number cell 3 column 1 of 3 row 1 of 5'"]))
 
 ########################################################################
 # Down arrow to the next line.
@@ -110,8 +109,7 @@ sequence.append(utils.AssertPresentationAction(
     "Table Where Am I (again)",
     ["BRAILLE LINE:  'gtk-demo Application Shopping list Frame ScrollPane Table Number ColumnHeader 5'",
      "     VISIBLE:  '5', cursor=1",
-     "SPEECH OUTPUT: 'table Number cell 5'",
-     "SPEECH OUTPUT: 'column 1 of 3 row 2 of 5'"]))
+     "SPEECH OUTPUT: 'table Number cell 5 column 1 of 3 row 2 of 5'"]))
 
 ########################################################################
 # Turn reading of rows off.
diff --git a/test/keystrokes/gtk-demo/role_text_multiline.py b/test/keystrokes/gtk-demo/role_text_multiline.py
index ab20a11..b04a483 100644
--- a/test/keystrokes/gtk-demo/role_text_multiline.py
+++ b/test/keystrokes/gtk-demo/role_text_multiline.py
@@ -230,8 +230,7 @@ sequence.append(utils.AssertPresentationAction(
     "Where Am I",
     ["BRAILLE LINE:  'gtk-demo Application Application Window Frame ScrollPane This is a test. $l'",
      "     VISIBLE:  'This is a test. $l', cursor=11",
-     "SPEECH OUTPUT: 'text  is a '",
-     "SPEECH OUTPUT: 'selected '"]))
+     "SPEECH OUTPUT: 'text  is a  selected'"]))
 
 ########################################################################
 # Press Home to move to the beginning of the line. Arrow down to 
@@ -307,8 +306,8 @@ sequence.append(utils.AssertPresentationAction(
     "Basic Where Am I multiline selection",
     ["BRAILLE LINE:  'The keyboard sure can get sticky. $l'",
      "     VISIBLE:  'The keyboard sure can get sticky', cursor=13",
-     "SPEECH OUTPUT: 'text The keyboard'",
-     "SPEECH OUTPUT: 'selected '"]))
+     "SPEECH OUTPUT: 'text I'm just typing away like a mad little monkey with nothing better to do in my life than eat fruit and type.",
+     "The keyboard selected'"]))
 
 ########################################################################
 # Do a detailed "Where Am I" via KP_Enter 2x.
@@ -323,11 +322,10 @@ sequence.append(utils.AssertPresentationAction(
      "     VISIBLE:  'The keyboard sure can get sticky', cursor=13",
      "BRAILLE LINE:  'The keyboard sure can get sticky. $l'",
      "     VISIBLE:  'The keyboard sure can get sticky', cursor=13",
-     "SPEECH OUTPUT: 'text The keyboard'",
-     "SPEECH OUTPUT: 'selected '",
      "SPEECH OUTPUT: 'text I'm just typing away like a mad little monkey with nothing better to do in my life than eat fruit and type.",
-     "The keyboard'",
-     "SPEECH OUTPUT: 'selected '"]))
+     "The keyboard selected'",
+     "SPEECH OUTPUT: 'text I'm just typing away like a mad little monkey with nothing better to do in my life than eat fruit and type.",
+     "The keyboard selected'"]))
 
 ########################################################################
 # Try a "SayAll".
diff --git a/test/keystrokes/gtk-demo/role_text_multiline_navigation.py b/test/keystrokes/gtk-demo/role_text_multiline_navigation.py
index a642443..53ab1be 100644
--- a/test/keystrokes/gtk-demo/role_text_multiline_navigation.py
+++ b/test/keystrokes/gtk-demo/role_text_multiline_navigation.py
@@ -356,7 +356,8 @@ sequence.append(utils.AssertPresentationAction(
     "KP_Subtract to enter flat review",
     ["BRAILLE LINE:  'PLEASE DO NOT PANIC. $l'",
      "     VISIBLE:  'PLEASE DO NOT PANIC. $l', cursor=1",
-     "SPEECH OUTPUT: 'PLEASE'"]))
+     "SPEECH OUTPUT: 'Entering flat review.'",
+     "SPEECH OUTPUT: 'PLEASE' voice=uppercase"]))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("KP_8"))
@@ -1081,7 +1082,8 @@ sequence.append(KeyComboAction("KP_Subtract"))
 sequence.append(utils.AssertPresentationAction(
     "KP_Subtract to exit flat review",
     ["BRAILLE LINE:  'This is only  $l'",
-     "     VISIBLE:  'This is only  $l', cursor=6"]))
+     "     VISIBLE:  'This is only  $l', cursor=6",
+     "SPEECH OUTPUT: 'Leaving flat review.'"]))
 
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("KP_5"))
diff --git a/test/keystrokes/gtk-demo/role_toolbar.py b/test/keystrokes/gtk-demo/role_toolbar.py
index c244954..7e96179 100644
--- a/test/keystrokes/gtk-demo/role_toolbar.py
+++ b/test/keystrokes/gtk-demo/role_toolbar.py
@@ -54,8 +54,7 @@ sequence.append(utils.AssertPresentationAction(
     "Open button Where Am I",
     ["BRAILLE LINE:  'gtk-demo Application Application Window Frame ToolBar Open Button'",
      "     VISIBLE:  'Open Button', cursor=1",
-     "SPEECH OUTPUT: 'tool bar'",
-     "SPEECH OUTPUT: 'Open button '"]))
+     "SPEECH OUTPUT: 'tool bar Open button'"]))
 
 ########################################################################
 # Arrow Right to the triangular button next to the "Open" button.
@@ -79,8 +78,7 @@ sequence.append(utils.AssertPresentationAction(
     "Open triangle toggle button Where Am I",
     ["BRAILLE LINE:  'gtk-demo Application Application Window Frame ToolBar & y ToggleButton'",
      "     VISIBLE:  '& y ToggleButton', cursor=1",
-     "SPEECH OUTPUT: 'tool bar'",
-     "SPEECH OUTPUT: 'toggle button not pressed'"]))
+     "SPEECH OUTPUT: 'tool bar toggle button not pressed'"]))
 
 ########################################################################
 # Arrow Right to the the "Quit" button.
@@ -104,8 +102,7 @@ sequence.append(utils.AssertPresentationAction(
     "Quit button Where Am I",
     ["BRAILLE LINE:  'gtk-demo Application Application Window Frame ToolBar Quit Button'",
      "     VISIBLE:  'Quit Button', cursor=1",
-     "SPEECH OUTPUT: 'tool bar'",
-     "SPEECH OUTPUT: 'Quit button '"]))
+     "SPEECH OUTPUT: 'tool bar Quit button'"]))
 
 ########################################################################
 # Close the Application Window demo window
diff --git a/test/keystrokes/gtk-demo/role_tree_table.py b/test/keystrokes/gtk-demo/role_tree_table.py
index 4a971b3..9893218 100644
--- a/test/keystrokes/gtk-demo/role_tree_table.py
+++ b/test/keystrokes/gtk-demo/role_tree_table.py
@@ -82,13 +82,19 @@ sequence.append(utils.AssertPresentationAction(
     "January cell basic Where Am I",
     ["BRAILLE LINE:  'gtk-demo Application Card planning sheet Frame ScrollPane TreeTable Holiday ColumnHeader January expanded < > Alex < > Havoc < > Tim < > Owen < > Dave TREE LEVEL 1'",
      "     VISIBLE:  'January expanded < > Alex < > Ha', cursor=1",
-     "SPEECH OUTPUT: 'tree table Holiday cell January'",
-     "SPEECH OUTPUT: 'column 1 of 6 row 1 of 53'",
-     "SPEECH OUTPUT: 'expanded tree level 1'"]))
+     "SPEECH OUTPUT: 'tree table Holiday cell January column 1 of 6 row 1 of 53 expanded tree level 1'"]))
 
 ########################################################################
 # Do a detailed "Where Am I" via KP_Enter.
 #
+# JD to WDW: another case of utterances being collapsed.
+# In addition, before the detailed Where Am I didn't include the
+# names of the other checkboxes on the row; just their states in
+# rapid succession. Now we speak the names. I think this is a bug
+# fix. :-) Thoughts?
+#
+# WDW to JD: Agreed.  This looks like a fix.
+#
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("KP_Enter"))
 sequence.append(KeyComboAction("KP_Enter"))
@@ -99,13 +105,8 @@ sequence.append(utils.AssertPresentationAction(
      "     VISIBLE:  'January expanded < > Alex < > Ha', cursor=1",
      "BRAILLE LINE:  'gtk-demo Application Card planning sheet Frame ScrollPane TreeTable Holiday ColumnHeader January expanded < > Alex < > Havoc < > Tim < > Owen < > Dave TREE LEVEL 1'",
      "     VISIBLE:  'January expanded < > Alex < > Ha', cursor=1",
-     "SPEECH OUTPUT: 'tree table Holiday cell January'",
-     "SPEECH OUTPUT: 'column 1 of 6 row 1 of 53'",
-     "SPEECH OUTPUT: 'expanded tree level 1'",
-     "SPEECH OUTPUT: 'tree table Holiday cell January'",
-     "SPEECH OUTPUT: 'column 1 of 6 row 1 of 53'",
-     "SPEECH OUTPUT: 'January check box not checked check box not checked check box not checked check box not checked check box not checked'",
-     "SPEECH OUTPUT: 'expanded tree level 1'"]))
+     "SPEECH OUTPUT: 'tree table Holiday cell January column 1 of 6 row 1 of 53 expanded tree level 1'",
+     "SPEECH OUTPUT: 'tree table Holiday cell January column 1 of 6 row 1 of 53 January expanded 3 items Alex check box not checked Havoc check box not checked Tim check box not checked Owen check box not checked Dave check box not checked expanded tree level 1'"]))
 
 ########################################################################
 # Collapse the cell.
@@ -133,13 +134,19 @@ sequence.append(utils.AssertPresentationAction(
     "January cell collapsed basic Where Am I",
     ["BRAILLE LINE:  'gtk-demo Application Card planning sheet Frame ScrollPane TreeTable Holiday ColumnHeader January collapsed < > Alex < > Havoc < > Tim < > Owen < > Dave TREE LEVEL 1'",
      "     VISIBLE:  'January collapsed < > Alex < > H', cursor=1",
-     "SPEECH OUTPUT: 'tree table Holiday cell January'",
-     "SPEECH OUTPUT: 'column 1 of 6 row 1 of 50'",
-     "SPEECH OUTPUT: 'collapsed tree level 1'"]))
+     "SPEECH OUTPUT: 'tree table Holiday cell January column 1 of 6 row 1 of 50 collapsed tree level 1'"]))
 
 ########################################################################
 # Do a detailed "Where Am I" via KP_Enter.
 #
+# JD to WDW: another case of utterances being collapsed.
+# In addition, before the detailed Where Am I didn't include the
+# names of the other checkboxes on the row; just their states in
+# rapid succession. Now we speak the names. I think this is a bug
+# fix. :-) Thoughts?
+#
+# WDW to JD: Agreed.  This looks like a fix.
+#
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("KP_Enter"))
 sequence.append(KeyComboAction("KP_Enter"))
@@ -150,13 +157,8 @@ sequence.append(utils.AssertPresentationAction(
      "     VISIBLE:  'January collapsed < > Alex < > H', cursor=1",
      "BRAILLE LINE:  'gtk-demo Application Card planning sheet Frame ScrollPane TreeTable Holiday ColumnHeader January collapsed < > Alex < > Havoc < > Tim < > Owen < > Dave TREE LEVEL 1'",
      "     VISIBLE:  'January collapsed < > Alex < > H', cursor=1",
-     "SPEECH OUTPUT: 'tree table Holiday cell January'",
-     "SPEECH OUTPUT: 'column 1 of 6 row 1 of 50'",
-     "SPEECH OUTPUT: 'collapsed tree level 1'",
-     "SPEECH OUTPUT: 'tree table Holiday cell January'",
-     "SPEECH OUTPUT: 'column 1 of 6 row 1 of 50'",
-     "SPEECH OUTPUT: 'January check box not checked check box not checked check box not checked check box not checked check box not checked'",
-     "SPEECH OUTPUT: 'collapsed tree level 1'"]))
+     "SPEECH OUTPUT: 'tree table Holiday cell January column 1 of 6 row 1 of 50 collapsed tree level 1'",
+     "SPEECH OUTPUT: 'tree table Holiday cell January column 1 of 6 row 1 of 50 January collapsed Alex check box not checked Havoc check box not checked Tim check box not checked Owen check box not checked Dave check box not checked collapsed tree level 1'"]))
 
 ########################################################################
 # Expand the cell again.
@@ -220,8 +222,7 @@ sequence.append(utils.AssertPresentationAction(
     "Alex checkbox cell basic Where Am I",
     ["BRAILLE LINE:  'gtk-demo Application Card planning sheet Frame ScrollPane TreeTable Alex ColumnHeader <x> Alex'",
      "     VISIBLE:  '<x> Alex', cursor=1",
-     "SPEECH OUTPUT: 'tree table Alex cell check box checked'",
-     "SPEECH OUTPUT: 'column 2 of 6 row 2 of 53'"]))
+     "SPEECH OUTPUT: 'tree table Alex cell check box checked column 2 of 6 row 2 of 53'"]))
 
 ########################################################################
 # Do a detailed "Where Am I" via KP_Enter.
@@ -236,11 +237,8 @@ sequence.append(utils.AssertPresentationAction(
      "     VISIBLE:  '<x> Alex', cursor=1",
      "BRAILLE LINE:  'gtk-demo Application Card planning sheet Frame ScrollPane TreeTable Alex ColumnHeader <x> Alex'",
      "     VISIBLE:  '<x> Alex', cursor=1",
-     "SPEECH OUTPUT: 'tree table Alex cell check box checked'",
-     "SPEECH OUTPUT: 'column 2 of 6 row 2 of 53'",
-     "SPEECH OUTPUT: 'tree table Alex cell check box checked'",
-     "SPEECH OUTPUT: 'column 2 of 6 row 2 of 53'",
-     "SPEECH OUTPUT: 'New Years Day check box checked check box checked check box checked check box checked check box not checked'"]))
+     "SPEECH OUTPUT: 'tree table Alex cell check box checked column 2 of 6 row 2 of 53'",
+     "SPEECH OUTPUT: 'tree table Alex cell check box checked column 2 of 6 row 2 of 53 New Years Day Alex check box checked Havoc check box checked Tim check box checked Owen check box checked Dave check box not checked'"]))
 
 ########################################################################
 # Change the state of the checkbox.
diff --git a/test/keystrokes/oocalc/bug_361167.py b/test/keystrokes/oocalc/bug_361167.py
index 3dc77d3..497df1c 100644
--- a/test/keystrokes/oocalc/bug_361167.py
+++ b/test/keystrokes/oocalc/bug_361167.py
@@ -31,10 +31,7 @@ sequence.append(utils.AssertPresentationAction(
     "Basic where am I with no dynamic headers set", 
     ["BRAILLE LINE:  'soffice Application fruit(.ods|) - " + utils.getOOoName("Calc") + " Frame fruit(.ods|) - " + utils.getOOoName("Calc") + " RootPane ScrollPane Document view3 Sheet Sheet1 Table Cell A1 '",
      "     VISIBLE:  'Cell A1 ', cursor=1",
-     "SPEECH OUTPUT: 'cell'",
-     "SPEECH OUTPUT: 'column 1'",
-     "SPEECH OUTPUT: 'row 1'",
-     "SPEECH OUTPUT: ''"]))
+     "SPEECH OUTPUT: 'cell column 1 row 1 blank'"]))
 
 ######################################################################
 # 4. Type Insert-r to set the dynamic column headers to the first column.
@@ -94,12 +91,7 @@ sequence.append(utils.AssertPresentationAction(
     "Basic where am I with dynamic headers set B2", 
     ["BRAILLE LINE:  'soffice Application fruit(.ods|) - " + utils.getOOoName("Calc") + " Frame fruit(.ods|) - " + utils.getOOoName("Calc") + " RootPane ScrollPane Document view3 Sheet Sheet1 Table Yes Cell B2 '",
      "     VISIBLE:  'Yes Cell B2 ', cursor=1",
-     "SPEECH OUTPUT: 'cell'",
-     "SPEECH OUTPUT: 'column 2'",
-     "SPEECH OUTPUT: 'Apples'",
-     "SPEECH OUTPUT: 'row 2'",
-     "SPEECH OUTPUT: 'Good in Pies'",
-     "SPEECH OUTPUT: 'Yes'"]))
+     "SPEECH OUTPUT: 'cell column 2 Apples row 2 Good in Pies Yes'"]))
 
 ######################################################################
 # 9. Press the down arrow to move to cell B3.
@@ -133,12 +125,7 @@ sequence.append(utils.AssertPresentationAction(
     "Basic where am I with dynamic headers set C3",
     ["BRAILLE LINE:  'soffice Application fruit(.ods|) - " + utils.getOOoName("Calc") + " Frame fruit(.ods|) - " + utils.getOOoName("Calc") + " RootPane ScrollPane Document view3 Sheet Sheet1 Table Yes Cell C3 '",
      "     VISIBLE:  'Yes Cell C3 ', cursor=1",
-     "SPEECH OUTPUT: 'cell'",
-     "SPEECH OUTPUT: 'column 3'",
-     "SPEECH OUTPUT: 'Pears'",
-     "SPEECH OUTPUT: 'row 3'",
-     "SPEECH OUTPUT: 'Juiceable'",
-     "SPEECH OUTPUT: 'Yes'"]))
+     "SPEECH OUTPUT: 'cell column 3 Pears row 3 Juiceable Yes'"]))
 
 ######################################################################
 # 13. Press the up arrow to move to cell C2.
diff --git a/test/keystrokes/oocalc/bug_363802.py b/test/keystrokes/oocalc/bug_363802.py
index fa509fa..a502078 100644
--- a/test/keystrokes/oocalc/bug_363802.py
+++ b/test/keystrokes/oocalc/bug_363802.py
@@ -24,7 +24,7 @@ sequence.append(utils.AssertPresentationAction(
     "Type Control-Home to position the text caret in cell A1",
     ["BRAILLE LINE:  'soffice Application fruit(.ods|) - " + utils.getOOoName("Calc") + " Frame fruit(.ods|) - " + utils.getOOoName("Calc") + " RootPane ScrollPane Document view3 Sheet Sheet1 Table Cell A1 '",
      "     VISIBLE:  'Cell A1 ', cursor=1",
-     "SPEECH OUTPUT: ' A1'"]))
+     "SPEECH OUTPUT: 'A1'"]))
 
 ######################################################################
 # 3. Press the down arrow to move to cell A2.
diff --git a/test/keystrokes/oocalc/bug_363804.py b/test/keystrokes/oocalc/bug_363804.py
index 9f87ff2..304b1ea 100644
--- a/test/keystrokes/oocalc/bug_363804.py
+++ b/test/keystrokes/oocalc/bug_363804.py
@@ -71,7 +71,7 @@ sequence.append(utils.AssertPresentationAction(
     "Control Home to A1 - speak cell coordinates",
     ["BRAILLE LINE:  'soffice Application fruit(.ods|) - " + utils.getOOoName("Calc") + " Frame fruit(.ods|) - " + utils.getOOoName("Calc") + " RootPane ScrollPane Document view3 Sheet Sheet1 Table Cell A1 '",
      "     VISIBLE:  'Cell A1 ', cursor=1",
-     "SPEECH OUTPUT: ' A1'"]))
+     "SPEECH OUTPUT: 'A1'"]))
 
 ######################################################################
 # Type Insert-Control-space to bring up the application specific
diff --git a/test/keystrokes/oocalc/bug_364086.py b/test/keystrokes/oocalc/bug_364086.py
index 6fe5bc1..f5cae2c 100644
--- a/test/keystrokes/oocalc/bug_364086.py
+++ b/test/keystrokes/oocalc/bug_364086.py
@@ -57,8 +57,8 @@ sequence.append(utils.AssertPresentationAction(
      "BRAILLE LINE:  'soffice Application Untitled[ ]*2 - " + utils.getOOoName("Calc") + " Frame Untitled[ ]*2 - " + utils.getOOoName("Calc") + " RootPane ScrollPane Document view3 Sheet Sheet1 Table Cell A2 '",
      "     VISIBLE:  'Cell A2 ', cursor=1",
      "SPEECH OUTPUT: 'Sheet Sheet1 table grayed'",
-     "SPEECH OUTPUT: ' A1'",
-     "SPEECH OUTPUT: ' A2'"]))
+     "SPEECH OUTPUT: 'A1'",
+     "SPEECH OUTPUT: 'A2'"]))
 
 ######################################################################
 # 4. Enter Alt-f, Alt-c to close the Calc spreadsheet window.
diff --git a/test/keystrokes/oocalc/bug_433398.py b/test/keystrokes/oocalc/bug_433398.py
index 5b77ca5..7a67937 100644
--- a/test/keystrokes/oocalc/bug_433398.py
+++ b/test/keystrokes/oocalc/bug_433398.py
@@ -42,7 +42,6 @@ sequence.append(utils.AssertPresentationAction(
     "Type down arrow to get to the Freeze menu item [1]",
     ["BRAILLE LINE:  'soffice Application fruit(.ods|) - " + utils.getOOoName("Calc") + " Frame fruit(.ods|) - " + utils.getOOoName("Calc") + " RootPane MenuBar Freeze'",
      "     VISIBLE:  'Freeze', cursor=1",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Freeze'"]))
 
 ######################################################################
@@ -77,7 +76,6 @@ sequence.append(utils.AssertPresentationAction(
     "Type down arrow to get to the Freeze menu item [2]",
     ["BRAILLE LINE:  'soffice Application fruit(.ods|) - " + utils.getOOoName("Calc") + " Frame fruit(.ods|) - " + utils.getOOoName("Calc") + " RootPane MenuBar <x> Freeze'",
      "     VISIBLE:  '<x> Freeze', cursor=1",
-     "SPEECH OUTPUT: ''",
      "SPEECH OUTPUT: 'Freeze checked'"]))
 
 ######################################################################
diff --git a/test/keystrokes/oocalc/bug_435852.py b/test/keystrokes/oocalc/bug_435852.py
index 07c380a..7e5638f 100644
--- a/test/keystrokes/oocalc/bug_435852.py
+++ b/test/keystrokes/oocalc/bug_435852.py
@@ -45,7 +45,7 @@ sequence.append(utils.AssertPresentationAction(
     "Enter right arrow to move to cell B1",
     ["BRAILLE LINE:  'soffice Application Untitled[ ]*2 - " + utils.getOOoName("Calc") + " Frame Untitled[ ]*2 - " + utils.getOOoName("Calc") + " RootPane ScrollPane Document view3 Sheet Sheet1 Table Cell B1 '",
      "     VISIBLE:  'Cell B1 ', cursor=1",
-     "SPEECH OUTPUT: ' B1'"]))
+     "SPEECH OUTPUT: 'B1'"]))
 
 ######################################################################
 # 5. Enter down arrow to move to cell B2.
@@ -56,7 +56,7 @@ sequence.append(utils.AssertPresentationAction(
     "Enter down arrow to move to cell B2",
     ["BRAILLE LINE:  'soffice Application Untitled[ ]*2 - " + utils.getOOoName("Calc") + " Frame Untitled[ ]*2 - " + utils.getOOoName("Calc") + " RootPane ScrollPane Document view3 Sheet Sheet1 Table Cell B2 '",
      "     VISIBLE:  'Cell B2 ', cursor=1",
-     "SPEECH OUTPUT: ' B2'"]))
+     "SPEECH OUTPUT: 'B2'"]))
 
 ######################################################################
 # 6. Enter left arrow to move to cell A2.
@@ -67,7 +67,7 @@ sequence.append(utils.AssertPresentationAction(
     "Enter left arrow to move to cell A2",
     ["BRAILLE LINE:  'soffice Application Untitled[ ]*2 - " + utils.getOOoName("Calc") + " Frame Untitled[ ]*2 - " + utils.getOOoName("Calc") + " RootPane ScrollPane Document view3 Sheet Sheet1 Table Cell A2 '",
      "     VISIBLE:  'Cell A2 ', cursor=1",
-     "SPEECH OUTPUT: ' A2'"]))
+     "SPEECH OUTPUT: 'A2'"]))
 
 ######################################################################
 # 7. Enter up arrow to move to cell A1.
@@ -78,7 +78,7 @@ sequence.append(utils.AssertPresentationAction(
     "Enter up arrow to move to cell A1",
     ["BRAILLE LINE:  'soffice Application Untitled[ ]*2 - " + utils.getOOoName("Calc") + " Frame Untitled[ ]*2 - " + utils.getOOoName("Calc") + " RootPane ScrollPane Document view3 Sheet Sheet1 Table Cell A1 '",
      "     VISIBLE:  'Cell A1 ', cursor=1",
-     "SPEECH OUTPUT: ' A1'"]))
+     "SPEECH OUTPUT: 'A1'"]))
 
 ######################################################################
 # 8. Enter Alt-f, Alt-c to close the Calc spreadsheet window.
diff --git a/test/keystrokes/oowriter/bug_364765.py b/test/keystrokes/oowriter/bug_364765.py
index e7214e3..e7f5de2 100644
--- a/test/keystrokes/oowriter/bug_364765.py
+++ b/test/keystrokes/oowriter/bug_364765.py
@@ -32,7 +32,7 @@ sequence.append(utils.AssertPresentationAction(
     "Press W to open the Wizards submenu",
     ["BRAILLE LINE:  'soffice Application Untitled[ ]*1 - " + utils.getOOoName("Writer") + " Frame Untitled[ ]*1 - " + utils.getOOoName("Writer") + " RootPane MenuBar File Menu Letter...'",
      "     VISIBLE:  'Letter...', cursor=1",
-     "SPEECH OUTPUT: 'Wizards menu Letter...'"]))
+     "SPEECH OUTPUT: 'Wizards menu  Letter...'"]))
 
 ######################################################################
 # 4. Press Escape to close the Wizards submenu.
diff --git a/test/keystrokes/oowriter/bug_435226.py b/test/keystrokes/oowriter/bug_435226.py
index 67bf1e3..a96658b 100644
--- a/test/keystrokes/oowriter/bug_435226.py
+++ b/test/keystrokes/oowriter/bug_435226.py
@@ -74,8 +74,7 @@ sequence.append(utils.AssertPresentationAction(
     "Type KP-Enter once to do a 'single-click' where-am-I operation",
     ["BRAILLE LINE:  '" + utils.getOOoBrailleLine("Writer", "spanish(.odt|)", "Hm! She is made of harder stuff! Cardinal Fang! Fetch the COMFY CHAIR! \$l") + "'",
      "     VISIBLE:  'Hm! She is made of harder stuff!', cursor=17",
-     "SPEECH OUTPUT: 'paragraph Hm! She is made '",
-     "SPEECH OUTPUT: 'selected '"]))
+     "SPEECH OUTPUT: 'Spanish Inquisition! Our chief weapon is surprise. Surprise and fear. Fear and surprise. Our two weapons are fear and surprise. And ruthless efficiency. Our three weapons are fear, surprise, and ruthless efficiency. And an almost fanatical devotion to the Pope. Our four. No. Amongst our weapons. Amongst our weaponry, are such elements as fear, surprise. I'll come in again. NOBODY expects the Spanish Inquisition! Amongst our weaponry are such diverse elements as: fear, surprise, ruthless efficiency, an almost fanatical devotion to the Pope, and nice red uniforms - Oh damn! Now old lady, you have one last chance. Confess the heinous sin of heresy, reject the works of the ungodly. Two last chances. And you shall be free. Three last chances. You have three last chances, the nature of which I have divulged in my previous utterance. Hm! She is made  selected'"]))
 
 ######################################################################
 # 8. Type KP-Enter twice to do a "double-click" where-am-I operation.
@@ -90,10 +89,8 @@ sequence.append(utils.AssertPresentationAction(
      "     VISIBLE:  'Hm! She is made of harder stuff!', cursor=17",
      "BRAILLE LINE:  '" + utils.getOOoBrailleLine("Writer", "spanish(.odt|)", "Hm! She is made of harder stuff! Cardinal Fang! Fetch the COMFY CHAIR! \$l") + "'",
      "     VISIBLE:  'Hm! She is made of harder stuff!', cursor=17",
-     "SPEECH OUTPUT: 'paragraph Hm! She is made '",
-     "SPEECH OUTPUT: 'selected '",
-     "SPEECH OUTPUT: 'paragraph Spanish Inquisition! Our chief weapon is surprise. Surprise and fear. Fear and surprise. Our two weapons are fear and surprise. And ruthless efficiency. Our three weapons are fear, surprise, and ruthless efficiency. And an almost fanatical devotion to the Pope. Our four. No. Amongst our weapons. Amongst our weaponry, are such elements as fear, surprise. I'll come in again. NOBODY expects the Spanish Inquisition! Amongst our weaponry are such diverse elements as: fear, surprise, ruthless efficiency, an almost fanatical devotion to the Pope, and nice red uniforms - Oh damn! Now old lady, you have one last chance. Confess the heinous sin of heresy, reject the works of the ungodly. Two last chances. And you shall be free. Three last chances. You have three last chances, the nature of which I have divulged in my previous utterance. Hm! She is made  ;  paragraph style Preformatted Text'",
-     "SPEECH OUTPUT: 'selected '"]))
+     "SPEECH OUTPUT: 'Spanish Inquisition! Our chief weapon is surprise. Surprise and fear. Fear and surprise. Our two weapons are fear and surprise. And ruthless efficiency. Our three weapons are fear, surprise, and ruthless efficiency. And an almost fanatical devotion to the Pope. Our four. No. Amongst our weapons. Amongst our weaponry, are such elements as fear, surprise. I'll come in again. NOBODY expects the Spanish Inquisition! Amongst our weaponry are such diverse elements as: fear, surprise, ruthless efficiency, an almost fanatical devotion to the Pope, and nice red uniforms - Oh damn! Now old lady, you have one last chance. Confess the heinous sin of heresy, reject the works of the ungodly. Two last chances. And you shall be free. Three last chances. You have three last chances, the nature of which I have divulged in my previous utterance. Hm! She is made  selected'",
+     "SPEECH OUTPUT: 'Spanish Inquisition! Our chief weapon is surprise. Surprise and fear. Fear and surprise. Our two weapons are fear and surprise. And ruthless efficiency. Our three weapons are fear, surprise, and ruthless efficiency. And an almost fanatical devotion to the Pope. Our four. No. Amongst our weapons. Amongst our weaponry, are such elements as fear, surprise. I'll come in again. NOBODY expects the Spanish Inquisition! Amongst our weaponry are such diverse elements as: fear, surprise, ruthless efficiency, an almost fanatical devotion to the Pope, and nice red uniforms - Oh damn! Now old lady, you have one last chance. Confess the heinous sin of heresy, reject the works of the ungodly. Two last chances. And you shall be free. Three last chances. You have three last chances, the nature of which I have divulged in my previous utterance. Hm! She is made  ;  paragraph style Preformatted Text selected'"]))
 
 ######################################################################
 # 9. Enter Alt-f, Alt-c to close the Writer application.
diff --git a/test/keystrokes/progressbar/progress_updates.py b/test/keystrokes/progressbar/progress_updates.py
index ee50275..8e9d304 100644
--- a/test/keystrokes/progressbar/progress_updates.py
+++ b/test/keystrokes/progressbar/progress_updates.py
@@ -143,16 +143,16 @@ sequence.append(utils.AssertPresentationAction(
      "     VISIBLE:  'Progress 90% $l', cursor=1",
      "BRAILLE LINE:  'Progress 100% $l'",
      "     VISIBLE:  'Progress 100% $l', cursor=1",
-     "SPEECH OUTPUT: '10 percent. '",
-     "SPEECH OUTPUT: '20 percent. '",
-     "SPEECH OUTPUT: '30 percent. '",
-     "SPEECH OUTPUT: '40 percent. '",
-     "SPEECH OUTPUT: '50 percent. '",
-     "SPEECH OUTPUT: '60 percent. '",
-     "SPEECH OUTPUT: '70 percent. '",
-     "SPEECH OUTPUT: '80 percent. '",
-     "SPEECH OUTPUT: '90 percent. '",
-     "SPEECH OUTPUT: '100 percent. '"]))
+     "SPEECH OUTPUT: '10 percent'",
+     "SPEECH OUTPUT: '20 percent'",
+     "SPEECH OUTPUT: '30 percent'",
+     "SPEECH OUTPUT: '40 percent'",
+     "SPEECH OUTPUT: '50 percent'",
+     "SPEECH OUTPUT: '60 percent'",
+     "SPEECH OUTPUT: '70 percent'",
+     "SPEECH OUTPUT: '80 percent'",
+     "SPEECH OUTPUT: '90 percent'",
+     "SPEECH OUTPUT: '100 percent'"]))
 
 # Just a little extra wait to let some events get through.
 #



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