[orca] Begin cleaning up LibreOffice's table-related support



commit 6e0b03fc67645acafac23110c460c4fcf62db424
Author: Joanmarie Diggs <jdiggs igalia com>
Date:   Tue Aug 2 19:54:39 2016 -0400

    Begin cleaning up LibreOffice's table-related support

 src/orca/script_utilities.py                       |   96 ++++++++++++++-
 src/orca/scripts/apps/soffice/Makefile.am          |    3 +-
 src/orca/scripts/apps/soffice/braille_generator.py |  128 ++++++--------------
 src/orca/scripts/apps/soffice/script.py            |   41 +++----
 src/orca/scripts/apps/soffice/script_utilities.py  |   88 ++++---------
 src/orca/scripts/apps/soffice/speech_generator.py  |   33 +++---
 .../scripts/apps/soffice/structural_navigation.py  |  100 ---------------
 src/orca/structural_navigation.py                  |    2 +-
 8 files changed, 195 insertions(+), 296 deletions(-)
---
diff --git a/src/orca/script_utilities.py b/src/orca/script_utilities.py
index db252f0..e8c1c14 100644
--- a/src/orca/script_utilities.py
+++ b/src/orca/script_utilities.py
@@ -1019,6 +1019,22 @@ class Utilities:
 
         return pyatspi.findAncestor(obj, self.isSpreadSheetTable)
 
+    def cellColumnChanged(self, cell):
+        row, column = self._script.utilities.coordinatesForCell(cell)
+        if column == -1:
+            return False
+
+        lastColumn = self._script.pointOfReference.get("lastColumn")
+        return column != lastColumn
+
+    def cellRowChanged(self, cell):
+        row, column = self._script.utilities.coordinatesForCell(cell)
+        if row == -1:
+            return False
+
+        lastRow = self._script.pointOfReference.get("lastRow")
+        return row != lastRow
+
     def shouldReadFullRow(self, obj):
         table = self.getTable(obj)
         if not table:
@@ -2173,6 +2189,9 @@ class Utilities:
         if textContents and self._script.pointOfReference.get('entireDocumentSelected'):
             return textContents, startOffset, endOffset
 
+        if self.isSpreadSheetCell(obj):
+            return textContents, startOffset, endOffset
+
         prevObj = self.findPreviousObject(obj)
         while prevObj:
             if self.queryNonEmptyText(prevObj):
@@ -2433,6 +2452,9 @@ class Utilities:
 
         return obj, offset
 
+    def getFirstCaretPosition(self, obj):
+        return obj, 0
+
     def setCaretPosition(self, obj, offset, documentFrame=None):
         orca.setLocusOfFocus(None, obj, False)
         self.setCaretOffset(obj, offset)
@@ -3482,13 +3504,83 @@ class Utilities:
 
         return table.nRows, table.nColumns
 
-    def cellForCoordinates(self, obj, row, column):
+    def _getTableRowRange(self, obj):
+        rowCount, columnCount = self.rowAndColumnCount(obj)
+        startIndex, endIndex = 0, columnCount
+        if not self.isSpreadSheetCell(obj):
+            return startIndex, endIndex
+
+        parent = self._script.utilities.getTable(obj)
+        try:
+            component = parent.queryComponent()
+        except:
+            msg = "ERROR: Exception querying component interface of %s" % parent
+            debug.println(debug.LEVEL_INFO, msg, True)
+            return startIndex, endIndex
+
+        x, y, width, height = component.getExtents(pyatspi.DESKTOP_COORDS)
+        cell = component.getAccessibleAtPoint(x+1, y, pyatspi.DESKTOP_COORDS)
+        if cell:
+            row, column = self.coordinatesForCell(cell)
+            startIndex = column
+
+        cell = component.getAccessibleAtPoint(x+width-1, y, pyatspi.DESKTOP_COORDS)
+        if cell:
+            row, column = self.coordinatesForCell(cell)
+            endIndex = column + 1
+
+        return startIndex, endIndex
+
+    def getShowingCellsInSameRow(self, obj):
+        parent = self._script.utilities.getTable(obj)
+        try:
+            table = parent.queryTable()
+        except:
+            msg = "ERROR: Exception querying table interface of %s" % parent
+            debug.println(debug.LEVEL_INFO, msg, True)
+            return []
+
+        row, column = self.coordinatesForCell(obj)
+        if row == -1:
+            return []
+
+        startIndex, endIndex = self._getTableRowRange(obj)
+        if startIndex == endIndex:
+            return []
+
+        cells = []
+        for i in range(startIndex, endIndex):
+            cell = table.getAccessibleAt(row, i)
+            try:
+                showing = cell.getState().contains(pyatspi.STATE_SHOWING)
+            except:
+                continue
+            if showing:
+                cells.append(cell)
+
+        return cells
+
+    def cellForCoordinates(self, obj, row, column, showingOnly=False):
         try:
             table = obj.queryTable()
         except:
             return None
 
-        return table.getAccessibleAt(row, column)
+        cell = table.getAccessibleAt(row, column)
+        if not showingOnly:
+            return cell
+
+        try:
+            state = cell.getState()
+        except:
+            msg = "ERROR: Exception getting state of %s" % cell
+            debug.println(debug.LEVEL_INFO, msg, True)
+            return None
+
+        if not state().contains(pyatspi.STATE_SHOWING):
+            return None
+
+        return cell
 
     def isLastCell(self, obj):
         try:
diff --git a/src/orca/scripts/apps/soffice/Makefile.am b/src/orca/scripts/apps/soffice/Makefile.am
index 9ddc441..145f91e 100644
--- a/src/orca/scripts/apps/soffice/Makefile.am
+++ b/src/orca/scripts/apps/soffice/Makefile.am
@@ -5,8 +5,7 @@ orca_python_PYTHON = \
        script.py \
        script_utilities.py \
        speech_generator.py \
-       spellcheck.py \
-       structural_navigation.py
+       spellcheck.py
 
 orca_pythondir=$(pkgpythondir)/scripts/apps/soffice
 
diff --git a/src/orca/scripts/apps/soffice/braille_generator.py 
b/src/orca/scripts/apps/soffice/braille_generator.py
index c12e8aa..21c662e 100644
--- a/src/orca/scripts/apps/soffice/braille_generator.py
+++ b/src/orca/scripts/apps/soffice/braille_generator.py
@@ -39,7 +39,7 @@ class BrailleGenerator(braille_generator.BrailleGenerator):
     # pylint: disable-msg=W0142
 
     def __init__(self, script):
-        braille_generator.BrailleGenerator.__init__(self, script)
+        super().__init__(script)
 
     def _generateRoleName(self, obj, **args):
         if self._script.utilities.isDocument(obj):
@@ -88,7 +88,17 @@ class BrailleGenerator(braille_generator.BrailleGenerator):
 
         return []
 
-    def _generateSpreadSheetCell(self, obj, **args):
+    def _generateRealTableCell(self, obj, **args):
+        if not obj.childCount:
+            result = super()._generateRealTableCell(obj, **args)
+        else:
+            result = []
+            for child in obj:
+                result.extend(self.generate(child))
+
+        if not self._script.utilities.isSpreadSheetCell(obj):
+            return result
+
         try:
             objectText = self._script.utilities.substring(obj, 0, -1)
             cellName = self._script.utilities.spreadSheetCellName(obj)
@@ -97,97 +107,27 @@ class BrailleGenerator(braille_generator.BrailleGenerator):
 
         return [braille.Component(obj, " ".join((objectText, cellName)))]
 
-    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
-
-        Returns a list of utterances to be spoken for the object.
-        """
-        result = []
-        if self._script.utilities.isSpreadSheetCell(obj):
-            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
-            # processed.
-            #
-            # If it's more than one, then get the speech for each child,
-            # and call this method again.
-            #
-            if obj.childCount <= 1:
-                result.extend(braille_generator.BrailleGenerator.\
-                              _generateRealTableCell(self, obj, **args))
-            else:
-                for child in obj:
-                    cellResult = self._generateRealTableCell(child, **args)
-                    if cellResult and result and self._mode == 'braille':
-                        result.append(braille.Region(
-                            object_properties.TABLE_CELL_DELIMITER_BRAILLE))
-                    result.extend(cellResult)
-        return result
+    def _generateTableCellDelimiter(self, obj, **args):
+        return braille.Region(object_properties.TABLE_CELL_DELIMITER_BRAILLE)
 
     def _generateTableCellRow(self, obj, **args):
-        """Get the speech for a table row or cell depending on settings.
+        if not self._script.utilities.shouldReadFullRow(obj):
+            return self._generateRealTableCell(obj, **args)
 
-        Arguments:
-        - obj: the table cell
+        if not self._script.utilities.isSpreadSheetCell(obj):
+            return super()._generateTableCellRow(obj, **args)
+
+        cells = self._script.utilities.getShowingCellsInSameRow(obj)
+        if not cells:
+            return []
 
-        Returns a list of utterances to be spoken for the object.
-        """
         result = []
-        if self._script.utilities.isSpreadSheetCell(obj):
-            # Adding in a check here to make sure that the parent is a
-            # valid table. It's possible that the parent could be a
-            # table cell too (see bug #351501).
-            #
-            parent = obj.parent
-            parentTable = parent.queryTable()
-            readFullRow = self._script.utilities.shouldReadFullRow(obj)
-            if readFullRow and parentTable:
-                index = self._script.utilities.cellIndex(obj)
-                row = parentTable.getRowAtIndex(index)
-                column = parentTable.getColumnAtIndex(index)
-                # This is an indication of whether we should present all the
-                # table cells (the user has moved focus up or down a row),
-                # or just the current one (focus has moved left or right in
-                # the same row).
-                #
-                presentAll = True
-                if "lastRow" in self._script.pointOfReference and \
-                    "lastColumn" in self._script.pointOfReference:
-                    pointOfReference = self._script.pointOfReference
-                    presentAll = \
-                        (self._mode == 'braille') \
-                        or ((pointOfReference["lastRow"] != row) \
-                            or ((row == 0 or row == parentTable.nRows-1) \
-                                and pointOfReference["lastColumn"] == column))
-                if presentAll:
-                    [startIndex, endIndex] = \
-                        self._script.utilities.getTableRowRange(obj)
-                    for i in range(startIndex, endIndex):
-                        cell = parentTable.getAccessibleAt(row, i)
-                        showing = cell.getState().contains( \
-                                      pyatspi.STATE_SHOWING)
-                        if showing:
-                            cellResult = self._generateRealTableCell(cell,
-                                                                     **args)
-                            if cellResult and result \
-                               and self._mode == 'braille':
-                                result.append(braille.Region(
-                                    object_properties.TABLE_CELL_DELIMITER_BRAILLE))
-                            result.extend(cellResult)
-                else:
-                    result.extend(self._generateRealTableCell(obj, **args))
-            else:
-                result.extend(self._generateRealTableCell(obj, **args))
-        else:
-            result.extend(
-                braille_generator.BrailleGenerator._generateTableCellRow(
-                    self, obj, **args))
+        for cell in cells:
+            cellResult = self._generateRealTableCell(cell, **args)
+            if cellResult and result:
+                result.append(self._generateTableCellDelimiter(obj, **args))
+            result.extend(cellResult)
+
         return result
 
     def _generateChildTab(self, obj, **args):
@@ -211,6 +151,18 @@ class BrailleGenerator(braille_generator.BrailleGenerator):
                             result.extend(self.generate(tab, **args))
         return result
 
+    def _generateAncestors(self, obj, **args):
+        if self._script._lastCommandWasStructNav:
+            return []
+
+        return super()._generateAncestors(obj, **args)
+
+    def _generateIncludeContext(self, obj, **args):
+        if self._script._lastCommandWasStructNav:
+            return False
+
+        return super()._generateIncludeContext(obj, **args)
+
     def generateBraille(self, obj, **args):
         args['useDefaultFormatting'] = self._script.utilities.isNonFocusableList(obj)
         oldRole = self._overrideRole(self._getAlternativeRole(obj, **args), args)
diff --git a/src/orca/scripts/apps/soffice/script.py b/src/orca/scripts/apps/soffice/script.py
index f1b3b3e..8eb1540 100644
--- a/src/orca/scripts/apps/soffice/script.py
+++ b/src/orca/scripts/apps/soffice/script.py
@@ -42,13 +42,13 @@ import orca.orca_state as orca_state
 import orca.speech as speech
 import orca.settings as settings
 import orca.settings_manager as settings_manager
+import orca.structural_navigation as structural_navigation
 
 from .braille_generator import BrailleGenerator
 from .formatting import Formatting
 from .script_utilities import Utilities
 from .spellcheck import SpellCheck
 from .speech_generator import SpeechGenerator
-from .structural_navigation import StructuralNavigation
 
 _settingsManager = settings_manager.getManager()
 
@@ -106,14 +106,14 @@ class Script(default.Script):
         """Returns the 'structural navigation' class for this script.
         """
         types = self.getEnabledStructuralNavigationTypes()
-        return StructuralNavigation(self, types, enabled=False)
+        return structural_navigation.StructuralNavigation(self, types, enabled=False)
 
     def getEnabledStructuralNavigationTypes(self):
         """Returns a list of the structural navigation object types
         enabled in this script.
         """
 
-        enabledTypes = [StructuralNavigation.TABLE_CELL]
+        enabledTypes = [structural_navigation.StructuralNavigation.TABLE_CELL]
 
         return enabledTypes
 
@@ -290,27 +290,6 @@ class Script(default.Script):
         prefs.update(self.spellcheck.getPreferencesFromGUI())
         return prefs
 
-    def isStructuralNavigationCommand(self, inputEvent=None):
-        """Checks to see if the inputEvent was a structural navigation
-        command. This is necessary to prevent double-presentation of
-        items. [[[TODO - JD: Perhaps this should be moved to default.py]]]
-
-        Arguments:
-        - inputEvent: The input event to examine. If none is provided,
-          orca_state.lastInputEvent will be used.
-
-        Returns True if inputEvent is a structural navigation command
-        enabled in this script.
-        """
-
-        inputEvent = inputEvent or orca_state.lastInputEvent
-        if isinstance(inputEvent, input_event.KeyboardEvent):
-            if self.structuralNavigation.keyBindings.\
-                    getInputHandler(inputEvent):
-                return True
-
-        return False
-
     def doWhereAmI(self, inputEvent, basicOnly):
         """Performs the whereAmI operation."""
 
@@ -319,6 +298,14 @@ class Script(default.Script):
 
         super().doWhereAmI(inputEvent, basicOnly)
 
+    def presentObject(self, obj, offset=0):
+        if not self._lastCommandWasStructNav:
+            super().presentObject(obj, offset)
+            return
+
+        utterances = self.speechGenerator.generateSpeech(obj)
+        speech.speak(utterances)
+
     def panBrailleLeft(self, inputEvent=None, panAmount=0):
         """In document content, we want to use the panning keys to browse the
         entire document.
@@ -745,7 +732,7 @@ class Script(default.Script):
         if self._inSayAll:
             return
 
-        if self.isStructuralNavigationCommand():
+        if self._lastCommandWasStructNav:
             return
 
         if not event.detail1:
@@ -810,7 +797,7 @@ class Script(default.Script):
         if self.utilities._flowsFromOrToSelection(event.source):
            return
 
-        if self.isStructuralNavigationCommand():
+        if self._lastCommandWasStructNav:
             return
 
         if self.utilities.isSpreadSheetCell(orca_state.locusOfFocus):
@@ -936,5 +923,7 @@ class Script(default.Script):
     def onWindowDeactivated(self, event):
         """Callback for window:deactivate accessibility events."""
 
+        self._lastCommandWasStructNav = False
+
         super().onWindowDeactivated(event)
         self.spellcheck.deactivate()
diff --git a/src/orca/scripts/apps/soffice/script_utilities.py 
b/src/orca/scripts/apps/soffice/script_utilities.py
index 6cf6c8c..308605d 100644
--- a/src/orca/scripts/apps/soffice/script_utilities.py
+++ b/src/orca/scripts/apps/soffice/script_utilities.py
@@ -96,6 +96,11 @@ class Utilities(script_utilities.Utilities):
            and (self.isSpreadSheetCell(obj) or self.isTextDocumentCell(obj)):
             return ""
 
+        # More bogusness from (at least) Calc combined with the aforementioned
+        # fallback-to-name behavior....
+        if self.isDocument(obj) and text == obj.name and obj.name.startswith("file:///"):
+            return ""
+
         # TODO - JD: Once the VCL script is completed and subclasses the
         # appropriate toolkit scripts, this should not be needed.
         if obj.parent and obj.parent.getRole() == pyatspi.ROLE_LIST_BOX:
@@ -156,67 +161,6 @@ class Utilities(script_utilities.Utilities):
 
         return row, column, table
 
-    def getShowingCellsInRow(self, obj):
-        row, column, parentTable = self.getRowColumnAndTable(obj)
-        try:
-            table = parentTable.queryTable()
-        except:
-            return []
-
-        startIndex, endIndex = self.getTableRowRange(obj)
-        cells = []
-        for i in range(startIndex, endIndex):
-            cell = table.getAccessibleAt(row, i)
-            try:
-                showing = cell.getState().contains(pyatspi.STATE_SHOWING)
-            except:
-                continue
-            if showing:
-                cells.append(cell)
-
-        return cells
-
-    def getTableRowRange(self, obj):
-        """If this is spread sheet cell, return the start and end indices
-        of the spread sheet cells for the table that obj is in. Otherwise
-        return the complete range (0, parentTable.nColumns).
-
-        Arguments:
-        - obj: a table cell.
-
-        Returns the start and end table cell indices.
-        """
-
-        parent = obj.parent
-        try:
-            parentTable = parent.queryTable()
-        except:
-            return [-1, -1]
-
-        startIndex = 0
-        endIndex = parentTable.nColumns
-
-        if self.isSpreadSheetCell(obj):
-            extents = parent.queryComponent().getExtents(pyatspi.DESKTOP_COORDS)
-            y = extents.y
-            leftX = extents.x + 1
-            leftCell = \
-                parent.queryComponent().getAccessibleAtPoint(leftX, y, 0)
-            if leftCell:
-                table = leftCell.parent.queryTable()
-                index = self.cellIndex(leftCell)
-                startIndex = table.getColumnAtIndex(index)
-
-            rightX = extents.x + extents.width - 1
-            rightCell = \
-                parent.queryComponent().getAccessibleAtPoint(rightX, y, 0)
-            if rightCell:
-                table = rightCell.parent.queryTable()
-                index = self.cellIndex(rightCell)
-                endIndex = table.getColumnAtIndex(index) + 1
-
-        return [startIndex, endIndex]
-
     def rowHeadersForCell(self, obj):
         rowHeader, colHeader = self.getDynamicHeadersForCell(obj)
         if rowHeader:
@@ -762,3 +706,25 @@ class Utilities(script_utilities.Utilities):
                 children.append(obj[i])
 
         return children
+
+    def getFirstCaretPosition(self, obj):
+        try:
+            text = obj.queryText()
+        except:
+            if obj and obj.childCount:
+                return self.getFirstCaretPosition(obj[0])
+
+        return obj, 0
+
+    def shouldReadFullRow(self, obj):
+        if self._script._lastCommandWasStructNav:
+            return False
+
+        lastKey, mods = self.lastKeyAndModifiers()
+        if lastKey in ["Tab", "ISO_Left_Tab"]:
+            return False
+
+        if not super().shouldReadFullRow(obj):
+            return False
+
+        return self.cellRowChanged(obj)
diff --git a/src/orca/scripts/apps/soffice/speech_generator.py 
b/src/orca/scripts/apps/soffice/speech_generator.py
index 703bd32..88e6231 100644
--- a/src/orca/scripts/apps/soffice/speech_generator.py
+++ b/src/orca/scripts/apps/soffice/speech_generator.py
@@ -361,10 +361,12 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
         Returns a list of utterances to be spoken for the object.
         """
 
-        result = speech_generator.SpeechGenerator._generateRealTableCell(
-            self, obj, **args)
+        result = super()._generateRealTableCell(obj, **args)
 
         if not self._script.utilities.isSpreadSheetCell(obj):
+            if self._script._lastCommandWasStructNav:
+                return result
+
             if _settingsManager.getSetting('speakCellCoordinates'):
                 result.append(obj.name)
             return result
@@ -393,24 +395,17 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
         return result
 
     def _generateTableCellRow(self, obj, **args):
-        """Get the speech for a table cell row if the user wants to hear
-        the full row and if the row has actually changed."""
+        if not self._script.utilities.shouldReadFullRow(obj):
+            return self._generateRealTableCell(obj, **args)
 
-        speakFullRow = self._script.utilities.shouldReadFullRow(obj)
-        if speakFullRow:
-            row, column, table = \
-                self._script.utilities.getRowColumnAndTable(obj)
-            lastRow = self._script.pointOfReference.get("lastRow")
-            if lastRow == -1 and self._script.utilities.isSpreadSheetCell(obj):
-                speakFullRow = False
-            else:
-                speakFullRow = row != lastRow
+        if not self._script.utilities.isSpreadSheetCell(obj):
+            return super()._generateTableCellRow(obj, **args)
 
-        if not speakFullRow:
-            return self._generateRealTableCell(obj, **args)
+        cells = self._script.utilities.getShowingCellsInSameRow(obj)
+        if not cells:
+            return []
 
         result = []
-        cells = self._script.utilities.getShowingCellsInRow(obj)
         for cell in cells:
             result.extend(self._generateRealTableCell(cell, **args))
 
@@ -424,6 +419,9 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
         dialog).
         """
 
+        if self._script._lastCommandWasStructNav:
+            return []
+
         topLevel = self._script.utilities.topLevelObject(obj)
         if topLevel and topLevel.getRole() == pyatspi.ROLE_DIALOG:
             return []
@@ -468,6 +466,9 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
         if self._script.utilities.isSpreadSheetCell(obj):
             return []
 
+        if self._script._lastCommandWasStructNav:
+            return []
+
         return super()._generateUnselectedCell(obj, **args)
 
     def generateSpeech(self, obj, **args):
diff --git a/src/orca/structural_navigation.py b/src/orca/structural_navigation.py
index e225a2b..3ed180b 100644
--- a/src/orca/structural_navigation.py
+++ b/src/orca/structural_navigation.py
@@ -1110,7 +1110,7 @@ class StructuralNavigation:
           positioned.
         """
 
-        return [obj, 0]
+        return self._script.utilities.getFirstCaretPosition(obj)
 
     def _setCaretPosition(self, obj, characterOffset):
         """Sets the caret at the specified offset within obj."""


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