[orca/570658-whereami] StarOffice/OpenOffice whereAmI refactor.



commit 67106bf5460957c1d958c0f83570ec2b7d952d4d
Author: Joanmarie Diggs <joanmarie diggs gmail com>
Date:   Mon Jun 8 17:01:49 2009 -0400

    StarOffice/OpenOffice whereAmI refactor.
---
 src/orca/formatting.py                            |   16 +-
 src/orca/scripts/apps/soffice/Makefile.am         |    3 +-
 src/orca/scripts/apps/soffice/formatting.py       |    6 +
 src/orca/scripts/apps/soffice/script.py           |   43 +++-
 src/orca/scripts/apps/soffice/speech_generator.py |  142 ++++++++++-
 src/orca/scripts/apps/soffice/where_am_i.py       |  286 ---------------------
 src/orca/speech_generator.py                      |   52 ++++-
 test/keystrokes/oowriter/bug_364765.py            |    2 +-
 test/keystrokes/oowriter/bug_435226.py            |   18 +-
 9 files changed, 256 insertions(+), 312 deletions(-)

diff --git a/src/orca/formatting.py b/src/orca/formatting.py
index ac7feea..c0e18f5 100644
--- a/src/orca/formatting.py
+++ b/src/orca/formatting.py
@@ -75,7 +75,7 @@ formatting = {
             'unfocused': 'labelAndName + unrelatedLabels'
             },
         pyatspi.ROLE_DOCUMENT_FRAME: {
-            'basicWhereAmI': 'label + readOnly + textRole + textContent + anyTextSelection + mnemonic + tutorial',
+            'basicWhereAmI': 'label + readOnly + textRole + textContent + anyTextSelection + mnemonic',
             'detailedWhereAmI': 'label + readOnly + textRole + textContentWithAttributes + anyTextSelection + mnemonic + tutorial'
             },
         pyatspi.ROLE_EMBEDDED: {
@@ -85,7 +85,7 @@ formatting = {
         pyatspi.ROLE_ENTRY: {
             'focused': 'labelOrName + readOnly + textRole + currentLineText + allTextSelection',
             'unfocused': 'labelOrName + readOnly + textRole + currentLineText + allTextSelection + mnemonic',
-            'basicWhereAmI': 'label + readOnly + textRole + textContent + anyTextSelection + mnemonic + tutorial',
+            'basicWhereAmI': 'label + readOnly + textRole + textContent + anyTextSelection + mnemonic',
             'detailedWhereAmI': 'label + readOnly + textRole + textContentWithAttributes + anyTextSelection + mnemonic + tutorial'
             },
         pyatspi.ROLE_FRAME: {
@@ -93,7 +93,7 @@ formatting = {
             'unfocused': 'labelAndName + allTextSelection + roleName + unfocusedDialogCount + availability'
             },
         pyatspi.ROLE_HEADING: {
-            'basicWhereAmI': 'label + readOnly + textRole + textContent + anyTextSelection + mnemonic + tutorial',
+            'basicWhereAmI': 'label + readOnly + textRole + textContent + anyTextSelection + mnemonic',
             'detailedWhereAmI': 'label + readOnly + textRole + textContentWithAttributes + anyTextSelection + mnemonic + tutorial'
             },
         pyatspi.ROLE_ICON: {
@@ -136,13 +136,13 @@ formatting = {
         pyatspi.ROLE_PARAGRAPH: {
             'focused': 'labelOrName + readOnly + textRole + currentLineText + allTextSelection',
             'unfocused': 'labelOrName + readOnly + textRole + currentLineText + allTextSelection + mnemonic',
-            'basicWhereAmI': 'label + readOnly + textRole + textContent + anyTextSelection + mnemonic + tutorial',
+            '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',
-            'basicWhereAmI': 'label + readOnly + textRole + textContent + anyTextSelection + mnemonic + tutorial',
+            'basicWhereAmI': 'label + readOnly + textRole + textContent + anyTextSelection + mnemonic',
             'detailedWhereAmI': 'label + readOnly + textRole + textContentWithAttributes + anyTextSelection + mnemonic + tutorial'
             },
         pyatspi.ROLE_PROGRESS_BAR: {
@@ -171,7 +171,7 @@ formatting = {
             'basicWhereAmI': 'ancestors + labelAndName + roleName + radioState + accelerator + positionInList + mnemonic'
             },
         pyatspi.ROLE_SECTION: {
-            'basicWhereAmI': 'label + readOnly + textRole + textContent + anyTextSelection + mnemonic + tutorial',
+            'basicWhereAmI': 'label + readOnly + textRole + textContent + anyTextSelection + mnemonic',
             'detailedWhereAmI': 'label + readOnly + textRole + textContentWithAttributes + anyTextSelection + mnemonic + tutorial'
             },
         pyatspi.ROLE_SLIDER: {
@@ -230,13 +230,13 @@ formatting = {
         pyatspi.ROLE_TERMINAL: {
             'focused': 'terminal',
             'unfocused': 'terminal',
-            'basicWhereAmI': 'label + readOnly + textRole + textContent + anyTextSelection + mnemonic + tutorial',
+            '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',
-            'basicWhereAmI': 'label + readOnly + textRole + textContent + anyTextSelection + mnemonic + tutorial',
+            'basicWhereAmI': 'label + readOnly + textRole + textContent + anyTextSelection + mnemonic',
             'detailedWhereAmI': 'label + readOnly + textRole + textContentWithAttributes + anyTextSelection + mnemonic + tutorial'
             },
         pyatspi.ROLE_TOGGLE_BUTTON: {
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 699697b..7b18df3 100644
--- a/src/orca/scripts/apps/soffice/formatting.py
+++ b/src/orca/scripts/apps/soffice/formatting.py
@@ -55,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 c8f8800..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
diff --git a/src/orca/scripts/apps/soffice/speech_generator.py b/src/orca/scripts/apps/soffice/speech_generator.py
index 274bf13..5cd47ab 100644
--- a/src/orca/scripts/apps/soffice/speech_generator.py
+++ b/src/orca/scripts/apps/soffice/speech_generator.py
@@ -69,6 +69,12 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
             override = self.__overrideParagraph(obj, **args)
             if override:
                 oldRole = self._overrideRole(pyatspi.ROLE_TEXT, args)
+            # 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:
@@ -83,6 +89,20 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
             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 _generateLabelOrName(self, obj, **args):
         """Gets the label or the name if the label is not preset."""
         result = []
@@ -110,6 +130,29 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
                 self, obj, **args))
         return result
 
+    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
@@ -131,6 +174,40 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
                 self, obj, **args))
         return result
 
+    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
@@ -164,6 +241,40 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
                         result.append(text)
         return result
 
+    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
@@ -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
@@ -308,3 +421,30 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
                 speech_generator.SpeechGenerator._generateTableCellRow(
                     self, obj, **args))
         return result
+
+    def generateSpeech(self, obj, **args):
+        result = []
+        # Basic WhereAmI for spreadsheet cells has a different formatting
+        # string than the one typically used for other table cells, even
+        # in OOo. [[[JD to WDW: What's the "right" way to do this?]]]
+        #
+        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/speech_generator.py b/src/orca/speech_generator.py
index 1aaebd5..42ffe08 100644
--- a/src/orca/speech_generator.py
+++ b/src/orca/speech_generator.py
@@ -1094,6 +1094,52 @@ 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
@@ -1276,7 +1322,11 @@ class SpeechGenerator:
             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):
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..96b59c4 100644
--- a/test/keystrokes/oowriter/bug_435226.py
+++ b/test/keystrokes/oowriter/bug_435226.py
@@ -67,6 +67,10 @@ sequence.append(KeyComboAction("<Shift><Control>Right"))
 ######################################################################
 # 7. Type KP-Enter once to do a "single-click" where-am-I operation.
 #
+# JD to WDW: As I understand it, the basic whereAmI is supposed to
+# speak all of the selected text, even if the text spans multiple
+# paragraphs. If so, then these results are correct. Please verify.
+#
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("KP_Enter"))
 sequence.append(PauseAction(3000))
@@ -74,12 +78,16 @@ 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.
 #
+# JD to WDW: Detailed whereAmI should include the formatting, correct?
+# If so, then I believe these results are also correct. (The first
+# output is the result of the single click; the second the results we
+# care about.)
+#
 sequence.append(utils.StartRecordingAction())
 sequence.append(KeyComboAction("KP_Enter"))
 sequence.append(KeyComboAction("KP_Enter"))
@@ -90,10 +98,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.



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