[orca] Create a dedicated object:state-changed callback for "showing" events



commit f07b55ba4b323fa4ca6268d59a5a43873d2557f1
Author: Joanmarie Diggs <jdiggs igalia com>
Date:   Mon Nov 4 18:18:55 2013 -0500

    Create a dedicated object:state-changed callback for "showing" events

 src/orca/scripts/apps/evince/script.py            |   17 ++--
 src/orca/scripts/apps/evolution/script.py         |   62 ++++++-------
 src/orca/scripts/apps/gnome-panel/script.py       |   36 ++------
 src/orca/scripts/apps/gnome-search-tool/script.py |  101 ++++-----------------
 src/orca/scripts/apps/metacity/script.py          |   29 +++----
 src/orca/scripts/default.py                       |   54 ++++-------
 src/orca/scripts/toolkits/CALLY/script.py         |   33 +++++++
 src/orca/scripts/toolkits/Gecko/script.py         |   62 ++++++--------
 8 files changed, 152 insertions(+), 242 deletions(-)
---
diff --git a/src/orca/scripts/apps/evince/script.py b/src/orca/scripts/apps/evince/script.py
index 73148c9..4e953d3 100644
--- a/src/orca/scripts/apps/evince/script.py
+++ b/src/orca/scripts/apps/evince/script.py
@@ -114,14 +114,15 @@ class Script(default.Script):
 
         return True
 
-    def onStateChanged(self, event):
-        """Called whenever an object's state changes."""
-
-        if event.type.startswith("object:state-changed:showing") \
-           and event.source.getRole() == pyatspi.ROLE_ALERT and event.detail1:
-            labels = self.utilities.unrelatedLabels(event.source)
+    def onShowingChanged(self, event):
+        """Callback for object:state-changed:showing accessibility events."""
+ 
+        obj = event.source
+        if obj.getRole() == pyatspi.ROLE_ALERT and event.detail1:
+            labels = self.utilities.unrelatedLabels(obj)
             message = " ".join(map(self.utilities.displayedText, labels))
             self.presentMessage(
                 message, voice=self.voices.get(settings.DEFAULT_VOICE))
-
-        return default.Script.onStateChanged(self, event)
+            return
+ 
+        default.Script.onShowingChanged(self, event)
diff --git a/src/orca/scripts/apps/evolution/script.py b/src/orca/scripts/apps/evolution/script.py
index 54cf2c4..cbb8991 100644
--- a/src/orca/scripts/apps/evolution/script.py
+++ b/src/orca/scripts/apps/evolution/script.py
@@ -112,48 +112,42 @@ class Script(WebKitGtk.Script):
 
         default.Script.onNameChanged(self, event)
 
-    def onStateChanged(self, event):
-        """Called whenever an object's state changes.
-
-        Arguments:
-        - event: the Event
-        """
-
+    def onShowingChanged(self, event):
+        """Callback for object:state-changed:showing accessibility events."""
+ 
         if not event.detail1:
-            default.Script.onStateChanged(self, event)
+            default.Script.onShowingChanged(self, event)
             return
 
-        # Present text in the Account Assistant
-        if event.type.startswith("object:state-changed:showing"):
-            try:
-                role = event.source.getRole()
-                relationSet = event.source.getRelationSet()
-            except:
-                return
+        obj = event.source
 
-            if role != pyatspi.ROLE_LABEL or relationSet:
-                default.Script.onStateChanged(self, event)
-                return
+        # Present text in the Account Assistant
+        try:
+            role = obj.getRole()
+            relationSet = obj.getRelationSet()
+        except:
+            return
 
-            window = self.utilities.topLevelObject(event.source)
-            focusedObj = self.utilities.focusedObject(window)
-            if self.utilities.spatialComparison(event.source, focusedObj) >= 0:
-                return
+        if role != pyatspi.ROLE_LABEL or relationSet:
+            default.Script.onShowingChanged(self, event)
+            return
 
-            # TODO - JD: The very last screen results in a crazy-huge number
-            # of events, and they come in an order that is not good for this
-            # approach. So we'll need to handle this particular case elsewhere.
-            if focusedObj.getRole() == pyatspi.ROLE_CHECK_BOX:
-                labels = self.utilities.unrelatedLabels(window)
-                if len(labels) > 15:
-                    return
-
-            voice = self.voices.get(settings.DEFAULT_VOICE)
-            text = self.utilities.displayedText(event.source)
-            self.presentMessage(text, voice=voice)
+        window = self.utilities.topLevelObject(obj)
+        focusedObj = self.utilities.focusedObject(window)
+        if self.utilities.spatialComparison(obj, focusedObj) >= 0:
             return
+ 
+        # TODO - JD: The very last screen results in a crazy-huge number
+        # of events, and they come in an order that is not good for this
+        # approach. So we'll need to handle this particular case elsewhere.
+        if focusedObj.getRole() == pyatspi.ROLE_CHECK_BOX:
+            labels = self.utilities.unrelatedLabels(window)
+            if len(labels) > 15:
+                return
 
-        default.Script.onStateChanged(self, event)
+        voice = self.voices.get(settings.DEFAULT_VOICE)
+        text = self.utilities.displayedText(obj)
+        self.presentMessage(text, voice=voice)
 
     def skipObjectEvent(self, event):
         # NOTE: This is here temporarily as part of the preparation for the
diff --git a/src/orca/scripts/apps/gnome-panel/script.py b/src/orca/scripts/apps/gnome-panel/script.py
index 3179141..983275e 100644
--- a/src/orca/scripts/apps/gnome-panel/script.py
+++ b/src/orca/scripts/apps/gnome-panel/script.py
@@ -26,11 +26,9 @@ __date__      = "$Date$"
 __copyright__ = "Copyright (c) 2005-2008 Sun Microsystems Inc."
 __license__   = "LGPL"
 
-import orca.scripts.default as default
-import orca.debug as debug
-import orca.speech as speech
 import pyatspi
 
+import orca.scripts.default as default
 from .speech_generator import SpeechGenerator
 
 ########################################################################
@@ -48,18 +46,8 @@ class Script(default.Script):
         - app: the application to create a script for.
         """
 
-        # Set the debug level for all the methods in this script.
-        #
-        self.debugLevel = debug.LEVEL_FINEST
-
-        self._debug("__init__")
         default.Script.__init__(self, app)
 
-    def _debug(self, msg):
-        """ Convenience method for printing debug messages
-        """
-        debug.println(self.debugLevel, "gnome-panel.py: "+msg)
-
     def getSpeechGenerator(self):
         """Returns the speech generator for this script."""
 
@@ -71,19 +59,8 @@ class Script(default.Script):
         Arguments:
         - event: the Event
         """
-        obj = event.source
-
-        self._debug("onStateChanged: '%s' %s (%d, %d)" % \
-                    (obj.name, event.type, event.detail1, event.detail2))
 
-        # Handle tooltip popups.
-        #
-        if obj.getRole() == pyatspi.ROLE_TOOL_TIP:
-            if event.type.startswith("object:state-changed:showing") and \
-               event.detail1 == 1:
-                self.displayBrailleMessage(obj.name)
-                utterances = self.speechGenerator.generateSpeech(obj)
-                speech.speak(utterances)
+        obj = event.source
 
         # If focus moves to something within a panel and focus was not
         # already in the containing panel, the panel will issue its
@@ -92,11 +69,10 @@ class Script(default.Script):
         # plus the extraneous event results in unnecessary chattiness
         # and updates the braille display to "panel."
         #
-        elif obj.getRole() == pyatspi.ROLE_PANEL and \
+        if obj.getRole() == pyatspi.ROLE_PANEL and \
              event.type.startswith("object:state-changed:focused") and \
-             event.detail1 == 1 and not \
-             event.source.getState().contains(pyatspi.STATE_FOCUSED):
+             event.detail1 == 1 \
+             and not obj.getState().contains(pyatspi.STATE_FOCUSED):
             return
 
-        else:
-            default.Script.onStateChanged(self, event)
+        default.Script.onStateChanged(self, event)
diff --git a/src/orca/scripts/apps/gnome-search-tool/script.py 
b/src/orca/scripts/apps/gnome-search-tool/script.py
index ef2ef9d..5e92b0e 100644
--- a/src/orca/scripts/apps/gnome-search-tool/script.py
+++ b/src/orca/scripts/apps/gnome-search-tool/script.py
@@ -25,16 +25,15 @@ __date__      = "$Date$"
 __copyright__ = "Copyright (c) 2005-2008 Sun Microsystems Inc."
 __license__   = "LGPL"
 
-import orca.debug as debug
+import pyatspi
+import time
+from gi.repository import GLib
+
 import orca.messages as messages
 import orca.scripts.default as default
 
 from orca.orca_i18n import _
 
-import pyatspi
-import time
-from gi.repository import GLib
-
 ########################################################################
 #                                                                      #
 # The gnome-search-tool script class.                                  #
@@ -51,26 +50,9 @@ class Script(default.Script):
         """
 
         default.Script.__init__(self, app)
-
-        # Set the debug level for all the methods in this script.
-        #
-        self.debugLevel = debug.LEVEL_FINEST
-
-        # The table of files found.
-        #
         self.fileTable = None
-
-        # Set to true if we are doing a search.
-        #
         self.searching = False
-
-        # Time value using by the interval timer.
-        #
         self.startTime = None
-
-        # Interval in seconds, between utterances of "Searching" when a
-        # search is in progress.
-        #
         self.searchInterval = 5.0
 
     def _speakSearching(self):
@@ -89,83 +71,40 @@ class Script(default.Script):
 
         return True
 
-    def onStateChanged(self, event):
-        """Called whenever an object's state changes.
+    def onShowingChanged(self, event):
+        """Callback for object:state-changed:showing accessibility events."""
 
-        Arguments:
-        - event: the Event
-        """
+        obj = event.source
+        if obj.getRole() != pyatspi.ROLE_PUSH_BUTTON \
+           or not obj.getState().contains(pyatspi.STATE_VISIBLE):
+            return default.Script.onShowingChanged(self, event)
 
-        details = debug.getAccessibleDetails(self.debugLevel, event.source)
-        debug.printObjectEvent(self.debugLevel, event, details)
-
-        rolesList = [pyatspi.ROLE_PUSH_BUTTON, \
-                    pyatspi.ROLE_FILLER, \
-                    pyatspi.ROLE_FILLER, \
-                    pyatspi.ROLE_FILLER, \
-                    pyatspi.ROLE_FRAME, \
-                    pyatspi.ROLE_APPLICATION]
-        visible = event.source.getState().contains(pyatspi.STATE_VISIBLE)
-
-        # Check to see if we have just had an "object:state-changed:showing"
-        # event for the Stop button. If the name is "Stop", and one of its
-        # states is VISIBLE, that means we have started a search. As the
-        # search progresses, regularly inform the user of this by speaking
-        # "Searching" (assuming the search tool has focus).
-        #
         # Translators: the "Stop" string must match what gnome-search-tool
         # is using.  We hate keying off stuff like this, but we're forced
         # to do so in this case.
-        #
-        if self.utilities.hasMatchingHierarchy(event.source, rolesList) and \
-           event.source.name == _("Stop") and visible:
-            debug.println(self.debugLevel,
-                          "gnome-search-tool.onNameChanged - " \
-                          + "search started.")
-
+        if obj.name == _("Stop"):
             self.searching = True
-
-            # If we don't already have a handle to the table containing the
-            # list of files found, then get it now.
-            #
             if not self.fileTable:
-                frame = self.utilities.topLevelObject(event.source)
+                frame = self.utilities.topLevelObject(obj)
                 allTables = self.utilities.descendantsWithRole(
                     frame, pyatspi.ROLE_TABLE)
                 self.fileTable = allTables[0]
 
             GLib.idle_add(self._speakSearching)
+            return
 
-        # Check to see if we have just had an "object:state-changed:showing"
-        # event for the Find button. If the name is "Find", and one of its
-        # states is VISIBLE and we are currently searching, that means we
-        # have just stopped a search. Inform the user that the search is
-        # complete and tell them how many files were found.
-        #
         # Translators: the "Find" string must match what gnome-search-tool
         # is using.  We hate keying off stuff like this, but we're forced
         # to do so in this case.
-        #
-        if self.utilities.hasMatchingHierarchy(event.source, rolesList) \
-           and event.source.name == _("Find") and visible and self.searching:
-            debug.println(self.debugLevel,
-                          "gnome-search-tool.onNameChanged - " \
-                          + "search completed.")
-
+        if obj.name == _("Find") and self.searching:
             self.searching = False
             self.presentMessage(messages.SEARCH_COMPLETE)
-            sensitive = self.fileTable.getState().contains( \
-                                                pyatspi.STATE_SENSITIVE)
-            if sensitive:
+            if self.fileTable.getState().contains(pyatspi.STATE_SENSITIVE):
                 try:
                     fileCount = self.fileTable.queryTable().nRows
+                    self.presentMessage(messages.filesFound(fileCount))
                 except NotImplementedError:
-                    fileCount = 0
-                noFilesString = messages.filesFound(fileCount)
-                self.presentMessage(noFilesString)
-            else:
-                self.presentMessage(messages.FILES_NOT_FOUND)
-
-        # Pass the event onto the parent class to be handled in the default way.
-        #
-        default.Script.onStateChanged(self, event)
+                    self.presentMessage(messages.FILES_NOT_FOUND)
+            return
+
+        default.Script.onShowingChanged(self, event)
diff --git a/src/orca/scripts/apps/metacity/script.py b/src/orca/scripts/apps/metacity/script.py
index 171213c..1b26b71 100644
--- a/src/orca/scripts/apps/metacity/script.py
+++ b/src/orca/scripts/apps/metacity/script.py
@@ -124,26 +124,19 @@ class Script(default.Script):
         if event.source.getRole() != pyatspi.ROLE_STATUS_BAR:
             default.Script.onNameChanged(self, event)
 
-    def onStateChanged(self, event):
-        """The status bar in metacity tells us what toplevel window
-        will be activated when tab is released.  We will key off the
-        text inserted event to determine when to say something, as it
-        seems to be the more reliable event.
+    def onShowingChanged(self, event):
+        """Callback for object:state-changed:showing accessibility events."""
 
-        Arguments:
-        - event: the object:state-changed: Event
-        """
+        obj = event.source
+        role = obj.getRole()
 
-        # Ignore changes on the status bar.  We handle them in
-        # onTextInserted.  The only exception is if the status bar is
-        # suddenly showing.  Then, we want to present it because we
-        # typically do not get onTextInserted events at that time.
-        #
-        if event.source.getRole() != pyatspi.ROLE_STATUS_BAR:
-            default.Script.onStateChanged(self, event)
-        elif (event.type.startswith("object:state-changed:showing")) \
-            and event.detail1:
-            self.presentStatusBar(event.source)
+        # If the status bar is suddenly showing, we need to handle it here
+        # because we typically do not get onTextInserted events at that time.
+        if role == pyatspi.ROLE_STATUS_BAR and event.detail1:
+            self.presentStatusBar(obj)
+            return
+
+        default.Script.onShowingChanged(self, event)
 
     def onTextInserted(self, event):
         """Called whenever text is inserted into an object.  This seems to
diff --git a/src/orca/scripts/default.py b/src/orca/scripts/default.py
index 35e24e0..7f097f2 100644
--- a/src/orca/scripts/default.py
+++ b/src/orca/scripts/default.py
@@ -534,7 +534,7 @@ class Script(script.Script):
         listeners["object:state-changed:focused"]           = \
             self.onStateChanged
         listeners["object:state-changed:showing"]           = \
-            self.onStateChanged
+            self.onShowingChanged
         listeners["object:state-changed:checked"]           = \
             self.onCheckedChanged
         listeners["object:state-changed:pressed"]           = \
@@ -2613,40 +2613,34 @@ class Script(script.Script):
                 #    orca.setLocusOfFocus(event, None)
                 return
 
-        # Handle tooltip popups.
-        #
-        if event.source.getRole() == pyatspi.ROLE_TOOL_TIP:
-            if not event.type.startswith("object:state-changed:showing"):
-                return
+    def onShowingChanged(self, event):
+        """Callback for object:state-changed:showing accessibility events."""
+
+        obj = event.source
+        role = obj.getRole()
+        if role == pyatspi.ROLE_NOTIFICATION:
+            speech.speak(self.speechGenerator.generateSpeech(obj))
+            labels = self.utilities.unrelatedLabels(obj)
+            msg = ''.join(map(self.utilities.displayedText, labels))
+            self.displayBrailleMessage(msg, flashTime=settings.brailleFlashTime)
+            notification_messages.saveMessage(msg)
+            return
 
+        if role == pyatspi.ROLE_TOOL_TIP:
             keyString, mods = self.utilities.lastKeyAndModifiers()
             if keyString != "F1" \
-               and not  _settingsManager.getSetting('presentToolTips'):
+               and not _settingsManager.getSetting('presentToolTips'):
                 return
-
-            if event.detail1 == 1:
-                self.presentToolTip(event.source)
+            if event.detail1:
+                self.presentToolTip(obj)
                 return
-
+ 
             if orca_state.locusOfFocus and keyString == "F1":
                 obj = orca_state.locusOfFocus
                 self.updateBraille(obj)
-                utterances = self.speechGenerator.generateSpeech(obj)
-                utterances.extend(
-                    self.tutorialGenerator.getTutorial(obj, False))
-                speech.speak(utterances)
+                speech.speak(self.speechGenerator.generateSpeech(obj))
                 return
 
-        if event.source.getRole() in state_change_notifiers:
-            notifiers = state_change_notifiers[event.source.getRole()]
-            found = False
-            for state in notifiers:
-                if state and event.type.endswith(state):
-                    found = True
-                    break
-            if found:
-                self.visualAppearanceChanged(event, event.source)
-
     def onTextAttributesChanged(self, event):
         """Called when an object's text attributes change. Right now this
         method is only to handle the presentation of spelling errors on
@@ -5099,13 +5093,3 @@ class Script(script.Script):
         message = time.strftime(dateFormat, time.localtime())
         self.presentMessage(message)
         return True
-
-# Dictionary that defines the state changes we care about for various
-# objects.  The key represents the role and the value represents a list
-# of states that we care about.
-#
-state_change_notifiers = {}
-
-state_change_notifiers[pyatspi.ROLE_PANEL]           = ("showing", None)
-state_change_notifiers[pyatspi.ROLE_LABEL]           = ("showing", None)
-state_change_notifiers[pyatspi.ROLE_NOTIFICATION]    = ("showing", None)
diff --git a/src/orca/scripts/toolkits/CALLY/script.py b/src/orca/scripts/toolkits/CALLY/script.py
index b1f71b4..409b8ee 100644
--- a/src/orca/scripts/toolkits/CALLY/script.py
+++ b/src/orca/scripts/toolkits/CALLY/script.py
@@ -258,6 +258,39 @@ class Script(default.Script):
 
         default.Script.onNameChanged(self, event)
 
+    def onShowingChanged(self, event):
+        """Callback for object:state-changed:showing accessibility events."""
+ 
+        try:
+            role = event.source.getRole()
+            name = event.source.name
+        except:
+            return
+ 
+        # When entering overview with many open windows, we get quite
+        # a few state-changed:showing events for nameless panels. The
+        # act of processing these by the default script causes us to
+        # present nothing, and introduces a significant delay before
+        # presenting the Top Bar button when Ctrl+Alt+Tab was pressed.
+        if role == pyatspi.ROLE_PANEL and not name:
+            return
+
+        # We cannot count on events or their order from dialog boxes.
+        # Therefore, the only way to reliably present a dialog is by
+        # ignoring the events of the dialog itself and keeping track
+        # of the current dialog.
+        activeDialog, timestamp = self._activeDialog
+        if not event.detail1 and event.source == activeDialog:
+            self._activeDialog = (None, 0)
+            self._activeDialogLabels = {}
+            return
+
+        if activeDialog and role == pyatspi.ROLE_LABEL and event.detail1:
+            if self.presentDialogLabel(event):
+                return
+
+        default.Script.onShowingChanged(self, event)
+
     def onStateChanged(self, event):
         """Called whenever an object's state changes.
 
diff --git a/src/orca/scripts/toolkits/Gecko/script.py b/src/orca/scripts/toolkits/Gecko/script.py
index ee9d4b2..72aaa51 100644
--- a/src/orca/scripts/toolkits/Gecko/script.py
+++ b/src/orca/scripts/toolkits/Gecko/script.py
@@ -441,12 +441,6 @@ class Script(default.Script):
             self.onDocumentLoadComplete
         listeners["document:load-stopped"]                  = \
             self.onDocumentLoadStopped
-        listeners["object:state-changed:showing"]           = \
-            self.onStateChanged
-        listeners["object:children-changed"]                = \
-            self.onChildrenChanged
-        listeners["object:text-changed:insert"]             = \
-            self.onTextInserted
         listeners["object:state-changed:focused"]           = \
             self.onStateFocused
 
@@ -1429,40 +1423,36 @@ class Script(default.Script):
 
         default.Script.onFocus(self, event)
 
-    def onStateChanged(self, event):
-        """Called whenever an object's state changes.
-
-        Arguments:
-        - event: the Event
-        """
+    def onShowingChanged(self, event):
+        """Callback for object:state-changed:showing accessibility events."""
+ 
+        try:
+            eventRole = event.source.getRole()
+            focusedRole = orca_state.locusOfFocus.getRole()
+        except:
+            default.Script.onShowingChanged(self, event)
+            return
 
         # If an autocomplete appears beneath an entry, we don't want
         # to prevent the user from being able to arrow into it.
-        #
-        if event.type.startswith("object:state-changed:showing") \
-           and event.source \
-           and (event.source.getRole() == pyatspi.ROLE_WINDOW) \
-           and orca_state.locusOfFocus:
-            if orca_state.locusOfFocus.getRole() in [pyatspi.ROLE_ENTRY,
-                                                     pyatspi.ROLE_LIST_ITEM]:
-                self._autocompleteVisible = event.detail1
-                # If the autocomplete has just appeared, we want to speak
-                # its appearance if the user's verbosity level is verbose
-                # or if the user forced it to appear with (Alt+)Down Arrow.
-                #
-                if self._autocompleteVisible:
-                    level = _settingsManager.getSetting('speechVerbosityLevel')
-                    speakIt = level == settings.VERBOSITY_LEVEL_VERBOSE
-                    if not speakIt \
-                       and isinstance(orca_state.lastInputEvent, 
-                                      input_event.KeyboardEvent):
-                        keyEvent = orca_state.lastNonModifierKeyEvent
-                        speakIt = (keyEvent.event_string == ("Down"))
-                    if speakIt:
-                        speech.speak(self.speechGenerator.getLocalizedRoleName(\
-                                event.source, pyatspi.ROLE_AUTOCOMPLETE))
+        if eventRole == pyatspi.ROLE_WINDOW \
+           and focusedRole in [pyatspi.ROLE_ENTRY, pyatspi.ROLE_LIST_ITEM]:
+            self._autocompleteVisible = event.detail1
+            # If the autocomplete has just appeared, we want to speak
+            # its appearance if the user's verbosity level is verbose
+            # or if the user forced it to appear with (Alt+)Down Arrow.
+            if self._autocompleteVisible:
+                level = _settingsManager.getSetting('speechVerbosityLevel')
+                speakIt = level == settings.VERBOSITY_LEVEL_VERBOSE
+                if not speakIt:
+                    eventString, mods = self.utilities.lastKeyAndModifiers()
+                    speakIt = eventString == "Down"
+                if speakIt:
+                    speech.speak(self.speechGenerator.getLocalizedRoleName(
+                        event.source, pyatspi.ROLE_AUTOCOMPLETE))
+                    return
 
-        default.Script.onStateChanged(self, event)
+        default.Script.onShowingChanged(self, event)
 
     def onStateFocused(self, event):
         default.Script.onStateChanged(self, event)


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