orca r3997 - in trunk: . po src/orca src/orca/scripts/toolkits/Gecko
- From: joanied svn gnome org
- To: svn-commits-list gnome org
- Subject: orca r3997 - in trunk: . po src/orca src/orca/scripts/toolkits/Gecko
- Date: Mon, 23 Jun 2008 17:18:28 +0000 (UTC)
Author: joanied
Date: Mon Jun 23 17:18:28 2008
New Revision: 3997
URL: http://svn.gnome.org/viewvc/orca?rev=3997&view=rev
Log:
* src/orca/scripts/toolkits/Gecko/Makefile.am:
* src/orca/scripts/toolkits/Gecko/script.py:
* src/orca/scripts/toolkits/Gecko/script_settings.py:
* src/orca/scripts/toolkits/Gecko/structural_navigation.py: (new)
* src/orca/structural_navigation.py: (new)
* src/orca/settings.py:
* src/orca/script.py:
* src/orca/Makefile.am:
* po/POTFILES.in:
Fix for bug #535023 - Structural Navigation should be pulled out
of Gecko and include more objects. Note: You will need to do a
full install due to the two new files which were added. The new
objects are: Anchors, Buttons, Check boxes, Combo boxes, Entries,
Paragraphs, and Radio buttons.
Added:
trunk/src/orca/scripts/toolkits/Gecko/structural_navigation.py
trunk/src/orca/structural_navigation.py
Modified:
trunk/ChangeLog
trunk/po/POTFILES.in
trunk/src/orca/Makefile.am
trunk/src/orca/script.py
trunk/src/orca/scripts/toolkits/Gecko/Makefile.am
trunk/src/orca/scripts/toolkits/Gecko/script.py
trunk/src/orca/scripts/toolkits/Gecko/script_settings.py
trunk/src/orca/settings.py
Modified: trunk/po/POTFILES.in
==============================================================================
--- trunk/po/POTFILES.in (original)
+++ trunk/po/POTFILES.in Mon Jun 23 17:18:28 2008
@@ -58,6 +58,7 @@
src/orca/scripts/toolkits/Gecko/bookmarks.py
src/orca/scripts/toolkits/Gecko/script.py
src/orca/scripts/toolkits/Gecko/speech_generator.py
+src/orca/scripts/toolkits/Gecko/structural_navigation.py
src/orca/scripts/toolkits/Gecko/where_am_i.py
src/orca/scripts/toolkits/J2SE-access-bridge.py
src/orca/settings.py
Modified: trunk/src/orca/Makefile.am
==============================================================================
--- trunk/src/orca/Makefile.am (original)
+++ trunk/src/orca/Makefile.am Mon Jun 23 17:18:28 2008
@@ -56,6 +56,7 @@
speechdispatcherfactory.py \
speechgenerator.py \
speechserver.py \
+ structural_navigation.py \
where_am_I.py
orca_pythondir=$(pyexecdir)/orca
Modified: trunk/src/orca/script.py
==============================================================================
--- trunk/src/orca/script.py (original)
+++ trunk/src/orca/script.py Mon Jun 23 17:18:28 2008
@@ -46,6 +46,7 @@
import orca_state
import settings
import speechgenerator
+import structural_navigation
import where_am_I
import bookmarks
@@ -76,6 +77,7 @@
#
self.presentIfInactive = True
+ self.structuralNavigation = self.getStructuralNavigation()
self.inputEventHandlers = {}
self.pointOfReference = {}
self.setupInputEventHandlers()
@@ -184,6 +186,27 @@
"""
return speechgenerator.SpeechGenerator(self)
+ def getEnabledStructuralNavigationTypes(self):
+ """Returns a list of the structural navigation object types
+ enabled in this script.
+ """
+ return []
+
+ def getStructuralNavigation(self):
+ """Returns the 'structural navigation' class for this script.
+ """
+ types = self.getEnabledStructuralNavigationTypes()
+ return structural_navigation.StructuralNavigation(self, types)
+
+ def useStructuralNavigationModel(self):
+ """Returns True if we should use structural navigation. Most
+ scripts will have no need to override this. Gecko does however
+ because within an HTML document there are times when we do want
+ to use it and times when we don't even though it is enabled,
+ e.g. in a form field.
+ """
+ return self.structuralNavigation.enabled
+
def getWhereAmI(self):
"""Returns the "where am I" class for this script.
"""
@@ -336,9 +359,19 @@
consumes = False
if user_bindings:
- consumes = user_bindings.getInputHandler(keyboardEvent) != None
+ handler = user_bindings.getInputHandler(keyboardEvent)
+ if handler \
+ and handler.function in self.structuralNavigation.functions:
+ return self.useStructuralNavigationModel()
+ else:
+ consumes = handler != None
if not consumes:
- consumes = self.keyBindings.getInputHandler(keyboardEvent) != None
+ handler = self.keyBindings.getInputHandler(keyboardEvent)
+ if handler \
+ and handler.function in self.structuralNavigation.functions:
+ return self.useStructuralNavigationModel()
+ else:
+ consumes = handler != None
return consumes
def processKeyboardEvent(self, keyboardEvent):
Modified: trunk/src/orca/scripts/toolkits/Gecko/Makefile.am
==============================================================================
--- trunk/src/orca/scripts/toolkits/Gecko/Makefile.am (original)
+++ trunk/src/orca/scripts/toolkits/Gecko/Makefile.am Mon Jun 23 17:18:28 2008
@@ -7,6 +7,7 @@
script.py \
script_settings.py \
speech_generator.py \
+ structural_navigation.py \
where_am_i.py
orca_pythondir=$(pyexecdir)/orca/scripts/toolkits/Gecko
Modified: trunk/src/orca/scripts/toolkits/Gecko/script.py
==============================================================================
--- trunk/src/orca/scripts/toolkits/Gecko/script.py (original)
+++ trunk/src/orca/scripts/toolkits/Gecko/script.py Mon Jun 23 17:18:28 2008
@@ -65,9 +65,10 @@
from speech_generator import SpeechGenerator
from where_am_i import GeckoWhereAmI
from bookmarks import GeckoBookmarks
+from structural_navigation import GeckoStructuralNavigation
from orca.orca_i18n import _
-from orca.orca_i18n import ngettext # for ngettext support
+
########################################################################
# #
@@ -86,7 +87,6 @@
def __init__(self, app):
default.Script.__init__(self, app)
-
# Initialize variables to make pylint happy.
#
self.arrowToLineBeginningCheckButton = None
@@ -120,57 +120,6 @@
Script.goBeginningOfLine,
Script.goEndOfLine]
- # _structuralNavigationFunctions are functions that represent
- # more complex navigation functions (e.g., moving by heading,
- # large object, etc.).
- #
- self._structuralNavigationFunctions = \
- [Script.goNextHeading,
- Script.goPreviousHeading,
- Script.goNextHeading1,
- Script.goPreviousHeading1,
- Script.goNextHeading2,
- Script.goPreviousHeading2,
- Script.goNextHeading3,
- Script.goPreviousHeading3,
- Script.goNextHeading4,
- Script.goPreviousHeading4,
- Script.goNextHeading5,
- Script.goPreviousHeading5,
- Script.goNextHeading6,
- Script.goPreviousHeading6,
- Script.goNextChunk,
- Script.goPreviousChunk,
- Script.goNextLandmark,
- Script.goPreviousLandmark,
- Script.goNextList,
- Script.goPreviousList,
- Script.goNextListItem,
- Script.goPreviousListItem,
- Script.goNextUnvisitedLink,
- Script.goPreviousUnvisitedLink,
- Script.goNextVisitedLink,
- Script.goPreviousVisitedLink,
- Script.goNextFormField,
- Script.goPreviousFormField,
- Script.goNextBlockquote,
- Script.goPreviousBlockquote,
- Script.goNextTable,
- Script.goPreviousTable,
- Script.goNextLiveRegion,
- Script.goPreviousLiveRegion,
- Script.goLastLiveRegion,
- Script.advanceLivePoliteness,
- Script.setLivePolitenessOff,
- Script.monitorLiveRegions,
- Script.reviewLiveAnnouncement,
- Script.goCellLeft,
- Script.goCellRight,
- Script.goCellUp,
- Script.goCellDown,
- Script.goCellFirst,
- Script.goCellLast]
-
if script_settings.controlCaretNavigation:
debug.println(debug.LEVEL_CONFIGURATION,
"Orca is controlling the caret.")
@@ -191,14 +140,6 @@
#
self._documentFrameCaretContext = {}
- # When navigating in a non-uniform table, one can move to a
- # cell which spans multiple rows and/or columns. When moving
- # beyond that cell, into a cell that does NOT span multiple
- # rows/columns, we want to be sure we land in the right place.
- # Therefore, we'll store the coordinates from "our perspective."
- #
- self.lastTableCell = [-1, -1]
-
# During a find we get caret-moved events reflecting the changing
# screen contents. The user can opt to have these changes announced.
# If the announcement is enabled, it still only will be made if the
@@ -263,12 +204,48 @@
"""
return SpeechGenerator(self)
+ def getEnabledStructuralNavigationTypes(self):
+ """Returns a list of the structural navigation object types
+ enabled in this script.
+ """
+
+ enabledTypes = [GeckoStructuralNavigation.ANCHOR,
+ GeckoStructuralNavigation.BLOCKQUOTE,
+ GeckoStructuralNavigation.BUTTON,
+ GeckoStructuralNavigation.CHECK_BOX,
+ GeckoStructuralNavigation.CHUNK,
+ GeckoStructuralNavigation.COMBO_BOX,
+ GeckoStructuralNavigation.ENTRY,
+ GeckoStructuralNavigation.FORM_FIELD,
+ GeckoStructuralNavigation.HEADING,
+ GeckoStructuralNavigation.LANDMARK,
+ GeckoStructuralNavigation.LIST,
+ GeckoStructuralNavigation.LIST_ITEM,
+ GeckoStructuralNavigation.LIVE_REGION,
+ GeckoStructuralNavigation.PARAGRAPH,
+ GeckoStructuralNavigation.RADIO_BUTTON,
+ GeckoStructuralNavigation.TABLE,
+ GeckoStructuralNavigation.TABLE_CELL,
+ GeckoStructuralNavigation.UNVISITED_LINK,
+ GeckoStructuralNavigation.VISITED_LINK]
+
+ return enabledTypes
+
+ def getStructuralNavigation(self):
+ """Returns the 'structural navigation' class for this script.
+ """
+ types = self.getEnabledStructuralNavigationTypes()
+ enable = script_settings.structuralNavigationEnabled
+ return GeckoStructuralNavigation(self, types, enable)
+
def setupInputEventHandlers(self):
"""Defines InputEventHandler fields for this script that can be
called by the key and braille bindings.
"""
default.Script.setupInputEventHandlers(self)
+ self.inputEventHandlers.update(\
+ self.structuralNavigation.inputEventHandlers)
# Debug only.
#
@@ -365,324 +342,6 @@
#
_("Causes the current combo box to be expanded."))
- self.inputEventHandlers["goCellLeftHandler"] = \
- input_event.InputEventHandler(
- Script.goCellLeft,
- # Translators: this is for navigating inside HTML tables.
- #
- _("Goes left one cell."))
-
- self.inputEventHandlers["goCellRightHandler"] = \
- input_event.InputEventHandler(
- Script.goCellRight,
- # Translators: this is for navigating inside HTML tables.
- #
- _("Goes right one cell."))
-
- self.inputEventHandlers["goCellDownHandler"] = \
- input_event.InputEventHandler(
- Script.goCellDown,
- # Translators: this is for navigating inside HTML tables.
- #
- _("Goes down one cell."))
-
- self.inputEventHandlers["goCellUpHandler"] = \
- input_event.InputEventHandler(
- Script.goCellUp,
- # Translators: this is for navigating inside HTML tables.
- #
- _("Goes up one cell."))
-
- self.inputEventHandlers["goCellFirstHandler"] = \
- input_event.InputEventHandler(
- Script.goCellFirst,
- # Translators: this is for navigating inside HTML tables.
- #
- _("Goes to the first cell in a table."))
-
- self.inputEventHandlers["goCellLastHandler"] = \
- input_event.InputEventHandler(
- Script.goCellLast,
- # Translators: this is for navigating inside HTML tables.
- #
- _("Goes to the last cell in a table."))
-
- self.inputEventHandlers["goPreviousHeadingHandler"] = \
- input_event.InputEventHandler(
- Script.goPreviousHeading,
- # Translators: this is for navigating HTML by headers
- # (e.g., <h1>).
- #
- _("Goes to previous heading."))
-
- self.inputEventHandlers["goNextHeadingHandler"] = \
- input_event.InputEventHandler(
- Script.goNextHeading,
- # Translators: this is for navigating HTML by headers
- # (e.g., <h1>).
- #
- _("Goes to next heading."))
-
- self.inputEventHandlers["goPreviousHeading1Handler"] = \
- input_event.InputEventHandler(
- Script.goPreviousHeading1,
- # Translators: this is for navigating HTML by headers
- # (e.g., <h1>).
- #
- _("Goes to previous heading at level 1."))
-
- self.inputEventHandlers["goNextHeading1Handler"] = \
- input_event.InputEventHandler(
- Script.goNextHeading1,
- # Translators: this is for navigating HTML by headers
- # (e.g., <h1>).
- #
- _("Goes to next heading at level 1."))
-
- self.inputEventHandlers["goPreviousHeading2Handler"] = \
- input_event.InputEventHandler(
- Script.goPreviousHeading2,
- # Translators: this is for navigating HTML by headers
- # (e.g., <h2>).
- #
- _("Goes to previous heading at level 2."))
-
- self.inputEventHandlers["goNextHeading2Handler"] = \
- input_event.InputEventHandler(
- Script.goNextHeading2,
- # Translators: this is for navigating HTML by headers
- # (e.g., <h2>).
- #
- _("Goes to next heading at level 2."))
-
- self.inputEventHandlers["goPreviousHeading3Handler"] = \
- input_event.InputEventHandler(
- Script.goPreviousHeading3,
- # Translators: this is for navigating HTML by headers
- # (e.g., <h3>).
- #
- _("Goes to previous heading at level 3."))
-
- self.inputEventHandlers["goNextHeading3Handler"] = \
- input_event.InputEventHandler(
- Script.goNextHeading3,
- # Translators: this is for navigating HTML by headers
- # (e.g., <h3>).
- #
- _("Goes to next heading at level 3."))
-
- self.inputEventHandlers["goPreviousHeading4Handler"] = \
- input_event.InputEventHandler(
- Script.goPreviousHeading4,
- # Translators: this is for navigating HTML by headers
- # (e.g., <h4>).
- #
- _("Goes to previous heading at level 4."))
-
- self.inputEventHandlers["goNextHeading4Handler"] = \
- input_event.InputEventHandler(
- Script.goNextHeading4,
- # Translators: this is for navigating HTML by headers
- # (e.g., <h4>).
- #
- _("Goes to next heading at level 4."))
-
- self.inputEventHandlers["goPreviousHeading5Handler"] = \
- input_event.InputEventHandler(
- Script.goPreviousHeading5,
- # Translators: this is for navigating HTML by headers
- # (e.g., <h5>).
- #
- _("Goes to previous heading at level 5."))
-
- self.inputEventHandlers["goNextHeading5Handler"] = \
- input_event.InputEventHandler(
- Script.goNextHeading5,
- # Translators: this is for navigating HTML by headers
- # (e.g., <h5>).
- #
- _("Goes to next heading at level 5."))
-
- self.inputEventHandlers["goPreviousHeading6Handler"] = \
- input_event.InputEventHandler(
- Script.goPreviousHeading6,
- # Translators: this is for navigating HTML by headers
- # (e.g., <h6>).
- #
- _("Goes to previous heading at level 6."))
-
- self.inputEventHandlers["goNextHeading6Handler"] = \
- input_event.InputEventHandler(
- Script.goNextHeading6,
- # Translators: this is for navigating HTML by headers
- # (e.g., <h6>).
- #
- _("Goes to next heading at level 6."))
-
- self.inputEventHandlers["goPreviousChunkHandler"] = \
- input_event.InputEventHandler(
- Script.goPreviousChunk,
- # Translators: this is for navigating HTML in a structural
- # manner, where a 'large object' is a logical chunk of
- # text, such as a paragraph, a list, a table, etc.
- #
- _("Goes to previous large object."))
-
- self.inputEventHandlers["goNextChunkHandler"] = \
- input_event.InputEventHandler(
- Script.goNextChunk,
- # Translators: this is for navigating HTML in a structural
- # manner, where a 'large object' is a logical chunk of
- # text, such as a paragraph, a list, a table, etc.
- #
- _("Goes to next large object."))
-
- self.inputEventHandlers["goPreviousLandmark"] = \
- input_event.InputEventHandler(
- Script.goPreviousLandmark,
- # Translators: this is for navigating to the previous ARIA
- # role landmark. ARIA role landmarks are the W3C defined HTML
- # tag attribute 'role' used to identify important part of
- # webpage like banners, main context, search etc.
- #
- _("Goes to previous landmark."))
-
- self.inputEventHandlers["goNextLandmark"] = \
- input_event.InputEventHandler(
- Script.goNextLandmark,
- # Translators: this is for navigating to the next ARIA
- # role landmark.
- #
- _("Goes to next landmark."))
-
- self.inputEventHandlers["goPreviousListHandler"] = \
- input_event.InputEventHandler(
- Script.goPreviousList,
- # Translators: this is for navigating between bulleted/numbered
- # lists in HTML
- #
- _("Goes to previous list."))
-
- self.inputEventHandlers["goNextListHandler"] = \
- input_event.InputEventHandler(
- Script.goNextList,
- # Translators: this is for navigating between bulleted/numbered
- # lists in HTML
- #
- _("Goes to next list."))
-
- self.inputEventHandlers["goPreviousListItemHandler"] = \
- input_event.InputEventHandler(
- Script.goPreviousListItem,
- # Translators: this is for navigating between bulleted/numbered
- # list items in HTML
- #
- _("Goes to previous list item."))
-
- self.inputEventHandlers["goNextListItemHandler"] = \
- input_event.InputEventHandler(
- Script.goNextListItem,
- # Translators: this is for navigating between bulleted/numbered
- # list items in HTML
- #
- _("Goes to next list item."))
-
- self.inputEventHandlers["goPreviousUnvisitedLinkHandler"] = \
- input_event.InputEventHandler(
- Script.goPreviousUnvisitedLink,
- # Translators: this is for navigating between links in HTML
- #
- _("Goes to previous unvisited link."))
-
- self.inputEventHandlers["goNextUnvisitedLinkHandler"] = \
- input_event.InputEventHandler(
- Script.goNextUnvisitedLink,
- # Translators: this is for navigating between links in HTML
- #
- _("Goes to next unvisited link."))
-
- self.inputEventHandlers["goPreviousVisitedLinkHandler"] = \
- input_event.InputEventHandler(
- Script.goPreviousVisitedLink,
- # Translators: this is for navigating between links in HTML
- #
- _("Goes to previous visited link."))
-
- self.inputEventHandlers["goNextVisitedLinkHandler"] = \
- input_event.InputEventHandler(
- Script.goNextVisitedLink,
- # Translators: this is for navigating between links in HTML
- #
- _("Goes to next visited link."))
-
- self.inputEventHandlers["goPreviousFormFieldHandler"] = \
- input_event.InputEventHandler(
- Script.goPreviousFormField,
- # Translators: this is for navigating between form fields in
- # HTML
- #
- _("Goes to previous form field."))
-
- self.inputEventHandlers["goNextFormFieldHandler"] = \
- input_event.InputEventHandler(
- Script.goNextFormField,
- # Translators: this is for navigating between form fields in
- # HTML
- #
- _("Goes to next form field."))
-
- self.inputEventHandlers["goPreviousBlockquoteHandler"] = \
- input_event.InputEventHandler(
- Script.goPreviousBlockquote,
- # Translators: this is for navigating among blockquotes in
- # HTML
- #
- _("Goes to previous blockquote."))
-
- self.inputEventHandlers["goNextBlockquoteHandler"] = \
- input_event.InputEventHandler(
- Script.goNextBlockquote,
- # Translators: this is for navigating among blockquotes in
- # HTML
- #
- _("Goes to next blockquote."))
-
- self.inputEventHandlers["goPreviousTableHandler"] = \
- input_event.InputEventHandler(
- Script.goPreviousTable,
- # Translators: this is for navigating between tables in HTML
- #
- _("Goes to previous table."))
-
- self.inputEventHandlers["goNextTableHandler"] = \
- input_event.InputEventHandler(
- Script.goNextTable,
- # Translators: this is for navigating between tables in HTML
- #
- _("Goes to next table."))
-
- self.inputEventHandlers["goPreviousLiveRegion"] = \
- input_event.InputEventHandler(
- Script.goPreviousLiveRegion,
- # Translators: this is for navigating between live regions
- #
- _("Goes to previous live region."))
-
- self.inputEventHandlers["goNextLiveRegion"] = \
- input_event.InputEventHandler(
- Script.goNextLiveRegion,
- # Translators: this is for navigating between live regions
- #
- _("Goes to next live region."))
-
- self.inputEventHandlers["goLastLiveRegion"] = \
- input_event.InputEventHandler(
- Script.goLastLiveRegion,
- # Translators: this is for navigating to the last live region
- # to make an announcement.
- #
- _("Goes to last live region."))
-
self.inputEventHandlers["advanceLivePoliteness"] = \
input_event.InputEventHandler(
Script.advanceLivePoliteness,
@@ -743,18 +402,6 @@
#
_("Switches between Gecko native and Orca caret navigation."))
- self.inputEventHandlers["toggleStructuralNavigationHandler"] = \
- input_event.InputEventHandler(
- Script.toggleStructuralNavigation,
- # Translators: the structural navigation keys are designed
- # to move the caret around the HTML content by object type.
- # Thus H moves you to the next heading, Shift H to the
- # previous heading, T to the next table, and so on. Some
- # users prefer to turn this off to use Firefox's search
- # when typing feature.
- #
- _("Toggles structural navigation keys."))
-
self.inputEventHandlers["sayAllHandler"] = \
input_event.InputEventHandler(
Script.sayAll,
@@ -861,48 +508,6 @@
keyBindings.add(
keybindings.KeyBinding(
- "Right",
- settings.defaultModifierMask,
- settings.SHIFT_ALT_MODIFIER_MASK,
- self.inputEventHandlers["goCellRightHandler"]))
-
- keyBindings.add(
- keybindings.KeyBinding(
- "Left",
- settings.defaultModifierMask,
- settings.SHIFT_ALT_MODIFIER_MASK,
- self.inputEventHandlers["goCellLeftHandler"]))
-
- keyBindings.add(
- keybindings.KeyBinding(
- "Up",
- settings.defaultModifierMask,
- settings.SHIFT_ALT_MODIFIER_MASK,
- self.inputEventHandlers["goCellUpHandler"]))
-
- keyBindings.add(
- keybindings.KeyBinding(
- "Down",
- settings.defaultModifierMask,
- settings.SHIFT_ALT_MODIFIER_MASK,
- self.inputEventHandlers["goCellDownHandler"]))
-
- keyBindings.add(
- keybindings.KeyBinding(
- "Home",
- settings.defaultModifierMask,
- settings.SHIFT_ALT_MODIFIER_MASK,
- self.inputEventHandlers["goCellFirstHandler"]))
-
- keyBindings.add(
- keybindings.KeyBinding(
- "End",
- settings.defaultModifierMask,
- settings.SHIFT_ALT_MODIFIER_MASK,
- self.inputEventHandlers["goCellLastHandler"]))
-
- keyBindings.add(
- keybindings.KeyBinding(
"Home",
settings.defaultModifierMask,
settings.CTRL_MODIFIER_MASK,
@@ -939,351 +544,98 @@
keyBindings = default.Script.getKeyBindings(self)
- keyBindings.add(
- keybindings.KeyBinding(
- "h",
- settings.defaultModifierMask,
- settings.SHIFT_MODIFIER_MASK,
- self.inputEventHandlers["goPreviousHeadingHandler"]))
+ # keybindings to provide chat room message history.
+ messageKeys = [ "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9" ]
+ for messageKey in messageKeys:
+ keyBindings.add(
+ keybindings.KeyBinding(
+ messageKey,
+ settings.defaultModifierMask,
+ settings.ORCA_MODIFIER_MASK,
+ self.inputEventHandlers["reviewLiveAnnouncement"]))
keyBindings.add(
keybindings.KeyBinding(
- "h",
+ "backslash",
settings.defaultModifierMask,
- settings.NO_MODIFIER_MASK,
- self.inputEventHandlers["goNextHeadingHandler"]))
+ settings.SHIFT_MODIFIER_MASK,
+ self.inputEventHandlers["setLivePolitenessOff"]))
keyBindings.add(
keybindings.KeyBinding(
- "1",
+ "backslash",
settings.defaultModifierMask,
- settings.SHIFT_MODIFIER_MASK,
- self.inputEventHandlers["goPreviousHeading1Handler"]))
+ settings.ORCA_SHIFT_MODIFIER_MASK,
+ self.inputEventHandlers["monitorLiveRegions"]))
keyBindings.add(
keybindings.KeyBinding(
- "1",
+ "backslash",
settings.defaultModifierMask,
settings.NO_MODIFIER_MASK,
- self.inputEventHandlers["goNextHeading1Handler"]))
+ self.inputEventHandlers["advanceLivePoliteness"]))
keyBindings.add(
keybindings.KeyBinding(
- "2",
+ "F12",
settings.defaultModifierMask,
- settings.SHIFT_MODIFIER_MASK,
- self.inputEventHandlers["goPreviousHeading2Handler"]))
+ settings.ORCA_MODIFIER_MASK,
+ self.inputEventHandlers["toggleCaretNavigationHandler"]))
keyBindings.add(
keybindings.KeyBinding(
- "2",
+ "SunF37",
settings.defaultModifierMask,
- settings.NO_MODIFIER_MASK,
- self.inputEventHandlers["goNextHeading2Handler"]))
+ settings.ORCA_MODIFIER_MASK,
+ self.inputEventHandlers["toggleCaretNavigationHandler"]))
keyBindings.add(
keybindings.KeyBinding(
- "3",
+ "Right",
settings.defaultModifierMask,
- settings.SHIFT_MODIFIER_MASK,
- self.inputEventHandlers["goPreviousHeading3Handler"]))
+ settings.ORCA_MODIFIER_MASK,
+ self.inputEventHandlers["goNextObjectInOrderHandler"]))
keyBindings.add(
keybindings.KeyBinding(
- "3",
+ "Left",
settings.defaultModifierMask,
- settings.NO_MODIFIER_MASK,
- self.inputEventHandlers["goNextHeading3Handler"]))
+ settings.ORCA_MODIFIER_MASK,
+ self.inputEventHandlers["goPreviousObjectInOrderHandler"]))
- keyBindings.add(
- keybindings.KeyBinding(
- "4",
- settings.defaultModifierMask,
- settings.SHIFT_MODIFIER_MASK,
- self.inputEventHandlers["goPreviousHeading4Handler"]))
+ if script_settings.controlCaretNavigation:
+ for keyBinding in self.__getArrowBindings().keyBindings:
+ keyBindings.add(keyBinding)
- keyBindings.add(
- keybindings.KeyBinding(
- "4",
- settings.defaultModifierMask,
- settings.NO_MODIFIER_MASK,
- self.inputEventHandlers["goNextHeading4Handler"]))
+ bindings = self.structuralNavigation.keyBindings
+ for keyBinding in bindings.keyBindings:
+ keyBindings.add(keyBinding)
- keyBindings.add(
- keybindings.KeyBinding(
- "5",
- settings.defaultModifierMask,
- settings.NO_MODIFIER_MASK,
- self.inputEventHandlers["goNextHeading5Handler"]))
+ return keyBindings
- keyBindings.add(
- keybindings.KeyBinding(
- "5",
- settings.defaultModifierMask,
- settings.SHIFT_MODIFIER_MASK,
- self.inputEventHandlers["goPreviousHeading5Handler"]))
+ def getAppPreferencesGUI(self):
+ """Return a GtkVBox contain the application unique configuration
+ GUI items for the current application.
+ """
- keyBindings.add(
- keybindings.KeyBinding(
- "6",
- settings.defaultModifierMask,
- settings.SHIFT_MODIFIER_MASK,
- self.inputEventHandlers["goPreviousHeading6Handler"]))
+ vbox = gtk.VBox(False, 0)
+ vbox.set_border_width(12)
+ gtk.Widget.show(vbox)
- keyBindings.add(
- keybindings.KeyBinding(
- "6",
- settings.defaultModifierMask,
- settings.NO_MODIFIER_MASK,
- self.inputEventHandlers["goNextHeading6Handler"]))
+ # General ("Page") Navigation frame.
+ #
+ generalFrame = gtk.Frame()
+ gtk.Widget.show(generalFrame)
+ gtk.Box.pack_start(vbox, generalFrame, False, False, 5)
- keyBindings.add(
- keybindings.KeyBinding(
- "o",
- settings.defaultModifierMask,
- settings.SHIFT_MODIFIER_MASK,
- self.inputEventHandlers["goPreviousChunkHandler"]))
+ generalAlignment = gtk.Alignment(0.5, 0.5, 1, 1)
+ gtk.Widget.show(generalAlignment)
+ gtk.Container.add(generalFrame, generalAlignment)
+ gtk.Alignment.set_padding(generalAlignment, 0, 0, 12, 0)
- keyBindings.add(
- keybindings.KeyBinding(
- "o",
- settings.defaultModifierMask,
- settings.NO_MODIFIER_MASK,
- self.inputEventHandlers["goNextChunkHandler"]))
-
- keyBindings.add(
- keybindings.KeyBinding(
- "l",
- settings.defaultModifierMask,
- settings.SHIFT_MODIFIER_MASK,
- self.inputEventHandlers["goPreviousListHandler"]))
-
- keyBindings.add(
- keybindings.KeyBinding(
- "l",
- settings.defaultModifierMask,
- settings.NO_MODIFIER_MASK,
- self.inputEventHandlers["goNextListHandler"]))
-
- keyBindings.add(
- keybindings.KeyBinding(
- "i",
- settings.defaultModifierMask,
- settings.SHIFT_MODIFIER_MASK,
- self.inputEventHandlers["goPreviousListItemHandler"]))
-
- keyBindings.add(
- keybindings.KeyBinding(
- "i",
- settings.defaultModifierMask,
- settings.NO_MODIFIER_MASK,
- self.inputEventHandlers["goNextListItemHandler"]))
-
- keyBindings.add(
- keybindings.KeyBinding(
- "u",
- settings.defaultModifierMask,
- settings.SHIFT_MODIFIER_MASK,
- self.inputEventHandlers["goPreviousUnvisitedLinkHandler"]))
-
- keyBindings.add(
- keybindings.KeyBinding(
- "u",
- settings.defaultModifierMask,
- settings.NO_MODIFIER_MASK,
- self.inputEventHandlers["goNextUnvisitedLinkHandler"]))
-
- keyBindings.add(
- keybindings.KeyBinding(
- "v",
- settings.defaultModifierMask,
- settings.SHIFT_MODIFIER_MASK,
- self.inputEventHandlers["goPreviousVisitedLinkHandler"]))
-
- keyBindings.add(
- keybindings.KeyBinding(
- "v",
- settings.defaultModifierMask,
- settings.NO_MODIFIER_MASK,
- self.inputEventHandlers["goNextVisitedLinkHandler"]))
-
- keyBindings.add(
- keybindings.KeyBinding(
- "Tab",
- settings.defaultModifierMask,
- settings.ORCA_SHIFT_MODIFIER_MASK,
- self.inputEventHandlers["goPreviousFormFieldHandler"]))
-
- keyBindings.add(
- keybindings.KeyBinding(
- "Tab",
- settings.defaultModifierMask,
- settings.ORCA_MODIFIER_MASK,
- self.inputEventHandlers["goNextFormFieldHandler"]))
-
- keyBindings.add(
- keybindings.KeyBinding(
- "q",
- settings.defaultModifierMask,
- settings.SHIFT_MODIFIER_MASK,
- self.inputEventHandlers["goPreviousBlockquoteHandler"]))
-
- keyBindings.add(
- keybindings.KeyBinding(
- "q",
- settings.defaultModifierMask,
- settings.NO_MODIFIER_MASK,
- self.inputEventHandlers["goNextBlockquoteHandler"]))
-
- keyBindings.add(
- keybindings.KeyBinding(
- "t",
- settings.defaultModifierMask,
- settings.SHIFT_MODIFIER_MASK,
- self.inputEventHandlers["goPreviousTableHandler"]))
-
- keyBindings.add(
- keybindings.KeyBinding(
- "t",
- settings.defaultModifierMask,
- settings.NO_MODIFIER_MASK,
- self.inputEventHandlers["goNextTableHandler"]))
-
- keyBindings.add(
- keybindings.KeyBinding(
- "r",
- settings.defaultModifierMask,
- settings.SHIFT_MODIFIER_MASK,
- self.inputEventHandlers["goPreviousLiveRegion"]))
-
- keyBindings.add(
- keybindings.KeyBinding(
- "r",
- settings.defaultModifierMask,
- settings.NO_MODIFIER_MASK,
- self.inputEventHandlers["goNextLiveRegion"]))
-
- keyBindings.add(
- keybindings.KeyBinding(
- "y",
- settings.defaultModifierMask,
- settings.NO_MODIFIER_MASK,
- self.inputEventHandlers["goLastLiveRegion"]))
-
- # keybindings to provide chat room message history.
- messageKeys = [ "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9" ]
- for messageKey in messageKeys:
- keyBindings.add(
- keybindings.KeyBinding(
- messageKey,
- settings.defaultModifierMask,
- settings.ORCA_MODIFIER_MASK,
- self.inputEventHandlers["reviewLiveAnnouncement"]))
-
- keyBindings.add(
- keybindings.KeyBinding(
- "backslash",
- settings.defaultModifierMask,
- settings.SHIFT_MODIFIER_MASK,
- self.inputEventHandlers["setLivePolitenessOff"]))
-
- keyBindings.add(
- keybindings.KeyBinding(
- "backslash",
- settings.defaultModifierMask,
- settings.ORCA_SHIFT_MODIFIER_MASK,
- self.inputEventHandlers["monitorLiveRegions"]))
-
- keyBindings.add(
- keybindings.KeyBinding(
- "backslash",
- settings.defaultModifierMask,
- settings.NO_MODIFIER_MASK,
- self.inputEventHandlers["advanceLivePoliteness"]))
-
- keyBindings.add(
- keybindings.KeyBinding(
- "F12",
- settings.defaultModifierMask,
- settings.ORCA_MODIFIER_MASK,
- self.inputEventHandlers["toggleCaretNavigationHandler"]))
-
- keyBindings.add(
- keybindings.KeyBinding(
- "SunF37",
- settings.defaultModifierMask,
- settings.ORCA_MODIFIER_MASK,
- self.inputEventHandlers["toggleCaretNavigationHandler"]))
-
- keyBindings.add(
- keybindings.KeyBinding(
- "z",
- settings.defaultModifierMask,
- settings.ORCA_MODIFIER_MASK,
- self.inputEventHandlers["toggleStructuralNavigationHandler"]))
-
- keyBindings.add(
- keybindings.KeyBinding(
- "Right",
- settings.defaultModifierMask,
- settings.ORCA_MODIFIER_MASK,
- self.inputEventHandlers["goNextObjectInOrderHandler"]))
-
- keyBindings.add(
- keybindings.KeyBinding(
- "Left",
- settings.defaultModifierMask,
- settings.ORCA_MODIFIER_MASK,
- self.inputEventHandlers["goPreviousObjectInOrderHandler"]))
-
- if script_settings.controlCaretNavigation:
- for keyBinding in self.__getArrowBindings().keyBindings:
- keyBindings.add(keyBinding)
-
- #####################################################################
- # #
- # Unbound handlers #
- # #
- #####################################################################
- keyBindings.add(
- keybindings.KeyBinding(
- "",
- settings.defaultModifierMask,
- settings.NO_MODIFIER_MASK,
- self.inputEventHandlers["goPreviousLandmark"]))
-
- keyBindings.add(
- keybindings.KeyBinding(
- "",
- settings.defaultModifierMask,
- settings.NO_MODIFIER_MASK,
- self.inputEventHandlers["goNextLandmark"]))
-
- return keyBindings
-
- def getAppPreferencesGUI(self):
- """Return a GtkVBox contain the application unique configuration
- GUI items for the current application.
- """
-
- vbox = gtk.VBox(False, 0)
- vbox.set_border_width(12)
- gtk.Widget.show(vbox)
-
- # General ("Page") Navigation frame.
- #
- generalFrame = gtk.Frame()
- gtk.Widget.show(generalFrame)
- gtk.Box.pack_start(vbox, generalFrame, False, False, 5)
-
- generalAlignment = gtk.Alignment(0.5, 0.5, 1, 1)
- gtk.Widget.show(generalAlignment)
- gtk.Container.add(generalFrame, generalAlignment)
- gtk.Alignment.set_padding(generalAlignment, 0, 0, 12, 0)
-
- generalVBox = gtk.VBox(False, 0)
- gtk.Widget.show(generalVBox)
- gtk.Container.add(generalAlignment, generalVBox)
+ generalVBox = gtk.VBox(False, 0)
+ gtk.Widget.show(generalVBox)
+ gtk.Container.add(generalAlignment, generalVBox)
# Translators: Gecko native caret navigation is where
# Firefox itself controls how the arrow keys move the caret
@@ -1311,7 +663,7 @@
gtk.Box.pack_start(generalVBox, self.structuralNavigationCheckButton,
False, False, 0)
gtk.ToggleButton.set_active(self.structuralNavigationCheckButton,
- script_settings.structuralNavigationEnabled)
+ self.structuralNavigation.enabled)
# Translators: when the user arrows up and down in HTML content,
# it is some times beneficial to always position the cursor at the
@@ -1374,7 +726,7 @@
gtk.Box.pack_start(tableVBox, self.speakCellCoordinatesCheckButton,
False, False, 0)
gtk.ToggleButton.set_active(self.speakCellCoordinatesCheckButton,
- script_settings.speakCellCoordinates)
+ settings.speakCellCoordinates)
# Translators: this is an option to tell Orca whether or not it
# should speak the span size of a table cell (e.g., how many
@@ -1386,7 +738,7 @@
gtk.Box.pack_start(tableVBox, self.speakCellSpanCheckButton,
False, False, 0)
gtk.ToggleButton.set_active(self.speakCellSpanCheckButton,
- script_settings.speakCellSpan)
+ settings.speakCellSpan)
# Translators: this is an option for whether or not to speak
# the header of a table cell in HTML content.
@@ -1397,7 +749,7 @@
gtk.Box.pack_start(tableVBox, self.speakCellHeadersCheckButton,
False, False, 0)
gtk.ToggleButton.set_active(self.speakCellHeadersCheckButton,
- script_settings.speakCellHeaders)
+ settings.speakCellHeaders)
# Translators: this is an option to allow users to skip over
# empty/blank cells when navigating tables in HTML content.
@@ -1408,7 +760,7 @@
gtk.Box.pack_start(tableVBox, self.skipBlankCellsCheckButton,
False, False, 0)
gtk.ToggleButton.set_active(self.skipBlankCellsCheckButton,
- script_settings.skipBlankCells)
+ settings.skipBlankCells)
# Translators: this is the title of a panel containing options
# for specifying how to navigate tables in HTML content.
@@ -1527,22 +879,6 @@
prefs.writelines("%s.sayAllOnLoad = %s\n" % (prefix, value))
script_settings.sayAllOnLoad = value
- value = self.speakCellCoordinatesCheckButton.get_active()
- prefs.writelines("%s.speakCellCoordinates = %s\n" % (prefix, value))
- script_settings.speakCellCoordinates = value
-
- value = self.speakCellSpanCheckButton.get_active()
- prefs.writelines("%s.speakCellSpan = %s\n" % (prefix, value))
- script_settings.speakCellSpan = value
-
- value = self.speakCellHeadersCheckButton.get_active()
- prefs.writelines("%s.speakCellHeaders = %s\n" % (prefix, value))
- script_settings.speakCellHeaders = value
-
- value = self.skipBlankCellsCheckButton.get_active()
- prefs.writelines("%s.skipBlankCells = %s\n" % (prefix, value))
- script_settings.skipBlankCells = value
-
value = self.speakResultsDuringFindCheckButton.get_active()
prefs.writelines("%s.speakResultsDuringFind = %s\n" % (prefix, value))
script_settings.speakResultsDuringFind = value
@@ -1556,13 +892,35 @@
prefs.writelines("%s.minimumFindLength = %s\n" % (prefix, value))
script_settings.minimumFindLength = value
+ # These structural navigation settings used to be application-
+ # specific preferences because at the time structural navigation
+ # was implemented it was part of the Gecko script. These settings
+ # are now part of settings.py so that other scripts can implement
+ # structural navigation. But until that happens, there's no need
+ # to move these controls/change the preferences dialog.
+ #
+ value = self.speakCellCoordinatesCheckButton.get_active()
+ prefs.writelines("orca.settings.speakCellCoordinates = %s\n" % value)
+ settings.speakCellCoordinates = value
+
+ value = self.speakCellSpanCheckButton.get_active()
+ prefs.writelines("orca.settings.speakCellSpan = %s\n" % value)
+ settings.speakCellSpan = value
+
+ value = self.speakCellHeadersCheckButton.get_active()
+ prefs.writelines("orca.settings.speakCellHeaders = %s\n" % value)
+ settings.speakCellHeaders = value
+
+ value = self.skipBlankCellsCheckButton.get_active()
+ prefs.writelines("orca.settings.skipBlankCells = %s\n" % value)
+ settings.skipBlankCells = value
+
def getAppState(self):
"""Returns an object that can be passed to setAppState. This
object will be use by setAppState to restore any state information
that was being maintained by the script."""
return [default.Script.getAppState(self),
- self._documentFrameCaretContext,
- self.lastTableCell]
+ self._documentFrameCaretContext]
def setAppState(self, appState):
"""Sets the application state using the given appState object.
@@ -1572,8 +930,7 @@
"""
try:
[defaultAppState,
- self._documentFrameCaretContext,
- self.lastTableCell] = appState
+ self._documentFrameCaretContext] = appState
default.Script.setAppState(self, defaultAppState)
except:
debug.printException(debug.LEVEL_WARNING)
@@ -1609,7 +966,7 @@
if handler and handler.function in self._caretNavigationFunctions:
return self.useCaretNavigationModel(keyboardEvent)
elif handler \
- and handler.function in self._structuralNavigationFunctions:
+ and handler.function in self.structuralNavigation.functions:
return self.useStructuralNavigationModel()
else:
consumes = handler != None
@@ -1618,7 +975,7 @@
if handler and handler.function in self._caretNavigationFunctions:
return self.useCaretNavigationModel(keyboardEvent)
elif handler \
- and handler.function in self._structuralNavigationFunctions:
+ and handler.function in self.structuralNavigation.functions:
return self.useStructuralNavigationModel()
else:
consumes = handler != None
@@ -3440,9 +2797,8 @@
def useStructuralNavigationModel(self):
"""Returns True if we should do our own structural navigation.
- [[[TODO: WDW - this should return False if we're in something
- like an entry area or a list because we want their keyboard
- navigation stuff to work.]]]
+ This should return False if we're in something like an entry
+ or a list.
"""
letThemDoItEditableRoles = [pyatspi.ROLE_ENTRY,
@@ -3452,7 +2808,7 @@
pyatspi.ROLE_LIST_ITEM,
pyatspi.ROLE_MENU_ITEM]
- if not script_settings.structuralNavigationEnabled:
+ if not self.structuralNavigation.enabled:
return False
if not self.isNavigableAria(orca_state.locusOfFocus):
@@ -3831,32 +3187,6 @@
return [0, 0]
- def isSameCell(self, obj, coordinates1, coordinates2):
- """Returns True if coordinates1 and coordinates2 refer to the
- same cell in the specified table.
-
- Arguments:
- - obj: the table in which to compare the coordinates
- - coordinates1: [row, col]
- - coordinates2: [row, col]
- """
-
- if coordinates1 == coordinates2:
- return True
-
- try:
- table = obj.queryTable()
- except:
- pass
- else:
- cell1 = table.getAccessibleAt(coordinates1[0],
- coordinates1[1])
- cell2 = table.getAccessibleAt(coordinates2[0],
- coordinates2[1])
- return self.isSameObject(cell1, cell2)
-
- return False
-
def isBlankCell(self, obj):
"""Returns True if the table cell is empty or consists of a single
non-breaking space.
@@ -3875,270 +3205,45 @@
return True
- def isNonUniformTable(self, obj):
- """Returns True if the obj is a non-uniform table (i.e. a table
- where at least one cell spans multiple rows and/or columns).
+ def getLinkBasename(self, obj):
+ """Returns the relevant information from the URI. The idea is
+ to attempt to strip off all prefix and suffix, much like the
+ basename command in a shell."""
- Arguments:
- - obj: the table to examine
- """
+ basename = None
try:
- table = obj.queryTable()
+ hyperlink = obj.queryHyperlink()
except:
pass
else:
- for i in xrange(obj.childCount):
- [isCell, row, col, rowExtents, colExtents, isSelected] = \
- table.getRowColumnExtentsAtIndex(i)
- if (rowExtents > 1) or (colExtents > 1):
- return True
-
- return False
+ uri = hyperlink.getURI(0)
+ if uri and len(uri):
+ # Get the last thing after all the /'s, unless it ends
+ # in a /. If it ends in a /, we'll look to the stuff
+ # before the ending /.
+ #
+ if uri[-1] == "/":
+ basename = uri[0:-1]
+ basename = basename.split('/')[-1]
+ else:
+ basename = uri.split('/')[-1]
+ if basename.startswith("index"):
+ basename = uri.split('/')[-2]
- def isHeader(self, obj):
- """Returns True if the table cell is a header"""
+ # Now, try to strip off the suffixes.
+ #
+ basename = basename.split('.')[0]
+ basename = basename.split('?')[0]
+ basename = basename.split('#')[0]
- if not obj:
- return False
+ return basename
- attributes = obj.getAttributes()
- if attributes:
- for attribute in attributes:
- if attribute == "tag:TH":
- return True
+ def isFormField(self, obj):
+ """Returns True if the given object is a field inside of a form."""
- return False
-
- def isInHeaderRow(self, obj):
- """Returns True if all of the cells in the same row as this cell are
- headers.
-
- Arguments:
- - obj: the table cell whose row is to be examined
- """
-
- if obj and obj.getRole() == pyatspi.ROLE_TABLE_CELL:
- parent = self.getAncestor(obj,
- [pyatspi.ROLE_TABLE],
- [pyatspi.ROLE_DOCUMENT_FRAME])
- try:
- table = parent.queryTable()
- except:
- return False
- else:
- index = self.getCellIndex(obj)
- row = table.getRowAtIndex(index)
- for col in xrange(table.nColumns):
- cell = table.getAccessibleAt(row, col)
- if not self.isHeader(cell):
- return False
-
- return True
-
- def isInHeaderColumn(self, obj):
- """Returns True if all of the cells in the same column as this cell
- are headers.
-
- Arguments:
- - obj: the table cell whose column is to be examined
- """
-
- if obj and obj.getRole() == pyatspi.ROLE_TABLE_CELL:
- parent = self.getAncestor(obj,
- [pyatspi.ROLE_TABLE],
- [pyatspi.ROLE_DOCUMENT_FRAME])
- try:
- table = parent.queryTable()
- except:
- return False
- else:
- index = self.getCellIndex(obj)
- col = table.getColumnAtIndex(index)
- for row in xrange(table.nRows):
- cell = table.getAccessibleAt(row, col)
- if not self.isHeader(cell):
- return False
-
- return True
-
- def getRowHeaders(self, obj):
- """Returns a list of table cells that serve as a row header for
- the specified TABLE_CELL.
- """
-
- rowHeaders = []
- if not obj:
- return rowHeaders
-
- try:
- table = obj.parent.queryTable()
- except:
- pass
- else:
- [row, col] = self.getCellCoordinates(obj)
- # Theoretically, we should be able to quickly get the text
- # of a {row, column}Header via get{Row,Column}Description().
- # Mozilla doesn't expose the information that way, however.
- # get{Row, Column}Header seems to work sometimes.
- #
- header = table.getRowHeader(row)
- if header:
- rowHeaders.append(header)
-
- # Headers that are strictly marked up with <th> do not seem
- # to be exposed through get{Row, Column}Header.
- #
- else:
- # If our cell spans multiple rows, we want to get all of
- # the headers that apply.
- #
- rowspan = table.getRowExtentAt(row, col)
- for r in range(row, row+rowspan):
- # We could have multiple headers for a given row, one
- # header per column. Presumably all of the headers are
- # prior to our present location.
- #
- for c in range(0, col):
- cell = table.getAccessibleAt(r, c)
- text = self.queryNonEmptyText(cell)
- if self.isHeader(cell) and text \
- and not cell in rowHeaders:
- rowHeaders.append(cell)
-
- return rowHeaders
-
- def getColumnHeaders(self, obj):
- """Returns a list of table cells that serve as a column header for
- the specified TABLE_CELL.
- """
-
- columnHeaders = []
- if not obj:
- return columnHeaders
-
- try:
- table = obj.parent.queryTable()
- except:
- pass
- else:
- [row, col] = self.getCellCoordinates(obj)
- # Theoretically, we should be able to quickly get the text
- # of a {row, column}Header via get{Row,Column}Description().
- # Mozilla doesn't expose the information that way, however.
- # get{Row, Column}Header seems to work sometimes.
- #
- header = table.getColumnHeader(col)
- if header:
- columnHeaders.append(header)
-
- # Headers that are strictly marked up with <th> do not seem
- # to be exposed through get{Row, Column}Header.
- #
- else:
- # If our cell spans multiple columns, we want to get all of
- # the headers that apply.
- #
- colspan = table.getColumnExtentAt(row, col)
- for c in range(col, col+colspan):
- # We could have multiple headers for a given column, one
- # header per row. Presumably all of the headers are
- # prior to our present location.
- #
- for r in range(0, row):
- cell = table.getAccessibleAt(r, c)
- text = self.queryNonEmptyText(cell)
- if self.isHeader(cell) and text \
- and not cell in columnHeaders:
- columnHeaders.append(cell)
-
- return columnHeaders
-
- def getCellSpanInfo(self, obj):
- """Returns a string reflecting the number of rows and/or columns
- spanned by a table cell when multiple rows and/or columns are spanned.
- """
-
- if not obj or (obj.getRole() != pyatspi.ROLE_TABLE_CELL):
- return
-
- [row, col] = self.getCellCoordinates(obj)
- table = obj.parent.queryTable()
- rowspan = table.getRowExtentAt(row, col)
- colspan = table.getColumnExtentAt(row, col)
- spanString = None
- if (colspan > 1) and (rowspan > 1):
- # Translators: The cell here refers to a cell within an HTML
- # table. We need to announce when the cell occupies or "spans"
- # more than a single row and/or column.
- #
- spanString = _("Cell spans %d rows and %d columns") % \
- (rowspan, colspan)
- elif (colspan > 1):
- # Translators: The cell here refers to a cell within an HTML
- # table. We need to announce when the cell occupies or "spans"
- # more than a single row and/or column.
- #
- spanString = _("Cell spans %d columns") % colspan
- elif (rowspan > 1):
- # Translators: The cell here refers to a cell within an HTML
- # table. We need to announce when the cell occupies or "spans"
- # more than a single row and/or column.
- #
- spanString = _("Cell spans %d rows") % rowspan
-
- return spanString
-
- def getTableCaption(self, obj):
- """Returns the ROLE_CAPTION object of a ROLE_TABLE object or None
- if the caption cannot be found.
- """
-
- for child in obj:
- if child and (child.getRole() == pyatspi.ROLE_CAPTION):
- return child
-
- return None
-
- def getLinkBasename(self, obj):
- """Returns the relevant information from the URI. The idea is
- to attempt to strip off all prefix and suffix, much like the
- basename command in a shell."""
-
- basename = None
-
- try:
- hyperlink = obj.queryHyperlink()
- except:
- pass
- else:
- uri = hyperlink.getURI(0)
- if uri and len(uri):
- # Get the last thing after all the /'s, unless it ends
- # in a /. If it ends in a /, we'll look to the stuff
- # before the ending /.
- #
- if uri[-1] == "/":
- basename = uri[0:-1]
- basename = basename.split('/')[-1]
- else:
- basename = uri.split('/')[-1]
- if basename.startswith("index"):
- basename = uri.split('/')[-2]
-
- # Now, try to strip off the suffixes.
- #
- basename = basename.split('.')[0]
- basename = basename.split('?')[0]
- basename = basename.split('#')[0]
-
- return basename
-
- def isFormField(self, obj):
- """Returns True if the given object is a field inside of a form."""
-
- if not obj or not self.inDocumentContent(obj):
- return False
+ if not obj or not self.inDocumentContent(obj):
+ return False
formRoles = [pyatspi.ROLE_CHECK_BOX,
pyatspi.ROLE_RADIO_BUTTON,
@@ -4155,20 +3260,6 @@
return isField
- def isBlockquote(self, obj):
- """Returns True if the object is a blockquote"""
-
- if not obj:
- return False
-
- attributes = obj.getAttributes()
- if attributes:
- for attribute in attributes:
- if attribute == "tag:BLOCKQUOTE":
- return True
-
- return False
-
def isLineBreakChar(self, obj, offset):
"""Returns True of the character at the given offset within
obj is a line break character (i.e. <br />) or is a newline
@@ -4305,7 +3396,8 @@
"""Returns the object and last caret offset at the bottom of the
document frame."""
- obj = self.getLastObject()
+ documentFrame = self.getDocumentFrame()
+ obj = self.getLastObject(documentFrame)
offset = 0
# obj should now be the very last item in the entire document frame
@@ -4316,7 +3408,7 @@
if text:
offset = text.characterCount - 1
else:
- obj = self.findPreviousObject(obj)
+ obj = self.findPreviousObject(obj, documentFrame)
while obj:
[lastObj, lastOffset] = self.findNextCaretInOrder(obj, offset)
@@ -4328,13 +3420,12 @@
return [obj, offset]
- def getLastObject(self):
+ def getLastObject(self, documentFrame):
"""Returns the last object in the document frame"""
- documentFrame = self.getDocumentFrame()
lastChild = documentFrame[documentFrame.childCount - 1]
while lastChild:
- lastObj = self.findNextObject(lastChild)
+ lastObj = self.findNextObject(lastChild, documentFrame)
if lastObj:
lastChild = lastObj
else:
@@ -5012,12 +4103,14 @@
return guess
- def guessTheLabel(self, obj):
+ def guessTheLabel(self, obj, focusedOnly=True):
"""Attempts to guess what the label of an unlabeled form control
might be.
Arguments
- obj: the form field about which to take a guess
+ - focusedOnly: If True, only take guesses about the form field
+ with focus.
Returns the text which we think might be the label or None if we
give up.
@@ -5035,8 +4128,9 @@
# If we're not in the document frame, we don't want to be guessing.
# We also don't want to be guessing if the item doesn't have focus.
#
+ isFocused = obj.getState().contains(pyatspi.STATE_FOCUSED)
if not self.inDocumentContent() \
- or not obj.getState().contains(pyatspi.STATE_FOCUSED) \
+ or (focusedOnly and not isFocused) \
or self.isAriaWidget(obj):
return guess
@@ -5354,7 +4448,7 @@
return [None, -1]
- def findPreviousObject(self, obj):
+ def findPreviousObject(self, obj, documentFrame):
"""Finds the object prior to this one, where the tree we're
dealing with is a DOM and 'prior' means the previous object
in a linear presentation sense.
@@ -5365,7 +4459,6 @@
previousObj = None
characterOffset = 0
- documentFrame = self.getDocumentFrame()
# If the object is the document frame, the previous object is
# the one that follows us relative to our offset.
@@ -5380,7 +4473,7 @@
else:
# We're likely at the very end of the document
# frame.
- previousObj = self.getLastObject()
+ previousObj = self.getLastObject(documentFrame)
else:
# [[[TODO: HACK - WDW defensive programming because Gecko
# ally hierarchies are not always working. Objects say
@@ -5450,7 +4543,7 @@
return previousObj
- def findNextObject(self, obj):
+ def findNextObject(self, obj, documentFrame):
"""Finds the object after to this one, where the tree we're
dealing with is a DOM and 'next' means the next object
in a linear presentation sense.
@@ -5461,7 +4554,6 @@
nextObj = None
characterOffset = 0
- documentFrame = self.getDocumentFrame()
# If the object is the document frame, the next object is
# the one that follows us relative to our offset.
@@ -5555,329 +4647,6 @@
return nextObj
- def findPreviousRole(self, roles, wrap, currentObj=None):
- if settings.useCollection:
- try:
- # The docframe is our collection
- docframe = self.getDocumentFrame()
- col = docframe.queryCollection()
- except:
- debug.printException(debug.LEVEL_SEVERE)
- # Collection is probably not implemented, use the fallback
- return self.iterFindPreviousRole(roles, wrap, currentObj)
-
- try:
- # We have our Collection so define our matchRule and go find it
- stateset = pyatspi.StateSet()
- rule = col.createMatchRule(stateset.raw(), col.MATCH_NONE,
- "", col.MATCH_NONE,
- roles, col.MATCH_ANY,
- "", col.MATCH_NONE,
- False)
- retval = self.findPrevByMatchRule(col, rule, wrap, currentObj)
- # we created the matchRule so we need to free it
- col.freeMatchRule(rule)
- return retval
- except:
- # we have created our matchRule at this point so free it
- col.freeMatchRule(rule)
- debug.printException(debug.LEVEL_SEVERE)
- # Collection is probably not implemented, use the fallback
- return self.iterFindPreviousRole(roles, wrap, currentObj)
- else:
- return self.iterFindPreviousRole(roles, wrap, currentObj)
-
- def iterFindPreviousRole(self, roles, wrap, currentObj=None):
- """Finds the caret offset at the beginning of the next object
- using the given roles list as a pattern to match.
-
- Arguments:
- -roles: a list of roles from rolenames.py
- -wrap: if True and the top of the document is reached, move
- to the bottom and keep looking.
- -currentObj: the object from which the search should begin
-
- Returns: [obj, wrapped] where wrapped is a boolean reflecting
- whether wrapping took place.
- """
-
- if not currentObj:
- [currentObj, characterOffset] = self.getCaretContext()
-
- ancestors = []
- nestableRoles = [pyatspi.ROLE_LIST, pyatspi.ROLE_TABLE]
- obj = currentObj.parent
- while obj:
- ancestors.append(obj)
- obj = obj.parent
-
- wrapped = False
- obj = self.findPreviousObject(currentObj)
- while obj:
- isNestedItem = ((obj != currentObj.parent) \
- and (currentObj.parent.getRole() == obj.getRole()) \
- and (obj.getRole() in nestableRoles))
- if ((not obj in ancestors) or isNestedItem) \
- and (obj.getRole() in roles) \
- and (not self.isLayoutOnly(obj)):
- if wrapped and self.isSameObject(currentObj, obj):
- obj = None
- return [obj, wrapped]
- else:
- obj = self.findPreviousObject(obj)
- if not obj and wrap and not wrapped:
- obj = self.getLastObject()
- wrapped = True
-
- return [None, wrapped]
-
- def findNextRole(self, roles, wrap, currentObj=None):
- if settings.useCollection:
- try:
- # The docframe is our collection
- docframe = self.getDocumentFrame()
- col = docframe.queryCollection()
- except:
- debug.printException(debug.LEVEL_SEVERE)
- # Collection is probably not implemented, use the fallback
- return self.iterFindNextRole(roles, wrap, currentObj)
-
- try:
- # We have our Collection so define our matchRule and go find it
- stateset = pyatspi.StateSet()
- rule = col.createMatchRule(stateset.raw(), col.MATCH_NONE,
- "", col.MATCH_NONE,
- roles, col.MATCH_ANY,
- "", col.MATCH_NONE,
- False)
- retval = self.findNextByMatchRule(col, rule, wrap, currentObj)
- # we created the matchRule so we need to free it
- col.freeMatchRule(rule)
- return retval
- except:
- # we created the matchRule at this point so free it
- col.freeMatchRule(rule)
- debug.printException(debug.LEVEL_SEVERE)
- # Collection is probably not implemented, use the fallback
- return self.iterFindNextRole(roles, wrap, currentObj)
- else:
- return self.iterFindNextRole(roles, wrap, currentObj)
-
- def iterFindNextRole(self, roles, wrap, currentObj=None):
- """Finds the caret offset at the beginning of the next object
- using the given roles list as a pattern to match or not match.
-
- Arguments:
- -roles: a list of roles from rolenames.py
- -wrap: if True and the bottom of the document is reached, move
- to the top and keep looking.
- -currentObj: the object from which the search should begin
-
- Returns: [obj, wrapped] where wrapped is a boolean reflecting
- whether wrapping took place.
- """
-
- if not currentObj:
- [currentObj, characterOffset] = self.getCaretContext()
-
- ancestors = []
- obj = currentObj.parent
- while obj:
- ancestors.append(obj)
- obj = obj.parent
-
- wrapped = False
- obj = self.findNextObject(currentObj)
- if not obj and wrap:
- documentFrame = self.getDocumentFrame()
- obj = documentFrame[0]
- wrapped = True
- while obj:
- if (not obj in ancestors) and (obj.getRole() in roles) \
- and (not self.isLayoutOnly(obj)):
- if wrapped and self.isSameObject(currentObj, obj):
- obj = None
- return [obj, wrapped]
- else:
- obj = self.findNextObject(obj)
- if not obj and wrap and not wrapped:
- documentFrame = self.getDocumentFrame()
- obj = documentFrame[0]
- wrapped = True
-
- return [None, wrapped]
-
- def findPrevByMatchRule(self, col, matchrule,
- wrap, currentObj, allowNesting=False):
- # get our current object
- if not currentObj:
- [currentObj, characterOffset] = self.getCaretContext()
-
- # Get the ancestors. We won't stop on any of them.
- ancestors = []
- obj = currentObj.parent
- if allowNesting:
- ancestors.append(obj)
- else:
- while obj:
- ancestors.append(obj)
- obj = obj.parent
-
- wrapped = False
- rs = col.getMatchesTo(currentObj, matchrule,
- col.SORT_ORDER_CANONICAL,
- col.TREE_INORDER, True, 1, True)
- while True:
- if len(rs) == 0:
- if wrapped:
- return [None, True]
- elif wrap:
- lastobj = self.getLastObject()
- # Collection does not do an inclusive search, meaning
- # that the start object is not part of the search. So
- # we need to test the lastobj separately using the given
- # matchRule. We don't have this problem for 'Next' because
- # the startobj is the doc frame.
- #
- secondlastobj = self.findPreviousObject(lastobj)
- rs = col.getMatchesFrom(secondlastobj, matchrule,
- col.SORT_ORDER_CANONICAL,
- col.TREE_INORDER, 1, True)
- if len(rs) > 0:
- return [rs[0], True]
- else:
- rs = col.getMatchesTo(lastobj, matchrule,
- col.SORT_ORDER_CANONICAL,
- col.TREE_INORDER, True, 1, True)
- wrapped = True
- # caller doesn't want us to wrap and we haven't found anything
- else:
- return [None, False]
- elif len(rs) > 0:
- if rs[0] in ancestors:
- rs = col.getMatchesTo(rs[0], matchrule,
- col.SORT_ORDER_CANONICAL,
- col.TREE_INORDER, True, 1, True)
- else:
- return [rs[0], wrapped]
-
- def findNextByMatchRule(self, col, matchrule, wrap, currentObj=None):
- # get our current object
- if not currentObj:
- [currentObj, characterOffset] = self.getCaretContext()
-
- # go find the next match
- rs = col.getMatchesFrom(currentObj,
- matchrule,
- col.SORT_ORDER_CANONICAL,
- col.TREE_INORDER,
- 1,
- True)
-
- if len(rs) > 0:
- return [rs[0], False]
- elif wrap:
- # We didn't find anything so start at the doc frame and try again
- rs = col.getMatchesFrom(self.getDocumentFrame(),
- matchrule,
- col.SORT_ORDER_CANONICAL,
- col.TREE_INORDER,
- 1,
- True)
- if len(rs) > 0:
- return [rs[0], True]
- else:
- return [None, True]
- else:
- return [None, False]
-
- def findPrevByPredicate(self, pred, wrap, currentObj=None):
- """Finds the caret offset at the beginning of the previous object
- using the given predicate as a pattern to match.
-
- Arguments:
- -pred: a python callable that takes an accessible argument and
- returns true/false based on some match criteria
- -wrap: if True and the top of the document is reached, move
- to the bottom and keep looking.
- -currentObj: the object from which the search should begin
-
- Returns: [obj, wrapped] where wrapped is a boolean reflecting
- whether wrapping took place.
- """
-
- if not currentObj:
- [currentObj, characterOffset] = self.getCaretContext()
-
- ancestors = []
- nestableRoles = [pyatspi.ROLE_LIST, pyatspi.ROLE_TABLE]
- obj = currentObj.parent
- while obj:
- ancestors.append(obj)
- obj = obj.parent
-
- wrapped = False
- obj = self.findPreviousObject(currentObj)
- while obj:
- isNestedItem = ((obj != currentObj.parent) \
- and (currentObj.parent.getRole() == obj.getRole()) \
- and (obj.getRole() in nestableRoles))
- if ((not obj in ancestors) or isNestedItem) and pred(obj):
- if wrapped and self.isSameObject(currentObj, obj):
- obj = None
- return [obj, wrapped]
- else:
- obj = self.findPreviousObject(obj)
- if not obj and wrap and not wrapped:
- obj = self.getLastObject()
- wrapped = True
-
- return [None, wrapped]
-
- def findNextByPredicate(self, pred, wrap, currentObj=None):
- """Finds the caret offset at the beginning of the next object
- using the given predicate as a pattern to match or not match.
-
- Arguments:
- -pred: a python callable that takes an accessible argument and
- returns true/false based on some match criteria
- -wrap: if True and the bottom of the document is reached, move
- to the top and keep looking.
- -currentObj: the object from which the search should begin
-
- Returns: [obj, wrapped] where wrapped is a boolean reflecting
- whether wrapping took place.
- """
-
- if not currentObj:
- [currentObj, characterOffset] = self.getCaretContext()
- ancestors = []
- obj = currentObj.parent
- while obj:
- ancestors.append(obj)
- obj = obj.parent
-
- wrapped = False
- obj = self.findNextObject(currentObj)
- if not obj and wrap:
- documentFrame = self.getDocumentFrame()
- obj = documentFrame[0]
- wrapped = True
-
- while obj:
- if (not obj in ancestors) and pred(obj):
- if wrapped and self.isSameObject(currentObj, obj):
- obj = None
- return [obj, wrapped]
- else:
- obj = self.findNextObject(obj)
- if not obj and wrap and not wrapped:
- documentFrame = self.getDocumentFrame()
- obj = documentFrame[0]
- wrapped = True
-
- return [None, wrapped]
-
####################################################################
# #
# Methods to get information about current object. #
@@ -6137,7 +4906,8 @@
break
if isAria:
- prevObj = self.findPreviousObject(firstObj)
+ documentFrame = self.getDocumentFrame()
+ prevObj = self.findPreviousObject(firstObj, documentFrame)
pOffset = 0
else:
[prevObj, pOffset] = \
@@ -6175,7 +4945,8 @@
break
if isAria:
- nextObj = self.findNextObject(lastObj)
+ documentFrame = self.getDocumentFrame()
+ nextObj = self.findNextObject(lastObj, documentFrame)
nOffset = 0
else:
[nextObj, nOffset] = self.findNextCaretInOrder(lastObj, end)
@@ -6491,15 +5262,6 @@
self.setCaretContext(obj, characterOffset)
- # If we're not in a table cell, reset self.lastTableCell.
- #
- if obj.getRole() != pyatspi.ROLE_TABLE_CELL:
- cell = self.getAncestor(obj,
- [pyatspi.ROLE_TABLE_CELL],
- [pyatspi.ROLE_DOCUMENT_FRAME])
- if not cell:
- self.lastTableCell = [-1, -1]
-
# If the item is a focusable list in an HTML form, we're here
# because we've arrowed to it. We don't want to grab focus on
# it and trap the user in the list. The same is true for combo
@@ -6730,7 +5492,8 @@
failureCount += 1
if currentLine == prevLine:
# print "find prev line still stuck", prevObj, prevOffset
- prevObj = self.findPreviousObject(prevObj)
+ documentFrame = self.getDocumentFrame()
+ prevObj = self.findPreviousObject(prevObj, documentFrame)
prevOffset = 0
[prevObj, prevOffset] = self.findNextCaretInOrder(prevObj,
@@ -6819,7 +5582,8 @@
failureCount += 1
if currentLine == nextLine:
#print "find next line still stuck", nextObj, nextOffset
- nextObj = self.findNextObject(nextObj)
+ documentFrame = self.getDocumentFrame()
+ nextObj = self.findNextObject(nextObj, documentFrame)
nextOffset = 0
[nextObj, nextOffset] = \
@@ -6959,369 +5723,20 @@
if name in ["open", _("open")]:
action.doAction(i)
break
+ def goPreviousObjectInOrder(self, inputEvent):
+ """Go to the previous object in order, regardless of type or size."""
- def goPreviousHeading(self, inputEvent):
- """Go to the previous heading regardless of level."""
- wrap = True
- [obj, wrapped] = self.findPreviousRole([pyatspi.ROLE_HEADING], wrap)
- if wrapped:
- # Translators: when the user is attempting to locate a
- # particular object and the top of the web page has been
- # reached without that object being found, we "wrap" to
- # the bottom of the page and continuing looking upwards.
- # We need to inform the user when this is taking place.
- #
- speech.speak(_("Wrapping to bottom."))
- if obj:
- [obj, characterOffset] = self.findFirstCaretContext(obj, 0)
- self.setCaretPosition(obj, characterOffset)
- self.updateBraille(obj)
- contents = self.getObjectContentsAtOffset(obj, characterOffset)
- self.speakContents(contents)
- else:
- # Translators: this is in reference to navigating HTML content
- # by heading (e.g., <h1>).
- #
- speech.speak(_("No more headings."))
-
- def goNextHeading(self, inputEvent):
- """Go to the next heading regardless of level."""
- wrap = True
- [obj, wrapped] = self.findNextRole([pyatspi.ROLE_HEADING], wrap)
- if wrapped:
- # Translators: when the user is attempting to locate a
- # particular object and the bottom of the web page has been
- # reached without that object being found, we "wrap" to the
- # top of the page and continuing looking downwards. We need
- # to inform the user when this is taking place.
- #
- speech.speak(_("Wrapping to top."))
- if obj:
- [obj, characterOffset] = self.findFirstCaretContext(obj, 0)
- self.setCaretPosition(obj, characterOffset)
- self.updateBraille(obj)
- contents = self.getObjectContentsAtOffset(obj, characterOffset)
- self.speakContents(contents)
- else:
- # Translators: this is in reference to navigating HTML content
- # by heading (e.g., <h1>).
- #
- speech.speak(_("No more headings."))
-
- def goPreviousHeadingAtLevel(self, inputEvent, desiredLevel):
- """Go to the previous heading at the specified level.
-
- Arguments:
- - desiredLevel: the level (1-6) of the heading to locate
- """
-
- found = False
- level = 0
[obj, characterOffset] = self.getCaretContext()
- if obj.parent.getRole() == pyatspi.ROLE_HEADING:
- obj = obj.parent
- wrap = True
- while obj and not found:
- [obj, wrapped] = \
- self.findPreviousRole([pyatspi.ROLE_HEADING], wrap, obj)
- # We should only wrap if we haven't already done so.
- #
- if wrapped:
- wrap = False
- if obj:
- level = self.getHeadingLevel(obj)
- if level == desiredLevel:
- found = True
- if wrapped:
- # Translators: when the user is attempting to locate a
- # particular object and the top of the web page has been
- # reached without that object being found, we "wrap" to
- # the bottom of the page and continuing looking upwards.
- # We need to inform the user when this is taking place.
- #
- speech.speak(_("Wrapping to bottom."))
- if obj and found:
- [obj, characterOffset] = self.findFirstCaretContext(obj, 0)
- self.setCaretPosition(obj, characterOffset)
- self.updateBraille(obj)
- contents = self.getObjectContentsAtOffset(obj, characterOffset)
- self.speakContents(contents)
- else:
- # Translators: this is in reference to navigating HTML content
- # by heading (e.g., <h1>).
- #
- speech.speak(_("No more headings at level %d.") % desiredLevel)
-
- def goNextHeadingAtLevel(self, inputEvent, desiredLevel):
- """Go to the next heading at the specified level.
- Arguments:
- - desiredLevel: the level (1-6) of the heading to locate
- """
+ # Work our way out of form lists and combo boxes.
+ #
+ if obj and obj.getState().contains(pyatspi.STATE_SELECTABLE):
+ obj = obj.parent.parent
+ characterOffset = self.getCharacterOffsetInParent(obj)
+ self.currentLineContents = None
- found = False
- level = 0
- [obj, characterOffset] = self.getCaretContext()
- wrap = True
- while obj and not found:
- [obj, wrapped] = \
- self.findNextRole([pyatspi.ROLE_HEADING], wrap, obj)
- # We should only wrap if we haven't already done so.
- #
- if wrapped:
- wrap = False
- if obj:
- level = self.getHeadingLevel(obj)
- if level == desiredLevel:
- found = True
- if wrapped:
- # Translators: when the user is attempting to locate a
- # particular object and the bottom of the web page has been
- # reached without that object being found, we "wrap" to the
- # top of the page and continuing looking downwards. We need
- # to inform the user when this is taking place.
- #
- speech.speak(_("Wrapping to top."))
- if obj and found:
- [obj, characterOffset] = self.findFirstCaretContext(obj, 0)
- self.setCaretPosition(obj, characterOffset)
- self.updateBraille(obj)
- contents = self.getObjectContentsAtOffset(obj, characterOffset)
- self.speakContents(contents)
- else:
- # Translators: this is in reference to navigating HTML content
- # by heading (e.g., <h1>).
- #
- speech.speak(_("No more headings at level %d.") % desiredLevel)
-
- def goNextHeading1(self, inputEvent):
- """Go to the next heading at the level 1."""
- self.goNextHeadingAtLevel(inputEvent, 1)
-
- def goPreviousHeading1(self, inputEvent):
- """Go to the previous heading at the level 1."""
- self.goPreviousHeadingAtLevel(inputEvent, 1)
-
- def goNextHeading2(self, inputEvent):
- """Go to the next heading at the level 2."""
- self.goNextHeadingAtLevel(inputEvent, 2)
-
- def goPreviousHeading2(self, inputEvent):
- """Go to the previous heading at the level 2."""
- self.goPreviousHeadingAtLevel(inputEvent, 2)
-
- def goNextHeading3(self, inputEvent):
- """Go to the next heading at the level 3."""
- self.goNextHeadingAtLevel(inputEvent, 3)
-
- def goPreviousHeading3(self, inputEvent):
- """Go to the previous heading at the level 3."""
- self.goPreviousHeadingAtLevel(inputEvent, 3)
-
- def goNextHeading4(self, inputEvent):
- """Go to the next heading at the level 4."""
- self.goNextHeadingAtLevel(inputEvent, 4)
-
- def goPreviousHeading4(self, inputEvent):
- """Go to the previous heading at the level 4."""
- self.goPreviousHeadingAtLevel(inputEvent, 4)
-
- def goNextHeading5(self, inputEvent):
- """Go to the next heading at the level 5."""
- self.goNextHeadingAtLevel(inputEvent, 5)
-
- def goPreviousHeading5(self, inputEvent):
- """Go to the previous heading at the level 5."""
- self.goPreviousHeadingAtLevel(inputEvent, 5)
-
- def goNextHeading6(self, inputEvent):
- """Go to the next heading at the level 6."""
- self.goNextHeadingAtLevel(inputEvent, 6)
-
- def goPreviousHeading6(self, inputEvent):
- """Go to the previous heading at the level 6."""
- self.goPreviousHeadingAtLevel(inputEvent, 6)
-
- def goPreviousChunk(self, inputEvent):
- """Go to the previous chunk/large object."""
- wrap = True
- [obj, wrapped] = self.findPrevByPredicate(self.__matchChunk, wrap)
- if wrapped:
- # Translators: when the user is attempting to locate a
- # particular object and the top of the web page has been
- # reached without that object being found, we "wrap" to
- # the bottom of the page and continuing looking upwards.
- # We need to inform the user when this is taking place.
- #
- speech.speak(_("Wrapping to bottom."))
- if obj:
- [obj, characterOffset] = self.findFirstCaretContext(obj, 0)
- self.setCaretPosition(obj, characterOffset)
- self.updateBraille(obj)
- self.speakContents(self.getObjectContentsAtOffset(obj,
- characterOffset))
- else:
- # Translators: this is for navigating HTML in a structural
- # manner, where a 'large object' is a logical chunk of
- # text, such as a paragraph, a list, a table, etc.
- #
- speech.speak(_("No more large objects."))
-
- def goNextChunk(self, inputEvent):
- """Go to the next chunk/large object."""
- wrap = True
- [obj, wrapped] = self.findNextByPredicate(self.__matchChunk, wrap)
- if wrapped:
- # Translators: when the user is attempting to locate a
- # particular object and the bottom of the web page has been
- # reached without that object being found, we "wrap" to the
- # top of the page and continuing looking downwards. We need
- # to inform the user when this is taking place.
- #
- speech.speak(_("Wrapping to top."))
- if obj:
- [obj, characterOffset] = self.findFirstCaretContext(obj, 0)
- self.setCaretPosition(obj, characterOffset)
- self.updateBraille(obj)
- self.speakContents(self.getObjectContentsAtOffset(obj,
- characterOffset))
- else:
- # Translators: this is for navigating HTML in a structural
- # manner, where a 'large object' is a logical chunk of
- # text, such as a paragraph, a list, a table, etc.
- #
- speech.speak(_("No more large objects."))
-
- def goPreviousLandmark(self, inputEvent):
- [obj, characterOffset] = self.getCaretContext()
-
- # Try to find it using Collection first
- success = False
- if settings.useCollection:
- try:
- startobj = obj
- found = False
- col = self.getDocumentFrame().queryCollection()
- # form our list of attribute strings
- attrs = []
- for landmark in script_settings.ARIA_LANDMARKS:
- attrs.append('xml-roles:' + landmark)
- # define matchRule and find it
- stateset = pyatspi.StateSet()
- rule = col.createMatchRule(stateset.raw(), col.MATCH_ANY,
- attrs, col.MATCH_ANY,
- "", col.MATCH_ANY,
- "", col.MATCH_ALL,
- False)
- [obj, wrapped] = self.findPrevByMatchRule(col, rule, True, obj)
- if obj != startobj:
- found = True
- success = True
- col.freeMatchRule(rule)
- except NotImplementedError:
- debug.printException(debug.LEVEL_SEVERE)
- except:
- debug.printException(debug.LEVEL_SEVERE)
- col.freeMatchRule(rule)
-
- # Do it iteratively when Collection failed or is disabled
- if not success or not settings.useCollection:
- [obj, wrapped] = self.findPrevByPredicate(self.__matchLandmark,
- True, obj)
- if wrapped:
- # Translators: when the user is attempting to locate a
- # particular object and the top of the web page has been
- # reached without that object being found, we "wrap" to
- # the bottom of the page and continuing looking upwards.
- # We need to inform the user when this is taking place.
- #
- speech.speak(_("Wrapping to bottom."))
- if obj:
- [obj, characterOffset] = self.findFirstCaretContext(obj, 0)
- self.setCaretPosition(obj, characterOffset)
- self.updateBraille(obj)
- self.speakContents(self.getObjectContentsAtOffset(obj,
- characterOffset))
- else:
- # Translators: this is for navigating to the previous ARIA
- # role landmark. ARIA role landmarks are the W3C defined HTML
- # tag attribute 'role' used to identify important part of
- # webpage like banners, main context, search etc. This is an
- # that one was not found.
- #
- speech.speak(_("No landmark found."))
-
- def goNextLandmark(self, inputEvent):
- [obj, characterOffset] = self.getCaretContext()
-
- # Try to find it using Collection first
- success = False
- if settings.useCollection:
- try:
- startobj = obj
- col = self.getDocumentFrame().queryCollection()
- # form our list of attribute strings
- attrs = []
- for landmark in script_settings.ARIA_LANDMARKS:
- attrs.append('xml-roles:' + landmark)
- # define matchRule and find it
- stateset = pyatspi.StateSet()
- rule = col.createMatchRule(stateset.raw(), col.MATCH_ANY,
- attrs, col.MATCH_ANY,
- "", col.MATCH_ANY,
- "", col.MATCH_ALL,
- False)
- [obj, wrapped] = self.findNextByMatchRule(col, rule, True, obj)
- if obj and obj != startobj:
- found = True
- else:
- found = False
- success = True
- col.freeMatchRule(rule)
- except NotImplementedError:
- debug.printException(debug.LEVEL_SEVERE)
- except:
- debug.printException(debug.LEVEL_SEVERE)
- col.freeMatchRule(rule)
-
- # Do it iteratively when Collection failed or is disabled
- if not success or not settings.useCollection:
- [obj, wrapped] = self.findNextByPredicate(self.__matchLandmark,
- True, obj)
- if wrapped:
- # Translators: when the user is attempting to locate a
- # particular object and the bottom of the web page has been
- # reached without that object being found, we "wrap" to the
- # top of the page and continuing looking downwards. We need
- # to inform the user when this is taking place.
- #
- speech.speak(_("Wrapping to top."))
- if obj:
- [obj, characterOffset] = self.findFirstCaretContext(obj, 0)
- self.setCaretPosition(obj, characterOffset)
- self.updateBraille(obj)
- self.speakContents(self.getObjectContentsAtOffset(obj,
- characterOffset))
- else:
- # Translators: this is for navigating to the next ARIA
- # role landmark. This is an that one was not found.
- #
- speech.speak(_("No landmark found."))
-
- def goPreviousObjectInOrder(self, inputEvent):
- """Go to the previous object in order, regardless of type or size."""
-
- [obj, characterOffset] = self.getCaretContext()
-
- # Work our way out of form lists and combo boxes.
- #
- if obj and obj.getState().contains(pyatspi.STATE_SELECTABLE):
- obj = obj.parent.parent
- characterOffset = self.getCharacterOffsetInParent(obj)
- self.currentLineContents = None
-
- characterOffset = max(0, characterOffset)
- [prevObj, prevOffset] = [obj, characterOffset]
+ characterOffset = max(0, characterOffset)
+ [prevObj, prevOffset] = [obj, characterOffset]
found = False
mayHaveGoneTooFar = False
@@ -7464,1536 +5879,94 @@
objectContents = [objectContents[0]]
self.speakContents(objectContents)
- def goPreviousList(self, inputEvent):
- """Go to the previous (un)ordered list."""
- [obj, characterOffset] = self.getCaretContext()
- found = False
- wrap = True
- success = False
-
- # Try to find it using Collection first
- if settings.useCollection:
- try:
- startobj = obj
- col = self.getDocumentFrame().queryCollection()
- stateset = pyatspi.StateSet()
- stateset.add(pyatspi.STATE_FOCUSABLE)
- rule = col.createMatchRule(stateset.raw(), col.MATCH_NONE,
- "", col.MATCH_ANY,
- [pyatspi.ROLE_LIST], col.MATCH_ANY,
- "", col.MATCH_ALL,
- False)
- [obj, wrapped] = self.findPrevByMatchRule(col,
- rule, True, obj, allowNesting = True)
- if obj and obj != startobj:
- found = True
- else:
- found = False
- success = True
- col.freeMatchRule(rule)
- except NotImplementedError:
- debug.printException(debug.LEVEL_SEVERE)
- except:
- debug.printException(debug.LEVEL_SEVERE)
- col.freeMatchRule(rule)
-
- # Do it iteratively when Collection failed or is disabled
- if not success or not settings.useCollection:
- while obj and not found:
- [obj, wrapped] = \
- self.findPreviousRole([pyatspi.ROLE_LIST], wrap, obj)
- # We should only wrap if we haven't already done so.
- #
- if wrapped:
- wrap = False
-
- # We need to be sure that the list in question is an (un)ordered
- # list rather than a list in a form field. Form field lists are
- # focusable; (un)ordered lists are not.
- #
- if obj and not obj.getState().contains(pyatspi.STATE_FOCUSABLE):
- found = True
-
- if wrapped:
- # Translators: when the user is attempting to locate a
- # particular object and the top of the web page has been
- # reached without that object being found, we "wrap" to
- # the bottom of the page and continuing looking upwards.
- # We need to inform the user when this is taking place.
+ def advanceLivePoliteness(self, inputEvent):
+ """Advances live region politeness level."""
+ if settings.inferLiveRegions:
+ self.liveMngr.advancePoliteness(orca_state.locusOfFocus)
+ else:
+ # Translators: this announces to the user that live region
+ # support has been turned off.
#
- speech.speak(_("Wrapping to bottom."))
- if obj and found:
- nItems = 0
- for child in obj:
- if child.getRole() == pyatspi.ROLE_LIST_ITEM:
- nItems += 1
- # Translators: this represents a list in HTML.
- #
- itemString = ngettext("List with %d item",
- "List with %d items",
- nItems) % nItems
- speech.speak(itemString)
- nestingLevel = 0
- parent = obj.parent
- while parent.getRole() == pyatspi.ROLE_LIST:
- nestingLevel += 1
- parent = parent.parent
- if nestingLevel:
- # Translators: this represents a list item in HTML.
- # The nesting level is how 'deep' the item is (e.g.,
- # a level of 2 represents a list item inside a list
- # that's inside another list).
- #
- speech.speak(_("Nesting level %d") % nestingLevel)
- [obj, characterOffset] = self.findFirstCaretContext(obj, 0)
- self.setCaretPosition(obj, characterOffset)
- self.presentLine(obj, characterOffset)
+ speech.speak(_("Live region support is off"))
+ def monitorLiveRegions(self, inputEvent):
+ if not settings.inferLiveRegions:
+ settings.inferLiveRegions = True
+ # Translators: this announces to the user that live region
+ # are being monitored.
+ #
+ speech.speak(_("Live regions monitoring on"))
else:
- # Translators: this is for navigating HTML content by moving
- # from bulleted/numbered list to bulleted/numbered list.
+ settings.inferLiveRegions = False
+ # Translators: this announces to the user that live region
+ # are not being monitored.
#
- speech.speak(_("No more lists."))
-
- def goNextList(self, inputEvent):
- """Go to the next (un)ordered list."""
- [obj, characterOffset] = self.getCaretContext()
- found = False
- wrap = True
- success = False
-
- # Try to find it using Collection first
- if settings.useCollection:
- try:
- startobj = obj
- col = self.getDocumentFrame().queryCollection()
- stateset = pyatspi.StateSet()
- stateset.add(pyatspi.STATE_FOCUSABLE)
- rule = col.createMatchRule(stateset.raw(), col.MATCH_NONE,
- "", col.MATCH_ANY,
- [pyatspi.ROLE_LIST], col.MATCH_ANY,
- "", col.MATCH_ALL,
- False)
- [obj, wrapped] = self.findNextByMatchRule(col, rule, True, obj)
- if obj and obj != startobj:
- found = True
- else:
- found = False
- success = True
- col.freeMatchRule(rule)
- except NotImplementedError:
- debug.printException(debug.LEVEL_SEVERE)
- except:
- debug.printException(debug.LEVEL_SEVERE)
- col.freeMatchRule(rule)
-
- # Do it iteratively when Collection failed or is disabled
- if not success or not settings.useCollection:
- while obj and not found:
- [obj, wrapped] = \
- self.findNextRole([pyatspi.ROLE_LIST], wrap, obj)
- # We should only wrap if we haven't already done so.
- #
- if wrapped:
- wrap = False
-
- # We need to be sure that the list in question is an (un)ordered
- # list rather than a list in a form field. Form field lists are
- # focusable; (un)ordered lists are not.
- #
- if obj and not obj.getState().contains(pyatspi.STATE_FOCUSABLE):
- found = True
+ self.liveMngr.flushMessages()
+ speech.speak(_("Live regions monitoring off"))
- if wrapped:
- # Translators: when the user is attempting to locate a
- # particular object and the bottom of the web page has been
- # reached without that object being found, we "wrap" to the
- # top of the page and continuing looking downwards. We need
- # to inform the user when this is taking place.
+ def setLivePolitenessOff(self, inputEvent):
+ if settings.inferLiveRegions:
+ self.liveMngr.setLivePolitenessOff()
+ else:
+ # Translators: this announces to the user that live region
+ # support has been turned off.
#
- speech.speak(_("Wrapping to top."))
- if obj and found:
- nItems = 0
- for child in obj:
- if child.getRole() == pyatspi.ROLE_LIST_ITEM:
- nItems += 1
- # Translators: this represents a list in HTML.
- #
- itemString = ngettext("List with %d item",
- "List with %d items",
- nItems) % nItems
- speech.speak(itemString)
- nestingLevel = 0
- parent = obj.parent
- while parent.getRole() == pyatspi.ROLE_LIST:
- nestingLevel += 1
- parent = parent.parent
- if nestingLevel:
- # Translators: this represents a list item in HTML.
- # The nesting level is how 'deep' the item is (e.g.,
- # a level of 2 represents a list item inside a list
- # that's inside another list).
- #
- speech.speak(_("Nesting level %d") % nestingLevel)
- [obj, characterOffset] = self.findFirstCaretContext(obj, 0)
- self.setCaretPosition(obj, characterOffset)
- self.presentLine(obj, characterOffset)
+ speech.speak(_("Live region support is off"))
+ def reviewLiveAnnouncement(self, inputEvent):
+ if settings.inferLiveRegions:
+ self.liveMngr.reviewLiveAnnouncement( \
+ int(inputEvent.event_string[1:]))
else:
- # Translators: this is for navigating HTML content by moving
- # from bulleted/numbered list to bulleted/numbered list.
+ # Translators: this announces to the user that live region
+ # support has been turned off.
#
- speech.speak(_("No more lists."))
-
- def goPreviousListItem(self, inputEvent):
- """Go to the previous item in an (un)ordered list."""
- [obj, characterOffset] = self.getCaretContext()
- found = False
- wrap = True
- success = False
-
- # Try to find it using Collection first
- if settings.useCollection:
- try:
- startobj = obj
- col = self.getDocumentFrame().queryCollection()
- stateset = pyatspi.StateSet()
- stateset.add(pyatspi.STATE_FOCUSABLE)
- rule = col.createMatchRule(stateset.raw(), col.MATCH_NONE,
- "", col.MATCH_ANY,
- [pyatspi.ROLE_LIST_ITEM], col.MATCH_ANY,
- "", col.MATCH_ALL,
- False)
- [obj, wrapped] = self.findPrevByMatchRule(col, rule, True, obj)
- if obj and obj != startobj:
- found = True
- else:
- found = False
- success = True
- col.freeMatchRule(rule)
- except NotImplementedError:
- debug.printException(debug.LEVEL_SEVERE)
- except:
- debug.printException(debug.LEVEL_SEVERE)
- col.freeMatchRule(rule)
+ speech.speak(_("Live region support is off"))
- # Do it iteratively when Collection failed or is disabled
- if not success or not settings.useCollection:
- while obj and not found:
- [obj, wrapped] = \
- self.findPreviousRole([pyatspi.ROLE_LIST_ITEM], wrap, obj)
- # We should only wrap if we haven't already done so.
- #
- if wrapped:
- wrap = False
-
- # We need to be sure that the list item in question is the
- # child of an (un)ordered list rather than a list in a form
- # field. Form field list items are focusable; (un)ordered
- # list items are not.
- #
- if obj and \
- not (obj.getState().contains(pyatspi.STATE_FOCUSABLE)):
- found = True
+ def toggleCaretNavigation(self, inputEvent):
+ """Toggles between Firefox native and Orca caret navigation."""
- if wrapped:
- # Translators: when the user is attempting to locate a
- # particular object and the top of the web page has been
- # reached without that object being found, we "wrap" to
- # the bottom of the page and continuing looking upwards.
- # We need to inform the user when this is taking place.
+ if script_settings.controlCaretNavigation:
+ for keyBinding in self.__getArrowBindings().keyBindings:
+ self.keyBindings.removeByHandler(keyBinding.handler)
+ script_settings.controlCaretNavigation = False
+ # Translators: Gecko native caret navigation is where
+ # Firefox itself controls how the arrow keys move the caret
+ # around HTML content. It's often broken, so Orca needs
+ # to provide its own support. As such, Orca offers the user
+ # the ability to switch between the Firefox mode and the
+ # Orca mode.
#
- speech.speak(_("Wrapping to bottom."))
- if obj and found:
- [obj, characterOffset] = self.findFirstCaretContext(obj, 0)
- self.setCaretPosition(obj, characterOffset)
- self.presentLine(obj, characterOffset)
+ string = _("Gecko is controlling the caret.")
else:
- # Translators: this is for navigating HTML content by
- # moving from bulleted/numbered list item to
- # bulleted/numbered list item.
+ script_settings.controlCaretNavigation = True
+ for keyBinding in self.__getArrowBindings().keyBindings:
+ self.keyBindings.add(keyBinding)
+ # Translators: Gecko native caret navigation is where
+ # Firefox itself controls how the arrow keys move the caret
+ # around HTML content. It's often broken, so Orca needs
+ # to provide its own support. As such, Orca offers the user
+ # the ability to switch between the Firefox mode and the
+ # Orca mode.
#
- speech.speak(_("No more list items."))
-
- def goNextListItem(self, inputEvent):
- """Go to the next item in an (un)ordered list."""
- [obj, characterOffset] = self.getCaretContext()
- found = False
- wrap = True
- success = False
-
- # Try to find it using Collection first
- if settings.useCollection:
- try:
- startobj = obj
- col = self.getDocumentFrame().queryCollection()
- stateset = pyatspi.StateSet()
- stateset.add(pyatspi.STATE_FOCUSABLE)
- rule = col.createMatchRule(stateset.raw(), col.MATCH_NONE,
- "", col.MATCH_ANY,
- [pyatspi.ROLE_LIST_ITEM], col.MATCH_ANY,
- "", col.MATCH_ALL,
- False)
- [obj, wrapped] = self.findNextByMatchRule(col, rule, True, obj)
- if obj and obj != startobj:
- found = True
- else:
- found = False
- success = True
- col.freeMatchRule(rule)
- except NotImplementedError:
- debug.printException(debug.LEVEL_SEVERE)
- except:
- debug.printException(debug.LEVEL_SEVERE)
- col.freeMatchRule(rule)
+ string = _("Orca is controlling the caret.")
- # Do it iteratively when Collection failed or is disabled
- if not success or not settings.useCollection:
- while obj and not found:
- [obj, wrapped] = \
- self.findNextRole([pyatspi.ROLE_LIST_ITEM], wrap, obj)
- # We should only wrap if we haven't already done so.
- #
- if wrapped:
- wrap = False
-
- # We need to be sure that the list item in question is the
- # child of an (un)ordered list rather than a list in a form
- # field. Form field list items are focusable; (un)ordered
- # list items are not.
- #
- if obj and \
- not (obj.getState().contains(pyatspi.STATE_FOCUSABLE)):
- found = True
+ debug.println(debug.LEVEL_CONFIGURATION, string)
+ speech.speak(string)
+ braille.displayMessage(string)
- if wrapped:
- # Translators: when the user is attempting to locate a
- # particular object and the bottom of the web page has been
- # reached without that object being found, we "wrap" to the
- # top of the page and continuing looking downwards. We need
- # to inform the user when this is taking place.
- #
- speech.speak(_("Wrapping to top."))
- if obj and found:
- [obj, characterOffset] = self.findFirstCaretContext(obj, 0)
- self.setCaretPosition(obj, characterOffset)
- self.presentLine(obj, characterOffset)
- else:
- # Translators: this is for navigating HTML content by
- # moving from bulleted/numbered list item to
- # bulleted/numbered list item.
- #
- speech.speak(_("No more list items."))
-
- def goPreviousUnvisitedLink(self, inputEvent):
- """Go to the previous unvisited link."""
-
- # If the currentObject has a link in its ancestry, we've
- # already started out on a link and need to move off of
- # it else we'll get stuck.
- #
- [obj, characterOffset] = self.getCaretContext()
- containingLink = self.getAncestor(obj,
- [pyatspi.ROLE_LINK],
- [pyatspi.ROLE_DOCUMENT_FRAME])
- if containingLink:
- obj = containingLink
+ def speakWordUnderMouse(self, acc):
+ """Determine if the speak-word-under-mouse capability applies to
+ the given accessible.
- success = False
- wrap = True
- found = False
+ Arguments:
+ - acc: Accessible to test.
- # Try to find it using Collection first
- if settings.useCollection:
+ Returns True if this accessible should provide the single word.
+ """
+ if self.inDocumentContent(acc):
try:
- startobj = obj
- col = self.getDocumentFrame().queryCollection()
- stateset = pyatspi.StateSet()
- stateset.add(pyatspi.STATE_VISITED)
- rule = col.createMatchRule(stateset.raw(), col.MATCH_NONE,
- "", col.MATCH_ANY,
- [pyatspi.ROLE_LINK], col.MATCH_ANY,
- "", col.MATCH_ALL,
- False)
- [obj, wrapped] = self.findPrevByMatchRule(col, rule, True, obj)
- if obj and obj != startobj:
- found = True
- else:
- found = False
- success = True
- col.freeMatchRule(rule)
- except NotImplementedError:
- debug.printException(debug.LEVEL_SEVERE)
- except:
- debug.printException(debug.LEVEL_SEVERE)
- col.freeMatchRule(rule)
-
- # Do it iteratively when Collection failed or is disabled
- if not success or not settings.useCollection:
-
- while obj and not found:
- [obj, wrapped] = \
- self.findPreviousRole([pyatspi.ROLE_LINK], wrap, obj)
- # We should only wrap if we haven't already done so.
- #
- if wrapped:
- wrap = False
- if obj and \
- not obj.getState().contains(pyatspi.STATE_VISITED):
- found = True
-
- if wrapped or not wrap:
- # Translators: when the user is attempting to locate a
- # particular object and the top of the web page has been
- # reached without that object being found, we "wrap" to
- # the bottom of the page and continuing looking upwards.
- # We need to inform the user when this is taking place.
- #
- speech.speak(_("Wrapping to bottom."))
- if obj and found:
- [obj, characterOffset] = self.findFirstCaretContext(obj, 0)
- self.setCaretPosition(obj, characterOffset)
- self.updateBraille(obj)
- self.speakContents(self.getObjectContentsAtOffset(obj,
- characterOffset))
- else:
- # Translators: this is for navigating HTML content by
- # moving from link to link.
- #
- speech.speak(_("No more unvisited links."))
-
- def goNextUnvisitedLink(self, inputEvent):
- """Go to the next unvisited link."""
- [obj, characterOffset] = self.getCaretContext()
-
- success = False
- wrap = True
- found = False
-
- # Try to find it using Collection first
- if settings.useCollection:
- try:
- startobj = obj
- col = self.getDocumentFrame().queryCollection()
- stateset = pyatspi.StateSet()
- stateset.add(pyatspi.STATE_VISITED)
- rule = col.createMatchRule(stateset.raw(), col.MATCH_NONE,
- "", col.MATCH_ANY,
- [pyatspi.ROLE_LINK], col.MATCH_ANY,
- "", col.MATCH_ALL,
- False)
- [obj, wrapped] = self.findNextByMatchRule(col, rule, True, obj)
- if obj and obj != startobj:
- found = True
- else:
- found = False
- success = True
- col.freeMatchRule(rule)
- except NotImplementedError:
- debug.printException(debug.LEVEL_SEVERE)
- except AttributeError:
- debug.printException(debug.LEVEL_SEVERE)
- col.freeMatchRule(rule)
-
- # Do it iteratively when Collection failed or is disabled
- if not success or not settings.useCollection:
- while obj and not found:
- [obj, wrapped] = \
- self.findNextRole([pyatspi.ROLE_LINK], wrap, obj)
- # We should only wrap if we haven't already done so.
- #
- if wrapped:
- wrap = False
- if obj and \
- not obj.getState().contains(pyatspi.STATE_VISITED):
- found = True
-
- if wrapped or not wrap:
- # Translators: when the user is attempting to locate a
- # particular object and the bottom of the web page has been
- # reached without that object being found, we "wrap" to the
- # top of the page and continuing looking downwards. We need
- # to inform the user when this is taking place.
- #
- speech.speak(_("Wrapping to top."))
- if obj and found:
- [obj, characterOffset] = self.findFirstCaretContext(obj, 0)
- self.setCaretPosition(obj, characterOffset)
- self.updateBraille(obj)
- self.speakContents(self.getObjectContentsAtOffset(obj,
- characterOffset))
- else:
- # Translators: this is for navigating HTML content by
- # moving from link to link.
- #
- speech.speak(_("No more unvisited links."))
-
- def goPreviousVisitedLink(self, inputEvent):
- """Go to the previous visited link."""
-
- # If the currentObject has a link in its ancestry, we've
- # already started out on a link and need to move off of
- # it else we'll get stuck.
- #
- [obj, characterOffset] = self.getCaretContext()
- containingLink = self.getAncestor(obj,
- [pyatspi.ROLE_LINK],
- [pyatspi.ROLE_DOCUMENT_FRAME])
- if containingLink:
- obj = containingLink
-
- success = False
- wrap = True
- found = False
-
- # Try to find it using Collection first
- if settings.useCollection:
- try:
- startobj = obj
- col = self.getDocumentFrame().queryCollection()
- stateset = pyatspi.StateSet()
- stateset.add(pyatspi.STATE_VISITED)
- rule = col.createMatchRule(stateset.raw(), col.MATCH_ANY,
- "", col.MATCH_ANY,
- [pyatspi.ROLE_LINK], col.MATCH_ANY,
- "", col.MATCH_ALL,
- False)
- [obj, wrapped] = self.findPrevByMatchRule(col, rule, True, obj)
- if obj and obj != startobj:
- found = True
- else:
- found = False
- success = True
- col.freeMatchRule(rule)
- except NotImplementedError:
- debug.printException(debug.LEVEL_SEVERE)
- except:
- debug.printException(debug.LEVEL_SEVERE)
- col.freeMatchRule(rule)
-
- # Do it iteratively when Collection failed or is disabled
- if not success or not settings.useCollection:
- while obj and not found:
- [obj, wrapped] = \
- self.findPreviousRole([pyatspi.ROLE_LINK], wrap, obj)
- # We should only wrap if we haven't already done so.
- #
- if wrapped:
- wrap = False
- if obj and \
- obj.getState().contains(pyatspi.STATE_VISITED):
- found = True
-
- if wrapped or not wrap:
- # Translators: when the user is attempting to locate a
- # particular object and the top of the web page has been
- # reached without that object being found, we "wrap" to
- # the bottom of the page and continuing looking upwards.
- # We need to inform the user when this is taking place.
- #
- speech.speak(_("Wrapping to bottom."))
- if obj and found:
- [obj, characterOffset] = self.findFirstCaretContext(obj, 0)
- self.setCaretPosition(obj, characterOffset)
- self.updateBraille(obj)
- self.speakContents(self.getObjectContentsAtOffset(obj,
- characterOffset))
- else:
- # Translators: this is for navigating HTML content by
- # moving from link to link.
- #
- speech.speak(_("No more visited links."))
-
- def goNextVisitedLink(self, inputEvent):
- """Go to the next visited link."""
- [obj, characterOffset] = self.getCaretContext()
-
- success = False
- found = False
- wrap = True
-
- # Try to find it using Collection first
- if settings.useCollection:
- try:
- startobj = obj
- col = self.getDocumentFrame().queryCollection()
- stateset = pyatspi.StateSet()
- stateset.add(pyatspi.STATE_VISITED)
- rule = col.createMatchRule(stateset.raw(), col.MATCH_ANY,
- "", col.MATCH_ANY,
- [pyatspi.ROLE_LINK], col.MATCH_ANY,
- "", col.MATCH_ALL,
- False)
- [obj, wrapped] = self.findNextByMatchRule(col, rule, True, obj)
- if obj and obj != startobj:
- found = True
- else:
- found = False
- success = True
- col.freeMatchRule(rule)
- except NotImplementedError:
- debug.printException(debug.LEVEL_SEVERE)
- except:
- debug.printException(debug.LEVEL_SEVERE)
- col.freeMatchRule(rule)
-
- # Do it iteratively when Collection failed or is disabled
- if not success or not settings.useCollection:
- while obj and not found:
- [obj, wrapped] = \
- self.findNextRole([pyatspi.ROLE_LINK], wrap, obj)
- # We should only wrap if we haven't already done so.
- #
- if wrapped:
- wrap = False
-
- if obj and \
- obj.getState().contains(pyatspi.STATE_VISITED):
- found = True
-
- if wrapped or not wrap:
- # Translators: when the user is attempting to locate a
- # particular object and the bottom of the web page has been
- # reached without that object being found, we "wrap" to the
- # top of the page and continuing looking downwards. We need
- # to inform the user when this is taking place.
- #
- speech.speak(_("Wrapping to top."))
- if obj and found:
- [obj, characterOffset] = self.findFirstCaretContext(obj, 0)
- self.setCaretPosition(obj, characterOffset)
- self.updateBraille(obj)
- self.speakContents(self.getObjectContentsAtOffset(obj,
- characterOffset))
- else:
- # Translators: this is for navigating HTML content by
- # moving from link to link.
- #
- speech.speak(_("No more visited links."))
-
- def goPreviousBlockquote(self, inputEvent):
- """Go to the previous blockquote."""
-
- # If the current object has a blockquote in its ancestry, we
- # need to move out of it else we'll get stuck.
- #
- [obj, characterOffset] = self.getCaretContext()
- candidate = obj
- while candidate and (candidate != candidate.parent) and \
- (candidate.getRole() != pyatspi.ROLE_DOCUMENT_FRAME):
- if self.isBlockquote(candidate):
- obj = candidate
- break
- else:
- candidate = candidate.parent
-
- # Try to find it using Collection first
- success = False
- if settings.useCollection:
- try:
- startobj = obj
- found = False
- col = self.getDocumentFrame().queryCollection()
- stateset = pyatspi.StateSet()
- rule = col.createMatchRule(stateset.raw(), col.MATCH_ANY,
- ['tag:BLOCKQUOTE'], col.MATCH_ANY,
- "", col.MATCH_ANY,
- "", col.MATCH_ALL,
- False)
- [obj, wrapped] = self.findPrevByMatchRule(col, rule, True, obj)
- if obj != startobj:
- found = True
- success = True
- col.freeMatchRule(rule)
- except NotImplementedError:
- debug.printException(debug.LEVEL_SEVERE)
- except:
- debug.printException(debug.LEVEL_SEVERE)
- col.freeMatchRule(rule)
-
- # Do it iteratively when Collection failed or is disabled
- if not success or not settings.useCollection:
- currentObj = obj
- found = False
- wrapped = False
- wrap = True
- while obj and not found:
- obj = self.findPreviousObject(obj)
- if not obj and wrap and not wrapped:
- obj = self.getLastObject()
- wrapped = True
- if obj and self.isBlockquote(obj) and \
- not self.isSameObject(currentObj, obj):
- found = True
-
- if wrapped:
- # Translators: when the user is attempting to locate a
- # particular object and the top of the web page has been
- # reached without that object being found, we "wrap" to
- # the bottom of the page and continuing looking upwards.
- # We need to inform the user when this is taking place.
- #
- speech.speak(_("Wrapping to bottom."))
- if obj and found:
- [obj, characterOffset] = self.findFirstCaretContext(obj, 0)
- self.setCaretPosition(obj, characterOffset)
- self.presentLine(obj, characterOffset)
- else:
- # Translators: this is for navigating HTML content by
- # moving from blockquote to blockquote.
- #
- speech.speak(_("No more blockquotes."))
-
- def goNextBlockquote(self, inputEvent):
- """Go to the next blockquote."""
- [obj, characterOffset] = self.getCaretContext()
-
- # get the ancestors because setCaretPosition may drop us lower
- # in the tree. This is used to check that the start object is not
- # the same as the found object.
- ancestors = []
- ancestor = obj
- while ancestor:
- ancestors.append(ancestor)
- ancestor = ancestor.parent
-
- # Try to find it using Collection first
- success = False
- if settings.useCollection:
- try:
- startobj = obj
- found = False
- col = self.getDocumentFrame().queryCollection()
- stateset = pyatspi.StateSet()
- rule = col.createMatchRule(stateset.raw(), col.MATCH_ANY,
- ['tag:BLOCKQUOTE'], col.MATCH_ANY,
- "", col.MATCH_ANY,
- "", col.MATCH_ALL,
- False)
- [obj, wrapped] = self.findNextByMatchRule(col, rule, True, obj)
- if obj and obj not in ancestors:
- found = True
- success = True
- col.freeMatchRule(rule)
- except NotImplementedError:
- debug.printException(debug.LEVEL_SEVERE)
- except:
- debug.printException(debug.LEVEL_SEVERE)
- col.freeMatchRule(rule)
-
- # Do it iteratively when Collection failed or is disabled
- if not success or not settings.useCollection:
- currentObj = obj
- found = False
- wrapped = False
- wrap = True
- while obj and not found:
- obj = self.findNextObject(obj)
- if not obj and wrap and not wrapped:
- documentFrame = self.getDocumentFrame()
- obj = documentFrame[0]
- wrapped = True
- if obj and self.isBlockquote(obj) and \
- not self.isSameObject(currentObj, obj):
- found = True
-
- if wrapped:
- # Translators: when the user is attempting to locate a
- # particular object and the bottom of the web page has been
- # reached without that object being found, we "wrap" to the
- # top of the page and continuing looking downwards. We need
- # to inform the user when this is taking place.
- #
- speech.speak(_("Wrapping to top."))
- if obj and found:
- [obj, characterOffset] = self.findFirstCaretContext(obj, 0)
- self.setCaretPosition(obj, characterOffset)
- self.presentLine(obj, characterOffset)
- else:
- # Translators: this is for navigating HTML content by
- # moving from blockquote to blockquote.
- #
- speech.speak(_("No more blockquotes."))
-
- def goPreviousFormField(self, inputEvent):
- """Go to the previous form field."""
-
- # If the current object is a list item in a form field, we
- # need to move up to the parent list before we search
- # for the previous list; otherwise we'll find the current
- # list first.
- #
- [obj, characterOffset] = self.getCaretContext()
- currentObj = obj
- if obj.getRole() == pyatspi.ROLE_LIST_ITEM and \
- obj.getState().contains(pyatspi.STATE_FOCUSABLE):
- obj = obj.parent
-
- found = False
- wrapped = False
- wrap = True
- while obj and not found:
- obj = self.findPreviousObject(obj)
- if not obj and wrap and not wrapped:
- obj = self.getLastObject()
- wrapped = True
- if obj:
- # For performance purposes, we do not want to work
- # our way up through list items in search of the
- # parent list.
- #
- if obj.getRole() == pyatspi.ROLE_LIST_ITEM:
- obj = obj.parent
- found = self.isFormField(obj)
- if wrapped and self.isSameObject(currentObj, obj):
- obj = None
- if wrapped:
- # Translators: when the user is attempting to locate a
- # particular object and the top of the web page has been
- # reached without that object being found, we "wrap" to
- # the bottom of the page and continuing looking upwards.
- # We need to inform the user when this is taking place.
- #
- speech.speak(_("Wrapping to bottom."))
- if obj:
- [obj, characterOffset] = self.findFirstCaretContext(obj, 0)
- # We actively avoid grabbing focus on lists in HTML forms
- # in setCaretPosition() so that a user doesn't accidentally
- # arrow into one and change its value. Here we actually
- # do want to grab focus should we be on a list or combo box.
- #
- if obj.getRole() in [pyatspi.ROLE_LIST, pyatspi.ROLE_COMBO_BOX]:
- obj.queryComponent().grabFocus()
- else:
- self.setCaretPosition(obj, characterOffset)
- self.updateBraille(obj)
- self.speakContents(self.getObjectContentsAtOffset(obj,
- characterOffset))
- else:
- # Translators: this is for navigating HTML content by
- # moving from form field to form field.
- #
- speech.speak(_("No more form fields."))
-
- def goNextFormField(self, inputEvent):
- """Go to the next form field."""
- [obj, characterOffset] = self.getCaretContext()
- currentObj = obj
- found = False
- wrapped = False
- wrap = True
- # For performance purposes, we do not want to examine each item
- # in the current combo box or list via findNextObject.
- #
- if obj.getRole() == pyatspi.ROLE_LIST_ITEM \
- and obj.getState().contains(pyatspi.STATE_FOCUSABLE):
- obj = obj.parent
- if obj.getRole() == pyatspi.ROLE_LIST \
- and obj.getState().contains(pyatspi.STATE_FOCUSABLE):
- obj = obj[obj.childCount - 1]
- elif obj.getRole() == pyatspi.ROLE_COMBO_BOX:
- menu = obj[0]
- obj = menu[menu.childCount - 1]
-
- while obj and not found:
- obj = self.findNextObject(obj)
- if not obj and wrap and not wrapped:
- documentFrame = self.getDocumentFrame()
- obj = documentFrame[0]
- wrapped = True
- if obj:
- found = self.isFormField(obj)
- if wrapped and self.isSameObject(currentObj, obj):
- obj = None
- if wrapped:
- # Translators: when the user is attempting to locate a
- # particular object and the bottom of the web page has been
- # reached without that object being found, we "wrap" to the
- # top of the page and continuing looking downwards. We need
- # to inform the user when this is taking place.
- #
- speech.speak(_("Wrapping to top."))
- if obj:
- [obj, characterOffset] = self.findFirstCaretContext(obj, 0)
-
- # We actively avoid grabbing focus on lists in HTML forms
- # in setCaretPosition() so that a user doesn't accidentally
- # arrow into one and change its value. Here we actually
- # do want to grab focus should we be on a list or combo box.
- #
- if obj.getRole() in [pyatspi.ROLE_LIST, pyatspi.ROLE_COMBO_BOX]:
- obj.queryComponent().grabFocus()
- else:
- self.setCaretPosition(obj, characterOffset)
- self.updateBraille(obj)
- self.speakContents(self.getObjectContentsAtOffset(obj,
- characterOffset))
- else:
- # Translators: this is for navigating HTML content by
- # moving from form field to form field.
- #
- speech.speak(_("No more form fields."))
-
- def moveToCell(self, obj):
- """Move to the specified cell in an HTML table.
-
- Arguments:
- - obj: the table cell to move to.
- """
- spanString = self.getCellSpanInfo (obj)
- blank = self.isBlankCell(obj)
- [obj, characterOffset] = self.findFirstCaretContext(obj, 0)
- self.setCaretPosition(obj, characterOffset)
- self.updateBraille(obj)
- if not blank:
- self.speakContents(self.getObjectContentsAtOffset(obj,
- characterOffset))
- else:
- # Translators: "blank" is a short word to mean the
- # user has navigated to an empty line.
- #
- speech.speak(_("blank"))
-
- if script_settings.speakCellCoordinates:
- [row, col] = self.getCellCoordinates(obj)
- # Translators: this represents the (row, col) position of
- # a cell in a table.
- #
- speech.speak(_("Row %d, column %d.") % (row + 1, col + 1))
-
- if spanString and script_settings.speakCellSpan:
- speech.speak(spanString)
-
- def goPreviousTable(self, inputEvent):
- """Go to the previous table."""
- wrap = True
- [obj, wrapped] = self.findPreviousRole([pyatspi.ROLE_TABLE], wrap)
- if wrapped:
- # Translators: when the user is attempting to locate a
- # particular object and the top of the web page has been
- # reached without that object being found, we "wrap" to
- # the bottom of the page and continuing looking upwards.
- # We need to inform the user when this is taking place.
- #
- speech.speak(_("Wrapping to bottom."))
- if obj:
- table = obj.queryTable()
- caption = self.getTableCaption(obj)
- if self.queryNonEmptyText(caption):
- text = self.getDisplayedText(caption)
- speech.speak(text)
- nonUniformString = ""
- nonUniform = self.isNonUniformTable(obj)
- if nonUniform:
- # Translators: a uniform table is one in which each table
- # cell occupies one row and one column (i.e. a perfect grid)
- # In contrast, a non-uniform table is one in which at least
- # one table cell occupies more than one row and/or column.
- #
- nonUniformString = _("Non-uniform")
- nRows = table.nRows
- nColumns = table.nColumns
- # Translators: this represents the number of rows in an HTML table.
- #
- rowString = ngettext("Table with %d row",
- "Table with %d rows",
- nRows) % nRows
- # Translators: this represents the number of cols in an HTML table.
- #
- colString = ngettext("%d column",
- "%d columns",
- nColumns) % nColumns
- speech.speak(nonUniformString + " " + rowString + " " + colString)
-
- obj = table.getAccessibleAt(0, 0)
- self.moveToCell(obj)
-
- else:
- # Translators: this is for navigating HTML content by
- # moving from table to table.
- #
- speech.speak(_("No more tables."))
-
- def goNextTable(self, inputEvent):
- """Go to the next table."""
- wrap = True
- [obj, wrapped] = self.findNextRole([pyatspi.ROLE_TABLE], wrap)
- if wrapped:
- # Translators: when the user is attempting to locate a
- # particular object and the bottom of the web page has been
- # reached without that object being found, we "wrap" to the
- # top of the page and continuing looking downwards. We need
- # to inform the user when this is taking place.
- #
- speech.speak(_("Wrapping to top."))
- if obj:
- table = obj.queryTable()
- caption = self.getTableCaption(obj)
- if self.queryNonEmptyText(caption):
- text = self.getDisplayedText(caption)
- speech.speak(text)
- nonUniformString = ""
- nonUniform = self.isNonUniformTable(obj)
- if nonUniform:
- # Translators: a uniform table is one in which each table
- # cell occupies one row and one column (i.e. a perfect grid)
- # In contrast, a non-uniform table is one in which at least
- # one table cell occupies more than one row and/or column.
- #
- nonUniformString = _("Non-uniform")
- nRows = table.nRows
- nColumns = table.nColumns
- # Translators: this represents the number of rows in an HTML table.
- #
- rowString = ngettext("Table with %d row",
- "Table with %d rows",
- nRows) % nRows
- # Translators: this represents the number of cols in an HTML table.
- #
- colString = ngettext("%d column",
- "%d columns",
- nColumns) % nColumns
- speech.speak(nonUniformString + " " + rowString + " " + colString)
- obj = table.getAccessibleAt(0, 0)
- self.moveToCell(obj)
-
- else:
- # Translators: this is for navigating HTML content by
- # moving from table to table.
- #
- speech.speak(_("No more tables."))
-
- def goCellLeft(self, inputEvent):
- """Move to the cell on the left in an HTML table."""
- [obj, characterOffset] = self.getCaretContext()
- if obj.getRole() != pyatspi.ROLE_TABLE_CELL:
- obj = self.getAncestor(obj,
- [pyatspi.ROLE_TABLE_CELL],
- [pyatspi.ROLE_DOCUMENT_FRAME])
- if obj:
- [row, col] = self.getCellCoordinates(obj)
- oldHeaders = self.getColumnHeaders(obj)
- table = obj.parent.queryTable()
- if self.isSameCell(obj.parent,
- [row, col],
- self.lastTableCell):
- # The stored row helps us maintain the correct position
- # when traversing cells that span multiple rows.
- #
- row = self.lastTableCell[0]
- found = False
- while not found and col > 0:
- obj = table.getAccessibleAt(row, col - 1)
- self.lastTableCell = [row, col - 1]
- if not self.isBlankCell(obj) or \
- not script_settings.skipBlankCells:
- found = True
- else:
- col -= 1
-
- if found:
- # We only want to speak the header information that has
- # changed, and we don't want to speak headers if we're in
- # a header column.
- #
- if script_settings.speakCellHeaders \
- and not self.isInHeaderColumn(obj):
- colHeaders = self.getColumnHeaders(obj)
- for header in colHeaders:
- if not header in oldHeaders:
- text = self.getDisplayedText(header)
- speech.speak(text)
- self.moveToCell(obj)
- else:
- # Translators: this is for navigating HTML content by
- # moving from table cell to table cell.
- #
- speech.speak(_("Beginning of row."))
- else:
- # Translators: this is for navigating HTML content by
- # moving from table cell to table cell.
- #
- speech.speak(_("Not in a table."))
-
- def goCellRight(self, inputEvent):
- """Move to the cell on the right in an HTML table."""
- [obj, characterOffset] = self.getCaretContext()
- if obj.getRole() != pyatspi.ROLE_TABLE_CELL:
- obj = self.getAncestor(obj,
- [pyatspi.ROLE_TABLE_CELL],
- [pyatspi.ROLE_DOCUMENT_FRAME])
- if obj:
- [row, col] = self.getCellCoordinates(obj)
- oldHeaders = self.getColumnHeaders(obj)
- table = obj.parent.queryTable()
- if self.isSameCell(obj.parent,
- [row, col],
- self.lastTableCell):
- # The stored row helps us maintain the correct position
- # when traversing cells that span multiple rows.
- #
- row = self.lastTableCell[0]
- colspan = table.getColumnExtentAt(row, col)
- nextCol = col + colspan
- found = False
- while not found and (nextCol <= table.nColumns - 1):
- obj = table.getAccessibleAt(row, nextCol)
- self.lastTableCell = [row, nextCol]
- if not self.isBlankCell(obj) or \
- not script_settings.skipBlankCells:
- found = True
- else:
- col += 1
- colspan = table.getColumnExtentAt(row, col)
- nextCol = col + colspan
-
- if found:
- # We only want to speak the header information that has
- # changed, and we don't want to speak headers if we're in
- # a header column.
- #
- if script_settings.speakCellHeaders \
- and not self.isInHeaderColumn(obj):
- colHeaders = self.getColumnHeaders(obj)
- for header in colHeaders:
- if not header in oldHeaders:
- text = self.getDisplayedText(header)
- speech.speak(text)
- self.moveToCell(obj)
- else:
- # Translators: this is for navigating HTML content by
- # moving from table cell to table cell.
- #
- speech.speak(_("End of row."))
- else:
- # Translators: this is for navigating HTML content by
- # moving from table cell to table cell.
- #
- speech.speak(_("Not in a table."))
-
- def goCellUp(self, inputEvent):
- """Move one cell up in an HTML table."""
- [obj, characterOffset] = self.getCaretContext()
- if obj.getRole() != pyatspi.ROLE_TABLE_CELL:
- obj = self.getAncestor(obj,
- [pyatspi.ROLE_TABLE_CELL],
- [pyatspi.ROLE_DOCUMENT_FRAME])
- if obj:
- [row, col] = self.getCellCoordinates(obj)
- oldHeaders = self.getRowHeaders(obj)
- table = obj.parent.queryTable()
- if self.isSameCell(obj.parent,
- [row, col],
- self.lastTableCell):
- # The stored column helps us maintain the correct position
- # when traversing cells that span multiple columns.
- #
- col = self.lastTableCell[1]
- found = False
- while not found and row > 0:
- obj = table.getAccessibleAt(row - 1, col)
- self.lastTableCell = [row - 1, col]
- if not self.isBlankCell(obj) or \
- not script_settings.skipBlankCells:
- found = True
- else:
- row -= 1
-
- if found:
- # We only want to speak the header information that has
- # changed, and we don't want to speak headers if we're in
- # a header row.
- #
- if script_settings.speakCellHeaders \
- and not self.isInHeaderRow(obj):
- rowHeaders = self.getRowHeaders(obj)
- for header in rowHeaders:
- if not header in oldHeaders:
- text = self.getDisplayedText(header)
- speech.speak(text)
- self.moveToCell(obj)
- else:
- # Translators: this is for navigating HTML content by
- # moving from table cell to table cell.
- #
- speech.speak(_("Top of column."))
- else:
- # Translators: this is for navigating HTML content by
- # moving from table cell to table cell.
- #
- speech.speak(_("Not in a table."))
-
- def goCellDown(self, inputEvent):
- """Move one cell down in an HTML table."""
- [obj, characterOffset] = self.getCaretContext()
- if obj.getRole() != pyatspi.ROLE_TABLE_CELL:
- obj = self.getAncestor(obj,
- [pyatspi.ROLE_TABLE_CELL],
- [pyatspi.ROLE_DOCUMENT_FRAME])
- if obj:
- [row, col] = self.getCellCoordinates(obj)
- oldHeaders = self.getRowHeaders(obj)
- table = obj.parent.queryTable()
- if self.isSameCell(obj.parent,
- [row, col],
- self.lastTableCell):
- # The stored column helps us maintain the correct position
- # when traversing cells that span multiple columns.
- #
- col = self.lastTableCell[1]
- rowspan = table.getRowExtentAt(row, col)
- nextRow = row + rowspan
- found = False
- while not found and (nextRow <= table.nRows - 1):
- obj = table.getAccessibleAt(nextRow, col)
- self.lastTableCell = [nextRow, col]
- if not self.isBlankCell(obj) or \
- not script_settings.skipBlankCells:
- found = True
- else:
- row += 1
- rowspan = table.getRowExtentAt(row, col)
- nextRow = row + rowspan
-
- if found:
- # We only want to speak the header information that has
- # changed, and we don't want to speak headers if we're in
- # a header row.
- #
- if script_settings.speakCellHeaders \
- and not self.isInHeaderRow(obj):
- rowHeaders = self.getRowHeaders(obj)
- for header in rowHeaders:
- if not header in oldHeaders:
- text = self.getDisplayedText(header)
- speech.speak(text)
- self.moveToCell(obj)
- else:
- # Translators: this is for navigating HTML content by
- # moving from table cell to table cell.
- #
- speech.speak(_("Bottom of column."))
- else:
- # Translators: this is for navigating HTML content by
- # moving from table cell to table cell.
- #
- speech.speak(_("Not in a table."))
-
- def goCellFirst(self, inputEvent):
- """Move to the first cell in an HTML table."""
- [obj, characterOffset] = self.getCaretContext()
- if obj.getRole() != pyatspi.ROLE_TABLE:
- obj = self.getAncestor(obj,
- [pyatspi.ROLE_TABLE],
- [pyatspi.ROLE_DOCUMENT_FRAME])
- if obj:
- obj = obj.queryTable().getAccessibleAt(0, 0)
- self.lastTableCell = [0, 0]
- self.moveToCell(obj)
- else:
- # Translators: this is for navigating HTML content by
- # moving from table cell to table cell.
- #
- speech.speak(_("Not in a table."))
-
- def goCellLast(self, inputEvent):
- """Move to the last cell in an HTML table."""
- [obj, characterOffset] = self.getCaretContext()
- if obj.getRole() != pyatspi.ROLE_TABLE:
- obj = self.getAncestor(obj,
- [pyatspi.ROLE_TABLE],
- [pyatspi.ROLE_DOCUMENT_FRAME])
- if obj:
- table = obj.queryTable()
- lastRow = table.nRows - 1
- lastCol = table.nColumns - 1
- obj = table.getAccessibleAt(lastRow, lastCol)
- self.lastTableCell = [lastRow, lastCol]
- self.moveToCell(obj)
- else:
- # Translators: this is for navigating HTML content by
- # moving from table cell to table cell.
- #
- speech.speak(_("Not in a table."))
-
- def goNextLiveRegion(self, inputEvent):
- # First, get any live regions that have been registered as LIVE_NONE
- # because there is no markup to test for these but we still want to
- # find them
- regobjs = self.liveMngr.getLiveNoneObjects()
- # define our search predicate
- pred = lambda obj: (self.liveMngr.matchLiveRegion(obj) \
- or obj in regobjs)
- # start looking
- wrap = True
- [obj, wrapped] = \
- self.findNextByPredicate(pred, wrap)
- if wrapped:
- # Translators: when the user is attempting to locate a
- # particular object and the bottom of the web page has been
- # reached without that object being found, we "wrap" to the
- # top of the page and continuing looking downwards. We need
- # to inform the user when this is taking place.
- #
- speech.speak(_("Wrapping to top."))
- if obj:
- # TODO: We don't want to move to a list item.
- # Is this the best place to handle this?
- if obj.getRole() == pyatspi.ROLE_LIST:
- characterOffset = 0
- obj = obj[0]
- else:
- [obj, characterOffset] = self.findFirstCaretContext(obj, 0)
- self.setCaretPosition(obj, characterOffset)
- self.updateBraille(obj)
- # For debugging
- self.outlineAccessible(obj)
- self.speakContents(self.getObjectContentsAtOffset(obj,
- characterOffset))
- else:
- # Translators: this is for navigating HTML in a structural
- # manner, where a 'live region' is a location in a web page
- # that are updated without having to refresh the entire page.
- #
- speech.speak(_("No more live regions."))
-
- def goPreviousLiveRegion(self, inputEvent):
- # First, get any live regions that have been registered as LIVE_NONE
- # because there is no markup to test for these but we still want to
- # find them
- regobjs = self.liveMngr.getLiveNoneObjects()
- # define our search predicate
- pred = lambda obj: (self.liveMngr.matchLiveRegion(obj) \
- or obj in regobjs)
- # start looking
- wrap = True
- [obj, wrapped] = \
- self.findPrevByPredicate(pred, wrap)
- if wrapped:
- # Translators: when the user is attempting to locate a
- # particular object and the bottom of the web page has been
- # reached without that object being found, we "wrap" to the
- # top of the page and continuing looking downwards. We need
- # to inform the user when this is taking place.
- #
- speech.speak(_("Wrapping to top."))
- if obj:
- # TODO: We don't want to move to a list item.
- # Is this the best place to handle this?
- if obj.getRole() == pyatspi.ROLE_LIST:
- characterOffset = 0
- else:
- [obj, characterOffset] = self.findFirstCaretContext(obj, 0)
- self.setCaretPosition(obj, characterOffset)
- self.updateBraille(obj)
- self.outlineAccessible(obj)
- self.speakContents(self.getObjectContentsAtOffset(obj,
- characterOffset))
- else:
- # Translators: this is for navigating HTML in a structural
- # manner, where a 'live region' is a location in a web page
- # that are updated without having to refresh the entire page.
- #
- speech.speak(_("No more live regions."))
-
- def goLastLiveRegion(self, inputEvent):
- if settings.inferLiveRegions:
- self.liveMngr.goLastLiveRegion()
- else:
- # Translators: this announces to the user that live region
- # support has been turned off.
- #
- speech.speak(_("Live region support is off"))
-
- def advanceLivePoliteness(self, inputEvent):
- """Advances live region politeness level."""
- if settings.inferLiveRegions:
- self.liveMngr.advancePoliteness(orca_state.locusOfFocus)
- else:
- # Translators: this announces to the user that live region
- # support has been turned off.
- #
- speech.speak(_("Live region support is off"))
-
- def monitorLiveRegions(self, inputEvent):
- if not settings.inferLiveRegions:
- settings.inferLiveRegions = True
- # Translators: this announces to the user that live region
- # are being monitored.
- #
- speech.speak(_("Live regions monitoring on"))
- else:
- settings.inferLiveRegions = False
- # Translators: this announces to the user that live region
- # are not being monitored.
- #
- self.liveMngr.flushMessages()
- speech.speak(_("Live regions monitoring off"))
-
- def setLivePolitenessOff(self, inputEvent):
- if settings.inferLiveRegions:
- self.liveMngr.setLivePolitenessOff()
- else:
- # Translators: this announces to the user that live region
- # support has been turned off.
- #
- speech.speak(_("Live region support is off"))
-
- def reviewLiveAnnouncement(self, inputEvent):
- if settings.inferLiveRegions:
- self.liveMngr.reviewLiveAnnouncement( \
- int(inputEvent.event_string[1:]))
- else:
- # Translators: this announces to the user that live region
- # support has been turned off.
- #
- speech.speak(_("Live region support is off"))
-
- def toggleCaretNavigation(self, inputEvent):
- """Toggles between Firefox native and Orca caret navigation."""
-
- if script_settings.controlCaretNavigation:
- for keyBinding in self.__getArrowBindings().keyBindings:
- self.keyBindings.removeByHandler(keyBinding.handler)
- script_settings.controlCaretNavigation = False
- # Translators: Gecko native caret navigation is where
- # Firefox itself controls how the arrow keys move the caret
- # around HTML content. It's often broken, so Orca needs
- # to provide its own support. As such, Orca offers the user
- # the ability to switch between the Firefox mode and the
- # Orca mode.
- #
- string = _("Gecko is controlling the caret.")
- else:
- script_settings.controlCaretNavigation = True
- for keyBinding in self.__getArrowBindings().keyBindings:
- self.keyBindings.add(keyBinding)
- # Translators: Gecko native caret navigation is where
- # Firefox itself controls how the arrow keys move the caret
- # around HTML content. It's often broken, so Orca needs
- # to provide its own support. As such, Orca offers the user
- # the ability to switch between the Firefox mode and the
- # Orca mode.
- #
- string = _("Orca is controlling the caret.")
-
- debug.println(debug.LEVEL_CONFIGURATION, string)
- speech.speak(string)
- braille.displayMessage(string)
-
- def toggleStructuralNavigation(self, inputEvent):
- """Toggles structural navigation keys."""
-
- script_settings.structuralNavigationEnabled = \
- not script_settings.structuralNavigationEnabled
-
- if script_settings.structuralNavigationEnabled:
- # Translators: the structural navigation keys are designed
- # to move the caret around the HTML content by object type.
- # Thus H moves you to the next heading, Shift H to the
- # previous heading, T to the next table, and so on. Some
- # users prefer to turn this off to use Firefox's search
- # when typing feature. This message is sent to both the
- # braille display and the speech synthesizer when the user
- # toggles the structural navigation feature of Orca.
- # It should be a brief informative message.
- #
- string = _("Structural navigation keys on.")
- else:
- # Translators: the structural navigation keys are designed
- # to move the caret around the HTML content by object type.
- # Thus H moves you to the next heading, Shift H to the
- # previous heading, T to the next table, and so on. Some
- # users prefer to turn this off to use Firefox's search
- # when typing feature. This message is sent to both the
- # braille display and the speech synthesizer when the user
- # toggles the structural navigation feature of Orca.
- # It should be a brief informative message.
- #
- string = _("Structural navigation keys off.")
-
- debug.println(debug.LEVEL_CONFIGURATION, string)
- speech.speak(string)
- braille.displayMessage(string)
-
- def speakWordUnderMouse(self, acc):
- """Determine if the speak-word-under-mouse capability applies to
- the given accessible.
-
- Arguments:
- - acc: Accessible to test.
-
- Returns True if this accessible should provide the single word.
- """
- if self.inDocumentContent(acc):
- try:
- ai = acc.queryAction()
+ ai = acc.queryAction()
except NotImplementedError:
return True
default.Script.speakWordUnderMouse(self, acc)
-
- ####################################################################
- # #
- # Match Predicates #
- # #
- ####################################################################
-
- def __matchChunk(self, obj):
- role = obj.getRole()
- if not role in script_settings.OBJECT_ROLES:
- return False
-
- if role in [pyatspi.ROLE_LIST, pyatspi.ROLE_TABLE]:
- # These roles are often serving as containers. We want to see
- # if what they contain is a bunch of text (as opposed to a
- # bunch of links or other embedded objects). As for lists:
- # We only care about those of the (un)ordered variety. Form
- # field lists are not chunks.
- #
- if not obj.getState().contains(pyatspi.STATE_FOCUSABLE):
- charCount = 0
- for child in obj:
- text = self.queryNonEmptyText(child)
- if not text:
- continue
-
- string = text.getText(0, -1)
- if not string.count(self.EMBEDDED_OBJECT_CHARACTER):
- charCount += text.characterCount
- if charCount > script_settings.largeObjectTextLength:
- return True
- return False
- else:
- # We're going to have to take a guess. It's probably a big
- # chunk of text if it contains at least the number of characters
- # specified by largeObjectTextLength, AND
- # - Guess #1: No more than 5% of the object's total characters
- # are EOCs, OR
- # - Guess #2: No more than 0.5% of the object's initial n
- # characters are EOCs, where n is the largeObjectTextLength.
- #
- text = self.queryNonEmptyText(obj)
- if text \
- and text.characterCount > script_settings.largeObjectTextLength:
- string = text.getText(0, -1).decode("UTF-8")
- eocs = float(string.count(self.EMBEDDED_OBJECT_CHARACTER))
- if eocs/text.characterCount < 0.05:
- # print "Guess #1", string, eocs/text.characterCount
- return True
- else:
- string = string[0:script_settings.largeObjectTextLength]
- eocs = float(string.count(self.EMBEDDED_OBJECT_CHARACTER))
- # print "Guess #2", string, eocs/len(string)
- return eocs/len(string) < 0.005
-
- def __matchLandmark(self, obj):
- if obj is None:
- return False
- attrs = self._getAttrDictionary(obj)
- try:
- if attrs['xml-roles'] in script_settings.ARIA_LANDMARKS:
- return True
- else:
- return False
- except KeyError:
- return False
Modified: trunk/src/orca/scripts/toolkits/Gecko/script_settings.py
==============================================================================
--- trunk/src/orca/scripts/toolkits/Gecko/script_settings.py (original)
+++ trunk/src/orca/scripts/toolkits/Gecko/script_settings.py Mon Jun 23 17:18:28 2008
@@ -23,8 +23,6 @@
__copyright__ = "Copyright (c) 2004-2008 Sun Microsystems Inc."
__license__ = "LGPL"
-import pyatspi
-
# If True, it tells us to take over caret navigation. This is something
# that can be set in user-settings.py:
#
@@ -49,26 +47,6 @@
#
structuralNavigationEnabled = True
-# Whether or not to speak the cell's coordinates when navigating
-# from cell to cell in HTML tables.
-#
-speakCellCoordinates = True
-
-# Whether or not to speak the number of cells spanned by a cell
-# that occupies more than one row or column of an HTML table.
-#
-speakCellSpan = True
-
-# Whether or not to announce the header that applies to the current
-# when navigating from cell to cell in HTML tables.
-#
-speakCellHeaders = True
-
-# Whether blank cells should be skipped when navigating in an HTML
-# table using table navigation commands
-#
-skipBlankCells = False
-
# Whether or not Orca should speak the changing location within the
# document frame *during* a find (i.e. while focus is still in the
# Find toolbar).
@@ -91,21 +69,5 @@
#
onlySpeakChangedLinesDuringFind = False
-# The minimum number of characters of text that an accessible object must
-# contain to be considered a match in go to next/prev large object
-largeObjectTextLength = 75
-
-# Roles that represent a logical chunk of information in a document
-#
-OBJECT_ROLES = [pyatspi.ROLE_HEADING,
- pyatspi.ROLE_LIST,
- pyatspi.ROLE_PARAGRAPH,
- pyatspi.ROLE_TABLE,
- pyatspi.ROLE_TABLE_CELL,
- pyatspi.ROLE_TEXT,
- pyatspi.ROLE_SECTION,
- pyatspi.ROLE_DOCUMENT_FRAME,
- pyatspi.ROLE_AUTOCOMPLETE]
-
ARIA_LANDMARKS = ["banner", "contentinfo", "definition", "main", "navigation",
"note", "search", "secondary", "seealso"]
Added: trunk/src/orca/scripts/toolkits/Gecko/structural_navigation.py
==============================================================================
--- (empty file)
+++ trunk/src/orca/scripts/toolkits/Gecko/structural_navigation.py Mon Jun 23 17:18:28 2008
@@ -0,0 +1,220 @@
+# Orca
+#
+# Copyright 2005-2008 Sun Microsystems Inc.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 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
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library 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.
+
+"""Custom structural navigation for the Gecko toolkit."""
+
+__id__ = "$Id$"
+__version__ = "$Revision$"
+__date__ = "$Date$"
+__copyright__ = "Copyright (c) 2005-2008 Sun Microsystems Inc."
+__license__ = "LGPL"
+
+import pyatspi
+
+import orca.structural_navigation as structural_navigation
+import orca.settings as settings
+
+########################################################################
+# #
+# Custom Structural Navigation #
+# #
+########################################################################
+
+class GeckoStructuralNavigation(structural_navigation.StructuralNavigation):
+
+ def __init__(self, script, enabledTypes, enabled):
+ """Gecko specific Structural Navigation."""
+
+ structural_navigation.StructuralNavigation.__init__(self,
+ script,
+ enabledTypes,
+ enabled)
+
+ #####################################################################
+ # #
+ # Methods for finding and moving to objects #
+ # #
+ #####################################################################
+
+ def getCurrentObject(self):
+ """Returns the current object -- normally, the locusOfFocus. But
+ in the case of Gecko, that doesn't always work.
+ """
+
+ [obj, offset] = self._script.getCaretContext()
+ return obj
+
+ def _findPreviousObject(self, obj, stopAncestor):
+ """Finds the object prior to this one, where the tree we're
+ dealing with is a DOM and 'prior' means the previous object
+ in a linear presentation sense.
+
+ Arguments:
+ -obj: the object where to start.
+ -stopAncestor: the ancestor at which the search should stop
+ """
+
+ return self._script.findPreviousObject(obj, stopAncestor)
+
+ def _findNextObject(self, obj, stopAncestor):
+ """Finds the object after to this one, where the tree we're
+ dealing with is a DOM and 'next' means the next object
+ in a linear presentation sense.
+
+ Arguments:
+ -obj: the object where to start.
+ -stopAncestor: the ancestor at which the search should stop
+ """
+
+ return self._script.findNextObject(obj, stopAncestor)
+
+ def _findLastObject(self, ancestor):
+ """Returns the last object in ancestor.
+
+ Arguments:
+ - ancestor: the accessible object whose last (child) object
+ is sought.
+ """
+
+ return self._script.getLastObject(ancestor)
+
+ def _getDocument(self):
+ """Returns the document or other object in which the object of
+ interest is contained.
+ """
+
+ return self._script.getDocumentFrame()
+
+ def _isInDocument(self, obj):
+ """Returns True of the object is inside of the document."""
+
+ return self._script.inDocumentContent(obj)
+
+ def _getCaretPosition(self, obj):
+ """Returns the [obj, characterOffset] where the caret should be
+ positioned.
+ """
+
+ return self._script.findFirstCaretContext(obj, 0)
+
+ def _setCaretPosition(self, obj, characterOffset):
+ """Sets the caret at the specified offset within obj."""
+
+ self._script.setCaretPosition(obj, characterOffset)
+
+ def _isUselessObject(self, obj):
+ """Returns true if the given object is an obj that doesn't
+ have any meaning associated with it.
+ """
+
+ return self._script.isUselessObject(obj)
+
+ #####################################################################
+ # #
+ # Methods for presenting objects #
+ # #
+ #####################################################################
+
+ def _presentLine(self, obj, offset):
+ """Presents the first line of the object to the user."""
+
+ self._script.presentLine(obj, offset)
+
+ def _presentObject(self, obj, offset):
+ """Presents the entire object to the user."""
+
+ self._script.updateBraille(obj)
+ contents = self._script.getObjectContentsAtOffset(obj, offset)
+ self._script.speakContents(contents)
+
+ #########################################################################
+ # #
+ # Objects #
+ # #
+ #########################################################################
+
+ ########################
+ # #
+ # Chunks/Large Objects #
+ # #
+ ########################
+
+ def _chunkPredicate(self, obj, arg=None):
+ """The predicate to be used for verifying that the object
+ obj is a chunk.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ role = obj.getRole()
+ if not role in self.OBJECT_ROLES:
+ return False
+
+ embeddedObjectChar = self._script.EMBEDDED_OBJECT_CHARACTER
+ if role in [pyatspi.ROLE_LIST, pyatspi.ROLE_TABLE]:
+ # These roles are often serving as containers. We want to see
+ # if what they contain is a bunch of text (as opposed to a
+ # bunch of links or other embedded objects). As for lists:
+ # We only care about those of the (un)ordered variety. Form
+ # field lists are not chunks.
+ #
+ if not obj.getState().contains(pyatspi.STATE_FOCUSABLE):
+ charCount = 0
+ for child in obj:
+ try:
+ text = child.queryText()
+ except:
+ text = None
+ if not text:
+ continue
+
+ string = text.getText(0, -1)
+ if not string.count(embeddedObjectChar):
+ charCount += text.characterCount
+ if charCount > settings.largeObjectTextLength:
+ return True
+ return False
+ else:
+ # We're going to have to take a guess. It's probably a big
+ # chunk of text if it contains at least the number of characters
+ # specified by largeObjectTextLength, AND
+ # - Guess #1: No more than 5% of the object's total characters
+ # are EOCs, OR
+ # - Guess #2: No more than 0.5% of the object's initial n
+ # characters are EOCs, where n is the largeObjectTextLength.
+ #
+ try:
+ text = obj.queryText()
+ except:
+ return False
+ if text \
+ and text.characterCount > settings.largeObjectTextLength:
+ string = text.getText(0, -1).decode("UTF-8")
+ eocs = float(string.count(embeddedObjectChar))
+ if eocs/text.characterCount < 0.05:
+ # print "Guess #1", string, eocs/text.characterCount
+ return True
+ else:
+ string = string[0:settings.largeObjectTextLength]
+ eocs = float(string.count(embeddedObjectChar))
+ # print "Guess #2", string, eocs/len(string)
+ return eocs/len(string) < 0.005
Modified: trunk/src/orca/settings.py
==============================================================================
--- trunk/src/orca/settings.py (original)
+++ trunk/src/orca/settings.py Mon Jun 23 17:18:28 2008
@@ -165,7 +165,12 @@
"enableContractedBraille",
"brailleContractionTable",
"enableMouseReview",
- "mouseDwellDelay"
+ "mouseDwellDelay",
+ "speakCellCoordinates",
+ "speakCellSpan",
+ "speakCellHeaders",
+ "skipBlankCells",
+ "largeObjectTextLength"
]
# The name of the module that hold the user interface for the main window
@@ -995,6 +1000,30 @@
#
useCollection = True
+# Whether or not to speak the cell's coordinates when navigating
+# from cell to cell in a table.
+#
+speakCellCoordinates = True
+
+# Whether or not to speak the number of cells spanned by a cell
+# that occupies more than one row or column of a table.
+#
+speakCellSpan = True
+
+# Whether or not to announce the header that applies to the current
+# when navigating from cell to cell in a table.
+#
+speakCellHeaders = True
+
+# Whether blank cells should be skipped when navigating in a table
+# using table navigation commands.
+#
+skipBlankCells = False
+
+# The minimum size in characters to be considered a "large object"
+# or "chunk" for structural navigation.
+#
+largeObjectTextLength = 75
# Report object under mouse.
#
Added: trunk/src/orca/structural_navigation.py
==============================================================================
--- (empty file)
+++ trunk/src/orca/structural_navigation.py Mon Jun 23 17:18:28 2008
@@ -0,0 +1,3439 @@
+# Orca
+#
+# Copyright 2005-2008 Sun Microsystems Inc.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 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
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library 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.
+
+"""Implements structural navigation. Right now this is only
+being implemented by Gecko; however it can be used in any
+script providing access to document content."""
+
+__id__ = "$Id$"
+__version__ = "$Revision$"
+__date__ = "$Date$"
+__copyright__ = "Copyright (c) 2005-2008 Sun Microsystems Inc."
+__license__ = "LGPL"
+
+import pyatspi
+
+import braille
+import debug
+import input_event
+import keybindings
+import orca
+import orca_state
+import settings
+import speech
+
+from orca_i18n import _
+from orca_i18n import ngettext
+
+#############################################################################
+# #
+# MatchCriteria #
+# #
+#############################################################################
+
+class MatchCriteria:
+ """Contains the criteria which will be used to generate a collection
+ matchRule. We don't want to create the rule until we need it and
+ are ready to use it. In addition, the creation of an AT-SPI match
+ rule requires you specify quite a few things (see the __init__),
+ most of which are irrelevant to the search at hand. This class
+ makes it possible for the StructuralNavigationObject creator to just
+ specify the few criteria that actually matter.
+ """
+
+ def __init__(self,
+ collection,
+ states = [],
+ matchStates = None,
+ objAttrs = [],
+ matchObjAttrs = None,
+ roles = [],
+ matchRoles = None,
+ interfaces = "",
+ matchInterfaces = None,
+ invert = False,
+ applyPredicate = False):
+
+ """Creates a new match criteria object.
+
+ Arguments:
+ - collection: the collection interface for the document in
+ which the accessible objects can be found.
+ - states: a list of pyatspi states of interest
+ - matchStates: whether an object must have all of the states
+ in the states list, any of the states in the list, or none
+ of the states in the list. Must be one of the collection
+ interface MatchTypes if provided.
+ - objAttrs: a list of object attributes (not text attributes)
+ - matchObjAttrs: whether an object must have all of the
+ attributes in the objAttrs list, any of the attributes in
+ the list, or none of the attributes in the list. Must be
+ one of the collection interface MatchTypes if provided.
+ - interfaces: (We aren't using this. According to the at-spi
+ idl, it is a string.)
+ - matchInterfaces: The collection MatchType for matching by
+ interface.
+ - invert: If true the match rule will find objects that don't
+ match. We always use False.
+ - applyPredicate: whether or not a predicate should be applied
+ as an additional check to see if an item is indeed a match.
+ This is necessary, for instance, when one of the things we
+ care about is a text attribute, something the collection
+ interface doesn't include in its criteria.
+ """
+
+ self.collection = collection
+ self.matchStates = matchStates or collection.MATCH_ANY
+ self.objAttrs = objAttrs
+ self.matchObjAttrs = matchObjAttrs or collection.MATCH_ANY
+ self.roles = roles
+ self.matchRoles = matchRoles or collection.MATCH_ANY
+ self.interfaces = interfaces
+ self.matchInterfaces = matchInterfaces or collection.MATCH_ALL
+ self.invert = invert
+ self.applyPredicate = applyPredicate
+
+ self.states = pyatspi.StateSet()
+ for state in states:
+ self.states.add(state)
+
+###########################################################################
+# #
+# StructuralNavigationObject #
+# #
+###########################################################################
+
+class StructuralNavigationObject:
+ """Represents a document object which has identifiable characteristics
+ which can be used for the purpose of navigation to and among instances
+ of that object. These characteristics may be something as simple as a
+ role and/or a state of interest. Or they may be something more complex
+ such as character counts, text attributes, and other object attributes.
+ """
+
+ def __init__(self, structuralNavigation, objType, bindings, predicate,
+ criteria, presentation):
+
+ """Creates a new structural navigation object.
+
+ Arguments:
+ - structuralNavigation: the StructuralNavigation class associated
+ with this object.
+ - objType: the type (e.g. BLOCKQUOTE) associated with this object.
+ - bindings: a dictionary of all of the possible bindings for this
+ object. In the case of all but the "atLevel" bindings, each
+ binding takes the form of [keysymstring, modifiers, description].
+ The goPreviousAtLevel and goNextAtLevel bindings are each a list
+ of bindings in that form.
+ - predicate: the predicate to use to determine if a given accessible
+ matches this structural navigation object. Used when a search via
+ collection is not possible or practical.
+ - criteria: a method which returns a MatchCriteria object which
+ can in turn be used to locate the next/previous matching accessible
+ via collection.
+ - presentation: the method which should be called after performing
+ the search for the structural navigation object.
+ """
+
+ self.structuralNavigation = structuralNavigation
+ self.objType = objType
+ self.bindings = bindings
+ self.predicate = predicate
+ self.criteria = criteria
+ self.present = presentation
+
+ self.inputEventHandlers = {}
+ self.keyBindings = keybindings.KeyBindings()
+ self.functions = []
+ self._setUpHandlersAndBindings()
+
+ def _setUpHandlersAndBindings(self):
+ """Adds the inputEventHandlers and keyBindings for this object."""
+
+ # Set up the basic handlers. These are our traditional goPrevious
+ # and goNext functions.
+ #
+ previous = self.bindings.get("previous")
+ if previous:
+ [keysymstring, modifiers, description] = previous
+ handlerName = "%sGoPrevious" % self.objType
+ self.inputEventHandlers[handlerName] = \
+ input_event.InputEventHandler(self.goPrevious, description)
+
+ self.keyBindings.add(
+ keybindings.KeyBinding(
+ keysymstring,
+ settings.defaultModifierMask,
+ modifiers,
+ self.inputEventHandlers[handlerName]))
+
+ self.functions.append(self.goPrevious)
+
+ next = self.bindings.get("next")
+ if next:
+ [keysymstring, modifiers, description] = next
+ handlerName = "%sGoNext" % self.objType
+ self.inputEventHandlers[handlerName] = \
+ input_event.InputEventHandler(self.goNext, description)
+
+ self.keyBindings.add(
+ keybindings.KeyBinding(
+ keysymstring,
+ settings.defaultModifierMask,
+ modifiers,
+ self.inputEventHandlers[handlerName]))
+
+ self.functions.append(self.goNext)
+
+ # Set up the "at level" handlers (e.g. to navigate among headings
+ # at the specified level).
+ #
+ previousAtLevel = self.bindings.get("previousAtLevel") or []
+ for i, binding in enumerate(previousAtLevel):
+ level = i + 1
+ handler = self.goPreviousAtLevelFactory(level)
+ handlerName = "%sGoPreviousLevel%dHandler" % (self.objType, level)
+ keysymstring, modifiers, description = binding
+
+ self.inputEventHandlers[handlerName] = \
+ input_event.InputEventHandler(handler, description)
+
+ self.keyBindings.add(
+ keybindings.KeyBinding(
+ keysymstring,
+ settings.defaultModifierMask,
+ modifiers,
+ self.inputEventHandlers[handlerName]))
+
+ self.functions.append(handler)
+
+ nextAtLevel = self.bindings.get("nextAtLevel") or []
+ for i, binding in enumerate(nextAtLevel):
+ level = i + 1
+ handler = self.goNextAtLevelFactory(level)
+ handlerName = "%sGoNextLevel%dHandler" % (self.objType, level)
+ keysymstring, modifiers, description = binding
+
+ self.inputEventHandlers[handlerName] = \
+ input_event.InputEventHandler(handler, description)
+
+ self.keyBindings.add(
+ keybindings.KeyBinding(
+ keysymstring,
+ settings.defaultModifierMask,
+ modifiers,
+ self.inputEventHandlers[handlerName]))
+
+ self.functions.append(handler)
+
+ # Set up the "directional" handlers (e.g. for table cells. Live
+ # region support has a handler to go to the last live region,
+ # so we'll handle that here as well).
+ #
+ directions = {}
+ directions["Left"] = self.bindings.get("left")
+ directions["Right"] = self.bindings.get("right")
+ directions["Up"] = self.bindings.get("up")
+ directions["Down"] = self.bindings.get("down")
+ directions["First"] = self.bindings.get("first")
+ directions["Last"] = self.bindings.get("last")
+
+ for direction in directions:
+ binding = directions.get(direction)
+ if not binding:
+ continue
+
+ handler = self.goDirectionFactory(direction)
+ handlerName = "%sGo%s" % (self.objType, direction)
+ keysymstring, modifiers, description = binding
+
+ self.inputEventHandlers[handlerName] = \
+ input_event.InputEventHandler(handler, description)
+
+ self.keyBindings.add(
+ keybindings.KeyBinding(
+ keysymstring,
+ settings.defaultModifierMask,
+ modifiers,
+ self.inputEventHandlers[handlerName]))
+
+ self.functions.append(handler)
+
+ def addHandlerAndBinding(self, binding, handlerName, function):
+ """Adds a custom inputEventHandler and keybinding to the object's
+ handlers and bindings. Right now this is unused, but here in
+ case a creator of a StructuralNavigationObject had some other
+ desired functionality in mind.
+
+ Arguments:
+ - binding: [keysymstring, modifiers, description]
+ - handlerName: a string uniquely identifying the handler
+ - function: the function associated with the binding
+ """
+
+ [keysymstring, modifiers, description] = binding
+ handler = input_event.InputEventHandler(function, description)
+ keyBinding = keybindings.KeyBinding(
+ keysymstring,
+ settings.defaultModifierMask,
+ modifiers,
+ handler)
+
+ self.inputEventHandlers[handlerName] = handler
+ self.structuralNavigation.inputEventHandlers[handlerName] = handler
+
+ self.functions.append(function)
+ self.structuralNavigation.functions.append(function)
+
+ self.keyBindings.add(keyBinding)
+ self.structuralNavigation.keyBindings.add(keyBinding)
+
+ def goPrevious(self, script, inputEvent):
+ """Go to the previous object."""
+ self.structuralNavigation.goObject(self, False)
+
+ def goNext(self, script, inputEvent):
+ """Go to the next object."""
+ self.structuralNavigation.goObject(self, True)
+
+ def goPreviousAtLevelFactory(self, level):
+ """Generates a goPrevious method for the specified level. Right
+ now, this is just for headings, but it may have applicability
+ for other objects such as list items (i.e. for level-based
+ navigation in an outline or other multi-tiered list.
+
+ Arguments:
+ - level: the desired level of the object as an int.
+ """
+
+ def goPreviousAtLevel(script, inputEvent):
+ self.structuralNavigation.goObject(self, False, arg=level)
+ return goPreviousAtLevel
+
+ def goNextAtLevelFactory(self, level):
+ """Generates a goNext method for the specified level. Right
+ now, this is just for headings, but it may have applicability
+ for other objects such as list items (i.e. for level-based
+ navigation in an outline or other multi-tiered list.
+
+ Arguments:
+ - level: the desired level of the object as an int.
+
+ """
+
+ def goNextAtLevel(script, inputEvent):
+ self.structuralNavigation.goObject(self, True, arg=level)
+ return goNextAtLevel
+
+ def goDirectionFactory(self, direction):
+ """Generates the methods for navigation in a particular direction
+ (i.e. left, right, up, down, first, last). Right now, this is
+ primarily for table cells, but it may have applicability for other
+ objects. For example, when navigating in an outline, one might
+ want the ability to navigate to the next item at a given level,
+ but then work his/her way up/down in the hierarchy.
+
+ Arguments:
+ - direction: the direction in which to navigate as a string.
+ """
+
+ def goCell(script, inputEvent):
+ thisCell = self.structuralNavigation.getCellForObj(\
+ self.structuralNavigation.getCurrentObject())
+ currentCoordinates = \
+ self.structuralNavigation.getCellCoordinates(thisCell)
+ if direction == "Left":
+ desiredCoordinates = [currentCoordinates[0],
+ currentCoordinates[1] - 1]
+ elif direction == "Right":
+ desiredCoordinates = [currentCoordinates[0],
+ currentCoordinates[1] + 1]
+ elif direction == "Up":
+ desiredCoordinates = [currentCoordinates[0] - 1,
+ currentCoordinates[1]]
+ elif direction == "Down":
+ desiredCoordinates = [currentCoordinates[0] + 1,
+ currentCoordinates[1]]
+ elif direction == "First":
+ desiredCoordinates = [0, 0]
+ else:
+ desiredCoordinates = [-1, -1]
+ table = self.structuralNavigation.getTableForCell(thisCell)
+ if table:
+ iTable = table.queryTable()
+ lastRow = iTable.nRows - 1
+ lastCol = iTable.nColumns - 1
+ desiredCoordinates = [lastRow, lastCol]
+ self.structuralNavigation.goCell(self,
+ thisCell,
+ currentCoordinates,
+ desiredCoordinates)
+
+ def goLastLiveRegion(script, inputEvent):
+ """Go to the last liveRegion."""
+ if settings.inferLiveRegions:
+ script.liveMngr.goLastLiveRegion()
+ else:
+ # Translators: this announces to the user that live region
+ # support has been turned off.
+ #
+ speech.speak(_("Live region support is off"))
+
+ if self.objType == StructuralNavigation.TABLE_CELL:
+ return goCell
+ elif self.objType == StructuralNavigation.LIVE_REGION \
+ and direction == "Last":
+ return goLastLiveRegion
+
+#############################################################################
+# #
+# StructuralNavigation #
+# #
+#############################################################################
+
+class StructuralNavigation:
+ """This class implements the structural navigation functionality which
+ is available to scripts. Scripts interested in implementing structural
+ navigation need to override getEnabledStructuralNavigationTypes() and
+ return a list of StructuralNavigation object types which should be
+ enabled.
+ """
+
+ # The available object types.
+ #
+ # Convenience methods have been put into place whereby one can
+ # create an object (FOO = "foo"), and then provide the following
+ # methods: _fooBindings(), _fooPredicate(), _fooCriteria(), and
+ # _fooPresentation(). With these in place, and with the object
+ # FOO included among the object types returned by the script's
+ # getEnabledStructuralNavigationTypes(), the StructuralNavigation
+ # object should be created and set up automagically. At least that
+ # is the idea. :-) This hopefully will also enable easy re-definition
+ # of existing StructuralNavigationObjects on a script-by-script basis.
+ # For instance, in the soffice script, overriding _blockquotePredicate
+ # should be all that is needed to implement navigation by blockquote
+ # in OOo Writer documents.
+ #
+ ANCHOR = "anchor"
+ BLOCKQUOTE = "blockquote"
+ BUTTON = "button"
+ CHECK_BOX = "checkBox"
+ CHUNK = "chunk"
+ COMBO_BOX = "comboBox"
+ ENTRY = "entry"
+ FORM_FIELD = "formField"
+ HEADING = "heading"
+ LANDMARK = "landmark"
+ LIST = "list" # Bulleted/numbered lists
+ LIST_ITEM = "listItem" # Bulleted/numbered list items
+ LIVE_REGION = "liveRegion"
+ PARAGRAPH = "paragraph"
+ RADIO_BUTTON = "radioButton"
+ TABLE = "table"
+ TABLE_CELL = "tableCell"
+ UNVISITED_LINK = "unvisitedLink"
+ VISITED_LINK = "visitedLink"
+
+ # Whether or not to attempt to use collection. There's no point
+ # in bothering if we know that the collection interface has not
+ # been implemented in a given app (e.g. StarOffice/OOo) so this
+ # variable can be overridden.
+ #
+ collectionEnabled = settings.useCollection
+
+ # Roles which are recognized as being a form field. Note that this
+ # is for the purpose of match rules and predicates and refers to
+ # AT-SPI roles.
+ #
+ FORM_ROLES = [pyatspi.ROLE_CHECK_BOX,
+ pyatspi.ROLE_RADIO_BUTTON,
+ pyatspi.ROLE_COMBO_BOX,
+ pyatspi.ROLE_LIST,
+ pyatspi.ROLE_ENTRY,
+ pyatspi.ROLE_PASSWORD_TEXT,
+ pyatspi.ROLE_PUSH_BUTTON,
+ pyatspi.ROLE_SPIN_BUTTON,
+ pyatspi.ROLE_TEXT]
+
+ # Tags associated with ARIA landmarks.
+ #
+ ARIA_LANDMARKS = ["banner", "contentinfo", "definition", "main",
+ "navigation", "note", "search", "secondary",
+ "seealso"]
+
+ # Roles which are recognized as being potential "large objects"
+ # or "chunks." Note that this refers to AT-SPI roles.
+ #
+ OBJECT_ROLES = [pyatspi.ROLE_HEADING,
+ pyatspi.ROLE_LIST,
+ pyatspi.ROLE_PARAGRAPH,
+ pyatspi.ROLE_TABLE,
+ pyatspi.ROLE_TABLE_CELL,
+ pyatspi.ROLE_TEXT,
+ pyatspi.ROLE_SECTION,
+ pyatspi.ROLE_DOCUMENT_FRAME]
+
+ def __init__(self, script, enabledTypes, enabled=False):
+ """Creates an instance of the StructuralNavigation class.
+
+ Arguments:
+ - script: the script which which this instance is associated.
+ - enabledTypes: a list of StructuralNavigation object types
+ which the script is interested in supporting.
+ - enabled: Whether structural navigation should start out
+ enabled. For instance, in Gecko by default we do what it
+ enabled; in soffice, we would want to start out with it
+ disabled and have the user enable it via a keystroke when
+ desired.
+ """
+
+ self._script = script
+ self.enabled = enabled
+
+ # Create all of the StructuralNavigationObject's in which the
+ # script is interested, using the convenience method
+ #
+ self.enabledObjects = {}
+ for objType in enabledTypes:
+ self.enabledObjects[objType] = \
+ self.structuralNavigationObjectCreator(objType)
+
+ self.functions = []
+ self.inputEventHandlers = {}
+ self.setupInputEventHandlers()
+ self.keyBindings = self.getKeyBindings()
+
+ # When navigating in a non-uniform table, one can move to a
+ # cell which spans multiple rows and/or columns. When moving
+ # beyond that cell, into a cell that does NOT span multiple
+ # rows/columns, we want to be sure we land in the right place.
+ # Therefore, we'll store the coordinates from "our perspective."
+ #
+ self.lastTableCell = [-1, -1]
+
+ def structuralNavigationObjectCreator(self, name):
+ """This convenience method creates a StructuralNavigationObject
+ with the specified name and associated characterists. (See the
+ "Objects" section of code near the end of this class. Creators
+ of StructuralNavigationObject's can still do things the old
+ fashioned way should they so choose, by creating the instance
+ and then adding it via addObject().
+
+ Arguments:
+ - name: the name/objType associated with this object.
+ """
+
+ # We're going to assume bindings. After all, a structural
+ # navigation object is by defintion an object which one can
+ # navigate to using the associated keybindings. For similar
+ # reasons we'll also assume a predicate and a presentation
+ # method. (See the Objects section towards the end of this
+ # class for examples of each.)
+ #
+ bindings = eval("self._%sBindings()" % name)
+ predicate = eval("self._%sPredicate" % name)
+ presentation = eval("self._%sPresentation" % name)
+
+ # We won't make this assumption for match criteria because
+ # the collection interface might not be implemented (e.g.
+ # StarOffice/OpenOffice) and/or its use might not be possible
+ # or practical for a given StructuralNavigationObject (e.g.
+ # matching by text attributes, spatial navigation within tables).
+ #
+ try:
+ criteria = eval("self._%sCriteria" % name)
+ except:
+ criteria = None
+
+ return StructuralNavigationObject(self, name, bindings, predicate,
+ criteria, presentation)
+
+ def addObject(self, objType, structuralNavigationObject):
+ """Adds structuralNavigationObject to the dictionary of enabled
+ objects.
+
+ Arguments:
+ - objType: the name/object type of the StructuralNavigationObject.
+ - structuralNavigationObject: the StructuralNavigationObject to
+ add.
+ """
+
+ self.enabledObjects[objType] = structuralNavigationObject
+
+ def setupInputEventHandlers(self):
+ """Defines InputEventHandler fields for a script."""
+
+ if not len(self.enabledObjects):
+ return
+
+ self.inputEventHandlers["toggleStructuralNavigationHandler"] = \
+ input_event.InputEventHandler(
+ self.toggleStructuralNavigation,
+ # Translators: the structural navigation keys are designed
+ # to move the caret around the document content by object
+ # type. Thus H moves you to the next heading, Shift H to
+ # the previous heading, T to the next table, and so on.
+ # This feature needs to be toggle-able so that it does not
+ # interfere with normal writing functions.
+ #
+ _("Toggles structural navigation keys."))
+
+ for structuralNavigationObject in self.enabledObjects.values():
+ self.inputEventHandlers.update(\
+ structuralNavigationObject.inputEventHandlers)
+ self.functions.extend(structuralNavigationObject.functions)
+
+ def getKeyBindings(self):
+ """Defines the structural navigation key bindings for a script.
+
+ Returns: an instance of keybindings.KeyBindings.
+ """
+
+ keyBindings = keybindings.KeyBindings()
+
+ if not len(self.enabledObjects):
+ return keyBindings
+
+ keyBindings.add(
+ keybindings.KeyBinding(
+ "z",
+ settings.defaultModifierMask,
+ settings.ORCA_MODIFIER_MASK,
+ self.inputEventHandlers["toggleStructuralNavigationHandler"]))
+
+ for structuralNavigationObject in self.enabledObjects.values():
+ bindings = structuralNavigationObject.keyBindings.keyBindings
+ for keybinding in bindings:
+ keyBindings.add(keybinding)
+
+ return keyBindings
+
+ #########################################################################
+ # #
+ # Input Event Handler Methods #
+ # #
+ #########################################################################
+
+ def toggleStructuralNavigation(self, script, inputEvent):
+ """Toggles structural navigation keys."""
+
+ self.enabled = not self.enabled
+
+ if self.enabled:
+ # Translators: the structural navigation keys are designed
+ # to move the caret around document content by object type.
+ # Thus H moves you to the next heading, Shift H to the
+ # previous heading, T to the next table, and so on. Some
+ # users prefer to turn this off to use Firefox's search
+ # when typing feature. This message is sent to both the
+ # braille display and the speech synthesizer when the user
+ # toggles the structural navigation feature of Orca.
+ # It should be a brief informative message.
+ #
+ string = _("Structural navigation keys on.")
+ else:
+ # Translators: the structural navigation keys are designed
+ # to move the caret around document content by object type.
+ # Thus H moves you to the next heading, Shift H to the
+ # previous heading, T to the next table, and so on. Some
+ # users prefer to turn this off to use Firefox's search
+ # when typing feature. This message is sent to both the
+ # braille display and the speech synthesizer when the user
+ # toggles the structural navigation feature of Orca.
+ # It should be a brief informative message.
+ #
+ string = _("Structural navigation keys off.")
+
+ debug.println(debug.LEVEL_CONFIGURATION, string)
+ speech.speak(string)
+ braille.displayMessage(string)
+
+ #########################################################################
+ # #
+ # Methods for Moving to Objects #
+ # #
+ #########################################################################
+
+ def goCell(self, structuralNavigationObject, thisCell,
+ currentCoordinates, desiredCoordinates):
+ """The method used for navigation among cells in a table.
+
+ Arguments:
+ - structuralNavigationObject: the StructuralNavigationObject which
+ represents the table cell.
+ - thisCell: the pyatspi accessible TABLE_CELL we're currently in
+ - currentCoordinates: the [row, column] of thisCell. Note, we
+ cannot just get the coordinates because in table cells which
+ span multiple rows and/or columns, the value returned by
+ table.getRowAtIndex() is the first row the cell spans. Likewise,
+ the value returned by table.getColumnAtIndex() is the left-most
+ column. Therefore, we keep track of the row and column from
+ our perspective to ensure we stay in the correct row and column.
+ - desiredCoordinates: the [row, column] where we think we'd like to
+ be.
+ """
+
+ table = self.getTableForCell(thisCell)
+ try:
+ iTable = table.queryTable()
+ except:
+ # Translators: this is for navigating document content by
+ # moving from table cell to table cell. If the user gives a
+ # table navigation command but is not in a table, Orca speaks
+ # this message.
+ #
+ speech.speak(_("Not in a table."))
+ return None
+
+ currentRow, currentCol = currentCoordinates
+ desiredRow, desiredCol = desiredCoordinates
+ rowDiff = desiredRow - currentRow
+ colDiff = desiredCol - currentCol
+ oldRowHeaders = self._getRowHeaders(thisCell)
+ oldColHeaders = self._getColumnHeaders(thisCell)
+ cell = thisCell
+ while cell:
+ cell = iTable.getAccessibleAt(desiredRow, desiredCol)
+ if not cell:
+ if desiredCol < 0:
+ # Translators: this is for navigating document
+ # content by moving from table cell to table cell.
+ # This is the message spoken when the user attempts
+ # to move to the left of the current cell and is
+ # already in the first column.
+ #
+ speech.speak(_("Beginning of row."))
+ desiredCol = 0
+ elif desiredCol > iTable.nColumns - 1:
+ # Translators: this is for navigating document
+ # content by moving from table cell to table cell.
+ # This is the message spoken when the user attempts
+ # to move to the right of the current cell and is
+ # already in the last column.
+ #
+ speech.speak(_("End of row."))
+ desiredCol = iTable.nColumns - 1
+ if desiredRow < 0:
+ # Translators: this is for navigating document
+ # content by moving from table cell to table cell.
+ # This is the message spoken when the user attempts
+ # to move to the cell above the current cell and is
+ # already in the first row.
+ #
+ speech.speak(_("Top of column."))
+ desiredRow = 0
+ elif desiredRow > iTable.nRows - 1:
+ # Translators: this is for navigating document
+ # content by moving from table cell to table cell.
+ # This is the message spoken when the user attempts
+ # to move to the cell below the current cell and is
+ # already in the last row.
+ #
+ speech.speak(_("Bottom of column."))
+ desiredRow = iTable.nRows - 1
+ elif self._script.isSameObject(thisCell, cell) \
+ or settings.skipBlankCells and self._isBlankCell(cell):
+ if colDiff < 0:
+ desiredCol -= 1
+ elif colDiff > 0:
+ desiredCol += 1
+ if rowDiff < 0:
+ desiredRow -= 1
+ elif rowDiff > 0:
+ desiredRow += 1
+ else:
+ break
+
+ self.lastTableCell = [desiredRow, desiredCol]
+ if cell:
+ arg = [rowDiff, colDiff, oldRowHeaders, oldColHeaders]
+ structuralNavigationObject.present(cell, arg)
+
+ def goObject(self, structuralNavigationObject, next, obj=None, arg=None):
+ """The method used for navigation among StructuralNavigationObjects
+ which are not table cells.
+
+ Arguments:
+ - structuralNavigationObject: the StructuralNavigationObject which
+ represents the object of interest.
+ - next: If True, we're interested in the next accessible object
+ which matches structuralNavigationObject. If False, we're
+ interested in the previous accessible object which matches.
+ - obj: the current object (typically the locusOfFocus).
+ - arg: optional arguments which may need to be passed along to
+ the predicate, presentation method, etc. For instance, in the
+ case of navigating amongst headings at a given level, the level
+ is needed and passed in as arg.
+ """
+
+ obj = obj or self.getCurrentObject()
+ success = False
+
+ # Try to find it using Collection first. But don't do this with form
+ # fields for now. It's a bit faster moving to the next form field,
+ # but not on pages with huge forms (e.g. bugzilla's advanced search
+ # page). And due to bug #538680, we definitely don't want to use
+ # collection to go to the previous chunk or form field.
+ #
+ formObjects = [self.BUTTON, self.CHECK_BOX, self.COMBO_BOX,
+ self.ENTRY, self.FORM_FIELD, self.RADIO_BUTTON]
+
+ criteria = None
+ objType = structuralNavigationObject.objType
+ if self.collectionEnabled \
+ and not objType in formObjects \
+ and (next or objType != self.CHUNK):
+ try:
+ document = self._getDocument()
+ collection = document.queryCollection()
+ if structuralNavigationObject.criteria:
+ criteria = structuralNavigationObject.criteria(collection,
+ arg)
+ except:
+ debug.printException(debug.LEVEL_SEVERE)
+
+ if criteria:
+ try:
+ rule = collection.createMatchRule(criteria.states.raw(),
+ criteria.matchStates,
+ criteria.objAttrs,
+ criteria.matchObjAttrs,
+ criteria.roles,
+ criteria.matchRoles,
+ criteria.interfaces,
+ criteria.matchInterfaces,
+ criteria.invert)
+ if criteria.applyPredicate:
+ predicate = structuralNavigationObject.predicate
+ else:
+ predicate = None
+
+ if not next:
+ [obj, wrapped] = self._findPrevByMatchRule(collection,
+ rule,
+ True,
+ obj,
+ predicate)
+ else:
+ [obj, wrapped] = self._findNextByMatchRule(collection,
+ rule,
+ True,
+ obj,
+ predicate)
+ success = True
+ collection.freeMatchRule(rule)
+ # print "collection", structuralNavigationObject.objType
+ except NotImplementedError:
+ debug.printException(debug.LEVEL_SEVERE)
+ except:
+ debug.printException(debug.LEVEL_SEVERE)
+ collection.freeMatchRule(rule)
+
+ # Do it iteratively when Collection failed or is disabled
+ #
+ if not success:
+ pred = structuralNavigationObject.predicate
+ if not next:
+ [obj, wrapped] = self._findPrevByPredicate(pred, True,
+ obj, arg)
+ else:
+ [obj, wrapped] = self._findNextByPredicate(pred, True,
+ obj, arg)
+ # print "predicate", structuralNavigationObject.objType
+ if wrapped:
+ if not next:
+ # Translators: when the user is attempting to locate a
+ # particular object and the top of the web page has been
+ # reached without that object being found, we "wrap" to
+ # the bottom of the page and continuing looking upwards.
+ # We need to inform the user when this is taking place.
+ #
+ speech.speak(_("Wrapping to bottom."))
+ else:
+ # Translators: when the user is attempting to locate a
+ # particular object and the bottom of the web page has been
+ # reached without that object being found, we "wrap" to the
+ # top of the page and continuing looking downwards. We need
+ # to inform the user when this is taking place.
+ #
+ speech.speak(_("Wrapping to top."))
+
+ structuralNavigationObject.present(obj, arg)
+
+ #########################################################################
+ # #
+ # Utility Methods for Finding Objects #
+ # #
+ #########################################################################
+
+ def getCurrentObject(self):
+ """Returns the current object. Normally, the locusOfFocus. But
+ in the case of Gecko, that doesn't always work.
+ """
+
+ return orca_state.locusOfFocus
+
+ def _findPrevByMatchRule(self, collection, matchRule, wrap, currentObj,
+ predicate=None):
+ """Finds the previous object using the given match rule as a
+ pattern to match or not match.
+
+ Arguments:
+ -collection: the accessible collection interface
+ -matchRule: the collections match rule to use
+ -wrap: if True and the bottom of the document is reached, move
+ to the top and keep looking.
+ -currentObj: the object from which the search should begin
+ -predicate: an optional predicate to further test if the item
+ found via collection is indeed a match.
+
+ Returns: [obj, wrapped] where wrapped is a boolean reflecting
+ whether wrapping took place.
+ """
+
+ currentObj = currentObj or self.getCurrentObject()
+ ancestors = []
+ obj = currentObj.parent
+ if obj.getRole() in [pyatspi.ROLE_LIST, pyatspi.ROLE_TABLE]:
+ ancestors.append(obj)
+ else:
+ while obj:
+ ancestors.append(obj)
+ obj = obj.parent
+
+ document = self._getDocument()
+ match, wrapped = None, False
+ results = collection.getMatchesTo(currentObj,
+ matchRule,
+ collection.SORT_ORDER_CANONICAL,
+ collection.TREE_INORDER,
+ True,
+ 1,
+ True)
+ while not match:
+ if len(results) == 0:
+ if wrapped or not wrap:
+ break
+ elif wrap:
+ lastObj = self._findLastObject(document)
+ # Collection does not do an inclusive search, meaning
+ # that the start object is not part of the search. So
+ # we need to test the lastobj separately using the given
+ # matchRule. We don't have this problem for 'Next' because
+ # the startobj is the doc frame.
+ #
+ secondLastObj = self._findPreviousObject(lastObj, document)
+ results = collection.getMatchesFrom(\
+ secondLastObj,
+ matchRule,
+ collection.SORT_ORDER_CANONICAL,
+ collection.TREE_INORDER,
+ 1,
+ True)
+ wrapped = True
+ if len(results) > 0 \
+ and (not predicate or predicate(results[0])):
+ match = results[0]
+ else:
+ results = collection.getMatchesTo(\
+ lastObj,
+ matchRule,
+ collection.SORT_ORDER_CANONICAL,
+ collection.TREE_INORDER,
+ True,
+ 1,
+ True)
+ elif len(results) > 0:
+ if results[0] in ancestors \
+ or predicate and not predicate(results[0]):
+ results = collection.getMatchesTo(\
+ results[0],
+ matchRule,
+ collection.SORT_ORDER_CANONICAL,
+ collection.TREE_INORDER,
+ True,
+ 1,
+ True)
+ else:
+ match = results[0]
+
+ return [match, wrapped]
+
+ def _findNextByMatchRule(self, collection, matchRule, wrap, currentObj,
+ predicate=None):
+ """Finds the next object using the given match rule as a pattern
+ to match or not match.
+
+ Arguments:
+ -collection: the accessible collection interface
+ -matchRule: the collections match rule to use
+ -wrap: if True and the bottom of the document is reached, move
+ to the top and keep looking.
+ -currentObj: the object from which the search should begin
+ -predicate: an optional predicate to further test if the item
+ found via collection is indeed a match.
+
+ Returns: [obj, wrapped] where wrapped is a boolean reflecting
+ whether wrapping took place.
+ """
+
+ currentObj = currentObj or self.getCurrentObject()
+ ancestors = []
+ [currentObj, offset] = self._script.getCaretContext()
+ obj = currentObj.parent
+ while obj:
+ ancestors.append(obj)
+ obj = obj.parent
+
+ match, wrapped = None, False
+ while not match:
+ results = collection.getMatchesFrom(\
+ currentObj,
+ matchRule,
+ collection.SORT_ORDER_CANONICAL,
+ collection.TREE_INORDER,
+ 1,
+ True)
+ if len(results) > 0 and not results[0] in ancestors:
+ currentObj = results[0]
+ if not predicate or predicate(currentObj):
+ match = currentObj
+ elif wrap and not wrapped:
+ wrapped = True
+ ancestors = [currentObj]
+ currentObj = self._getDocument()
+ else:
+ break
+
+ return [match, wrapped]
+
+ def _findPrevByPredicate(self, pred, wrap, currentObj=None, arg=None):
+ """Finds the caret offset at the beginning of the previous object
+ using the given predicate as a pattern to match.
+
+ Arguments:
+ -pred: a python callable that takes an accessible argument and
+ returns true/false based on some match criteria
+ -wrap: if True and the top of the document is reached, move
+ to the bottom and keep looking.
+ -currentObj: the object from which the search should begin
+ -arg: an additional value to be passed to the predicate
+
+ Returns: [obj, wrapped] where wrapped is a boolean reflecting
+ whether wrapping took place.
+ """
+
+ currentObj = currentObj or self.getCurrentObject()
+ ancestors = []
+ nestableRoles = [pyatspi.ROLE_LIST, pyatspi.ROLE_TABLE]
+ obj = currentObj.parent
+ while obj:
+ ancestors.append(obj)
+ obj = obj.parent
+
+ document = self._getDocument()
+ obj = self._findPreviousObject(currentObj, document)
+ wrapped = obj is None
+ match = None
+
+ if wrapped:
+ obj = self._findLastObject(document)
+
+ while obj and not match:
+ isNested = (obj != currentObj.parent \
+ and currentObj.parent.getRole() == obj.getRole() \
+ and obj.getRole() in nestableRoles)
+ if (not obj in ancestors or isNested) and pred(obj):
+ if wrapped and self._script.isSameObject(currentObj, obj):
+ break
+ else:
+ match = obj
+ else:
+ obj = self._findPreviousObject(obj, document)
+ if not obj and wrap and not wrapped:
+ obj = self._findLastObject(document)
+ wrapped = True
+
+ return [match, wrapped]
+
+ def _findNextByPredicate(self, pred, wrap, currentObj=None, arg=None):
+ """Finds the caret offset at the beginning of the next object
+ using the given predicate as a pattern to match or not match.
+
+ Arguments:
+ -pred: a python callable that takes an accessible argument and
+ returns true/false based on some match criteria
+ -wrap: if True and the bottom of the document is reached, move
+ to the top and keep looking.
+ -currentObj: the object from which the search should begin
+ -arg: an additional value to be passed to the predicate
+
+ Returns: [obj, wrapped] where wrapped is a boolean reflecting
+ whether wrapping took place.
+ """
+ currentObj = currentObj or self.getCurrentObject()
+ ancestors = []
+ obj = currentObj.parent
+ while obj:
+ ancestors.append(obj)
+ obj = obj.parent
+
+ document = self._getDocument()
+ obj = self._findNextObject(currentObj, document)
+ wrapped = obj is None
+ match = None
+
+ if wrapped:
+ [obj, offset] = self._getCaretPosition(document)
+
+ while obj and not match:
+ if (not obj in ancestors) and pred(obj, arg):
+ if wrapped and self._script.isSameObject(currentObj, obj):
+ break
+ else:
+ match = obj
+ else:
+ obj = self._findNextObject(obj, document)
+ if not obj and wrap and not wrapped:
+ [obj, offset] = self._getCaretPosition(document)
+ wrapped = True
+
+ return [match, wrapped]
+
+ def _findPreviousObject(self, obj, stopAncestor):
+ """Finds the object prior to this one, where the tree we're
+ dealing with is a DOM and 'prior' means the previous object
+ in a linear presentation sense.
+
+ Arguments:
+ -obj: the object where to start.
+ -stopAncestor: the ancestor at which the search should stop
+ """
+
+ # NOTE: This method is based on some intial experimentation
+ # with OOo structural navigation. It might need refining
+ # or fixing and is being overridden by the Gecko method
+ # regardless, so this one can be modified as appropriate.
+ #
+ prevObj = None
+
+ index = obj.getIndexInParent() - 1
+ if index >= 0:
+ prevObj = obj.parent[index]
+ if prevObj.childCount:
+ prevObj = prevObj[prevObj.childCount - 1]
+ elif not self._script.isSameObject(obj.parent, stopAncestor):
+ prevObj = obj.parent
+
+ return prevObj
+
+ def _findNextObject(self, obj, stopAncestor):
+ """Finds the object after to this one, where the tree we're
+ dealing with is a DOM and 'next' means the next object
+ in a linear presentation sense.
+
+ Arguments:
+ -obj: the object where to start.
+ -stopAncestor: the ancestor at which the search should stop
+ """
+
+ # NOTE: This method is based on some intial experimentation
+ # with OOo structural navigation. It might need refining
+ # or fixing and is being overridden by the Gecko method
+ # regardless, so this one can be modified as appropriate.
+ #
+ nextObj = None
+
+ if obj and obj.childCount:
+ nextObj = obj[0]
+
+ while obj and obj.parent != obj and not nextObj:
+ index = obj.getIndexInParent() + 1
+ if 0 < index < obj.parent.childCount:
+ nextObj = obj.parent[index]
+ elif not self._script.isSameObject(obj.parent, stopAncestor):
+ obj = obj.parent
+ else:
+ break
+
+ return nextObj
+
+ def _findLastObject(self, ancestor):
+ """Returns the last object in ancestor.
+
+ Arguments:
+ - ancestor: the accessible object whose last (child) object
+ is sought.
+ """
+
+ # NOTE: This method is based on some intial experimentation
+ # with OOo structural navigation. It might need refining
+ # or fixing and is being overridden by the Gecko method
+ # regardless, so this one can be modified as appropriate.
+ #
+ if not ancestor or not ancestor.childCount:
+ return ancestor
+
+ lastChild = ancestor[ancestor.childCount - 1]
+ while lastChild:
+ lastObj = self._findNextObject(lastChild, ancestor)
+ if lastObj:
+ lastChild = lastObj
+ else:
+ break
+
+ return lastChild
+
+ def _getDocument(self):
+ """Returns the document or other object in which the object of
+ interest is contained.
+ """
+
+ # This is script-specific and will need to be defined in the
+ # script's custom StructuralNavigation class. But if this
+ # method does nothing, pylint complains. So... We might as
+ # well take a guess for a generic version to make pylint
+ # happy. :-) In some initial experimentation with OOo, this
+ # method seemed to reliably return the child of the document
+ # view, so it might not be too far off. It's also being
+ # overridden by Gecko, so one should feel free to modify this
+ # one.
+ #
+ obj = self.getCurrentObject()
+ lastTextObj = obj
+ while obj and obj.getRole() != pyatspi.ROLE_FRAME:
+ try:
+ obj.queryText()
+ except:
+ pass
+ else:
+ lastTextObj = obj
+ obj = obj.parent
+
+ return lastTextObj
+
+ def _isInDocument(self, obj):
+ """Returns True if the accessible object obj is inside of
+ the document.
+
+ Arguments:
+ -obj: the accessible object of interest.
+ """
+
+ document = self._getDocument()
+ while obj and obj.parent:
+ if self._script.isSameObject(obj.parent, document):
+ return True
+ else:
+ obj = obj.parent
+
+ return False
+
+ def _isUselessObject(self, obj):
+ """Returns True if the accessible object obj is an object
+ that doesn't have any meaning associated with it. Individual
+ scripts should override this method as needed. Gecko does.
+
+ Arguments:
+ - obj: the accessible object of interest.
+ """
+
+ return False
+
+ #########################################################################
+ # #
+ # Methods for Presenting Objects #
+ # #
+ #########################################################################
+
+ def _getTableCaption(self, obj):
+ """Returns a string which contains the table caption, or
+ None if a caption could not be found.
+
+ Arguments:
+ - obj: the accessible table whose caption we want.
+ """
+
+ caption = None
+ for child in obj:
+ if child and (child.getRole() == pyatspi.ROLE_CAPTION):
+ caption = child
+
+ try:
+ caption.queryText()
+ except:
+ return None
+ else:
+ return self._script.getDisplayedText(caption)
+
+ def _getTableDescription(self, obj):
+ """Returns a string which describes the table."""
+
+ nonUniformString = ""
+ nonUniform = self._isNonUniformTable(obj)
+ if nonUniform:
+ # Translators: a uniform table is one in which each table
+ # cell occupies one row and one column (i.e. a perfect grid)
+ # In contrast, a non-uniform table is one in which at least
+ # one table cell occupies more than one row and/or column.
+ #
+ nonUniformString = _("Non-uniform") + " "
+
+ table = obj.queryTable()
+ nRows = table.nRows
+ nColumns = table.nColumns
+ # Translators: this represents the number of rows in a table.
+ #
+ rowString = ngettext("Table with %d row",
+ "Table with %d rows",
+ nRows) % nRows
+ # Translators: this represents the number of cols in a table.
+ #
+ colString = ngettext("%d column",
+ "%d columns",
+ nColumns) % nColumns
+
+ return (nonUniformString + rowString + " " + colString)
+
+ def _isNonUniformTable(self, obj):
+ """Returns True if the obj is a non-uniform table (i.e. a table
+ where at least one cell spans multiple rows and/or columns).
+
+ Arguments:
+ - obj: the table to examine
+ """
+
+ try:
+ table = obj.queryTable()
+ except:
+ pass
+ else:
+ for i in xrange(obj.childCount):
+ [isCell, row, col, rowExtents, colExtents, isSelected] = \
+ table.getRowColumnExtentsAtIndex(i)
+ if (rowExtents > 1) or (colExtents > 1):
+ return True
+
+ return False
+
+ def getCellForObj(self, obj):
+ """Looks for a table cell in the ancestry of obj, if obj is not a
+ table cell.
+
+ Arguments:
+ - obj: the accessible object of interest.
+ """
+
+ if obj and obj.getRole() != pyatspi.ROLE_TABLE_CELL:
+ document = self._getDocument()
+ obj = self._script.getAncestor(obj,
+ [pyatspi.ROLE_TABLE_CELL],
+ [document.getRole()])
+ return obj
+
+ def getTableForCell(self, obj):
+ """Looks for a table in the ancestry of obj, if obj is not a table.
+
+ Arguments:
+ - obj: the accessible object of interest.
+ """
+
+ if obj and obj.getRole() != pyatspi.ROLE_TABLE:
+ document = self._getDocument()
+ obj = self._script.getAncestor(obj,
+ [pyatspi.ROLE_TABLE],
+ [document.getRole()])
+ return obj
+
+ def _isBlankCell(self, obj):
+ """Returns True if the table cell is empty or consists of whitespace.
+
+ Arguments:
+ - obj: the accessible table cell to examime
+ """
+
+ text = self._script.getDisplayedText(obj)
+ if text and len(text.strip()) and text != obj.name:
+ return False
+ else:
+ for child in obj:
+ text = self._script.getDisplayedText(child)
+ if text and len(text.strip()) \
+ or child.getRole() == pyatspi.ROLE_LINK:
+ return False
+
+ return True
+
+ def _getCellText(self, obj):
+ """Looks at the table cell and tries to get its text.
+
+ Arguments:
+ - obj: the accessible table cell to examime
+ """
+
+ text = ""
+ if obj and not obj.childCount:
+ text = self._script.getDisplayedText(obj)
+ else:
+ for child in obj:
+ childText = self._script.getDisplayedText(child)
+ text = self._script.appendString(text, childText)
+
+ return text
+
+ def _presentCellHeaders(self, cell, oldCellInfo):
+ """Speaks the headers of the accessible table cell, cell.
+
+ Arguments:
+ - cell: the accessible table cell whose headers we wish to
+ present.
+ - oldCellInfo: [rowDiff, colDiff, oldRowHeaders, oldColHeaders]
+ """
+
+ if not cell or not oldCellInfo:
+ return
+
+ rowDiff, colDiff, oldRowHeaders, oldColHeaders = oldCellInfo
+ if not (oldRowHeaders or oldColHeaders):
+ return
+
+ # We only want to speak the header information that has
+ # changed, and we don't want to speak headers if we're in
+ # a header row/col.
+ #
+ if rowDiff and not self._isInHeaderRow(cell):
+ rowHeaders = self._getRowHeaders(cell)
+ for header in rowHeaders:
+ if not header in oldRowHeaders:
+ text = self._getCellText(header)
+ speech.speak(text)
+
+ if colDiff and not self._isInHeaderColumn(cell):
+ colHeaders = self._getColumnHeaders(cell)
+ for header in colHeaders:
+ if not header in oldColHeaders:
+ text = self._getCellText(header)
+ speech.speak(text)
+
+ def _getCellSpanInfo(self, obj):
+ """Returns a string reflecting the number of rows and/or columns
+ spanned by a table cell when multiple rows and/or columns are
+ spanned.
+
+ Arguments:
+ - obj: the accessible table cell whose cell span we want.
+ """
+
+ if not obj or (obj.getRole() != pyatspi.ROLE_TABLE_CELL):
+ return
+
+ [row, col] = self.getCellCoordinates(obj)
+ table = obj.parent.queryTable()
+ rowspan = table.getRowExtentAt(row, col)
+ colspan = table.getColumnExtentAt(row, col)
+ spanString = ""
+ if (colspan > 1) and (rowspan > 1):
+ # Translators: The cell here refers to a cell within a table
+ # within a document. We need to announce when the cell occupies
+ # or "spans" more than a single row and/or column.
+ #
+ spanString = _("Cell spans %d rows and %d columns") % \
+ (rowspan, colspan)
+ elif (colspan > 1):
+ # Translators: The cell here refers to a cell within a table
+ # within a document. We need to announce when the cell occupies
+ # or "spans" more than a single row and/or column.
+ #
+ spanString = _("Cell spans %d columns") % colspan
+ elif (rowspan > 1):
+ # Translators: The cell here refers to a cell within a table
+ # within a document. We need to announce when the cell occupies
+ # or "spans" more than a single row and/or column.
+ #
+ spanString = _("Cell spans %d rows") % rowspan
+
+ return spanString
+
+ def getCellCoordinates(self, obj):
+ """Returns the [row, col] of a ROLE_TABLE_CELL or [-1, -1]
+ if the coordinates cannot be found.
+
+ Arguments:
+ - obj: the accessible table cell whose coordinates we want.
+ """
+
+ obj = self.getCellForObj(obj)
+ parent = self.getTableForCell(obj)
+ try:
+ table = parent.queryTable()
+ except:
+ pass
+ else:
+ # If we're in a cell that spans multiple rows and/or columns,
+ # thisRow and thisCol will refer to the upper left cell in
+ # the spanned range(s). We're storing the lastTableCell that
+ # we're aware of in order to facilitate more linear movement.
+ # Therefore, if the lastTableCell and this table cell are the
+ # same cell, we'll go with the stored coordinates.
+ #
+ lastRow, lastCol = self.lastTableCell
+ lastKnownCell = table.getAccessibleAt(lastRow, lastCol)
+ if self._script.isSameObject(lastKnownCell, obj):
+ return [lastRow, lastCol]
+ else:
+ index = self._script.getCellIndex(obj)
+ thisRow = table.getRowAtIndex(index)
+ thisCol = table.getColumnAtIndex(index)
+ return [thisRow, thisCol]
+
+ return [-1, -1]
+
+ def _getRowHeaders(self, obj):
+ """Returns a list of table cells that serve as a row header for
+ the specified TABLE_CELL.
+
+ Arguments:
+ - obj: the accessible table cell whose header(s) we want.
+ """
+
+ rowHeaders = []
+ if not obj:
+ return rowHeaders
+
+ try:
+ table = obj.parent.queryTable()
+ except:
+ pass
+ else:
+ [row, col] = self.getCellCoordinates(obj)
+ # Theoretically, we should be able to quickly get the text
+ # of a {row, column}Header via get{Row,Column}Description().
+ # Gecko doesn't expose the information that way, however.
+ # get{Row,Column}Header seems to work sometimes.
+ #
+ header = table.getRowHeader(row)
+ if header:
+ rowHeaders.append(header)
+
+ # Headers that are strictly marked up with <th> do not seem
+ # to be exposed through get{Row, Column}Header.
+ #
+ else:
+ # If our cell spans multiple rows, we want to get all of
+ # the headers that apply.
+ #
+ rowspan = table.getRowExtentAt(row, col)
+ for r in range(row, row+rowspan):
+ # We could have multiple headers for a given row, one
+ # header per column. Presumably all of the headers are
+ # prior to our present location.
+ #
+ for c in range(0, col):
+ cell = table.getAccessibleAt(r, c)
+ if self._isHeader(cell) and not cell in rowHeaders:
+ rowHeaders.append(cell)
+
+ return rowHeaders
+
+ def _getColumnHeaders(self, obj):
+ """Returns a list of table cells that serve as a column header for
+ the specified TABLE_CELL.
+
+ Arguments:
+ - obj: the accessible table cell whose header(s) we want.
+ """
+
+ columnHeaders = []
+ if not obj:
+ return columnHeaders
+
+ try:
+ table = obj.parent.queryTable()
+ except:
+ pass
+ else:
+ [row, col] = self.getCellCoordinates(obj)
+ # Theoretically, we should be able to quickly get the text
+ # of a {row, column}Header via get{Row,Column}Description().
+ # Gecko doesn't expose the information that way, however.
+ # get{Row,Column}Header seems to work sometimes.
+ #
+ header = table.getColumnHeader(col)
+ if header:
+ columnHeaders.append(header)
+
+ # Headers that are strictly marked up with <th> do not seem
+ # to be exposed through get{Row, Column}Header.
+ #
+ else:
+ # If our cell spans multiple columns, we want to get all of
+ # the headers that apply.
+ #
+ colspan = table.getColumnExtentAt(row, col)
+ for c in range(col, col+colspan):
+ # We could have multiple headers for a given column, one
+ # header per row. Presumably all of the headers are
+ # prior to our present location.
+ #
+ for r in range(0, row):
+ cell = table.getAccessibleAt(r, c)
+ if self._isHeader(cell) and not cell in columnHeaders:
+ columnHeaders.append(cell)
+
+ return columnHeaders
+
+ def _isInHeaderRow(self, obj):
+ """Returns True if all of the cells in the same row as this cell are
+ headers.
+
+ Arguments:
+ - obj: the accessible table cell whose row is to be examined.
+ """
+
+ if obj and obj.getRole() == pyatspi.ROLE_TABLE_CELL:
+ table = obj.parent.queryTable()
+ index = self._script.getCellIndex(obj)
+ row = table.getRowAtIndex(index)
+ for col in xrange(table.nColumns):
+ cell = table.getAccessibleAt(row, col)
+ if not self._isHeader(cell):
+ return False
+
+ return True
+
+ def _isInHeaderColumn(self, obj):
+ """Returns True if all of the cells in the same column as this cell
+ are headers.
+
+ Arguments:
+ - obj: the accessible table cell whose column is to be examined.
+ """
+
+ if obj and obj.getRole() == pyatspi.ROLE_TABLE_CELL:
+ table = obj.parent.queryTable()
+ index = self._script.getCellIndex(obj)
+ col = table.getColumnAtIndex(index)
+ for row in xrange(table.nRows):
+ cell = table.getAccessibleAt(row, col)
+ if not self._isHeader(cell):
+ return False
+
+ return True
+
+ def _isHeader(self, obj):
+ """Returns True if the table cell is a header.
+
+ Arguments:
+ - obj: the accessible table cell to examine.
+ """
+
+ if not obj:
+ return False
+
+ elif obj.getRole() in [pyatspi.ROLE_TABLE_COLUMN_HEADER,
+ pyatspi.ROLE_TABLE_ROW_HEADER]:
+ return True
+
+ else:
+ attributes = obj.getAttributes()
+ if attributes:
+ for attribute in attributes:
+ if attribute == "tag:TH":
+ return True
+
+ def _getHeadingLevel(self, obj):
+ """Determines the heading level of the given object. A value
+ of 0 means there is no heading level.
+
+ Arguments:
+ - obj: the accessible whose heading level we want.
+ """
+
+ level = 0
+
+ if obj is None:
+ return level
+
+ if obj.getRole() == pyatspi.ROLE_HEADING:
+ attributes = obj.getAttributes()
+ if attributes is None:
+ return level
+ for attribute in attributes:
+ if attribute.startswith("level:"):
+ level = int(attribute.split(":")[1])
+ break
+
+ return level
+
+ def _getCaretPosition(self, obj):
+ """Returns the [obj, characterOffset] where the caret should be
+ positioned. For most scripts, the object should not change and
+ the offset should be 0. That's not always the case with Gecko.
+
+ Arguments:
+ - obj: the accessible object in which the caret should be
+ positioned.
+ """
+
+ return [obj, 0]
+
+ def _setCaretPosition(self, obj, characterOffset):
+ """Sets the caret at the specified offset within obj.
+
+ Arguments:
+ - obj: the accessible object in which the caret should be
+ positioned.
+ - characterOffset: the offset at which to position the caret.
+ """
+
+ try:
+ text = obj.queryText()
+ text.setCaretOffset(characterOffset)
+ except NotImplementedError:
+ try:
+ obj.queryComponent().grabFocus()
+ except:
+ debug.printException(debug.LEVEL_SEVERE)
+ except:
+ debug.printException(debug.LEVEL_SEVERE)
+
+ orca.setLocusOfFocus(None, obj, False)
+
+ def _presentLine(self, obj, offset):
+ """Presents the first line of the object to the user.
+
+ Arguments:
+ - obj: the accessible object to be presented.
+ - offset: the character offset within obj.
+ """
+
+ self._script.updateBraille(obj)
+ self._script.sayLine(obj)
+
+ def _presentObject(self, obj, offset):
+ """Presents the entire object to the user.
+
+ Arguments:
+ - obj: the accessible object to be presented.
+ - offset: the character offset within obj.
+ """
+
+ self._script.updateBraille(obj)
+ utterances = []
+ strings = self._script.speechGenerator.getSpeech(obj, False)
+ for string in strings:
+ voice = self._getVoice(obj, string)
+ speech.speak(string, voice)
+
+ def _getVoice(self, obj, string):
+ """Returns the voice to speak anything for the given obj.
+
+ Arguments:
+ - obj: the accessible object to be presented.
+ - string: the string to be spoken for obj.
+ """
+
+ voices = self._script.voices
+
+ if obj.getRole() == pyatspi.ROLE_LINK:
+ voice = voices[settings.HYPERLINK_VOICE]
+ elif string and string.isupper() and string.strip().isalpha():
+ voice = voices[settings.UPPERCASE_VOICE]
+ else:
+ voice = voices[settings.DEFAULT_VOICE]
+
+ return voice
+
+ #########################################################################
+ # #
+ # Objects #
+ # #
+ #########################################################################
+
+ # All structural navigation objects have the following essential
+ # characteristics:
+ #
+ # 1. Keybindings for goPrevious, goNext, and other such methods
+ # 2. A means of identification (at least a predicate and possibly
+ # also criteria for generating a collection match rule)
+ # 3. A definition of how the object should be presented (both
+ # when another instance of that object is found as well as
+ # when it is not)
+ #
+ # Convenience methods have been put into place whereby one can
+ # create an object (FOO = "foo"), and then provide the following
+ # methods: _fooBindings(), _fooPredicate(), _fooCriteria(), and
+ # _fooPresentation(). With these in place, and with the object
+ # FOO included among the StructuralNavigation.enabledTypes for
+ # the script, the structural navigation object should be created
+ # and set up automagically. At least that is the idea. :-) This
+ # hopefully will also enable easy re-definition of existing
+ # objects on a script-by-script basis. For instance, in the
+ # StarOffice script, overriding the _blockquotePredicate should
+ # be all that is needed to implement navigation by blockquote
+ # in OOo Writer documents.
+ #
+
+ ########################
+ # #
+ # Anchors #
+ # #
+ ########################
+
+ def _anchorBindings(self):
+ """Returns a dictionary of [keysymstring, modifiers, description]
+ lists for navigating amongst anchors.
+ """
+
+ # NOTE: This doesn't handle the case where the anchor is not an
+ # old-school <a name/id="foo"></a> anchor. For instance on the
+ # GNOME wiki, an "anchor" is actually an id applied to some other
+ # tag (e.g. <h2 id="foo">My Heading</h2>. We'll have to be a
+ # bit more clever for those. With the old-school anchors, this
+ # seems to work nicely and provides the user with a way to jump
+ # among defined areas without having to find a Table of Contents
+ # group of links (assuming such a thing is even present on the
+ # page).
+
+ bindings = {}
+ # Translators: this is for navigating among anchors in a document.
+ # An anchor is a named spot that one can jump to.
+ #
+ prevDesc = _("Goes to previous anchor.")
+ bindings["previous"] = ["", settings.NO_MODIFIER_MASK, prevDesc]
+ # Translators: this is for navigating among anchors in a document.
+ # An anchor is a named spot that one can jump to.
+ #
+ nextDesc = _("Goes to next anchor.")
+ bindings["next"] = ["", settings.NO_MODIFIER_MASK, nextDesc]
+ return bindings
+
+ def _anchorCriteria(self, collection, arg=None):
+ """Returns the MatchCriteria to be used for locating anchors
+ by collection.
+
+ Arguments:
+ - collection: the collection interface for the document
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ role = [pyatspi.ROLE_LINK]
+ state = [pyatspi.STATE_FOCUSABLE]
+ stateMatch = collection.MATCH_NONE
+ return MatchCriteria(collection,
+ states=state,
+ matchStates=stateMatch,
+ roles=role)
+
+ def _anchorPredicate(self, obj, arg=None):
+ """The predicate to be used for verifying that the object
+ obj is an anchor.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ isMatch = False
+ if obj and obj.getRole() == pyatspi.ROLE_LINK:
+ state = obj.getState()
+ isMatch = not state.contains(pyatspi.STATE_FOCUSABLE)
+ return isMatch
+
+ def _anchorPresentation(self, obj, arg=None):
+ """Presents the anchor or indicates that one was not found.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ if obj:
+ [obj, characterOffset] = self._getCaretPosition(obj)
+ self._setCaretPosition(obj, characterOffset)
+ self._presentObject(obj, characterOffset)
+ else:
+ # Translators: this is for navigating document content by
+ # moving from anchor to anchor. (An anchor is a named spot
+ # that one can jump to. This stirng is what orca will say
+ # if there are no more anchors found.
+ #
+ speech.speak(_("No more anchors."))
+
+ ########################
+ # #
+ # Blockquotes #
+ # #
+ ########################
+
+ def _blockquoteBindings(self):
+ """Returns a dictionary of [keysymstring, modifiers, description]
+ lists for navigating among blockquotes.
+ """
+
+ bindings = {}
+ # Translators: this is for navigating among blockquotes in a
+ # document.
+ #
+ prevDesc = _("Goes to previous blockquote.")
+ bindings["previous"] = ["q", settings.SHIFT_MODIFIER_MASK, prevDesc]
+ # Translators: this is for navigating among blockquotes in a
+ # document.
+ #
+ nextDesc = _("Goes to next blockquote.")
+ bindings["next"] = ["q", settings.NO_MODIFIER_MASK, nextDesc]
+ return bindings
+
+ def _blockquoteCriteria(self, collection, arg=None):
+ """Returns the MatchCriteria to be used for locating blockquotes
+ by collection.
+
+ Arguments:
+ - collection: the collection interface for the document
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ attrs = ['tag:BLOCKQUOTE']
+ return MatchCriteria(collection, objAttrs=attrs)
+
+ def _blockquotePredicate(self, obj, arg=None):
+ """The predicate to be used for verifying that the object
+ obj is a blockquote.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ if not obj:
+ return False
+
+ attributes = obj.getAttributes()
+ if attributes:
+ for attribute in attributes:
+ if attribute == "tag:BLOCKQUOTE":
+ return True
+
+ return False
+
+ def _blockquotePresentation(self, obj, arg=None):
+ """Presents the blockquote or indicates that one was not found.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ if obj:
+ [obj, characterOffset] = self._getCaretPosition(obj)
+ self._setCaretPosition(obj, characterOffset)
+ # TODO: We currently present the line, so that's kept here.
+ # But we should probably present the object, which would
+ # be consistent with the change made recently for headings.
+ #
+ self._presentLine(obj, characterOffset)
+ else:
+ # Translators: this is for navigating document content by
+ # moving from blockquote to blockquote. This string is what
+ # Orca will say if there are no more blockquotes found.
+ #
+ speech.speak(_("No more blockquotes."))
+
+ ########################
+ # #
+ # Buttons #
+ # #
+ ########################
+
+ def _buttonBindings(self):
+ """Returns a dictionary of [keysymstring, modifiers, description]
+ lists for navigating amongst buttons.
+ """
+
+ bindings = {}
+ # Translators: this is for navigating among buttons in a form
+ # within a document.
+ #
+ prevDesc = _("Goes to previous button.")
+ bindings["previous"] = ["", settings.NO_MODIFIER_MASK, prevDesc]
+ # Translators: this is for navigating among buttons in a form
+ # within a document.
+ #
+ nextDesc = _("Goes to next button.")
+ bindings["next"] = ["", settings.NO_MODIFIER_MASK, nextDesc]
+ return bindings
+
+ def _buttonCriteria(self, collection, arg=None):
+ """Returns the MatchCriteria to be used for locating buttons
+ by collection.
+
+ Arguments:
+ - collection: the collection interface for the document
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ role = [pyatspi.ROLE_PUSH_BUTTON]
+ state = [pyatspi.STATE_FOCUSABLE, pyatspi.STATE_SENSITIVE]
+ stateMatch = collection.MATCH_ALL
+ return MatchCriteria(collection,
+ states=state,
+ matchStates=stateMatch,
+ roles=role)
+
+ def _buttonPredicate(self, obj, arg=None):
+ """The predicate to be used for verifying that the object
+ obj is a button.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ isMatch = False
+ if obj and obj.getRole() == pyatspi.ROLE_PUSH_BUTTON:
+ state = obj.getState()
+ isMatch = state.contains(pyatspi.STATE_FOCUSABLE) \
+ and state.contains(pyatspi.STATE_SENSITIVE)
+
+ return isMatch
+
+ def _buttonPresentation(self, obj, arg=None):
+ """Presents the button or indicates that one was not found.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ if obj:
+ obj.queryComponent().grabFocus()
+ else:
+ # Translators: this is for navigating in document content
+ # by moving from push button to push button in a form. This
+ # string is what Orca will say if there are no more buttons
+ # found.
+ #
+ speech.speak(_("No more buttons."))
+
+ ########################
+ # #
+ # Check boxes #
+ # #
+ ########################
+
+ def _checkBoxBindings(self):
+ """Returns a dictionary of [keysymstring, modifiers, description]
+ lists for navigating amongst check boxes.
+ """
+
+ bindings = {}
+ # Translators: this is for navigating among check boxes in a form
+ # within a document.
+ #
+ prevDesc = _("Goes to previous check box.")
+ bindings["previous"] = ["", settings.NO_MODIFIER_MASK, prevDesc]
+ # Translators: this is for navigating among check boxes in a form
+ # within a document.
+ #
+ nextDesc = _("Goes to next check box.")
+ bindings["next"] = ["", settings.NO_MODIFIER_MASK, nextDesc]
+ return bindings
+
+ def _checkBoxCriteria(self, collection, arg=None):
+ """Returns the MatchCriteria to be used for locating check boxes
+ by collection.
+
+ Arguments:
+ - collection: the collection interface for the document
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ role = [pyatspi.ROLE_CHECK_BOX]
+ state = [pyatspi.STATE_FOCUSABLE, pyatspi.STATE_SENSITIVE]
+ stateMatch = collection.MATCH_ALL
+ return MatchCriteria(collection,
+ states=state,
+ matchStates=stateMatch,
+ roles=role)
+
+ def _checkBoxPredicate(self, obj, arg=None):
+ """The predicate to be used for verifying that the object
+ obj is a check box.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ isMatch = False
+ if obj and obj.getRole() == pyatspi.ROLE_CHECK_BOX:
+ state = obj.getState()
+ isMatch = state.contains(pyatspi.STATE_FOCUSABLE) \
+ and state.contains(pyatspi.STATE_SENSITIVE)
+
+ return isMatch
+
+ def _checkBoxPresentation(self, obj, arg=None):
+ """Presents the check box or indicates that one was not found.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ if obj:
+ obj.queryComponent().grabFocus()
+ else:
+ # Translators: this is for navigating in document content
+ # by moving from checkbox to checkbox in a form. This
+ # string is what Orca will say if there are no more check
+ # boxes found.
+ #
+ speech.speak(_("No more check boxes."))
+
+ ########################
+ # #
+ # Chunks/Large Objects #
+ # #
+ ########################
+
+ def _chunkBindings(self):
+ """Returns a dictionary of [keysymstring, modifiers, description]
+ lists for navigating amongst chunks/large objects.
+ """
+
+ bindings = {}
+ # Translators: this is for navigating a document in a
+ # structural manner, where a 'large object' is a logical
+ # chunk of text, such as a paragraph, a list, a table, etc.
+ #
+ prevDesc = _("Goes to previous large object.")
+ bindings["previous"] = ["o", settings.SHIFT_MODIFIER_MASK, prevDesc]
+ # Translators: this is for navigating a document in a
+ # structural manner, where a 'large object' is a logical
+ # chunk of text, such as a paragraph, a list, a table, etc.
+ #
+ nextDesc = _("Goes to next large object.")
+ bindings["next"] = ["o", settings.NO_MODIFIER_MASK, nextDesc]
+ # I don't think it makes sense to add support for a list
+ # of chunks. But one could always change that here.
+ #
+ return bindings
+
+ def _chunkCriteria(self, collection, arg=None):
+ """Returns the MatchCriteria to be used for locating chunks/
+ large objects by collection.
+
+ Arguments:
+ - collection: the collection interface for the document
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ role = self.OBJECT_ROLES
+ roleMatch = collection.MATCH_ANY
+ return MatchCriteria(collection,
+ roles=role,
+ matchRoles=roleMatch,
+ applyPredicate=True)
+
+ def _chunkPredicate(self, obj, arg=None):
+ """The predicate to be used for verifying that the object
+ obj is a chunk.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ isMatch = False
+
+ if obj and obj.getRole() in self.OBJECT_ROLES:
+ try:
+ text = obj.queryText()
+ characterCount = text.characterCount
+ except:
+ characterCount = 0
+
+ if characterCount > settings.largeObjectTextLength \
+ and not self._isUselessObject(obj):
+ isMatch = True
+
+ return isMatch
+
+ def _chunkPresentation(self, obj, arg=None):
+ """Presents the chunk or indicates that one was not found.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ if obj:
+ [newObj, characterOffset] = self._getCaretPosition(obj)
+ self._setCaretPosition(newObj, characterOffset)
+ self._presentObject(obj, 0)
+ else:
+ # Translators: this is for navigating document content by
+ # moving from 'large object' to 'large object'. A 'large
+ # object' is a logical chunk of text, such as a paragraph,
+ # a list, a table, etc. This string is what Orca will say
+ # if there are no more large objects found.
+ #
+ speech.speak(_("No more large objects."))
+
+ ########################
+ # #
+ # Combo Boxes #
+ # #
+ ########################
+
+ def _comboBoxBindings(self):
+ """Returns a dictionary of [keysymstring, modifiers, description]
+ lists for navigating amongst combo boxes.
+ """
+
+ bindings = {}
+ # Translators: this is for navigating among combo boxes in a form
+ # within a document.
+ #
+ prevDesc = _("Goes to previous combo box.")
+ bindings["previous"] = ["", settings.NO_MODIFIER_MASK, prevDesc]
+ # Translators: this is for navigating among combo boxes in a form
+ # within a document.
+ #
+ nextDesc = _("Goes to next combo box.")
+ bindings["next"] = ["", settings.NO_MODIFIER_MASK, nextDesc]
+ return bindings
+
+ def _comboBoxCriteria(self, collection, arg=None):
+ """Returns the MatchCriteria to be used for locating combo boxes
+ by collection.
+
+ Arguments:
+ - collection: the collection interface for the document
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ role = [pyatspi.ROLE_COMBO_BOX]
+ state = [pyatspi.STATE_FOCUSABLE, pyatspi.STATE_SENSITIVE]
+ stateMatch = collection.MATCH_ALL
+ return MatchCriteria(collection,
+ states=state,
+ matchStates=stateMatch,
+ roles=role)
+
+ def _comboBoxPredicate(self, obj, arg=None):
+ """The predicate to be used for verifying that the object
+ obj is a combo box.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ isMatch = False
+ if obj and obj.getRole() == pyatspi.ROLE_COMBO_BOX:
+ state = obj.getState()
+ isMatch = state.contains(pyatspi.STATE_FOCUSABLE) \
+ and state.contains(pyatspi.STATE_SENSITIVE)
+
+ return isMatch
+
+ def _comboBoxPresentation(self, obj, arg=None):
+ """Presents the combo box or indicates that one was not found.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ if obj:
+ obj.queryComponent().grabFocus()
+ else:
+ # Translators: this is for navigating in document content
+ # by moving from combo box to combo box in a form. This
+ # string is what Orca will say if there are no more combo
+ # boxes found.
+ #
+ speech.speak(_("No more combo boxes."))
+
+ ########################
+ # #
+ # Entries #
+ # #
+ ########################
+
+ def _entryBindings(self):
+ """Returns a dictionary of [keysymstring, modifiers, description]
+ lists for navigating amongst entries.
+ """
+
+ bindings = {}
+ # Translators: this is for navigating among text entries in a form
+ # within a document.
+ #
+ prevDesc = _("Goes to previous entry.")
+ bindings["previous"] = ["", settings.NO_MODIFIER_MASK, prevDesc]
+ # Translators: this is for navigating among text entries
+ # in a form.
+ #
+ nextDesc = _("Goes to next entry.")
+ bindings["next"] = ["", settings.NO_MODIFIER_MASK, nextDesc]
+ return bindings
+
+ def _entryCriteria(self, collection, arg=None):
+ """Returns the MatchCriteria to be used for locating entries
+ by collection.
+
+ Arguments:
+ - collection: the collection interface for the document
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ role = [pyatspi.ROLE_ENTRY,
+ pyatspi.ROLE_PASSWORD_TEXT,
+ pyatspi.ROLE_TEXT]
+ roleMatch = collection.MATCH_ANY
+ state = [pyatspi.STATE_FOCUSABLE,
+ pyatspi.STATE_SENSITIVE,
+ pyatspi.STATE_EDITABLE]
+ stateMatch = collection.MATCH_ALL
+ return MatchCriteria(collection,
+ states=state,
+ matchStates=stateMatch,
+ roles=role,
+ matchRoles=roleMatch)
+
+ def _entryPredicate(self, obj, arg=None):
+ """The predicate to be used for verifying that the object
+ obj is an entry.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ isMatch = False
+ if obj and obj.getRole() in [pyatspi.ROLE_ENTRY,
+ pyatspi.ROLE_PASSWORD_TEXT,
+ pyatspi.ROLE_TEXT]:
+ state = obj.getState()
+ isMatch = state.contains(pyatspi.STATE_FOCUSABLE) \
+ and state.contains(pyatspi.STATE_SENSITIVE) \
+ and state.contains(pyatspi.STATE_EDITABLE)
+
+ return isMatch
+
+ def _entryPresentation(self, obj, arg=None):
+ """Presents the entry or indicates that one was not found.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ if obj:
+ obj.queryComponent().grabFocus()
+ else:
+ # Translators: this is for navigating in document content
+ # by moving from text entry to text entry in a form. This
+ # string is what Orca will say if there are no more entries
+ # found.
+ #
+ speech.speak(_("No more entries."))
+
+ ########################
+ # #
+ # Form Fields #
+ # #
+ ########################
+
+ def _formFieldBindings(self):
+ """Returns a dictionary of [keysymstring, modifiers, description]
+ lists for navigating amongst form fields.
+ """
+
+ bindings = {}
+ # Translators: this is for navigating among fields in a form within
+ # a document.
+ #
+ prevDesc = _("Goes to previous form field.")
+ bindings["previous"] = ["Tab",
+ settings.ORCA_SHIFT_MODIFIER_MASK,
+ prevDesc]
+ # Translators: this is for navigating among fields in a form within
+ # a document.
+ #
+ nextDesc = _("Goes to next form field.")
+ bindings["next"] = ["Tab", settings.ORCA_MODIFIER_MASK, nextDesc]
+ return bindings
+
+ def _formFieldCriteria(self, collection, arg=None):
+ """Returns the MatchCriteria to be used for locating form fields
+ by collection.
+
+ Arguments:
+ - collection: the collection interface for the document
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ role = self.FORM_ROLES
+ roleMatch = collection.MATCH_ANY
+ state = [pyatspi.STATE_FOCUSABLE, pyatspi.STATE_SENSITIVE]
+ stateMatch = collection.MATCH_ALL
+ return MatchCriteria(collection,
+ states=state,
+ matchStates=stateMatch,
+ roles=role,
+ matchRoles=roleMatch)
+
+ def _formFieldPredicate(self, obj, arg=None):
+ """The predicate to be used for verifying that the object
+ obj is a form field.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ isMatch = False
+ if obj and obj.getRole() in self.FORM_ROLES:
+ state = obj.getState()
+ isMatch = state.contains(pyatspi.STATE_FOCUSABLE) \
+ and state.contains(pyatspi.STATE_SENSITIVE)
+
+ return isMatch
+
+ def _formFieldPresentation(self, obj, arg=None):
+ """Presents the form field or indicates that one was not found.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ if obj:
+ if obj.getRole() in [pyatspi.ROLE_LIST, pyatspi.ROLE_COMBO_BOX]:
+ obj.queryComponent().grabFocus()
+ else:
+ # TODO: I think we should just grab focus on the object
+ # regardless of the object type. But that's not what we
+ # do now, and it causes an extra newline character to show
+ # up in the regression test output for entries, so for the
+ # purpose of passing the regression tests, I'm not making
+ # that change yet.
+ #
+ [obj, characterOffset] = self._getCaretPosition(obj)
+ self._setCaretPosition(obj, characterOffset)
+ self._presentObject(obj, characterOffset)
+ else:
+ # Translators: this is for navigating in document content
+ # by moving from form field to form field. This string is
+ # what Orca will say if there are no more form fields found.
+ #
+ speech.speak(_("No more form fields."))
+
+ ########################
+ # #
+ # Headings #
+ # #
+ ########################
+
+ def _headingBindings(self):
+ """Returns a dictionary of [keysymstring, modifiers, description]
+ lists for navigating amongst headings.
+ """
+
+ bindings = {}
+ # Translators: this is for navigating in a document by heading.
+ # (e.g. <h1>)
+ #
+ prevDesc = _("Goes to previous heading.")
+ bindings["previous"] = ["h", settings.SHIFT_MODIFIER_MASK, prevDesc]
+ # Translators: this is for navigating in a document by heading.
+ # (e.g., <h1>)
+ #
+ nextDesc = _("Goes to next heading.")
+ bindings["next"] = ["h", settings.NO_MODIFIER_MASK, nextDesc]
+
+ prevAtLevelBindings = []
+ nextAtLevelBindings = []
+ minLevel, maxLevel = self._headingLevels()
+ for i in range(minLevel, maxLevel + 1):
+ # Translators: this is for navigating in a document by heading.
+ # (e.g. <h1> is a heading at level 1).
+ #
+ prevDesc = _("Goes to previous heading at level %d." % i)
+ prevAtLevelBindings.append([str(i),
+ settings.SHIFT_MODIFIER_MASK,
+ prevDesc])
+ # Translators: this is for navigating in a document by heading.
+ # (e.g. <h1> is a heading at level 1).
+ #
+ nextDesc = _("Goes to next heading at level %d." % i)
+ nextAtLevelBindings.append([str(i),
+ settings.NO_MODIFIER_MASK,
+ nextDesc])
+ bindings["previousAtLevel"] = prevAtLevelBindings
+ bindings["nextAtLevel"] = nextAtLevelBindings
+ return bindings
+
+ def _headingLevels(self):
+ """Returns the [minimum heading level, maximum heading level]
+ which should be navigable via structural navigation.
+ """
+
+ return [1, 6]
+
+ def _headingCriteria(self, collection, arg=None):
+ """Returns the MatchCriteria to be used for locating headings
+ by collection.
+
+ Arguments:
+ - collection: the collection interface for the document
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ role = [pyatspi.ROLE_HEADING]
+ attrs = []
+ if arg:
+ attrs.append('level:%d' % arg)
+
+ return MatchCriteria(collection,
+ roles=role,
+ objAttrs=attrs)
+
+ def _headingPredicate(self, obj, arg=None):
+ """The predicate to be used for verifying that the object
+ obj is a heading.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ isMatch = False
+ if obj and obj.getRole() == pyatspi.ROLE_HEADING:
+ if arg:
+ isMatch = (arg == self._getHeadingLevel(obj))
+ else:
+ isMatch = True
+
+ return isMatch
+
+ def _headingPresentation(self, obj, arg=None):
+ """Presents the heading or indicates that one was not found.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ if obj:
+ [obj, characterOffset] = self._getCaretPosition(obj)
+ self._setCaretPosition(obj, characterOffset)
+ self._presentObject(obj, characterOffset)
+ elif not arg:
+ # Translators: this is for navigating HTML content by
+ # moving from heading to heading (e.g. <h1>, <h2>, etc).
+ # This string is what Orca will say if there are no more
+ # headings found.
+ #
+ speech.speak(_("No more headings."))
+ else:
+ # Translators: this is for navigating HTML content by
+ # moving from heading to heading at a particular level
+ # (i.e. only <h1> or only <h2>, etc.) This string is
+ # what Orca will say if there are no more headings found.
+ #
+ speech.speak(_("No more headings at level %d.") % arg)
+
+ ########################
+ # #
+ # Landmarks #
+ # #
+ ########################
+
+ def _landmarkBindings(self):
+ """Returns a dictionary of [keysymstring, modifiers, description]
+ lists for navigating amongst landmarks.
+ """
+
+ bindings = {}
+ # Translators: this is for navigating to the previous ARIA
+ # role landmark. ARIA role landmarks are the W3C defined
+ # HTML tag attribute 'role' used to identify important part
+ # of webpage like banners, main context, search etc.
+ #
+ prevDesc = _("Goes to previous landmark.")
+ bindings["previous"] = ["", settings.NO_MODIFIER_MASK, prevDesc]
+ # Translators: this is for navigating to the next ARIA
+ # role landmark. ARIA role landmarks are the W3C defined
+ # HTML tag attribute 'role' used to identify important part
+ # of webpage like banners, main context, search etc.
+ #
+ nextDesc = _("Goes to next landmark.")
+ bindings["next"] = ["", settings.NO_MODIFIER_MASK, nextDesc]
+ return bindings
+
+ def _landmarkCriteria(self, collection, arg=None):
+ """Returns the MatchCriteria to be used for locating landmarks
+ by collection.
+
+ Arguments:
+ - collection: the collection interface for the document
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ attrs = []
+ for landmark in self.ARIA_LANDMARKS:
+ attrs.append('xml-roles:' + landmark)
+
+ return MatchCriteria(collection, objAttrs=attrs)
+
+ def _landmarkPredicate(self, obj, arg=None):
+ """The predicate to be used for verifying that the object
+ obj is a landmark.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ if obj is None:
+ return False
+
+ attrs = dict([attr.split(':', 1) for attr in obj.getAttributes()])
+ try:
+ if attrs['xml-roles'] in self.ARIA_LANDMARKS:
+ return True
+ else:
+ return False
+ except KeyError:
+ return False
+
+ def _landmarkPresentation(self, obj, arg=None):
+ """Presents the landmark or indicates that one was not found.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ if obj:
+ [obj, characterOffset] = self._getCaretPosition(obj)
+ self._setCaretPosition(obj, characterOffset)
+ self._presentObject(obj, characterOffset)
+ else:
+ # Translators: this is for navigating to the previous ARIA
+ # role landmark. ARIA role landmarks are the W3C defined
+ # HTML tag attribute 'role' used to identify important part
+ # of webpage like banners, main context, search etc. This
+ # is an indication that one was not found.
+ #
+ speech.speak(_("No landmark found."))
+
+ ########################
+ # #
+ # Lists #
+ # #
+ ########################
+
+ def _listBindings(self):
+ """Returns a dictionary of [keysymstring, modifiers, description]
+ lists for navigating amongst (un)ordered lists.
+ """
+
+ bindings = {}
+ # Translators: this is for navigating among bulleted/numbered
+ # lists in a document.
+ #
+ prevDesc = _("Goes to previous list.")
+ bindings["previous"] = ["l", settings.SHIFT_MODIFIER_MASK, prevDesc]
+ # Translators: this is for navigating among bulleted/numbered
+ # lists in a document.
+ #
+ nextDesc = _("Goes to next list.")
+ bindings["next"] = ["l", settings.NO_MODIFIER_MASK, nextDesc]
+ return bindings
+
+ def _listCriteria(self, collection, arg=None):
+ """Returns the MatchCriteria to be used for locating (un)ordered
+ lists by collection.
+
+ Arguments:
+ - collection: the collection interface for the document
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ role = [pyatspi.ROLE_LIST]
+ state = [pyatspi.STATE_FOCUSABLE]
+ stateMatch = collection.MATCH_NONE
+ return MatchCriteria(collection,
+ states=state,
+ matchStates=stateMatch,
+ roles=role)
+
+ def _listPredicate(self, obj, arg=None):
+ """The predicate to be used for verifying that the object
+ obj is an (un)ordered list.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ isMatch = False
+
+ if obj and obj.getRole() == pyatspi.ROLE_LIST:
+ isMatch = not obj.getState().contains(pyatspi.STATE_FOCUSABLE)
+
+ return isMatch
+
+ def _listPresentation(self, obj, arg=None):
+ """Presents the (un)ordered list or indicates that one was not
+ found.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ # TODO: Ultimately it should be the job of the speech (and braille)
+ # generator to present things like this.
+ #
+ if obj:
+ nItems = 0
+ for child in obj:
+ if child.getRole() == pyatspi.ROLE_LIST_ITEM:
+ nItems += 1
+ # Translators: this represents a list in HTML.
+ #
+ itemString = ngettext("List with %d item",
+ "List with %d items",
+ nItems) % nItems
+ speech.speak(itemString)
+ nestingLevel = 0
+ parent = obj.parent
+ while parent.getRole() == pyatspi.ROLE_LIST:
+ nestingLevel += 1
+ parent = parent.parent
+ if nestingLevel:
+ # Translators: this represents a list item in a document.
+ # The nesting level is how 'deep' the item is (e.g., a
+ # level of 2 represents a list item inside a list that's
+ # inside another list).
+ #
+ speech.speak(_("Nesting level %d") % nestingLevel)
+ [obj, characterOffset] = self._getCaretPosition(obj)
+ self._setCaretPosition(obj, characterOffset)
+ self._presentLine(obj, characterOffset)
+ else:
+ # Translators: this is for navigating document content by moving
+ # from bulleted/numbered list to bulleted/numbered list. This
+ # string is what Orca will say if there are no more lists found.
+ #
+ speech.speak(_("No more lists."))
+
+ ########################
+ # #
+ # List Items #
+ # #
+ ########################
+
+ def _listItemBindings(self):
+ """Returns a dictionary of [keysymstring, modifiers, description]
+ lists for navigating amongst items in an (un)ordered list.
+ """
+
+ bindings = {}
+ # Translators: this is for navigating among bulleted/numbered list
+ # items in a document.
+ #
+ prevDesc = _("Goes to previous list item.")
+ bindings["previous"] = ["i", settings.SHIFT_MODIFIER_MASK, prevDesc]
+ # Translators: this is for navigating among bulleted/numbered list
+ # items in a document.
+ #
+ nextDesc = _("Goes to next list item.")
+ bindings["next"] = ["i", settings.NO_MODIFIER_MASK, nextDesc]
+ return bindings
+
+ def _listItemCriteria(self, collection, arg=None):
+ """Returns the MatchCriteria to be used for locating items in an
+ (un)ordered list by collection.
+
+ Arguments:
+ - collection: the collection interface for the document
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ role = [pyatspi.ROLE_LIST_ITEM]
+ state = [pyatspi.STATE_FOCUSABLE]
+ stateMatch = collection.MATCH_NONE
+ return MatchCriteria(collection,
+ states=state,
+ matchStates=stateMatch,
+ roles=role)
+
+ def _listItemPredicate(self, obj, arg=None):
+ """The predicate to be used for verifying that the object
+ obj is an item in an (un)ordered list.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ isMatch = False
+
+ if obj and obj.getRole() == pyatspi.ROLE_LIST_ITEM:
+ isMatch = not obj.getState().contains(pyatspi.STATE_FOCUSABLE)
+
+ return isMatch
+
+ def _listItemPresentation(self, obj, arg=None):
+ """Presents the (un)ordered list item or indicates that one was not
+ found.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ if obj:
+ [obj, characterOffset] = self._getCaretPosition(obj)
+ self._setCaretPosition(obj, characterOffset)
+ # TODO: We currently present the line, so that's kept here.
+ # But we should probably present the object, which would
+ # be consistent with the change made recently for headings.
+ #
+ self._presentLine(obj, characterOffset)
+ else:
+ # Translators: this is for navigating document content by
+ # moving from bulleted/numbered list item to bulleted/
+ # numbered list item. This string is what Orca will say
+ # if there are no more list items found.
+ #
+ speech.speak(_("No more list items."))
+
+ ########################
+ # #
+ # Live Regions #
+ # #
+ ########################
+
+ def _liveRegionBindings(self):
+ """Returns a dictionary of [keysymstring, modifiers, description]
+ lists for navigating amongst live regions.
+ """
+
+ bindings = {}
+ # Translators: this is for navigating between live regions
+ #
+ prevDesc = _("Goes to previous live region.")
+ bindings["previous"] = ["r", settings.SHIFT_MODIFIER_MASK, prevDesc]
+ # Translators: this is for navigating between live regions
+ #
+ nextDesc = _("Goes to next live region.")
+ bindings["next"] = ["r", settings.NO_MODIFIER_MASK, nextDesc]
+ # Translators: this is for navigating to the last live region
+ # to make an announcement.
+ #
+ desc = _("Goes to last live region.")
+ bindings["last"] = ["y", settings.NO_MODIFIER_MASK, desc]
+ return bindings
+
+ def _liveRegionPredicate(self, obj, arg=None):
+ """The predicate to be used for verifying that the object
+ obj is a live region.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ isMatch = False
+
+ regobjs = self._script.liveMngr.getLiveNoneObjects()
+ if self._script.liveMngr.matchLiveRegion(obj) or obj in regobjs:
+ isMatch = True
+
+ return isMatch
+
+ def _liveRegionPresentation(self, obj, arg=None):
+ """Presents the live region or indicates that one was not found.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ if obj:
+ # TODO: We don't want to move to a list item.
+ # Is this the best place to handle this?
+ #
+ if obj.getRole() == pyatspi.ROLE_LIST:
+ characterOffset = 0
+ else:
+ [obj, characterOffset] = self._getCaretPosition(obj)
+ self._setCaretPosition(obj, characterOffset)
+ self._presentObject(obj, characterOffset)
+ # For debugging
+ #
+ self._script.outlineAccessible(obj)
+ else:
+ # Translators: this is for navigating HTML in a structural
+ # manner, where a 'live region' is a location in a web page
+ # that are updated without having to refresh the entire page.
+ #
+ speech.speak(_("No more live regions."))
+
+ ########################
+ # #
+ # Paragraphs #
+ # #
+ ########################
+
+ def _paragraphBindings(self):
+ """Returns a dictionary of [keysymstring, modifiers, description]
+ lists for navigating amongst paragraphs.
+ """
+
+ bindings = {}
+ # Translators: this is for navigating among paragraphs in a document.
+ #
+ prevDesc = _("Goes to previous paragraph.")
+ bindings["previous"] = ["p", settings.SHIFT_MODIFIER_MASK, prevDesc]
+ # Translators: this is for navigating among paragraphs in a document.
+ #
+ nextDesc = _("Goes to next paragraph.")
+ bindings["next"] = ["p", settings.NO_MODIFIER_MASK, nextDesc]
+ return bindings
+
+ def _paragraphCriteria(self, collection, arg=None):
+ """Returns the MatchCriteria to be used for locating paragraphs
+ by collection.
+
+ Arguments:
+ - collection: the collection interface for the document
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ role = [pyatspi.ROLE_PARAGRAPH]
+ return MatchCriteria(collection, roles=role, applyPredicate=True)
+
+ def _paragraphPredicate(self, obj, arg=None):
+ """The predicate to be used for verifying that the object
+ obj is a paragraph.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ isMatch = False
+ if obj and obj.getRole() == pyatspi.ROLE_PARAGRAPH:
+ try:
+ text = obj.queryText()
+ # We're choosing 3 characters as the minimum because some
+ # paragraphs contain a single image or link and a text
+ # of length 2: An embedded object character and a space.
+ # We want to skip these.
+ #
+ isMatch = text.characterCount > 2
+ except:
+ pass
+
+ return isMatch
+
+ def _paragraphPresentation(self, obj, arg=None):
+ """Presents the paragraph or indicates that one was not found.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ if obj:
+ [newObj, characterOffset] = self._getCaretPosition(obj)
+ self._setCaretPosition(newObj, characterOffset)
+ self._presentObject(obj, 0)
+ else:
+ # Translators: this is for navigating document content by
+ # moving from paragraph to paragraph. This string is what
+ # Orca will say if there are no more large objects found.
+ #
+ speech.speak(_("No more paragraphs."))
+
+ ########################
+ # #
+ # Radio Buttons #
+ # #
+ ########################
+
+ def _radioButtonBindings(self):
+ """Returns a dictionary of [keysymstring, modifiers, description]
+ lists for navigating amongst radio buttons.
+ """
+
+ bindings = {}
+ # Translators: this is for navigating among radio buttons in a
+ # form within a document.
+ #
+ prevDesc = _("Goes to previous radio button.")
+ bindings["previous"] = ["", settings.NO_MODIFIER_MASK, prevDesc]
+ # Translators: this is for navigating among radio buttons in a
+ # form within a document.
+ #
+ nextDesc = _("Goes to next radio button.")
+ bindings["next"] = ["", settings.NO_MODIFIER_MASK, nextDesc]
+ return bindings
+
+ def _radioButtonCriteria(self, collection, arg=None):
+ """Returns the MatchCriteria to be used for locating radio buttons
+ by collection.
+
+ Arguments:
+ - collection: the collection interface for the document
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ role = [pyatspi.ROLE_RADIO_BUTTON]
+ state = [pyatspi.STATE_FOCUSABLE, pyatspi.STATE_SENSITIVE]
+ stateMatch = collection.MATCH_ALL
+ return MatchCriteria(collection,
+ states=state,
+ matchStates=stateMatch,
+ roles=role)
+
+ def _radioButtonPredicate(self, obj, arg=None):
+ """The predicate to be used for verifying that the object
+ obj is a radio button.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ isMatch = False
+ if obj and obj.getRole() == pyatspi.ROLE_RADIO_BUTTON:
+ state = obj.getState()
+ isMatch = state.contains(pyatspi.STATE_FOCUSABLE) \
+ and state.contains(pyatspi.STATE_SENSITIVE)
+
+ return isMatch
+
+ def _radioButtonPresentation(self, obj, arg=None):
+ """Presents the radio button or indicates that one was not found.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ if obj:
+ obj.queryComponent().grabFocus()
+ else:
+ # Translators: this is for navigating in document content
+ # by moving from radio button to radio button in a form.
+ # This string is what Orca will say if there are no more
+ # radio buttons found.
+ #
+ speech.speak(_("No more radio buttons."))
+
+ ########################
+ # #
+ # Tables #
+ # #
+ ########################
+
+ def _tableBindings(self):
+ """Returns a dictionary of [keysymstring, modifiers, description]
+ lists for navigating amongst tables.
+ """
+
+ bindings = {}
+ # Translators: this is for navigating among tables in a document.
+ #
+ prevDesc = _("Goes to previous table.")
+ bindings["previous"] = ["t", settings.SHIFT_MODIFIER_MASK, prevDesc]
+ # Translators: this is for navigating among tables in a document.
+ #
+ nextDesc = _("Goes to next table.")
+ bindings["next"] = ["t", settings.NO_MODIFIER_MASK, nextDesc]
+ return bindings
+
+ def _tableCriteria(self, collection, arg=None):
+ """Returns the MatchCriteria to be used for locating tables
+ by collection.
+
+ Arguments:
+ - collection: the collection interface for the document
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ role = [pyatspi.ROLE_TABLE]
+ return MatchCriteria(collection, roles=role)
+
+ def _tablePredicate(self, obj, arg=None):
+ """The predicate to be used for verifying that the object
+ obj is a table.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ return (obj and obj.getRole() == pyatspi.ROLE_TABLE)
+
+ def _tablePresentation(self, obj, arg=None):
+ """Presents the table or indicates that one was not found.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ if obj:
+ caption = self._getTableCaption(obj)
+ if caption:
+ speech.speak(caption)
+ speech.speak(self._getTableDescription(obj))
+ cell = obj.queryTable().getAccessibleAt(0, 0)
+ self.lastTableCell = [0, 0]
+ [cell, characterOffset] = self._getCaretPosition(cell)
+ self._setCaretPosition(cell, characterOffset)
+ self._presentObject(cell, characterOffset)
+ else:
+ # Translators: this is for navigating document content by moving
+ # from table to table. This string is what Orca will say if there
+ # are no more tables found.
+ #
+ speech.speak(_("No more tables."))
+
+ ########################
+ # #
+ # Table Cells #
+ # #
+ ########################
+
+ def _tableCellBindings(self):
+ """Returns a dictionary of [keysymstring, modifiers, description]
+ lists for navigating spatially amongst table cells.
+ """
+
+ bindings = {}
+ # Translators: this is for navigating among table cells in a document.
+ #
+ desc = _("Goes left one cell.")
+ bindings["left"] = ["Left", settings.SHIFT_ALT_MODIFIER_MASK, desc]
+ # Translators: this is for navigating among table cells in a document.
+ #
+ desc = _("Goes right one cell.")
+ bindings["right"] = ["Right", settings.SHIFT_ALT_MODIFIER_MASK, desc]
+ # Translators: this is for navigating among table cells in a document.
+ #
+ desc = _("Goes up one cell.")
+ bindings["up"] = ["Up", settings.SHIFT_ALT_MODIFIER_MASK, desc]
+ # Translators: this is for navigating among table cells in a document.
+ #
+ desc = _("Goes down one cell.")
+ bindings["down"] = ["Down", settings.SHIFT_ALT_MODIFIER_MASK, desc]
+ # Translators: this is for navigating among table cells in a document.
+ #
+ desc = _("Goes to the first cell in a table.")
+ bindings["first"] = ["Home", settings.SHIFT_ALT_MODIFIER_MASK, desc]
+ # Translators: this is for navigating among table cells in a document.
+ #
+ desc = _("Goes to the last cell in a table.")
+ bindings["last"] = ["End", settings.SHIFT_ALT_MODIFIER_MASK, desc]
+ return bindings
+
+ def _tableCellCriteria(self, collection, arg=None):
+ """Returns the MatchCriteria to be used for locating table cells
+ by collection.
+
+ Arguments:
+ - collection: the collection interface for the document
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ role = [pyatspi.ROLE_TABLE_CELL]
+ return MatchCriteria(collection, roles=role)
+
+ def _tableCellPredicate(self, obj, arg=None):
+ """The predicate to be used for verifying that the object
+ obj is a table cell.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ return (obj and obj.getRole() == pyatspi.ROLE_TABLE_CELL)
+
+ def _tableCellPresentation(self, cell, arg):
+ """Presents the table cell or indicates that one was not found.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ if not cell:
+ return
+
+ if settings.speakCellHeaders:
+ self._presentCellHeaders(cell, arg)
+
+ [obj, characterOffset] = self._getCaretPosition(cell)
+ self._setCaretPosition(obj, characterOffset)
+ self._script.updateBraille(obj)
+
+ blank = self._isBlankCell(cell)
+ if not blank:
+ self._presentObject(obj, characterOffset)
+ else:
+ # Translators: "blank" is a short word to mean the
+ # user has navigated to an empty line.
+ #
+ speech.speak(_("blank"))
+
+ if settings.speakCellCoordinates:
+ [row, col] = self.getCellCoordinates(cell)
+ # Translators: this represents the (row, col) position of
+ # a cell in a table.
+ #
+ speech.speak(_("Row %d, column %d.") % (row + 1, col + 1))
+
+ spanString = self._getCellSpanInfo(cell)
+ if spanString and settings.speakCellSpan:
+ speech.speak(spanString)
+
+ ########################
+ # #
+ # Unvisited Links #
+ # #
+ ########################
+
+ def _unvisitedLinkBindings(self):
+ """Returns a dictionary of [keysymstring, modifiers, description]
+ lists for navigating amongst unvisited links.
+ """
+
+ bindings = {}
+ # Translators: this is for navigating among unvisited links in a
+ # document.
+ #
+ prevDesc = _("Goes to previous unvisited link.")
+ bindings["previous"] = ["u", settings.SHIFT_MODIFIER_MASK, prevDesc]
+ # Translators: this is for navigating among unvisited links in a
+ # document.
+ #
+ nextDesc = _("Goes to next unvisited link.")
+ bindings["next"] = ["u", settings.NO_MODIFIER_MASK, nextDesc]
+ return bindings
+
+ def _unvisitedLinkCriteria(self, collection, arg=None):
+ """Returns the MatchCriteria to be used for locating unvisited links
+ by collection.
+
+ Arguments:
+ - collection: the collection interface for the document
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ role = [pyatspi.ROLE_LINK]
+ state = [pyatspi.STATE_VISITED]
+ stateMatch = collection.MATCH_NONE
+ return MatchCriteria(collection,
+ states=state,
+ matchStates=stateMatch,
+ roles=role)
+
+ def _unvisitedLinkPredicate(self, obj, arg=None):
+ """The predicate to be used for verifying that the object
+ obj is an unvisited link.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ isMatch = False
+
+ if obj and obj.getRole() == pyatspi.ROLE_LINK:
+ isMatch = not obj.getState().contains(pyatspi.STATE_VISITED)
+
+ return isMatch
+
+ def _unvisitedLinkPresentation(self, obj, arg=None):
+ """Presents the unvisited link or indicates that one was not
+ found.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ if obj:
+ [obj, characterOffset] = self._getCaretPosition(obj)
+ self._setCaretPosition(obj, characterOffset)
+ self._presentObject(obj, characterOffset)
+ else:
+ # Translators: this is for navigating document content by
+ # moving from unvisited link to unvisited link. This string
+ # is what Orca will say if there are no more unvisited links
+ # found.
+ #
+ speech.speak(_("No more unvisited links."))
+
+ ########################
+ # #
+ # Visited Links #
+ # #
+ ########################
+
+ def _visitedLinkBindings(self):
+ """Returns a dictionary of [keysymstring, modifiers, description]
+ lists for navigating amongst visited links.
+ """
+
+ bindings = {}
+ # Translators: this is for navigating among visited links in a
+ # document.
+ #
+ prevDesc = _("Goes to previous visited link.")
+ bindings["previous"] = ["v", settings.SHIFT_MODIFIER_MASK, prevDesc]
+ # Translators: this is for navigating among visited links in a
+ # document.
+ #
+ nextDesc = _("Goes to next visited link.")
+ bindings["next"] = ["v", settings.NO_MODIFIER_MASK, nextDesc]
+ return bindings
+
+ def _visitedLinkCriteria(self, collection, arg=None):
+ """Returns the MatchCriteria to be used for locating visited links
+ by collection.
+
+ Arguments:
+ - collection: the collection interface for the document
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ role = [pyatspi.ROLE_LINK]
+ state = [pyatspi.STATE_VISITED]
+ stateMatch = collection.MATCH_ANY
+ return MatchCriteria(collection,
+ states=state,
+ matchStates=stateMatch,
+ roles=role)
+
+ def _visitedLinkPredicate(self, obj, arg=None):
+ """The predicate to be used for verifying that the object
+ obj is a visited link.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+
+ isMatch = False
+
+ if obj and obj.getRole() == pyatspi.ROLE_LINK:
+ isMatch = obj.getState().contains(pyatspi.STATE_VISITED)
+
+ return isMatch
+
+ def _visitedLinkPresentation(self, obj, arg=None):
+ """Presents the visited link or indicates that one was not
+ found.
+
+ Arguments:
+ - obj: the accessible object under consideration.
+ - arg: an optional argument which may need to be included in
+ the criteria (e.g. the level of a heading).
+ """
+ if obj:
+ [obj, characterOffset] = self._getCaretPosition(obj)
+ self._setCaretPosition(obj, characterOffset)
+ self._presentObject(obj, characterOffset)
+ else:
+ # Translators: this is for navigating document content by
+ # moving from visited link to visited link. This string is
+ # what Orca will say if there are no more visited links
+ # found.
+ #
+ speech.speak(_("No more visited links."))
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]