[orca] Create an app script for gnome-shell



commit 530aecb03414d0bff7c0a0efbb4b0033e08f3ca7
Author: Joanmarie Diggs <jdiggs igalia com>
Date:   Sat Nov 16 16:50:01 2013 -0500

    Create an app script for gnome-shell

 configure.ac                                    |    1 +
 src/orca/scripts/apps/Makefile.am               |    1 +
 src/orca/scripts/apps/__init__.py               |    1 +
 src/orca/scripts/apps/gnome-shell/Makefile.am   |    6 +
 src/orca/scripts/apps/gnome-shell/__init__.py   |    1 +
 src/orca/scripts/apps/gnome-shell/formatting.py |   64 +++++++
 src/orca/scripts/apps/gnome-shell/script.py     |  212 ++++++++++++++++++++++
 src/orca/scripts/toolkits/clutter/script.py     |  221 +----------------------
 8 files changed, 287 insertions(+), 220 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 5818c6a..406e76b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -95,6 +95,7 @@ src/orca/scripts/apps/gnome-mud/Makefile
 src/orca/scripts/apps/gnome-panel/Makefile
 src/orca/scripts/apps/gnome-screensaver-dialog/Makefile
 src/orca/scripts/apps/gnome-search-tool/Makefile
+src/orca/scripts/apps/gnome-shell/Makefile
 src/orca/scripts/apps/gnome-terminal/Makefile
 src/orca/scripts/apps/gnome-window-properties/Makefile
 src/orca/scripts/apps/gtk-window-decorator/Makefile
diff --git a/src/orca/scripts/apps/Makefile.am b/src/orca/scripts/apps/Makefile.am
index cd038b5..f110d60 100644
--- a/src/orca/scripts/apps/Makefile.am
+++ b/src/orca/scripts/apps/Makefile.am
@@ -15,6 +15,7 @@ SUBDIRS = \
        gnome-mud \
        gnome-panel \
        gnome-screensaver-dialog \
+       gnome-shell \
        gnome-search-tool \
        gnome-terminal \
        gnome-window-properties \
diff --git a/src/orca/scripts/apps/__init__.py b/src/orca/scripts/apps/__init__.py
index 42f80fe..3348399 100644
--- a/src/orca/scripts/apps/__init__.py
+++ b/src/orca/scripts/apps/__init__.py
@@ -14,6 +14,7 @@ __all__ = ['Banshee',
            'gnome-panel',
            'gnome-screensaver-dialog',
            'gnome-search-tool',
+           'gnome-shell',
            'gnome-terminal',
            'gnome-window-properties',
            'gtk-window-decorator',
diff --git a/src/orca/scripts/apps/gnome-shell/Makefile.am b/src/orca/scripts/apps/gnome-shell/Makefile.am
new file mode 100644
index 0000000..c49648e
--- /dev/null
+++ b/src/orca/scripts/apps/gnome-shell/Makefile.am
@@ -0,0 +1,6 @@
+orca_python_PYTHON = \
+       __init__.py \
+       formatting.py \
+       script.py
+
+orca_pythondir=$(pkgpythondir)/scripts/apps/gnome-shell
diff --git a/src/orca/scripts/apps/gnome-shell/__init__.py b/src/orca/scripts/apps/gnome-shell/__init__.py
new file mode 100644
index 0000000..585c964
--- /dev/null
+++ b/src/orca/scripts/apps/gnome-shell/__init__.py
@@ -0,0 +1 @@
+from .script import Script
diff --git a/src/orca/scripts/apps/gnome-shell/formatting.py b/src/orca/scripts/apps/gnome-shell/formatting.py
new file mode 100644
index 0000000..87dabc9
--- /dev/null
+++ b/src/orca/scripts/apps/gnome-shell/formatting.py
@@ -0,0 +1,64 @@
+# Orca
+#
+# Copyright 2013 Igalia, S.L.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
+# Boston MA  02110-1301 USA.
+
+__id__ = "$Id$"
+__version__   = "$Revision$"
+__date__      = "$Date$"
+__copyright__ = "Copyright (c) 2013 Igalia, S.L."
+__license__   = "LGPL"
+
+import copy
+import pyatspi
+
+import orca.formatting
+import orca.settings
+
+formatting = {
+    'speech': {
+        pyatspi.ROLE_MENU_ITEM: {
+            'focused': 'expandableState',
+            'unfocused': 'labelAndName + unrelatedLabels + menuItemCheckedState + expandableState + 
availability + ' + orca.formatting.MNEMONIC + ' + accelerator + positionInList',
+            'basicWhereAmI': 'ancestors + labelAndName + unrelatedLabels + accelerator + positionInList + ' 
+ orca.formatting.MNEMONIC
+            },
+    },
+    'braille': {
+        pyatspi.ROLE_MENU_ITEM: {
+            'unfocused': '[Component(obj,\
+                                     asString(label + (displayedText or unrelatedLabels) + expandableState + 
availability) + asString(accelerator),\
+                                     indicator=asString(menuItemCheckedState))]'
+            },
+    }
+}
+
+if orca.settings.useExperimentalSpeechProsody:
+    formatting['speech'][pyatspi.ROLE_MENU_ITEM]['unfocused'] = 'labelAndName + pause + unrelatedLabels + 
pause + menuItemCheckedState + expandableState + availability + ' + orca.formatting.MNEMONIC + ' + 
accelerator + pause + positionInList'
+    formatting['speech'][pyatspi.ROLE_MENU_ITEM]['basicWhereAmI'] = \
+        'ancestors + pause + labelAndName + pause + unrelatedLabels + pause + accelerator + pause + 
positionInList + ' + orca.formatting.MNEMONIC
+
+class Formatting(orca.formatting.Formatting):
+    def __init__(self, script):
+        orca.formatting.Formatting.__init__(self, script)
+        self.update(copy.deepcopy(formatting))
+        self._defaultFormatting = orca.formatting.Formatting(script)
+
+    def getFormat(self, **args):
+        if args.get('useDefaultFormatting', False):
+            return self._defaultFormatting.getFormat(**args)
+        else:
+            return orca.formatting.Formatting.getFormat(self, **args)
diff --git a/src/orca/scripts/apps/gnome-shell/script.py b/src/orca/scripts/apps/gnome-shell/script.py
new file mode 100644
index 0000000..f857354
--- /dev/null
+++ b/src/orca/scripts/apps/gnome-shell/script.py
@@ -0,0 +1,212 @@
+# Orca
+#
+# Copyright (C) 2010-2013 Igalia, S.L.
+#
+# Author: Alejandro Pinheiro Iglesias <apinheiro igalia com>
+# Author: Joanmarie Diggs <jdiggs igalia com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
+# Boston MA  02110-1301 USA.
+
+__id__        = "$Id$"
+__version__   = "$Revision$"
+__date__      = "$Date$"
+__copyright__ = "Copyright (c) 2010-2013 Igalia, S.L."
+__license__   = "LGPL"
+
+import pyatspi
+import time
+
+import orca.orca as orca
+import orca.scripts.toolkits.clutter as clutter
+
+from .formatting import Formatting
+
+class Script(clutter.Script):
+
+    def __init__(self, app):
+        clutter.Script.__init__(self, app)
+        self._activeDialog = (None, 0) # (Accessible, Timestamp)
+        self._activeDialogLabels = {}  # key == hash(obj), value == name
+
+    def getFormatting(self):
+        """Returns the formatting strings for this script."""
+        return Formatting(self)
+
+    def skipObjectEvent(self, event):
+        """Determines whether or not this event should be skipped due to
+        being redundant, part of an event flood, etc."""
+
+        try:
+            role = event.source.getRole()
+        except:
+            pass
+        else:
+            # We must handle all dialogs ourselves in this script.
+            if role == pyatspi.ROLE_DIALOG:
+                return False
+
+        return clutter.Script.skipObjectEvent(self, event)
+
+    def _presentDialogLabel(self, event):
+        try:
+            role = event.source.getRole()
+            name = event.source.name
+        except:
+            return False
+
+        activeDialog, timestamp = self._activeDialog
+        if not activeDialog or role != pyatspi.ROLE_LABEL:
+            return False
+
+        obj = hash(event.source)
+        if name == self._activeDialogLabels.get(obj):
+            return True
+
+        isDialog = lambda x: x and x.getRole() == pyatspi.ROLE_DIALOG
+        parentDialog = pyatspi.utils.findAncestor(event.source, isDialog)
+        if activeDialog == parentDialog:
+            self.presentMessage(name)
+            self._activeDialogLabels[obj] = name
+            return True
+
+        return False
+
+    def onNameChanged(self, event):
+        """Callback for object:property-change:accessible-name events."""
+
+        if self._presentDialogLabel(event):
+            return
+
+        clutter.Script.onNameChanged(self, event)
+
+    def onShowingChanged(self, event):
+        """Callback for object:state-changed:showing accessibility events."""
+
+        if not event.detail1:
+            return
+
+        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
+
+        clutter.Script.onShowingChanged(self, event)
+
+    def onSelectedChanged(self, event):
+        """Callback for object:state-changed:selected accessibility events."""
+        try:
+            state = event.source.getState()
+        except:
+            return
+
+        # Some buttons, like the Wikipedia button, claim to be selected but
+        # lack STATE_SELECTED. The other buttons, such as in the Dash and
+        # event switcher, seem to have the right state. Since the ones with
+        # the wrong state seem to be things we don't want to present anyway
+        # we'll stop doing so and hope we are right.
+
+        if event.detail1:
+            if state.contains(pyatspi.STATE_SELECTED):
+                orca.setLocusOfFocus(event, event.source)
+            return
+
+        clutter.Script.onSelectedChanged(self, event)
+
+    def onFocusedChanged(self, event):
+        """Callback for object:state-changed:focused accessibility events."""
+
+        if not event.detail1:
+            return
+
+        obj = event.source
+        try:
+            role = obj.getRole()
+            name = obj.name
+        except:
+            return
+
+        # The dialog will get presented when its first child gets focus.
+        if role == pyatspi.ROLE_DIALOG:
+            return
+
+        if role == pyatspi.ROLE_MENU_ITEM and not name \
+           and not self.utilities.labelsForObject(obj):
+            isRealFocus = lambda x: x and x.getRole() == pyatspi.ROLE_SLIDER
+            descendant = pyatspi.findDescendant(obj, isRealFocus)
+            if descendant:
+                orca.setLocusOfFocus(event, descendant)
+                return
+
+        # This is to present dialog boxes which are, to the user, newly
+        # activated. And if something is claiming to be focused that is
+        # not in a dialog, that's good to know as well, so update our
+        # state regardless.
+        activeDialog, timestamp = self._activeDialog
+        if not activeDialog:
+            isDialog = lambda x: x and x.getRole() == pyatspi.ROLE_DIALOG
+            dialog = pyatspi.utils.findAncestor(obj, isDialog)
+            self._activeDialog = (dialog, time.time())
+            if dialog:
+                orca.setLocusOfFocus(None, dialog)
+                labels = self.utilities.unrelatedLabels(dialog)
+                for label in labels:
+                    self._activeDialogLabels[hash(label)] = label.name
+
+        clutter.Script.onFocusedChanged(self, event)
+
+    def getTextLineAtCaret(self, obj, offset=None):
+        """Gets the line of text where the caret is."""
+
+        # TODO - JD/API: This is to work around the braille issue reported
+        # in bgo 677221. When that is resolved, this workaround can be
+        # removed.
+        string, caretOffset, startOffset = \
+            clutter.Script.getTextLineAtCaret(self, obj, offset)
+
+        if string:
+            return [string, caretOffset, startOffset]
+
+        try:
+            text = obj.queryText()
+        except:
+            pass
+        else:
+            string = text.getText(0, -1)
+
+        return [string, caretOffset, startOffset]
diff --git a/src/orca/scripts/toolkits/clutter/script.py b/src/orca/scripts/toolkits/clutter/script.py
index d97715b..b485e0b 100644
--- a/src/orca/scripts/toolkits/clutter/script.py
+++ b/src/orca/scripts/toolkits/clutter/script.py
@@ -23,30 +23,18 @@
 __id__        = "$Id$"
 __version__   = "$Revision$"
 __date__      = "$Date$"
-__copyright__ = "Copyright (c) 2010-2012 Igalia, S.L."
+__copyright__ = "Copyright (c) 2010-2013 Igalia, S.L."
 __license__   = "LGPL"
 
-import pyatspi
-import time
 from gi.repository import Gdk
 
-import orca.orca as orca
 import orca.scripts.default as default
 import orca.debug as debug
 
-from .formatting import Formatting
-
 # Set with non printable unicode categories. Full table:
 # http://www.fileformat.info/info/unicode/category/index.htm
-#
 non_printable_set = ('Cc', 'Cf', 'Cn', 'Co', 'Cs')
 
-########################################################################
-#                                                                      #
-# Utility methods.                                                     #
-#                                                                      #
-########################################################################
-
 def _unicharIsPrint(unichar):
     """ Checks if the unichar is printable
 
@@ -62,7 +50,6 @@ def _unicharIsPrint(unichar):
     except:
         # Normally a exception is because there are a string
         # instead of a single unicode, 'Control_L'
-        #
         result = False
 
     return result
@@ -86,42 +73,10 @@ def _computeIsText(string):
 
     return is_text
 
-def _parentDialog(obj):
-    """Looks for an object of ROLE_DIALOG in the ancestry of obj.
-
-    Arguments:
-    - obj: an accessible object
-
-    Returns the accessible object if found; otherwise None.
-    """
-
-    isDialog = lambda x: x and x.getRole() == pyatspi.ROLE_DIALOG
-
-    return pyatspi.utils.findAncestor(obj, isDialog)
-
-########################################################################
-#                                                                      #
-# The Cally script class.                                              #
-#                                                                      #
-########################################################################
-
 class Script(default.Script):
 
     def __init__(self, app):
-        """Creates a new script for Cally applications.
-
-        Arguments:
-        - app: the application to create a script for.
-        """
-
         default.Script.__init__(self, app)
-        self._activeDialog = (None, 0) # (Accessible, Timestamp)
-        self._activeDialogLabels = {}  # key == hash(obj), value == name
-
-    def getFormatting(self):
-        """Returns the formatting strings for this script."""
-
-        return Formatting(self)
 
     def checkKeyboardEventData(self, keyboardEvent):
         """Processes the given keyboard event.
@@ -198,177 +153,3 @@ class Script(default.Script):
             keyboardEvent.is_text = _computeIsText(keyboardEvent.event_string)
 
         return default.Script.checkKeyboardEventData(self, keyboardEvent)
-
-    def skipObjectEvent(self, event):
-        """Gives us, and scripts, the ability to decide an event isn't
-        worth taking the time to process under the current circumstances.
-
-        Arguments:
-        - event: the Event
-
-        Returns True if we shouldn't bother processing this object event.
-        """
-
-        try:
-            role = event.source.getRole()
-        except:
-            pass
-        else:
-            # We must handle all dialogs ourselves in this script.
-            if role == pyatspi.ROLE_DIALOG:
-                return False
-
-        return default.Script.skipObjectEvent(self, event)
-
-    def presentDialogLabel(self, event):
-        """Examines and, if appropriate, presents a new or changed label
-        found in a dialog box. Returns True if we handled the presentation
-        here."""
-
-        try:
-            role = event.source.getRole()
-            name = event.source.name
-        except:
-            return False
-
-        activeDialog, timestamp = self._activeDialog
-        if not activeDialog or role != pyatspi.ROLE_LABEL:
-            return False
-
-        obj = hash(event.source)
-        if name == self._activeDialogLabels.get(obj):
-            return True
-
-        if activeDialog == _parentDialog(event.source):
-            self.presentMessage(name)
-            self._activeDialogLabels[obj] = name
-            return True
-
-        return False
-
-    def onNameChanged(self, event):
-        """Called whenever a property on an object changes.
-
-        Arguments:
-        - event: the Event
-        """
-
-        if self.presentDialogLabel(event):
-            return
-
-        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 onSelectedChanged(self, event):
-        """Callback for object:state-changed:selected accessibility events."""
-        try:
-            state = event.source.getState()
-        except:
-            return
-
-        # Some buttons, like the Wikipedia button, claim to be selected but
-        # lack STATE_SELECTED. The other buttons, such as in the Dash and
-        # event switcher, seem to have the right state. Since the ones with
-        # the wrong state seem to be things we don't want to present anyway
-        # we'll stop doing so and hope we are right.
-
-        if event.detail1:
-            if state.contains(pyatspi.STATE_SELECTED):
-                orca.setLocusOfFocus(event, event.source)
-            return
-
-        default.Script.onSelectedChanged(self, event)
-
-    def onFocusedChanged(self, event):
-        """Callback for object:state-changed:focused accessibility events."""
-
-        if not event.detail1:
-            return
-
-        obj = event.source
-        try:
-            role = obj.getRole()
-            name = obj.name
-        except:
-            return
-
-        # The dialog will get presented when its first child gets focus.
-        if role == pyatspi.ROLE_DIALOG:
-            return
-
-        if role == pyatspi.ROLE_MENU_ITEM and not name \
-           and not self.utilities.labelsForObject(obj):
-            isRealFocus = lambda x: x and x.getRole() == pyatspi.ROLE_SLIDER
-            descendant = pyatspi.findDescendant(obj, isRealFocus)
-            if descendant:
-                orca.setLocusOfFocus(event, descendant)
-                return
-
-        # This is to present dialog boxes which are, to the user, newly
-        # activated. And if something is claiming to be focused that is
-        # not in a dialog, that's good to know as well, so update our
-        # state regardless.
-        activeDialog, timestamp = self._activeDialog
-        if not activeDialog:
-            dialog = _parentDialog(obj)
-            self._activeDialog = (dialog, time.time())
-            if dialog:
-                orca.setLocusOfFocus(None, dialog)
-                labels = self.utilities.unrelatedLabels(dialog)
-                for label in labels:
-                    self._activeDialogLabels[hash(label)] = label.name
-
-        default.Script.onFocusedChanged(self, event)
-
-    def getTextLineAtCaret(self, obj, offset=None):
-        """Gets the line of text where the caret is."""
-
-        # TODO - JD/API: This is to work around the braille issue reported
-        # in bgo 677221. When that is resolved, this workaround can be
-        # removed.
-        string, caretOffset, startOffset = \
-            default.Script.getTextLineAtCaret(self, obj, offset)
-
-        if string:
-            return [string, caretOffset, startOffset]
-
-        try:
-            text = obj.queryText()
-        except:
-            pass
-        else:
-            string = text.getText(0, -1)
-
-        return [string, caretOffset, startOffset]


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