[orca] Create an app script for gnome-shell
- From: Joanmarie Diggs <joanied src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [orca] Create an app script for gnome-shell
- Date: Sat, 16 Nov 2013 22:24:48 +0000 (UTC)
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]