orca r3997 - in trunk: . po src/orca src/orca/scripts/toolkits/Gecko



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]