[orca] Create a separate caret navigation module



commit 42193c04bfb154fa983d98b7c7f561e4d18ed98c
Author: Joanmarie Diggs <jdiggs igalia com>
Date:   Mon Jun 8 19:46:40 2015 -0400

    Create a separate caret navigation module

 src/orca/Makefile.am                        |    1 +
 src/orca/caret_navigation.py                |  495 +++++++++++++++++++++++++++
 src/orca/messages.py                        |   11 +-
 src/orca/script.py                          |    5 +
 src/orca/scripts/toolkits/Gecko/Makefile.am |    1 -
 src/orca/scripts/toolkits/Gecko/keymaps.py  |   62 ----
 src/orca/scripts/toolkits/Gecko/script.py   |  465 +++----------------------
 7 files changed, 554 insertions(+), 486 deletions(-)
---
diff --git a/src/orca/Makefile.am b/src/orca/Makefile.am
index 87adb66..6445bfc 100644
--- a/src/orca/Makefile.am
+++ b/src/orca/Makefile.am
@@ -14,6 +14,7 @@ orca_python_PYTHON = \
        braille_rolenames.py \
        brlmon.py \
        brltablenames.py \
+       caret_navigation.py \
        chat.py \
        chnames.py \
        cmdnames.py \
diff --git a/src/orca/caret_navigation.py b/src/orca/caret_navigation.py
new file mode 100644
index 0000000..7e06e47
--- /dev/null
+++ b/src/orca/caret_navigation.py
@@ -0,0 +1,495 @@
+# Orca
+#
+# Copyright 2013-2015 Igalia, S.L.
+# Author: Joanmarie Diggs <jdiggs igalia com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
+# Boston MA  02110-1301 USA.
+
+"""Provides an Orca-controlled caret for text content."""
+
+__id__ = "$Id$"
+__version__ = "$Revision$"
+__date__ = "$Date$"
+__copyright__ = "Copyright (c) 2013-2015 Igalia, S.L."
+__license__ = "LGPL"
+
+from . import cmdnames
+from . import debug
+from . import input_event
+from . import keybindings
+from . import messages
+from . import settings_manager
+
+
+class CaretNavigation:
+    """Implements the caret navigation support available to scripts."""
+
+    def __init__(self, script):
+        if not (script and script.app):
+            msg = "INFO: Caret navigation requires a script and app."
+            debug.println(debug.LEVEL_INFO, msg)
+
+        self._script = script
+        self._handlers = self._setup_handlers()
+        self._bindings = self._setup_bindings()
+
+    def handles_navigation(self, handler):
+        """Returns True if handler is a navigation command."""
+
+        if not handler in self._handlers.values():
+            return False
+
+        if handler.function == self._toggle_enabled:
+            return False
+
+        return True
+
+    def get_bindings(self):
+        """Returns the caret-navigation keybindings."""
+
+        return self._bindings
+
+    def get_handlers(self):
+        """Returns the caret-navigation handlers."""
+
+        return self._handlers
+
+    def _setup_handlers(self):
+        """Sets up and returns the caret-navigation input event handlers."""
+
+        handlers = {}
+
+        if not (self._script and self._script.app):
+            return handlers
+
+        handlers["toggle_enabled"] = \
+            input_event.InputEventHandler(
+                self._toggle_enabled,
+                cmdnames.CARET_NAVIGATION_TOGGLE)
+
+        handlers["next_character"] = \
+            input_event.InputEventHandler(
+                self._next_character,
+                cmdnames.CARET_NAVIGATION_NEXT_CHAR)
+
+        handlers["previous_character"] = \
+            input_event.InputEventHandler(
+                self._previous_character,
+                cmdnames.CARET_NAVIGATION_PREV_CHAR)
+
+        handlers["next_word"] = \
+            input_event.InputEventHandler(
+                self._next_word,
+                cmdnames.CARET_NAVIGATION_NEXT_WORD)
+
+        handlers["previous_word"] = \
+            input_event.InputEventHandler(
+                self._previous_word,
+                cmdnames.CARET_NAVIGATION_PREV_WORD)
+
+        handlers["next_line"] = \
+            input_event.InputEventHandler(
+                self._next_line,
+                cmdnames.CARET_NAVIGATION_NEXT_LINE)
+
+        handlers["previous_line"] = \
+            input_event.InputEventHandler(
+                self._previous_line,
+                cmdnames.CARET_NAVIGATION_PREV_LINE)
+
+        handlers["start_of_file"] = \
+            input_event.InputEventHandler(
+                self._start_of_file,
+                cmdnames.CARET_NAVIGATION_FILE_START)
+
+        handlers["end_of_file"] = \
+            input_event.InputEventHandler(
+                self._end_of_file,
+                cmdnames.CARET_NAVIGATION_FILE_END)
+
+        handlers["start_of_line"] = \
+            input_event.InputEventHandler(
+                self._start_of_line,
+                cmdnames.CARET_NAVIGATION_LINE_START)
+
+        handlers["end_of_line"] = \
+            input_event.InputEventHandler(
+                self._end_of_line,
+                cmdnames.CARET_NAVIGATION_LINE_END)
+
+        return handlers
+
+    def _setup_bindings(self):
+        """Sets up and returns the caret-navigation key bindings."""
+
+        bindings = keybindings.KeyBindings()
+
+        if not (self._script and self._script.app):
+            return bindings
+
+        bindings.add(
+            keybindings.KeyBinding(
+                "F12",
+                keybindings.defaultModifierMask,
+                keybindings.ORCA_MODIFIER_MASK,
+                self._handlers.get("toggle_enabled")))
+
+        bindings.add(
+            keybindings.KeyBinding(
+                "Right",
+                keybindings.defaultModifierMask,
+                keybindings.NO_MODIFIER_MASK,
+                self._handlers.get("next_character")))
+
+        bindings.add(
+            keybindings.KeyBinding(
+                "Left",
+                keybindings.defaultModifierMask,
+                keybindings.NO_MODIFIER_MASK,
+                self._handlers.get("previous_character")))
+
+        bindings.add(
+            keybindings.KeyBinding(
+                "Right",
+                keybindings.defaultModifierMask,
+                keybindings.CTRL_MODIFIER_MASK,
+                self._handlers.get("next_word")))
+
+        bindings.add(
+            keybindings.KeyBinding(
+                "Left",
+                keybindings.defaultModifierMask,
+                keybindings.CTRL_MODIFIER_MASK,
+                self._handlers.get("previous_word")))
+
+        bindings.add(
+            keybindings.KeyBinding(
+                "Down",
+                keybindings.defaultModifierMask,
+                keybindings.NO_MODIFIER_MASK,
+                self._handlers.get("next_line")))
+
+        bindings.add(
+            keybindings.KeyBinding(
+                "Up",
+                keybindings.defaultModifierMask,
+                keybindings.NO_MODIFIER_MASK,
+                self._handlers.get("previous_line")))
+
+        bindings.add(
+            keybindings.KeyBinding(
+                "End",
+                keybindings.defaultModifierMask,
+                keybindings.NO_MODIFIER_MASK,
+                self._handlers.get("end_of_line")))
+
+        bindings.add(
+            keybindings.KeyBinding(
+                "Home",
+                keybindings.defaultModifierMask,
+                keybindings.NO_MODIFIER_MASK,
+                self._handlers.get("start_of_line")))
+
+        bindings.add(
+            keybindings.KeyBinding(
+                "End",
+                keybindings.defaultModifierMask,
+                keybindings.CTRL_MODIFIER_MASK,
+                self._handlers.get("end_of_file")))
+
+        bindings.add(
+            keybindings.KeyBinding(
+                "Home",
+                keybindings.defaultModifierMask,
+                keybindings.CTRL_MODIFIER_MASK,
+                self._handlers.get("start_of_file")))
+
+        return bindings
+
+    @staticmethod
+    def _toggle_enabled(script, event):
+        """Toggles caret navigation."""
+
+        if not event:
+            return False
+
+        _settings_manager = settings_manager.getManager()
+        enabled = not _settings_manager.getSetting('caretNavigationEnabled')
+        if enabled:
+            string = messages.CARET_CONTROL_ORCA
+        else:
+            string = messages.CARET_CONTROL_APP
+
+        script.presentMessage(string)
+        _settings_manager.setSetting('caretNavigationEnabled', enabled)
+        return True
+
+    @staticmethod
+    def _next_character(script, event):
+        """Moves to the next character."""
+
+        if not event:
+            return False
+
+        obj, offset = script.utilities.nextContext()
+        if not obj:
+            return False
+
+        script.utilities.setCaretPosition(obj, offset)
+        script.updateBraille(obj)
+        script.sayCharacter(obj)
+        return True
+
+    @staticmethod
+    def _previous_character(script, event):
+        """Moves to the previous character."""
+
+        if not event:
+            return False
+
+        obj, offset = script.utilities.previousContext()
+        if not obj:
+            return False
+
+        script.utilities.setCaretPosition(obj, offset)
+        script.updateBraille(obj)
+        script.sayCharacter(obj)
+        return True
+
+    @staticmethod
+    def _next_word(script, event):
+        """Moves to the next word."""
+
+        if not event:
+            return False
+
+        obj, offset = script.utilities.nextContext(skipSpace=True)
+        contents = script.utilities.getWordContentsAtOffset(obj, offset)
+        if not contents:
+            return False
+
+        obj, end, string = contents[-1][0], contents[-1][2], contents[-1][3]
+        if string and not string[-1].isalnum():
+            end -= 1
+
+        script.utilities.setCaretPosition(obj, end)
+        script.updateBraille(obj)
+        script.sayWord(obj)
+        return True
+
+    @staticmethod
+    def _previous_word(script, event):
+        """Moves to the previous word."""
+
+        if not event:
+            return False
+
+        obj, offset = script.utilities.previousContext(skipSpace=True)
+        contents = script.utilities.getWordContentsAtOffset(obj, offset)
+        if not contents:
+            return False
+
+        obj, start = contents[0][0], contents[0][1]
+        script.utilities.setCaretPosition(obj, start)
+        script.updateBraille(obj)
+        script.sayWord(obj)
+        return True
+
+    @staticmethod
+    def _next_line(script, event):
+        """Moves to the next line."""
+
+        if not event:
+            return False
+
+        if script.inSayAll():
+            _settings_manager = settings_manager.getManager()
+            if _settings_manager.getSetting('rewindAndFastForwardInSayAll'):
+                msg = "INFO: inSayAll and rewindAndFastforwardInSayAll is enabled"
+                debug.println(debug.LEVEL_INFO, msg)
+                return True
+
+        obj, offset = script.utilities.getCaretContext()
+        msg = "INFO: Current context is: %s, %i" % (obj, offset)
+        debug.println(debug.LEVEL_INFO, msg)
+
+        if obj and script.utilities.isZombie(obj):
+            msg = "INFO: Current context obj %s is zombie" % obj
+            debug.println(debug.LEVEL_INFO, msg)
+
+        line = script.utilities.getLineContentsAtOffset(obj, offset)
+        msg = "INFO: Line contents for %s, %i: %s" % (obj, offset, line)
+        debug.println(debug.LEVEL_INFO, msg)
+
+        if not (line and line[0]):
+            return False
+
+        lastObj, lastOffset = line[-1][0], line[-1][2] - 1
+        msg = "INFO: Last context on line is: %s, %i" % (lastObj, lastOffset)
+        debug.println(debug.LEVEL_INFO, msg)
+
+        obj, offset = script.utilities.nextContext(lastObj, lastOffset, True)
+        msg = "INFO: Next context is: %s, %i" % (obj, offset)
+        debug.println(debug.LEVEL_INFO, msg)
+
+        contents = script.utilities.getLineContentsAtOffset(obj, offset)
+        if not contents:
+            msg = "INFO: Could not get line contents for %s, %i" % (obj, offset)
+            debug.println(debug.LEVEL_INFO, msg)
+            return False
+
+        obj, start = contents[0][0], contents[0][1]
+        script.utilities.setCaretPosition(obj, start)
+        script.speakContents(contents)
+        script.displayContents(contents)
+        return True
+
+    @staticmethod
+    def _previous_line(script, event):
+        """Moves to the previous line."""
+
+        if not event:
+            return False
+
+        if script.inSayAll():
+            _settings_manager = settings_manager.getManager()
+            if _settings_manager.getSetting('rewindAndFastForwardInSayAll'):
+                msg = "INFO: inSayAll and rewindAndFastforwardInSayAll is enabled"
+                debug.println(debug.LEVEL_INFO, msg)
+                return True
+
+        obj, offset = script.utilities.getCaretContext()
+        msg = "INFO: Current context is: %s, %i" % (obj, offset)
+        debug.println(debug.LEVEL_INFO, msg)
+
+        if obj and script.utilities.isZombie(obj):
+            msg = "INFO: Current context obj %s is zombie" % obj
+            debug.println(debug.LEVEL_INFO, msg)
+
+        line = script.utilities.getLineContentsAtOffset(obj, offset)
+        msg = "INFO: Line contents for %s, %i: %s" % (obj, offset, line)
+        debug.println(debug.LEVEL_INFO, msg)
+
+        if not (line and line[0]):
+            return False
+
+        firstObj, firstOffset = line[0][0], line[0][1]
+        msg = "INFO: First context on line is: %s, %i" % (firstObj, firstOffset)
+        debug.println(debug.LEVEL_INFO, msg)
+
+        obj, offset = script.utilities.previousContext(firstObj, firstOffset, True)
+        msg = "INFO: Previous context is: %s, %i" % (obj, offset)
+        debug.println(debug.LEVEL_INFO, msg)
+
+        contents = script.utilities.getLineContentsAtOffset(obj, offset)
+        if not contents:
+            msg = "INFO: Could not get line contents for %s, %i" % (obj, offset)
+            debug.println(debug.LEVEL_INFO, msg)
+            return False
+
+        obj, start = contents[0][0], contents[0][1]
+        script.utilities.setCaretPosition(obj, start)
+        script.speakContents(contents)
+        script.displayContents(contents)
+        return True
+
+    @staticmethod
+    def _start_of_line(script, event):
+        """Moves to the start of the line."""
+
+        if not event:
+            return False
+
+        obj, offset = script.utilities.getCaretContext()
+        line = script.utilities.getLineContentsAtOffset(obj, offset)
+        if not (line and line[0]):
+            return False
+
+        obj, start = line[0][0], line[0][1]
+        script.utilities.setCaretPosition(obj, start)
+        script.sayCharacter(obj)
+        script.displayContents(line)
+        return True
+
+    @staticmethod
+    def _end_of_line(script, event):
+        """Moves to the end of the line."""
+
+        if not event:
+            return False
+
+        obj, offset = script.utilities.getCaretContext()
+        line = script.utilities.getLineContentsAtOffset(obj, offset)
+        if not (line and line[0]):
+            return False
+
+        obj, end, string = line[-1][0], line[-1][2], line[-1][3]
+        if string.strip() and string[-1].isspace():
+            end -= 1
+
+        script.utilities.setCaretPosition(obj, end)
+        script.sayCharacter(obj)
+        script.displayContents(line)
+        return True
+
+    @staticmethod
+    def _start_of_file(script, event):
+        """Moves to the start of the file."""
+
+        if not event:
+            return False
+
+        document = script.utilities.documentFrame()
+        obj, offset = script.utilities.findFirstCaretContext(document, 0)
+        contents = script.utilities.getLineContentsAtOffset(obj, offset)
+        if not contents:
+            return False
+
+        obj, offset = contents[0][0], contents[0][1]
+        script.utilities.setCaretPosition(obj, offset)
+        script.speakContents(contents)
+        script.displayContents(contents)
+        return True
+
+    @staticmethod
+    def _end_of_file(script, event):
+        """Moves to the end of the file."""
+
+        if not event:
+            return False
+
+        document = script.utilities.documentFrame()
+        obj = script.utilities.getLastObjectInDocument(document)
+        offset = 0
+        text = script.utilities.queryNonEmptyText(obj)
+        if text:
+            offset = text.characterCount - 1
+
+        while obj:
+            lastobj, lastoffset = script.utilities.nextContext(obj, offset)
+            if not lastobj:
+                break
+            obj, offset = lastobj, lastoffset
+
+        contents = script.utilities.getLineContentsAtOffset(obj, offset)
+        if not contents:
+            return False
+
+        obj, offset = contents[-1][0], contents[-1][2]
+        script.utilities.setCaretPosition(obj, offset)
+        script.speakContents(contents)
+        script.displayContents(contents)
+        return True
diff --git a/src/orca/messages.py b/src/orca/messages.py
index 2771a2b..429119a 100644
--- a/src/orca/messages.py
+++ b/src/orca/messages.py
@@ -164,11 +164,12 @@ CAPITALIZATION_SPELL_BRIEF = C_("capitalization style", "spell")
 # to get into a GUI.
 CAPITALIZATION_SPELL_FULL = _("Capitalization style set to spell.")
 
-# Translators: Gecko native caret navigation is where Firefox (or Thunderbird)
-# 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 toggle which application is controlling the caret.
-CARET_CONTROL_GECKO = _("Gecko is controlling the caret.")
+# Translators: Native application caret navigation does not always work as the
+# Orca user wants. As such, Orca offers the user the ability to toggle between
+# the application controlling the caret and Orca controlling it. This message
+# is presented to indicate that the application's native caret navigation is
+# active / not being overridden by Orca.
+CARET_CONTROL_APP = _("The application is controlling the caret.")
 
 # Translators: Gecko native caret navigation is where Firefox (or Thunderbird)
 # itself controls how the arrow keys move the caret around HTML content. It's
diff --git a/src/orca/script.py b/src/orca/script.py
index 05e1ae3..a228382 100644
--- a/src/orca/script.py
+++ b/src/orca/script.py
@@ -100,6 +100,7 @@ class Script:
         self.utilities = self.getUtilities()
         self.labelInference = self.getLabelInference()
         self.structuralNavigation = self.getStructuralNavigation()
+        self.caretNavigation = self.getCaretNavigation()
         self.bookmarks = self.getBookmarks()
         self.liveRegionManager = self.getLiveRegionManager()
 
@@ -226,6 +227,10 @@ class Script:
         """Returns the spellcheck support for this script."""
         return None
 
+    def getCaretNavigation(self):
+        """Returns the caret navigation support for this script."""
+        return None
+
     def getUtilities(self):
         """Returns the utilites for this script.
         """
diff --git a/src/orca/scripts/toolkits/Gecko/Makefile.am b/src/orca/scripts/toolkits/Gecko/Makefile.am
index 9086687..ddcdb9c 100644
--- a/src/orca/scripts/toolkits/Gecko/Makefile.am
+++ b/src/orca/scripts/toolkits/Gecko/Makefile.am
@@ -2,7 +2,6 @@ orca_python_PYTHON = \
        __init__.py \
        bookmarks.py \
        braille_generator.py \
-       keymaps.py \
        script.py \
        script_utilities.py \
        speech_generator.py \
diff --git a/src/orca/scripts/toolkits/Gecko/script.py b/src/orca/scripts/toolkits/Gecko/script.py
index 0424d5e..8520ff2 100644
--- a/src/orca/scripts/toolkits/Gecko/script.py
+++ b/src/orca/scripts/toolkits/Gecko/script.py
@@ -43,6 +43,7 @@ import pyatspi
 import time
 
 import orca.braille as braille
+import orca.caret_navigation as caret_navigation
 import orca.cmdnames as cmdnames
 import orca.debug as debug
 import orca.scripts.default as default
@@ -59,7 +60,6 @@ import orca.settings_manager as settings_manager
 import orca.speech as speech
 import orca.speechserver as speechserver
 
-from . import keymaps
 from .braille_generator import BrailleGenerator
 from .speech_generator import SpeechGenerator
 from .bookmarks import GeckoBookmarks
@@ -106,21 +106,6 @@ class Script(default.Script):
         self.autoFocusModeCaretNavCheckButton = None
         self.layoutModeCheckButton = None
 
-        # _caretNavigationFunctions are functions that represent fundamental
-        # ways to move the caret (e.g., by the arrow keys).
-        #
-        self._caretNavigationFunctions = \
-            [Script.goNextCharacter,
-             Script.goPreviousCharacter,
-             Script.goNextWord,
-             Script.goPreviousWord,
-             Script.goNextLine,
-             Script.goPreviousLine,
-             Script.goTopOfFile,
-             Script.goBottomOfFile,
-             Script.goBeginningOfLine,
-             Script.goEndOfLine]
-
         if _settingsManager.getSetting('caretNavigationEnabled') == None:
             _settingsManager.setSetting('caretNavigationEnabled', True)
         if _settingsManager.getSetting('sayAllOnLoad') == None:
@@ -296,71 +281,23 @@ class Script(default.Script):
         enable = _settingsManager.getSetting('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)
+    def getCaretNavigation(self):
+        """Returns the caret navigation support for this script."""
 
-        self.inputEventHandlers.update(self.liveRegionManager.inputEventHandlers)
+        return caret_navigation.CaretNavigation(self)
 
-        self.inputEventHandlers["goNextCharacterHandler"] = \
-            input_event.InputEventHandler(
-                Script.goNextCharacter,
-                cmdnames.CARET_NAVIGATION_NEXT_CHAR)
-
-        self.inputEventHandlers["goPreviousCharacterHandler"] = \
-            input_event.InputEventHandler(
-                Script.goPreviousCharacter,
-                cmdnames.CARET_NAVIGATION_PREV_CHAR)
-
-        self.inputEventHandlers["goNextWordHandler"] = \
-            input_event.InputEventHandler(
-                Script.goNextWord,
-                cmdnames.CARET_NAVIGATION_NEXT_WORD)
-
-        self.inputEventHandlers["goPreviousWordHandler"] = \
-            input_event.InputEventHandler(
-                Script.goPreviousWord,
-                cmdnames.CARET_NAVIGATION_PREV_WORD)
-
-        self.inputEventHandlers["goNextLineHandler"] = \
-            input_event.InputEventHandler(
-                Script.goNextLine,
-                cmdnames.CARET_NAVIGATION_NEXT_LINE)
-
-        self.inputEventHandlers["goPreviousLineHandler"] = \
-            input_event.InputEventHandler(
-                Script.goPreviousLine,
-                cmdnames.CARET_NAVIGATION_PREV_LINE)
-
-        self.inputEventHandlers["goTopOfFileHandler"] = \
-            input_event.InputEventHandler(
-                Script.goTopOfFile,
-                cmdnames.CARET_NAVIGATION_FILE_START)
-
-        self.inputEventHandlers["goBottomOfFileHandler"] = \
-            input_event.InputEventHandler(
-                Script.goBottomOfFile,
-                cmdnames.CARET_NAVIGATION_FILE_END)
+    def setupInputEventHandlers(self):
+        """Defines InputEventHandlers for this script."""
 
-        self.inputEventHandlers["goBeginningOfLineHandler"] = \
-            input_event.InputEventHandler(
-                Script.goBeginningOfLine,
-                cmdnames.CARET_NAVIGATION_LINE_START)
+        super().setupInputEventHandlers()
+        self.inputEventHandlers.update(
+            self.structuralNavigation.inputEventHandlers)
 
-        self.inputEventHandlers["goEndOfLineHandler"] = \
-            input_event.InputEventHandler(
-                Script.goEndOfLine,
-                cmdnames.CARET_NAVIGATION_LINE_END)
+        self.inputEventHandlers.update(
+            self.caretNavigation.get_handlers())
 
-        self.inputEventHandlers["toggleCaretNavigationHandler"] = \
-            input_event.InputEventHandler(
-                Script.toggleCaretNavigation,
-                cmdnames.CARET_NAVIGATION_TOGGLE)
+        self.inputEventHandlers.update(
+            self.liveRegionManager.inputEventHandlers)
 
         self.inputEventHandlers["sayAllHandler"] = \
             input_event.InputEventHandler(
@@ -394,28 +331,17 @@ class Script(default.Script):
                 Script.enableStickyFocusMode,
                 cmdnames.SET_FOCUS_MODE_STICKY)
 
-    def __getArrowBindings(self):
-        """Returns an instance of keybindings.KeyBindings that use the
-        arrow keys for navigating HTML content.
-        """
-
-        keyBindings = keybindings.KeyBindings()
-        keyBindings.load(keymaps.arrowKeymap, self.inputEventHandlers)
-        return keyBindings
-
     def getToolkitKeyBindings(self):
         """Returns the toolkit-specific keybindings for this script."""
 
         keyBindings = keybindings.KeyBindings()
 
-        keyBindings.load(keymaps.commonKeymap, self.inputEventHandlers)
-
-        if _settingsManager.getSetting('caretNavigationEnabled'):
-            for keyBinding in self.__getArrowBindings().keyBindings:
-                keyBindings.add(keyBinding)
+        structNavBindings = self.structuralNavigation.keyBindings
+        for keyBinding in structNavBindings.keyBindings:
+            keyBindings.add(keyBinding)
 
-        bindings = self.structuralNavigation.keyBindings
-        for keyBinding in bindings.keyBindings:
+        caretNavBindings = self.caretNavigation.get_bindings()
+        for keyBinding in caretNavBindings.keyBindings:
             keyBindings.add(keyBinding)
 
         liveRegionBindings = self.liveRegionManager.keyBindings
@@ -627,85 +553,41 @@ class Script(default.Script):
         }
 
     def consumesKeyboardEvent(self, keyboardEvent):
-        """Called when a key is pressed on the keyboard.
-
-        Arguments:
-        - keyboardEvent: an instance of input_event.KeyboardEvent
-
-        Returns True if the event is of interest.
-        """
+        """Returns True if the script will consume this keyboard event."""
 
         # We need to do this here. Orca caret and structural navigation
         # often result in the user being repositioned without our getting
         # a corresponding AT-SPI event. Without an AT-SPI event, script.py
         # won't know to dump the generator cache. See bgo#618827.
-        #
         self.generatorCache = {}
 
-        # The reason we override this method is that we only want
-        # to consume keystrokes under certain conditions.  For
-        # example, we only control the arrow keys when we're
-        # managing caret navigation and we're inside document content.
-        #
-        # [[[TODO: WDW - this might be broken when we're inside a
-        # text area that's inside document (or anything else that
-        # we want to allow to control its own destiny).]]]
-
-        user_bindings = None
-        user_bindings_map = _settingsManager.getSetting('keyBindingsMap')
-        if self.__module__ in user_bindings_map:
-            user_bindings = user_bindings_map[self.__module__]
-        elif "default" in user_bindings_map:
-            user_bindings = user_bindings_map["default"]
-
-        consumes = False
-        if user_bindings:
-            handler = user_bindings.getInputHandler(keyboardEvent)
-            if handler and handler.function in self._caretNavigationFunctions:
-                consumes = self.useCaretNavigationModel(keyboardEvent)
-                self._lastCommandWasCaretNav = consumes
-                self._lastCommandWasStructNav = False
-                self._lastCommandWasMouseButton = False
-            elif handler and handler.function in self.structuralNavigation.functions:
-                consumes = self.useStructuralNavigationModel()
-                self._lastCommandWasCaretNav = False
-                self._lastCommandWasStructNav = consumes
-                self._lastCommandWasMouseButton = False
-            elif handler and handler.function in self.liveRegionManager.functions:
-                # This is temporary.
-                consumes = self.useStructuralNavigationModel()
-                self._lastCommandWasCaretNav = False
-                self._lastCommandWasStructNav = consumes
-                self._lastCommandWasMouseButton = False
-            else:
-                consumes = handler != None
-                self._lastCommandWasCaretNav = False
-                self._lastCommandWasStructNav = False
-                self._lastCommandWasMouseButton = False
-        if not consumes:
-            handler = self.keyBindings.getInputHandler(keyboardEvent)
-            if handler and handler.function in self._caretNavigationFunctions:
-                consumes = self.useCaretNavigationModel(keyboardEvent)
-                self._lastCommandWasCaretNav = consumes
-                self._lastCommandWasStructNav = False
-                self._lastCommandWasMouseButton = False
-            elif handler and handler.function in self.structuralNavigation.functions:
-                consumes = self.useStructuralNavigationModel()
-                self._lastCommandWasCaretNav = False
-                self._lastCommandWasStructNav = consumes
-                self._lastCommandWasMouseButton = False
-            elif handler and handler.function in self.liveRegionManager.functions:
-                # This is temporary.
-                consumes = self.useStructuralNavigationModel()
-                self._lastCommandWasCaretNav = False
-                self._lastCommandWasStructNav = consumes
-                self._lastCommandWasMouseButton = False
-            else:
-                consumes = handler != None
-                self._lastCommandWasCaretNav = False
-                self._lastCommandWasStructNav = False
-                self._lastCommandWasMouseButton = False
-        return consumes
+        handler = self.keyBindings.getInputHandler(keyboardEvent)
+        if handler and self.caretNavigation.handles_navigation(handler):
+            consumes = self.useCaretNavigationModel(keyboardEvent)
+            self._lastCommandWasCaretNav = consumes
+            self._lastCommandWasStructNav = False
+            self._lastCommandWasMouseButton = False
+            return consumes
+
+        if handler and handler.function in self.structuralNavigation.functions:
+            consumes = self.useStructuralNavigationModel()
+            self._lastCommandWasCaretNav = False
+            self._lastCommandWasStructNav = consumes
+            self._lastCommandWasMouseButton = False
+            return consumes
+
+        if handler and handler.function in self.liveRegionManager.functions:
+            # This is temporary.
+            consumes = self.useStructuralNavigationModel()
+            self._lastCommandWasCaretNav = False
+            self._lastCommandWasStructNav = consumes
+            self._lastCommandWasMouseButton = False
+            return consumes
+
+        self._lastCommandWasCaretNav = False
+        self._lastCommandWasStructNav = False
+        self._lastCommandWasMouseButton = False
+        return handler != None
 
     # TODO - JD: This needs to be moved out of the scripts.
     def textLines(self, obj, offset=None):
@@ -1711,8 +1593,7 @@ class Script(default.Script):
         return True
 
     def useCaretNavigationModel(self, keyboardEvent):
-        """Returns True if we should do our own caret navigation.
-        """
+        """Returns True if we should do our own caret navigation."""
 
         if not _settingsManager.getSetting('caretNavigationEnabled') \
            or self._inFocusMode:
@@ -1721,15 +1602,9 @@ class Script(default.Script):
         if not self.utilities.inDocumentContent():
             return False
 
-        if keyboardEvent.event_string in ["Page_Up", "Page_Down"]:
-            return False
-
         if keyboardEvent.modifiers & keybindings.SHIFT_MODIFIER_MASK:
             return False
 
-        if not orca_state.locusOfFocus:
-            return False
-
         return True
 
     def useStructuralNavigationModel(self):
@@ -1849,235 +1724,6 @@ class Script(default.Script):
         self.inMouseOverObject = False
         self.lastMouseOverObject = None
 
-    def goNextCharacter(self, inputEvent):
-        """Positions the caret offset to the next character or object
-        in the document window.
-        """
-        [obj, characterOffset] = self.utilities.getCaretContext()
-        while obj:
-            [obj, characterOffset] = self.utilities.findNextCaretInOrder(obj,
-                                                               characterOffset)
-            if obj and obj.getState().contains(pyatspi.STATE_VISIBLE):
-                break
-
-        if not obj:
-            [obj, characterOffset] = self.utilities.getBottomOfFile()
-
-        self.utilities.setCaretPosition(obj, characterOffset)
-        self.speakCharacterAtOffset(obj, characterOffset)
-        self.updateBraille(obj)
-
-    def goPreviousCharacter(self, inputEvent):
-        """Positions the caret offset to the previous character or object
-        in the document window.
-        """
-        [obj, characterOffset] = self.utilities.getCaretContext()
-        while obj:
-            [obj, characterOffset] = self.utilities.findPreviousCaretInOrder(
-                obj, characterOffset)
-            if obj and obj.getState().contains(pyatspi.STATE_VISIBLE):
-                break
-
-        if not obj:
-            [obj, characterOffset] = self.utilities.getTopOfFile()
-
-        self.utilities.setCaretPosition(obj, characterOffset)
-        self.speakCharacterAtOffset(obj, characterOffset)
-        self.updateBraille(obj)
-
-    def goPreviousWord(self, inputEvent):
-        """Positions the caret offset to beginning of the previous
-        word or object in the document window.
-        """
-
-        [obj, characterOffset] = self.utilities.getCaretContext()
-
-        # Make sure we have a word.
-        #
-        [obj, characterOffset] = \
-            self.utilities.findPreviousCaretInOrder(obj, characterOffset)
-
-        contents = self.utilities.getWordContentsAtOffset(obj, characterOffset)
-        if not len(contents):
-            return
-
-        [obj, startOffset, endOffset, string] = contents[0]
-        if len(contents) == 1 \
-           and endOffset - startOffset == 1 \
-           and self.utilities.getCharacterAtOffset(obj, startOffset) == " ":
-            # Our "word" is just a space. This can happen if the previous
-            # word was a mark of punctuation surrounded by whitespace (e.g.
-            # " | ").
-            #
-            [obj, characterOffset] = \
-                self.utilities.findPreviousCaretInOrder(obj, startOffset)
-            contents = self.utilities.getWordContentsAtOffset(obj, characterOffset)
-            if len(contents):
-                [obj, startOffset, endOffset, string] = contents[0]
-
-        self.utilities.setCaretPosition(obj, startOffset)
-        self.updateBraille(obj)
-        self.speakMisspelledIndicator(obj, startOffset)
-        self.speakContents(contents)
-
-    def goNextWord(self, inputEvent):
-        """Positions the caret offset to the end of next word or object
-        in the document window.
-        """
-
-        [obj, characterOffset] = self.utilities.getCaretContext()
-
-        # Make sure we have a word.
-        #
-        characterOffset = max(0, characterOffset)
-        [obj, characterOffset] = \
-            self.utilities.findNextCaretInOrder(obj, characterOffset)
-
-        contents = self.utilities.getWordContentsAtOffset(obj, characterOffset)
-        if not (len(contents) and contents[-1][2]):
-            return
-
-        [obj, startOffset, endOffset, string] = contents[-1]
-        if string and string[-1].isspace():
-            endOffset -= 1
-        self.utilities.setCaretPosition(obj, endOffset)
-        self.updateBraille(obj)
-        self.speakMisspelledIndicator(obj, startOffset)
-        self.speakContents(contents)
-
-    def goPreviousLine(self, inputEvent):
-        """Positions the caret offset at the previous line in the document
-        window, attempting to preserve horizontal caret position.
-
-        Returns True if we actually moved.
-        """
-
-        if self._inSayAll \
-           and _settingsManager.getSetting('rewindAndFastForwardInSayAll'):
-            msg = "INFO: inSayAll and rewindAndFastforwardInSayAll is enabled"
-            debug.println(debug.LEVEL_INFO, msg)
-            return True
-
-        obj, offset = self.utilities.getCaretContext()
-        msg = "INFO: Current context is: %s, %i" % (obj, offset)
-        debug.println(debug.LEVEL_INFO, msg)
-
-        if obj and self.utilities.isZombie(obj):
-            msg = "INFO: Current context obj %s is zombie" % obj
-            debug.println(debug.LEVEL_INFO, msg)
-
-        line = self.utilities.getLineContentsAtOffset(obj, offset)
-        msg = "INFO: Line contents for %s, %i: %s" % (obj, offset, line)
-        debug.println(debug.LEVEL_INFO, msg)
-
-        if not (line and line[0]):
-            return False
-
-        firstObj, firstOffset = line[0][0], line[0][1]
-        msg = "INFO: First context on line is: %s, %i" % (firstObj, firstOffset)
-        debug.println(debug.LEVEL_INFO, msg)
-
-        obj, offset = self.utilities.previousContext(firstObj, firstOffset, True)
-        msg = "INFO: Previous context is: %s, %i" % (obj, offset)
-        debug.println(debug.LEVEL_INFO, msg)
-
-        contents = self.utilities.getLineContentsAtOffset(obj, offset)
-        if not contents:
-            msg = "INFO: Could not get line contents for %s, %i" % (obj, offset)
-            debug.println(debug.LEVEL_INFO, msg)
-            return False
-
-        obj, start = contents[0][0], contents[0][1]
-        self.utilities.setCaretPosition(obj, start)
-        self.displayContents(contents)
-        self.speakContents(contents)
-
-        return True
-
-    def goNextLine(self, inputEvent):
-        """Positions the caret offset at the next line in the document
-        window, attempting to preserve horizontal caret position.
-
-        Returns True if we actually moved.
-        """
-
-        if self._inSayAll \
-           and _settingsManager.getSetting('rewindAndFastForwardInSayAll'):
-            msg = "INFO: inSayAll and rewindAndFastforwardInSayAll is enabled"
-            debug.println(debug.LEVEL_INFO, msg)
-            return True
-
-        obj, offset = self.utilities.getCaretContext()
-        msg = "INFO: Current context is: %s, %i" % (obj, offset)
-        debug.println(debug.LEVEL_INFO, msg)
-
-        if obj and self.utilities.isZombie(obj):
-            msg = "INFO: Current context obj %s is zombie" % obj
-            debug.println(debug.LEVEL_INFO, msg)
-
-        line = self.utilities.getLineContentsAtOffset(obj, offset)
-        msg = "INFO: Line contents for %s, %i: %s" % (obj, offset, line)
-        debug.println(debug.LEVEL_INFO, msg)
-
-        if not (line and line[0]):
-            return False
-
-        lastObj, lastOffset = line[-1][0], line[-1][2] - 1
-        msg = "INFO: Last context on line is: %s, %i" % (lastObj, lastOffset)
-        debug.println(debug.LEVEL_INFO, msg)
-
-        obj, offset = self.utilities.nextContext(lastObj, lastOffset, True)
-        msg = "INFO: Next context is: %s, %i" % (obj, offset)
-        debug.println(debug.LEVEL_INFO, msg)
-
-        contents = self.utilities.getLineContentsAtOffset(obj, offset)
-        if not contents:
-            msg = "INFO: Could not get line contents for %s, %i" % (obj, offset)
-            debug.println(debug.LEVEL_INFO, msg)
-            return False
-
-        obj, start = contents[0][0], contents[0][1]
-        self.utilities.setCaretPosition(obj, start)
-        self.speakContents(contents)
-        self.displayContents(contents)
-        return True
-
-    def goBeginningOfLine(self, inputEvent):
-        """Positions the caret offset at the beginning of the line."""
-
-        [obj, characterOffset] = self.utilities.getCaretContext()
-        line = self.utilities.getLineContentsAtOffset(obj, characterOffset)
-        obj, characterOffset = self.utilities.findFirstCaretContext(line[0][0], line[0][1])
-        self.utilities.setCaretPosition(obj, characterOffset)
-        if not isinstance(orca_state.lastInputEvent, input_event.BrailleEvent):
-            self.speakCharacterAtOffset(obj, characterOffset)
-        self.updateBraille(obj)
-
-    def goEndOfLine(self, inputEvent):
-        """Positions the caret offset at the end of the line."""
-
-        [obj, characterOffset] = self.utilities.getCaretContext()
-        line = self.utilities.getLineContentsAtOffset(obj, characterOffset)
-        obj, characterOffset = line[-1][0], line[-1][2] - 1
-        self.utilities.setCaretPosition(obj, characterOffset)
-        if not isinstance(orca_state.lastInputEvent, input_event.BrailleEvent):
-            self.speakCharacterAtOffset(obj, characterOffset)
-        self.updateBraille(obj)
-
-    def goTopOfFile(self, inputEvent):
-        """Positions the caret offset at the beginning of the document."""
-
-        [obj, characterOffset] = self.utilities.getTopOfFile()
-        self.utilities.setCaretPosition(obj, characterOffset)
-        self.presentLine(obj, characterOffset)
-
-    def goBottomOfFile(self, inputEvent):
-        """Positions the caret offset at the end of the document."""
-
-        [obj, characterOffset] = self.utilities.getBottomOfFile()
-        self.utilities.setCaretPosition(obj, characterOffset)
-        self.presentLine(obj, characterOffset)
-
     def enableStickyFocusMode(self, inputEvent):
         self._inFocusMode = True
         self._focusModeIsSticky = True
@@ -2101,23 +1747,6 @@ class Script(default.Script):
         self._inFocusMode = not self._inFocusMode
         self._focusModeIsSticky = False
 
-    def toggleCaretNavigation(self, inputEvent):
-        """Toggles between Firefox native and Orca caret navigation."""
-
-        if _settingsManager.getSetting('caretNavigationEnabled'):
-            for keyBinding in self.__getArrowBindings().keyBindings:
-                self.keyBindings.removeByHandler(keyBinding.handler)
-            _settingsManager.setSetting('caretNavigationEnabled', False)
-            string = messages.CARET_CONTROL_GECKO
-        else:
-            _settingsManager.setSetting('caretNavigationEnabled', True)
-            for keyBinding in self.__getArrowBindings().keyBindings:
-                self.keyBindings.add(keyBinding)
-            string = messages.CARET_CONTROL_ORCA
-
-        debug.println(debug.LEVEL_CONFIGURATION, string)
-        self.presentMessage(string)
-
     def speakWordUnderMouse(self, acc):
         """Determine if the speak-word-under-mouse capability applies to
         the given accessible.



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