[orca] Create a general (non-app-specific) terminal script



commit 5a6703107310060ac9e4b0d0bee92240aa8ac017
Author: Joanmarie Diggs <jdiggs igalia com>
Date:   Tue Aug 23 04:36:36 2016 -0400

    Create a general (non-app-specific) terminal script
    
    Also work around several issues we're seeing in the accessible text
    support of VTE.

 configure.ac                                       |    4 +-
 src/orca/generator.py                              |    5 -
 src/orca/input_event.py                            |    5 +
 src/orca/script_manager.py                         |   36 +++++-
 src/orca/script_utilities.py                       |   55 +-------
 src/orca/scripts/Makefile.am                       |    2 +-
 src/orca/scripts/apps/Makefile.am                  |    1 -
 src/orca/scripts/apps/__init__.py                  |    1 -
 src/orca/scripts/apps/gnome-terminal/Makefile.am   |    5 -
 src/orca/scripts/apps/gnome-terminal/script.py     |  107 ---------------
 src/orca/scripts/default.py                        |   39 +-----
 src/orca/scripts/terminal/Makefile.am              |    8 +
 .../{apps/gnome-terminal => terminal}/__init__.py  |    8 +-
 src/orca/scripts/terminal/braille_generator.py     |   38 +++++
 src/orca/scripts/terminal/script.py                |  144 ++++++++++++++++++++
 src/orca/scripts/terminal/script_utilities.py      |  110 +++++++++++++++
 src/orca/scripts/terminal/speech_generator.py      |   38 +++++
 src/orca/scripts/toolkits/gtk/script.py            |    5 -
 18 files changed, 393 insertions(+), 218 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index a0979d6..230f6ff 100644
--- a/configure.ac
+++ b/configure.ac
@@ -106,7 +106,6 @@ src/orca/scripts/apps/gnome-panel/Makefile
 src/orca/scripts/apps/gnome-screensaver-dialog/Makefile
 src/orca/scripts/apps/gnome-search-tool/Makefile
 src/orca/scripts/apps/gnome-shell/Makefile
-src/orca/scripts/apps/gnome-terminal/Makefile
 src/orca/scripts/apps/gnome-window-properties/Makefile
 src/orca/scripts/apps/gtk-window-decorator/Makefile
 src/orca/scripts/apps/Instantbird/Makefile
@@ -122,8 +121,9 @@ src/orca/scripts/apps/soffice/Makefile
 src/orca/scripts/apps/SeaMonkey/Makefile
 src/orca/scripts/apps/Thunderbird/Makefile
 src/orca/scripts/apps/xfwm4/Makefile
-src/orca/scripts/toolkits/Makefile
+src/orca/scripts/terminal/Makefile
 src/orca/scripts/web/Makefile
+src/orca/scripts/toolkits/Makefile
 src/orca/scripts/toolkits/Gecko/Makefile
 src/orca/scripts/toolkits/J2SE-access-bridge/Makefile
 src/orca/scripts/toolkits/clutter/Makefile
diff --git a/src/orca/generator.py b/src/orca/generator.py
index 38a38ba..cf193ad 100644
--- a/src/orca/generator.py
+++ b/src/orca/generator.py
@@ -387,11 +387,6 @@ class Generator:
 
         role = args.get('role', obj.getRole())
 
-        # The text in the description is the same as the text in the page
-        # tab and similar to (and sometimes the same as) the prompt.
-        if role == pyatspi.ROLE_TERMINAL:
-            return []
-
         # Unity Panel Service menubar items are labels which claim focus and
         # have an accessible description of the text + underscore symbol used
         # to create the mnemonic. We'll work around that here for now.
diff --git a/src/orca/input_event.py b/src/orca/input_event.py
index d0fb0f2..e4a79c6 100644
--- a/src/orca/input_event.py
+++ b/src/orca/input_event.py
@@ -458,6 +458,11 @@ class KeyboardEvent(InputEvent):
 
         return keynames.getKeyName(self.event_string)
 
+    def getObject(self):
+        """Returns the object believed to be associated with this key event."""
+
+        return self._obj
+
     def _getUserHandler(self):
         # TODO - JD: This should go away once plugin support is in place.
         try:
diff --git a/src/orca/script_manager.py b/src/orca/script_manager.py
index 131868c..0b408ba 100644
--- a/src/orca/script_manager.py
+++ b/src/orca/script_manager.py
@@ -37,6 +37,7 @@ class ScriptManager:
         debug.println(debug.LEVEL_INFO, 'SCRIPT MANAGER: Initializing', True)
         self.appScripts = {}
         self.toolkitScripts = {}
+        self.customScripts = {}
         self._appModules = apps.__all__
         self._toolkitModules = toolkits.__all__
         self._defaultScript = None
@@ -62,8 +63,6 @@ class ScriptManager:
              'gnome-calculator': 'gcalctool',
              'marco':            'metacity',
              'Nereid':           'Banshee',
-             'vte':              'gnome-terminal',
-             'gnome-terminal-server': 'gnome-terminal',
              'mate-notification-daemon': 'notification-daemon',
             }
 
@@ -90,6 +89,7 @@ class ScriptManager:
         self.setActiveScript(None, "deactivate")
         self.appScripts = {}
         self.toolkitScripts = {}
+        self.customScripts = {}
         debug.println(debug.LEVEL_INFO, 'SCRIPT MANAGER: Deactivated', True)
 
     def getModuleName(self, app):
@@ -139,6 +139,17 @@ class ScriptManager:
 
         return name
 
+    def _scriptForRole(self, obj):
+        try:
+            role = obj.getRole()
+        except:
+            return ''
+
+        if role == pyatspi.ROLE_TERMINAL:
+            return 'terminal'
+
+        return ''
+
     def _newNamedScript(self, app, name):
         """Attempts to locate and load the named module. If successful, returns
         a script based on this module."""
@@ -221,9 +232,19 @@ class ScriptManager:
         Returns an instance of a Script.
         """
 
+        customScript = None
         appScript = None
         toolkitScript = None
 
+        roleName = self._scriptForRole(obj)
+        if roleName:
+            customScripts = self.customScripts.get(app, {})
+            customScript = customScripts.get(roleName)
+            if not customScript:
+                customScript = self._newNamedScript(app, roleName)
+                customScripts[roleName] = customScript
+            self.customScripts[app] = customScripts
+
         objToolkit = self._toolkitForObject(obj)
         if objToolkit:
             toolkitScripts = self.toolkitScripts.get(app, {})
@@ -247,6 +268,9 @@ class ScriptManager:
             debug.println(debug.LEVEL_WARNING, msg, True)
             appScript = self.getDefaultScript()
 
+        if customScript:
+            return customScript
+
         # Only defer to the toolkit script for this object if the app script
         # is based on a different toolkit.
         if toolkitScript \
@@ -301,6 +325,14 @@ class ScriptManager:
                 for toolkitScript in toolkitScripts.values():
                     del toolkitScript
 
+            try:
+                customScripts = self.customScripts.pop(app)
+            except KeyError:
+                pass
+            else:
+                for customScript in customScripts.values():
+                    del customScript
+
             del app
 
 _manager = ScriptManager()
diff --git a/src/orca/script_utilities.py b/src/orca/script_utilities.py
index 389f0ca..df5c8fa 100644
--- a/src/orca/script_utilities.py
+++ b/src/orca/script_utilities.py
@@ -1356,8 +1356,7 @@ class Utilities:
 
         return obj and obj.getRole() in (pyatspi.ROLE_TEXT,
                                          pyatspi.ROLE_ENTRY,
-                                         pyatspi.ROLE_PARAGRAPH,
-                                         pyatspi.ROLE_TERMINAL)
+                                         pyatspi.ROLE_PARAGRAPH)
 
     @staticmethod
     def knownApplications():
@@ -2412,9 +2411,6 @@ class Utilities:
         if event.any_data:
             return event.any_data
 
-        msg = "ERROR: Broken text insertion event"
-        debug.println(debug.LEVEL_INFO, msg, True)
-
         try:
             role = event.source.getRole()
         except:
@@ -2422,6 +2418,9 @@ class Utilities:
             debug.println(debug.LEVEL_INFO, msg, True)
             role = None
 
+        msg = "ERROR: Broken text insertion event"
+        debug.println(debug.LEVEL_INFO, msg, True)
+
         if role == pyatspi.ROLE_PASSWORD_TEXT:
             text = self.queryNonEmptyText(event.source)
             if text:
@@ -2626,8 +2625,6 @@ class Utilities:
 
         obj = orca_state.locusOfFocus
         role = obj.getRole()
-        if role == pyatspi.ROLE_TERMINAL:
-            return True
         if role == pyatspi.ROLE_PASSWORD_TEXT:
             return False
 
@@ -4102,53 +4099,9 @@ class Utilities:
         return role in roles
 
     def treatEventAsTerminalCommand(self, event):
-        try:
-            role = event.source.getRole()
-        except:
-            msg = "ERROR: Exception getting role of %s" % event.source
-            debug.println(debug.LEVEL_INFO, msg, True)
-            return False
-
-        if role != pyatspi.ROLE_TERMINAL:
-            return False
-
-        if self.lastInputEventWasCommand():
-            return True
-
-        if event.type.startswith("object:text-changed:insert") and event.any_data.strip():
-            keyString, mods = self.lastKeyAndModifiers()
-            if keyString in ["Return", "Tab", "space", " "]:
-                return True
-            if mods & keybindings.ALT_MODIFIER_MASK:
-                return True
-            if len(event.any_data) > 1 and self.lastInputEventWasPrintableKey():
-                return True
-
         return False
 
     def treatEventAsTerminalNoise(self, event):
-        try:
-            role = event.source.getRole()
-        except:
-            msg = "ERROR: Exception getting role of %s" % event.source
-            debug.println(debug.LEVEL_INFO, msg, True)
-            return False
-
-        if role != pyatspi.ROLE_TERMINAL:
-            return False
-
-        if self.lastInputEventWasCommand():
-            return False
-
-        if event.type.startswith("object:text-changed:delete") and event.any_data.strip():
-            keyString, mods = self.lastKeyAndModifiers()
-            if keyString in ["Return", "Tab", "space", " "]:
-                return True
-            if mods & keybindings.ALT_MODIFIER_MASK:
-                return True
-            if len(event.any_data) > 1 and self.lastInputEventWasPrintableKey():
-                return True
-
         return False
 
     def isPresentableTextChangedEventForLocusOfFocus(self, event):
diff --git a/src/orca/scripts/Makefile.am b/src/orca/scripts/Makefile.am
index bd5c6c3..60c4a4a 100644
--- a/src/orca/scripts/Makefile.am
+++ b/src/orca/scripts/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = apps toolkits web
+SUBDIRS = apps toolkits terminal web
 
 orca_python_PYTHON = \
        __init__.py \
diff --git a/src/orca/scripts/apps/Makefile.am b/src/orca/scripts/apps/Makefile.am
index c485655..3b1b955 100644
--- a/src/orca/scripts/apps/Makefile.am
+++ b/src/orca/scripts/apps/Makefile.am
@@ -16,7 +16,6 @@ SUBDIRS = \
        gnome-screensaver-dialog \
        gnome-shell \
        gnome-search-tool \
-       gnome-terminal \
        gnome-window-properties \
        gtk-window-decorator \
        Instantbird \
diff --git a/src/orca/scripts/apps/__init__.py b/src/orca/scripts/apps/__init__.py
index 38d33e9..95ecd98 100644
--- a/src/orca/scripts/apps/__init__.py
+++ b/src/orca/scripts/apps/__init__.py
@@ -15,7 +15,6 @@ __all__ = ['Banshee',
            'gnome-screensaver-dialog',
            'gnome-search-tool',
            'gnome-shell',
-           'gnome-terminal',
            'gnome-window-properties',
            'gtk-window-decorator',
            'Instantbird',
diff --git a/src/orca/scripts/default.py b/src/orca/scripts/default.py
index 2c34ecf..6f44852 100644
--- a/src/orca/scripts/default.py
+++ b/src/orca/scripts/default.py
@@ -2507,11 +2507,6 @@ class Script(script.Script):
         if not self.utilities.isPresentableTextChangedEventForLocusOfFocus(event):
             return
 
-        if self.utilities.treatEventAsTerminalNoise(event):
-            msg = "DEFAULT: Deletion is believed to be noise"
-            debug.println(debug.LEVEL_INFO, msg, True)
-            return
-
         self.utilities.handleUndoTextEvent(event)
 
         orca.setLocusOfFocus(event, event.source, False)
@@ -2585,9 +2580,6 @@ class Script(script.Script):
         if self.utilities.lastInputEventWasCommand():
             msg = "DEFAULT: Insertion is believed to be due to command"
             debug.println(debug.LEVEL_INFO, msg, True)
-        elif self.utilities.treatEventAsTerminalCommand(event):
-            msg = "DEFAULT: Insertion is believed to be due to terminal command"
-            debug.println(debug.LEVEL_INFO, msg, True)
         elif self.utilities.isMiddleMouseButtonTextInsertionEvent(event):
             msg = "DEFAULT: Insertion is believed to be due to middle mouse button"
             debug.println(debug.LEVEL_INFO, msg, True)
@@ -3472,14 +3464,6 @@ class Script(script.Script):
                     [lineString, startOffset, endOffset] = \
                         text.getTextAtOffset(offset, mode)
 
-                # [[[WDW - HACK: well...gnome-terminal sometimes wants to
-                # give us outrageous values back from getTextAtOffset
-                # (see http://bugzilla.gnome.org/show_bug.cgi?id=343133),
-                # so we try to handle it.]]]
-                #
-                if startOffset < 0:
-                    break
-
                 # [[[WDW - HACK: this is here because getTextAtOffset
                 # tends not to be implemented consistently across toolkits.
                 # Sometimes it behaves properly (i.e., giving us an endOffset
@@ -3714,25 +3698,7 @@ class Script(script.Script):
         if role == pyatspi.ROLE_PASSWORD_TEXT and not event.isLockingKey():
             return False
 
-        # Worst. Hack. EVER. We have no reliable way of knowing a password is
-        # being entered into a terminal -- other than the fact that the text
-        # typed ain't there. As a result, we have to do special things when
-        # not in special modes. :( See bgo 668025.
-        if role == pyatspi.ROLE_TERMINAL:
-            if not event.isPressedKey():
-                try:
-                    text = orca_state.locusOfFocus.queryText()
-                    o = text.caretOffset
-                    string = text.getText(o-1, o)
-                except:
-                    pass
-                else:
-                    if not event.event_string in [string, 'space']:
-                        return False
-            elif not (orca_state.learnModeEnabled or event.isLockingKey()):
-                return False
-
-        elif not event.isPressedKey():
+        if not event.isPressedKey():
             return False
 
         braille.displayKeyEvent(event)
@@ -3748,6 +3714,9 @@ class Script(script.Script):
         if event.isPrintableKey():
             string = event.event_string
 
+        msg = "DEFAULT: Presenting keyboard event"
+        debug.println(debug.LEVEL_INFO, msg, True)
+
         voice = self.speechGenerator.voice(string=string)
         speech.speakKeyEvent(event, voice)
         return True
diff --git a/src/orca/scripts/terminal/Makefile.am b/src/orca/scripts/terminal/Makefile.am
new file mode 100644
index 0000000..9a2b4a2
--- /dev/null
+++ b/src/orca/scripts/terminal/Makefile.am
@@ -0,0 +1,8 @@
+orca_python_PYTHON = \
+       __init__.py \
+       braille_generator.py \
+       script.py \
+       script_utilities.py \
+       speech_generator.py
+
+orca_pythondir=$(pkgpythondir)/scripts/terminal
diff --git a/src/orca/scripts/apps/gnome-terminal/__init__.py b/src/orca/scripts/terminal/__init__.py
similarity index 78%
rename from src/orca/scripts/apps/gnome-terminal/__init__.py
rename to src/orca/scripts/terminal/__init__.py
index 52f01ef..b616fd0 100644
--- a/src/orca/scripts/apps/gnome-terminal/__init__.py
+++ b/src/orca/scripts/terminal/__init__.py
@@ -1,6 +1,7 @@
 # Orca
 #
-# Copyright 2005-2008 Sun Microsystems Inc.
+# Copyright 2016 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
@@ -17,7 +18,8 @@
 # Free Software Foundation, Inc., Franklin Street, Fifth Floor,
 # Boston MA  02110-1301 USA.
 
-"""Custom script for gnome-terminal."""
-
+from .braille_generator import BrailleGenerator
 from .script import Script
+from .speech_generator import SpeechGenerator
+from .script_utilities import Utilities
 
diff --git a/src/orca/scripts/terminal/braille_generator.py b/src/orca/scripts/terminal/braille_generator.py
new file mode 100644
index 0000000..d52b099
--- /dev/null
+++ b/src/orca/scripts/terminal/braille_generator.py
@@ -0,0 +1,38 @@
+# Orca
+#
+# Copyright 2016 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.
+
+__id__        = "$Id$"
+__version__   = "$Revision$"
+__date__      = "$Date$"
+__copyright__ = "Copyright (c) 2016 Igalia, S.L."
+__license__   = "LGPL"
+
+from orca import braille_generator
+
+
+class BrailleGenerator(braille_generator.BrailleGenerator):
+
+    def __init__(self, script):
+        super().__init__(script)
+
+    def _generateDescription(self, obj, **args):
+        # The text in the description is the same as the text in the page
+        # tab and similar to (and sometimes the same as) the prompt.
+        return []
diff --git a/src/orca/scripts/terminal/script.py b/src/orca/scripts/terminal/script.py
new file mode 100644
index 0000000..66f33a7
--- /dev/null
+++ b/src/orca/scripts/terminal/script.py
@@ -0,0 +1,144 @@
+# Orca
+#
+# Copyright 2016 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.
+
+__id__        = "$Id$"
+__version__   = "$Revision$"
+__date__      = "$Date$"
+__copyright__ = "Copyright (c) 2016 Igalia, S.L."
+__license__   = "LGPL"
+
+from orca import debug
+from orca import orca
+from orca import orca_state
+from orca import speech
+from orca.scripts import default
+
+from .braille_generator import BrailleGenerator
+from .speech_generator import SpeechGenerator
+from .script_utilities import Utilities
+
+
+class Script(default.Script):
+
+    def __init__(self, app):
+        super().__init__(app)
+        self.presentIfInactive = False
+
+    def deactivate(self):
+        """Called when this script is deactivated."""
+
+        self.utilities.clearCache()
+        super().deactivate()
+
+    def getBrailleGenerator(self):
+        """Returns the braille generator for this script."""
+
+        return BrailleGenerator(self)
+
+    def getSpeechGenerator(self):
+        """Returns the speech generator for this script."""
+
+        return SpeechGenerator(self)
+
+    def getUtilities(self):
+        """Returns the utilites for this script."""
+
+        return Utilities(self)
+
+    def locusOfFocusChanged(self, event, oldFocus, newFocus):
+        """Handles changes of focus of interest to the script."""
+
+        super().locusOfFocusChanged(event, oldFocus, newFocus)
+
+    def onCaretMoved(self, event):
+        """Callback for object:text-caret-moved accessibility events."""
+
+        super().onCaretMoved(event)
+
+    def onFocus(self, event):
+        """Callback for focus: accessibility events."""
+
+        # https://bugzilla.gnome.org/show_bug.cgi?id=748311
+        orca.setLocusOfFocus(event, event.source)
+
+    def onTextDeleted(self, event):
+        """Callback for object:text-changed:delete accessibility events."""
+
+        if self.utilities.treatEventAsTerminalNoise(event):
+            msg = "TERMINAL: Deletion is believed to be noise"
+            debug.println(debug.LEVEL_INFO, msg, True)
+            return
+
+        super().onTextDeleted(event)
+
+    def onTextInserted(self, event):
+        """Callback for object:text-changed:insert accessibility events."""
+
+        if not self.utilities.treatEventAsTerminalCommand(event):
+            super().onTextInserted(event)
+            return
+
+        msg = "TERMINAL: Insertion is believed to be due to terminal command"
+        debug.println(debug.LEVEL_INFO, msg, True)
+
+        oldString = event.any_data
+        newString = self.utilities.insertedText(event)
+        if oldString != newString:
+            msg = "TERMINAL: Adjusting event string to: %s" % newString
+            debug.println(debug.LEVEL_INFO, msg, True)
+
+        if len(newString) == 1:
+            self.speakCharacter(newString)
+        else:
+            voice = self.speechGenerator.voice(string=newString)
+            speech.speak(newString, voice)
+
+    def presentKeyboardEvent(self, event):
+        if orca_state.learnModeEnabled or not event.isPrintableKey():
+            return super().presentKeyboardEvent(event)
+
+        if event.isPressedKey():
+            return False
+
+        self._sayAllIsInterrupted = False
+        self.utilities.clearCachedCommandState()
+        if event.shouldEcho == False or event.isOrcaModified():
+            return False
+
+        # We have no reliable way of knowing a password is being entered into
+        # a terminal -- other than the fact that the text typed isn't there.
+        try:
+            text = event.getObject().queryText()
+            offset = text.caretOffset
+            prevChar = text.getText(offset - 1, offset)
+            char = text.getText(offset, offset + 1)
+        except:
+            return False
+
+        string = event.event_string
+        if string not in [prevChar, "space", char]:
+            return False
+
+        msg = "TERMINAL: Presenting keyboard event %s" % string
+        debug.println(debug.LEVEL_INFO, msg, True)
+
+        voice = self.speechGenerator.voice(string=string)
+        speech.speakKeyEvent(event, voice)
+        return True
diff --git a/src/orca/scripts/terminal/script_utilities.py b/src/orca/scripts/terminal/script_utilities.py
new file mode 100644
index 0000000..3a8e1bd
--- /dev/null
+++ b/src/orca/scripts/terminal/script_utilities.py
@@ -0,0 +1,110 @@
+# Orca
+#
+# Copyright 2016 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.
+
+__id__        = "$Id$"
+__version__   = "$Revision$"
+__date__      = "$Date$"
+__copyright__ = "Copyright (c) 2016 Igalia, S.L."
+__license__   = "LGPL"
+
+import pyatspi
+
+from orca import debug
+from orca import keybindings
+from orca import script_utilities
+from orca import settings_manager
+
+_settingsManager = settings_manager.getManager()
+
+
+class Utilities(script_utilities.Utilities):
+
+    def __init__(self, script):
+        super().__init__(script)
+
+    def clearCache(self):
+        pass
+
+    def insertedText(self, event):
+        if len(event.any_data) == 1:
+            return event.any_data
+
+        try:
+            text = event.source.queryText()
+        except:
+            msg = "ERROR: Exception querying text for %s" % event.source
+            debug.println(debug.LEVEL_INFO, msg, True)
+            return event.any_data
+
+        start, end = event.detail1, event.detail1 + len(event.any_data)
+        boundary = pyatspi.TEXT_BOUNDARY_LINE_START
+
+        firstLine = text.getTextAtOffset(start, boundary)
+        if firstLine != ("", 0, 0):
+            start = firstLine[1]
+
+        lastLine = text.getTextAtOffset(end - 1, boundary)
+        if lastLine != ("", 0, 0):
+            end = min(lastLine[2], text.caretOffset)
+
+        return text.getText(start, end)
+
+    def isTextArea(self, obj):
+        return True
+
+    def treatEventAsTerminalCommand(self, event):
+        if self.lastInputEventWasCommand():
+            return True
+
+        if event.type.startswith("object:text-changed:insert") and event.any_data.strip():
+            keyString, mods = self.lastKeyAndModifiers()
+            if keyString in ["Return", "Tab", "space", " "]:
+                return True
+            if mods & keybindings.ALT_MODIFIER_MASK:
+                return True
+            if len(event.any_data) > 1 and self.lastInputEventWasPrintableKey():
+                return True
+
+        return False
+
+    def treatEventAsTerminalNoise(self, event):
+        if self.lastInputEventWasCommand():
+            return False
+
+        if event.type.startswith("object:text-changed:delete") and event.any_data.strip():
+            keyString, mods = self.lastKeyAndModifiers()
+            if keyString in ["Return", "Tab", "space", " "]:
+                return True
+            if mods & keybindings.ALT_MODIFIER_MASK:
+                return True
+            if len(event.any_data) > 1 and self.lastInputEventWasPrintableKey():
+                return True
+
+        return False
+
+    def willEchoCharacter(self, event):
+        if not _settingsManager.getSetting("enableEchoByCharacter"):
+            return False
+
+        if len(event.event_string) != 1 \
+           or event.modifiers & keybindings.ORCA_CTRL_MODIFIER_MASK:
+            return False
+
+        return True
diff --git a/src/orca/scripts/terminal/speech_generator.py b/src/orca/scripts/terminal/speech_generator.py
new file mode 100644
index 0000000..e6167b1
--- /dev/null
+++ b/src/orca/scripts/terminal/speech_generator.py
@@ -0,0 +1,38 @@
+# Orca
+#
+# Copyright 2016 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.
+
+__id__        = "$Id$"
+__version__   = "$Revision$"
+__date__      = "$Date$"
+__copyright__ = "Copyright (c) 2016 Igalia, S.L."
+__license__   = "LGPL"
+
+from orca import speech_generator
+
+
+class SpeechGenerator(speech_generator.SpeechGenerator):
+
+    def __init__(self, script):
+        super().__init__(script)
+
+    def _generateDescription(self, obj, **args):
+        # The text in the description is the same as the text in the page
+        # tab and similar to (and sometimes the same as) the prompt.
+        return []
diff --git a/src/orca/scripts/toolkits/gtk/script.py b/src/orca/scripts/toolkits/gtk/script.py
index 164677c..a6507ca 100644
--- a/src/orca/scripts/toolkits/gtk/script.py
+++ b/src/orca/scripts/toolkits/gtk/script.py
@@ -129,11 +129,6 @@ class Script(default.Script):
             orca.setLocusOfFocus(event, event.source)
             return
 
-        # https://bugzilla.gnome.org/show_bug.cgi?id=748311
-        if role == pyatspi.ROLE_TERMINAL:
-            orca.setLocusOfFocus(event, event.source)
-            return
-
         # https://bugzilla.gnome.org/show_bug.cgi?id=720987
         if role == pyatspi.ROLE_TABLE_COLUMN_HEADER:
             orca.setLocusOfFocus(event, event.source)


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