[orca] Move some speech generator methods to superclass; fix children-changed bug



commit 6665ad900a31a4c2b5970b41e2ef1355e7d70365
Author: Joanmarie Diggs <jdiggs igalia com>
Date:   Tue Feb 16 05:47:00 2016 -0500

    Move some speech generator methods to superclass; fix children-changed bug

 src/orca/generator.py                   |   41 +++++++++++++++
 src/orca/script_utilities.py            |   26 ++++++++++
 src/orca/scripts/apps/soffice/script.py |   57 ++++++++-------------
 src/orca/speech_generator.py            |   83 ++++++-------------------------
 4 files changed, 105 insertions(+), 102 deletions(-)
---
diff --git a/src/orca/generator.py b/src/orca/generator.py
index 5adda16..9472e10 100644
--- a/src/orca/generator.py
+++ b/src/orca/generator.py
@@ -592,6 +592,22 @@ class Generator:
                 result.append(indicators[0])
         return result
 
+    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 list boxes. If the object
+        is not multiselectable, an empty array will be returned.
+        """
+
+        result = []
+        if not args.get('mode', None):
+            args['mode'] = self._mode
+        args['stringType'] = 'multiselect'
+        if obj.getState().contains(pyatspi.STATE_MULTISELECTABLE) \
+           and obj.childCount:
+            result.append(self._script.formatting.getString(**args))
+        return result
+
     #####################################################################
     #                                                                   #
     # Table interface information                                       #
@@ -1035,3 +1051,28 @@ class Generator:
 
     def _generateProgressBarValue(self, obj, **args):
         return []
+
+    def _getAlternativeRole(self, obj, **args):
+        if self._script.utilities.isMath(obj):
+            if self._script.utilities.isMathFraction(obj):
+                return 'ROLE_MATH_FRACTION'
+            if self._script.utilities.isMathRoot(obj):
+                return 'ROLE_MATH_ROOT'
+            if self._script.utilities.isMathSubOrSuperScript(obj):
+                return 'ROLE_MATH_SCRIPT_SUBSUPER'
+            if self._script.utilities.isMathUnderOrOverScript(obj):
+                return 'ROLE_MATH_SCRIPT_UNDEROVER'
+            if self._script.utilities.isMathMultiScript(obj):
+                return 'ROLE_MATH_MULTISCRIPT'
+            if self._script.utilities.isMathEnclose(obj):
+                return 'ROLE_MATH_ENCLOSED'
+            if self._script.utilities.isMathFenced(obj):
+                return 'ROLE_MATH_FENCED'
+            if self._script.utilities.isMathTable(obj):
+                return 'ROLE_MATH_TABLE'
+            if self._script.utilities.isMathTableRow(obj):
+                return 'ROLE_MATH_TABLE_ROW'
+        if self._script.utilities.isStatic(obj):
+            return 'ROLE_STATIC'
+
+        return args.get('role', obj.getRole())
diff --git a/src/orca/script_utilities.py b/src/orca/script_utilities.py
index 339822c..da32a99 100644
--- a/src/orca/script_utilities.py
+++ b/src/orca/script_utilities.py
@@ -3340,6 +3340,17 @@ class Utilities:
         row, col = table.getRowAtIndex(index), table.getColumnAtIndex(index)
         return table.getRowExtentAt(row, col), table.getColumnExtentAt(row, col)
 
+    def rowAndColumnCount(self, obj):
+        if not (obj and obj.getRole() == pyatspi.ROLE_TABLE):
+            return -1, -1
+
+        try:
+            table = obj.queryTable()
+        except:
+            return -1, -1
+
+        return table.nRows, table.nColumns
+
     def cellForCoordinates(self, obj, row, column):
         try:
             table = obj.queryTable()
@@ -3348,6 +3359,21 @@ class Utilities:
 
         return table.getAccessibleAt(row, column)
 
+    def isLastCell(self, obj):
+        if not (obj and obj.getRole() == pyatspi.ROLE_TABLE_CELL):
+            return False
+
+        isTable = lambda x: x and 'Table' in pyatspi.listInterfaces(x)
+        parent = pyatspi.findAncestor(obj, isTable)
+        try:
+            table = parent.queryTable()
+        except:
+            return False
+
+        index = self.cellIndex(obj)
+        row, col = table.getRowAtIndex(index), table.getColumnAtIndex(index)
+        return row + 1 == table.nRows and col + 1 == table.nColumns
+
     def isNonUniformTable(self, obj):
         try:
             table = obj.queryTable()
diff --git a/src/orca/scripts/apps/soffice/script.py b/src/orca/scripts/apps/soffice/script.py
index 482f2bb..7f0e868 100644
--- a/src/orca/scripts/apps/soffice/script.py
+++ b/src/orca/scripts/apps/soffice/script.py
@@ -595,10 +595,16 @@ class Script(default.Script):
         # Pass the event onto the parent class to be handled in the default way.
         default.Script.locusOfFocusChanged(self, event,
                                            oldLocusOfFocus, newLocusOfFocus)
+        if not newLocusOfFocus:
+            return
 
+        cell = None
         if self.utilities.isTextDocumentCell(newLocusOfFocus):
-            row, column, table = \
-                self.utilities.getRowColumnAndTable(newLocusOfFocus.parent)
+            cell = newLocusOfFocus
+        elif self.utilities.isTextDocumentCell(newLocusOfFocus.parent):
+            cell = newLocusOfFocus.parent
+        if cell:
+            row, column = self.utilities.coordinatesForCell(cell)
             self.pointOfReference['lastRow'] = row
             self.pointOfReference['lastColumn'] = column
 
@@ -661,47 +667,28 @@ class Script(default.Script):
         default.Script.onActiveDescendantChanged(self, event)
 
     def onChildrenChanged(self, event):
-        """Called when a child node has changed.
-
-        Arguments:
-        - event: the Event
-        """
-
-        try:
-            anyDataRole = event.any_data.getRole()
-        except:
-            return
+        """Callback for object:children-changed accessibility events."""
 
-        if anyDataRole == pyatspi.ROLE_TABLE:
-            if self.utilities.isSpreadSheetCell(event.any_data):
-                orca.setLocusOfFocus(event, event.any_data)
+        if self.utilities.isSpreadSheetCell(event.any_data):
+            orca.setLocusOfFocus(event, event.any_data)
             return
 
-        if anyDataRole == pyatspi.ROLE_TABLE_CELL:
+        if self.utilities.isLastCell(event.any_data):
             activeRow = self.pointOfReference.get('lastRow', -1)
             activeCol = self.pointOfReference.get('lastColumn', -1)
             if activeRow < 0 or activeCol < 0:
                 return
 
-            eventRow, eventCol, table = \
-                self.utilities.getRowColumnAndTable(event.any_data)
-            try:
-                itable = table.queryTable()
-            except NotImplementedError:
-                return
-
-            if eventRow == itable.nRows - 1 and eventCol == itable.nColumns - 1:
-                fullMessage = briefMessage = ""
-                voice = self.voices.get(settings.SYSTEM_VOICE)
-                if activeRow == itable.nRows:
-                    fullMessage = messages.TABLE_ROW_DELETED_FROM_END
-                    briefMessage = messages.TABLE_ROW_DELETED
-                else:
-                    fullMessage =  messages.TABLE_ROW_INSERTED_AT_END
-                    briefMessage = messages.TABLE_ROW_INSERTED
-                if fullMessage:
-                    self.presentMessage(fullMessage, briefMessage, voice)
-                return
+            self.utilities.handleUndoTextEvent(event)
+            rowCount, colCount = self.utilities.rowAndColumnCount(event.source)
+            if activeRow == rowCount:
+                full = messages.TABLE_ROW_DELETED_FROM_END
+                brief = messages.TABLE_ROW_DELETED
+            else:
+                full = messages.TABLE_ROW_INSERTED_AT_END
+                brief = messages.TABLE_ROW_INSERTED
+            self.presentMessage(full, brief)
+            return
 
         default.Script.onChildrenChanged(self, event)
 
diff --git a/src/orca/speech_generator.py b/src/orca/speech_generator.py
index c50ea6e..b525a7f 100644
--- a/src/orca/speech_generator.py
+++ b/src/orca/speech_generator.py
@@ -465,19 +465,15 @@ class SpeechGenerator(generator.Generator):
     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
+        the object.  This is typically for list boxes. If the object
         is not multiselectable, an empty array will be returned.
         """
         if _settingsManager.getSetting('onlySpeakDisplayedText'):
             return []
 
-        result = []
         acss = self.voice(STATE)
-        if obj.getState().contains(pyatspi.STATE_MULTISELECTABLE) \
-           and obj.childCount:
-            result.append(self._script.formatting.getString(
-                mode='speech',
-                stringType='multiselect'))
+        result = super()._generateMultiselectableState(obj, **args)
+        if result:
             result.extend(acss)
         return result
 
@@ -928,27 +924,13 @@ class SpeechGenerator(generator.Generator):
         if _settingsManager.getSetting('onlySpeakDisplayedText'):
             return []
 
-        result = []
-        acss = self.voice(SYSTEM)
         if _settingsManager.getSetting('speechVerbosityLevel') \
-                == settings.VERBOSITY_LEVEL_VERBOSE:
-            if obj.getRole() == pyatspi.ROLE_TABLE_CELL:
-                cell = obj
-            else:
-                cell = self._script.utilities.ancestorWithRole(
-                    obj, [pyatspi.ROLE_TABLE_CELL], [pyatspi.ROLE_FRAME])
-            try:
-                table = cell.parent.queryTable()
-            except:
-                pass
-            else:
-                index = self._script.utilities.cellIndex(cell)
-                row = table.getRowAtIndex(index)
-                col = table.getColumnAtIndex(index)
-                if row + 1 == table.nRows and col + 1 == table.nColumns:
-                    result.append(messages.TABLE_END)
-        if result:
-            result.extend(acss)
+           != settings.VERBOSITY_LEVEL_VERBOSE:
+            return []
+
+        if self._script.utilities.isLastCell(obj):
+            result = [messages.TABLE_END]
+            result.extend(self.voice(SYSTEM))
         return result
 
     #####################################################################
@@ -1315,21 +1297,13 @@ class SpeechGenerator(generator.Generator):
         if _settingsManager.getSetting('onlySpeakDisplayedText'):
             return []
 
-        result = []
-        acss = self.voice(SYSTEM)
-        try:
-            value = obj.queryValue()
-        except NotImplementedError:
-            pass
-        else:
-            percentValue = \
-                (value.currentValue
-                 / (value.maximumValue - value.minimumValue)) \
-                * 100.0
-            result.append(messages.percentage(percentValue))
-        if result:
-            result.extend(acss)
-        return result
+        percentValue = self._script.utilities.getValueAsPercent(obj)
+        if percentValue is not None:
+            result = [percentValue]
+            result.extend(self.voice(SYSTEM))
+            return result
+
+        return []
 
     #####################################################################
     #                                                                   #
@@ -2252,31 +2226,6 @@ class SpeechGenerator(generator.Generator):
     #                                                                   #
     #####################################################################
 
-    def _getAlternativeRole(self, obj, **args):
-        if self._script.utilities.isMath(obj):
-            if self._script.utilities.isMathFraction(obj):
-                return 'ROLE_MATH_FRACTION'
-            if self._script.utilities.isMathRoot(obj):
-                return 'ROLE_MATH_ROOT'
-            if self._script.utilities.isMathSubOrSuperScript(obj):
-                return 'ROLE_MATH_SCRIPT_SUBSUPER'
-            if self._script.utilities.isMathUnderOrOverScript(obj):
-                return 'ROLE_MATH_SCRIPT_UNDEROVER'
-            if self._script.utilities.isMathMultiScript(obj):
-                return 'ROLE_MATH_MULTISCRIPT'
-            if self._script.utilities.isMathEnclose(obj):
-                return 'ROLE_MATH_ENCLOSED'
-            if self._script.utilities.isMathFenced(obj):
-                return 'ROLE_MATH_FENCED'
-            if self._script.utilities.isMathTable(obj):
-                return 'ROLE_MATH_TABLE'
-            if self._script.utilities.isMathTableRow(obj):
-                return 'ROLE_MATH_TABLE_ROW'
-        if self._script.utilities.isStatic(obj):
-            return 'ROLE_STATIC'
-
-        return args.get('role', obj.getRole())
-
     def _generatePause(self, obj, **args):
         if not _settingsManager.getSetting('enablePauseBreaks') \
            or args.get('eliminatePauses', False):


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