[orca] Work on bgo#575784 - accented characters are not echoed correctly



commit 6be078c9ad7889b4710a1f120f149d3cb44f0153
Author: Willie Walker <william walker sun com>
Date:   Mon Jul 27 11:08:11 2009 -0400

    Work on bgo#575784 - accented characters are not echoed correctly
    
    There is one remaining issue with this, which is that gnome-terminal
    does not do key echo.  Instead, it will always do character echo if
    either key echo or character echo are enabled.

 src/orca/default.py                     |   33 +++++++++++++
 src/orca/keynames.py                    |   72 ++++++++++++++++++++++------
 src/orca/orca-setup.ui                  |   56 ++++++++++++++++++++++
 src/orca/orca.py                        |   77 +++++++++++++++++++++++++------
 src/orca/orca_gui_prefs.py              |   28 +++++++++++
 src/orca/scripts/apps/gnome-terminal.py |   16 ++++++-
 src/orca/settings.py                    |   12 +++++
 7 files changed, 263 insertions(+), 31 deletions(-)
---
diff --git a/src/orca/default.py b/src/orca/default.py
index e48f23c..34f5a43 100644
--- a/src/orca/default.py
+++ b/src/orca/default.py
@@ -3643,6 +3643,30 @@ class Script(script.Script):
         else:
             speech.speak(character, voice, False)
 
+    def willEchoCharacter(self, event):
+        """Given a keyboard event containing an alphanumeric key,
+        determine if the script is likely to echo it as a character.
+        """
+
+        # The check here in English is something like this: "If this
+        # character echo is enabled, then character echo is likely to
+        # happen if the locus of focus is a focusable editable text
+        # area or terminal and neither of the Ctrl, Alt, or Orca
+        # modifiers are pressed.  If that's the case, then character
+        # echo will kick in for us."
+        #
+        return  settings.enableEchoByCharacter \
+                and orca_state.locusOfFocus \
+                and (self.isTextArea(orca_state.locusOfFocus)\
+                     or orca_state.locusOfFocus.getRole() \
+                        == pyatspi.ROLE_ENTRY) \
+                and (orca_state.locusOfFocus.getRole() \
+                     == pyatspi.ROLE_TERMINAL \
+                     or (not self.isReadOnlyTextArea(orca_state.locusOfFocus) \
+                         and (orca_state.locusOfFocus.getState().contains( \
+                                  pyatspi.STATE_FOCUSABLE)))) \
+                and not (event.modifiers & settings.ORCA_CTRL_MODIFIER_MASK)
+
     def onTextInserted(self, event):
         """Called whenever text is inserted into an object.
 
@@ -3720,6 +3744,15 @@ class Script(script.Script):
              orca_state.lastInputEvent.button == "2":
             speakThis = True
 
+        # We might need to echo this if it is a single character.
+        #
+        speakThis = speakThis \
+                    or (settings.enableEchoByCharacter \
+                        and text \
+                        and event.source.getRole() \
+                            != pyatspi.ROLE_PASSWORD_TEXT \
+                        and len(text.decode("UTF-8")) == 1)
+
         if speakThis:
             if text.isupper():
                 speech.speak(text, self.voices[settings.UPPERCASE_VOICE])
diff --git a/src/orca/keynames.py b/src/orca/keynames.py
index 289cb55..c1351e6 100644
--- a/src/orca/keynames.py
+++ b/src/orca/keynames.py
@@ -144,67 +144,67 @@ __keynames["Return"] = _("return")
 #
 __keynames["KP_Enter"] = _("enter")
 
-# Translators: this is how someone would speak the name of the up arrow key 
+# Translators: this is how someone would speak the name of the up arrow key
 #
 __keynames["Up"] = _("up")
 
-# Translators: this is how someone would speak the name of the up arrow key 
+# Translators: this is how someone would speak the name of the up arrow key
 #
 __keynames["KP_Up"] = _("up")
 
-# Translators: this is how someone would speak the name of the down arrow key 
+# Translators: this is how someone would speak the name of the down arrow key
 #
 __keynames["Down"] = _("down")
 
-# Translators: this is how someone would speak the name of the down arrow key 
+# Translators: this is how someone would speak the name of the down arrow key
 #
 __keynames["KP_Down"] = _("down")
 
-# Translators: this is how someone would speak the name of the left arrow key 
+# Translators: this is how someone would speak the name of the left arrow key
 #
 __keynames["Left"] = _("left")
 
-# Translators: this is how someone would speak the name of the left arrow key 
+# Translators: this is how someone would speak the name of the left arrow key
 #
 __keynames["KP_Left"] = _("left")
 
-# Translators: this is how someone would speak the name of the right arrow key 
+# Translators: this is how someone would speak the name of the right arrow key
 #
 __keynames["Right"] = _("right")
 
-# Translators: this is how someone would speak the name of the right arrow key 
+# Translators: this is how someone would speak the name of the right arrow key
 #
 __keynames["KP_Right"] = _("right")
 
-# Translators: this is how someone would speak the name of the left super key 
+# Translators: this is how someone would speak the name of the left super key
 #
 __keynames["Super_L"] = _("left super")
 
-# Translators: this is how someone would speak the name of the right super key 
+# Translators: this is how someone would speak the name of the right super key
 #
 __keynames["Super_R"] = _("right super")
 
-# Translators: this is how someone would speak the name of the menu key 
+# Translators: this is how someone would speak the name of the menu key
 #
 __keynames["Menu"] = _("menu")
 
-# Translators: this is how someone would speak the name of the ISO shift key 
+# Translators: this is how someone would speak the name of the ISO shift key
 #
 __keynames["ISO_Level3_Shift"] = _("ISO level 3 shift")
 
-# Translators: this is how someone would speak the name of the help key 
+# Translators: this is how someone would speak the name of the help key
 #
 __keynames["Help"] = _("help")
 
-# Translators: this is how someone would speak the name of the multi key 
+# Translators: this is how someone would speak the name of the multi key
 #
 __keynames["Multi_key"] = _("multi")
 
-# Translators: this is how someone would speak the name of the mode switch key 
+# Translators: this is how someone would speak the name of the mode switch key
 #
 __keynames["Mode_switch"] = _("mode switch")
 
-# Translators: this is how someone would speak the name of the escape key 
+# Translators: this is how someone would speak the name of the escape key
 #
 __keynames["Escape"] = _("escape")
 
@@ -244,6 +244,46 @@ __keynames["KP_End"] = _("end")
 #
 __keynames["KP_Begin"] = _("begin")
 
+# Translators: this is how someone would speak the name of the
+# non-spacing diacritical key for the grave glyph
+#
+__keynames["dead_grave"] = _("grave")
+
+# Translators: this is how someone would speak the name of the
+# non-spacing diacritical key for the acute glyph
+#
+__keynames["dead_acute"] = _("acute")
+
+# Translators: this is how someone would speak the name of the
+# non-spacing diacritical key for the circumflex glyph
+#
+__keynames["dead_circumflex"] = _("circumflex")
+
+# Translators: this is how someone would speak the name of the
+# non-spacing diacritical key for the tilde glyph
+#
+__keynames["dead_tilde"] = _("tilde")
+
+# Translators: this is how someone would speak the name of the
+# non-spacing diacritical key for the diaeresis glyph
+#
+__keynames["dead_diaeresis"] = _("diaeresis")
+
+# Translators: this is how someone would speak the name of the
+# non-spacing diacritical key for the ring glyph
+#
+__keynames["dead_abovering"] = _("ring")
+
+# Translators: this is how someone would speak the name of the
+# non-spacing diacritical key for the cedilla glyph
+#
+__keynames["dead_cedilla"] = _("cedilla")
+
+# Translators: this is how someone would speak the name of the
+# non-spacing diacritical key for the stroke glyph
+#
+__keynames["dead_stroke"] = _("stroke")
+
 def getKeyName(key):
     """Given a keyboard key, return its name as people might refer to it
     in ordinary conversation.
diff --git a/src/orca/orca-setup.ui b/src/orca/orca-setup.ui
index 4345764..74a7986 100644
--- a/src/orca/orca-setup.ui
+++ b/src/orca/orca-setup.ui
@@ -2546,6 +2546,38 @@
                       </packing>
                     </child>
                     <child>
+                      <object class="GtkAlignment" id="alignment59">
+                        <property name="visible">True</property>
+                        <property name="xalign">0.5</property>
+                        <property name="yalign">0.5</property>
+                        <property name="xscale">1</property>
+                        <property name="yscale">1</property>
+                        <property name="top_padding">0</property>
+                        <property name="bottom_padding">0</property>
+                        <property name="left_padding">12</property>
+                        <property name="right_padding">0</property>
+                        <child>
+                          <object class="GtkCheckButton" id="diacriticalCheckbutton">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="label" translatable="yes">Enable non-spacing _diacritical keys</property>
+                            <property name="use_underline">True</property>
+                            <property name="relief">GTK_RELIEF_NORMAL</property>
+                            <property name="focus_on_click">True</property>
+                            <property name="active">False</property>
+                            <property name="inconsistent">False</property>
+                            <property name="draw_indicator">True</property>
+                            <signal handler="diacriticalKeysChecked" last_modification_time="Tue, 30 Oct 2007 20:11:26 GMT" name="toggled"/>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="padding">0</property>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                      </packing>
+                    </child>
+                    <child>
                       <object class="GtkAlignment" id="alignment49">
                         <property name="visible">True</property>
                         <property name="xalign">0.5</property>
@@ -2557,6 +2589,30 @@
                         <property name="left_padding">0</property>
                         <property name="right_padding">0</property>
                         <child>
+                          <object class="GtkCheckButton" id="echoByCharacterCheckbutton">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="label" translatable="yes">Enable echo by cha_racter</property>
+                            <property name="use_underline">True</property>
+                            <property name="relief">GTK_RELIEF_NORMAL</property>
+                            <property name="focus_on_click">True</property>
+                            <property name="active">False</property>
+                            <property name="inconsistent">False</property>
+                            <property name="draw_indicator">True</property>
+                            <signal handler="echoByCharacterChecked" last_modification_time="Thu, 20 Apr 2006 15:29:51 GMT" name="toggled"/>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="padding">0</property>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkAlignment" id="alignment49">
+                        <property name="visible">True</property>
+                        <child>
                           <object class="GtkCheckButton" id="echoByWordCheckbutton">
                             <property name="visible">True</property>
                             <property name="can_focus">True</property>
diff --git a/src/orca/orca.py b/src/orca/orca.py
index 8106e5f..8324d1c 100644
--- a/src/orca/orca.py
+++ b/src/orca/orca.py
@@ -489,6 +489,22 @@ def isNavigationKey(event_string):
                   "orca.isNavigationKey: returning: %s" % reply)
     return reply
 
+def isDiacriticalKey(event_string):
+    """Return an indication of whether this is a non-spacing diacritical key
+
+    Arguments:
+    - event: the event string
+    - modifiers: key modifiers state
+
+    Returns True if this is a non-spacing diacritical key
+    """
+
+    reply = event_string.startswith("dead_")
+
+    debug.println(debug.LEVEL_FINEST,
+                  "orca.isDiacriticalKey: returning: %s" % reply)
+    return reply
+
 class KeyEventType:
     """Definition of available key event types."""
 
@@ -516,6 +532,9 @@ class KeyEventType:
     # A navigation key event.
     NAVIGATION = 'NAVIGATION'
 
+    # A diacritical key event.
+    DIACRITICAL = 'DIACRITICAL'
+
     def __init__(self):
         pass
 
@@ -536,7 +555,7 @@ def keyEcho(event):
     #
     if orca_state.locusOfFocus \
         and (orca_state.locusOfFocus.getRole() == pyatspi.ROLE_PASSWORD_TEXT):
-        return
+        return False
 
     event_string = event.event_string
     debug.println(debug.LEVEL_FINEST,
@@ -550,22 +569,28 @@ def keyEcho(event):
 
         if isModifierKey(event_string):
             if not settings.enableModifierKeys:
-                return
+                return False
             eventType = KeyEventType.MODIFIER
 
         elif isNavigationKey(event_string):
             if not settings.enableNavigationKeys:
-                return
+                return False
             eventType = KeyEventType.NAVIGATION
 
+        elif isDiacriticalKey(event_string):
+            if not settings.enableDiacriticalKeys:
+                return False
+            eventType = KeyEventType.DIACRITICAL
+
         elif isPrintableKey(event_string):
-            if not settings.enablePrintableKeys:
-                return
+            if not (settings.enablePrintableKeys \
+                    or settings.enableEchoByCharacter):
+                return False
             eventType = KeyEventType.PRINTABLE
 
         elif isLockingKey(event_string):
             if not settings.enableLockingKeys:
-                return
+                return False
             eventType = KeyEventType.LOCKING
 
             modifiers = event.modifiers
@@ -590,28 +615,49 @@ def keyEcho(event):
 
         elif isFunctionKey(event_string):
             if not settings.enableFunctionKeys:
-                return
+                return False
             eventType = KeyEventType.FUNCTION
 
         elif isActionKey(event_string):
             if not settings.enableActionKeys:
-                return
+                return False
             eventType = KeyEventType.ACTION
 
         else:
             debug.println(debug.LEVEL_FINEST,
                   "orca.keyEcho: event string not handled: %s" % event_string)
-            return
-
-        debug.println(debug.LEVEL_FINEST,
-                      "orca.keyEcho: speaking: %s" % event_string)
+            return False
 
         # We keep track of the time as means to let others know that
         # we are probably echoing a key and should not be interrupted.
         #
         orca_state.lastKeyEchoTime = time.time()
 
-        speech.speakKeyEvent(event_string, eventType)
+        # Before we echo printable keys, let's try to make sure the
+        # character echo isn't going to also echo something.
+        #
+        characterEcho = \
+            eventType == KeyEventType.PRINTABLE \
+            and not _orcaModifierPressed \
+            and orca_state.activeScript \
+            and orca_state.activeScript.willEchoCharacter(event)
+
+        # One last check for echoing -- a PRINTABLE key may have squeaked
+        # through due to the enableEchoByCharacter check above.  We only
+        # want to echo PRINTABLE keys if enablePrintableKeys is True.
+        #
+        if not (characterEcho or
+                (eventType == KeyEventType.PRINTABLE \
+                 and not settings.enablePrintableKeys)):
+            debug.println(debug.LEVEL_FINEST,
+                          "orca.keyEcho: speaking: %s" % event_string)
+            speech.speakKeyEvent(event_string, eventType)
+            return True
+        elif characterEcho:
+            debug.println(debug.LEVEL_FINEST,
+                          "orca.keyEcho: letting character echo handle: %s" \
+                          % event_string)
+            return False
 
 def _setClickCount(inputEvent):
     """Sets the count of the number of clicks a user has made to one
@@ -762,7 +808,10 @@ def _processKeyboardEvent(event):
         #
         if not settings.learnModeEnabled and \
            orca_state.activeScript.echoKey(keyboardEvent):
-            keyEcho(keyboardEvent)
+            try:
+                keyEcho(keyboardEvent)
+            except:
+                debug.printException(debug.LEVEL_SEVERE)
 
     elif isOrcaModifier \
         and (keyboardEvent.type == pyatspi.KEY_RELEASED_EVENT):
diff --git a/src/orca/orca_gui_prefs.py b/src/orca/orca_gui_prefs.py
index 40b53b1..3ca8f57 100644
--- a/src/orca/orca_gui_prefs.py
+++ b/src/orca/orca_gui_prefs.py
@@ -1626,6 +1626,10 @@ class OrcaSetupGUI(orca_gtkbuilder.GtkBuilderWrapper):
                         prefs["enableActionKeys"])
         self.get_widget("navigationCheckbutton").set_active( \
                         prefs["enableNavigationKeys"])
+        self.get_widget("diacriticalCheckbutton").set_active( \
+                        prefs["enableDiacriticalKeys"])
+        self.get_widget("echoByCharacterCheckbutton").set_active( \
+                        prefs["enableEchoByCharacter"])
         self.get_widget("echoByWordCheckbutton").set_active( \
                         prefs["enableEchoByWord"])
         self.get_widget("echoBySentenceCheckbutton").set_active( \
@@ -2073,6 +2077,7 @@ class OrcaSetupGUI(orca_gtkbuilder.GtkBuilderWrapper):
         self.get_widget("functionCheckbutton").set_sensitive(enable)
         self.get_widget("actionCheckbutton").set_sensitive(enable)
         self.get_widget("navigationCheckbutton").set_sensitive(enable)
+        self.get_widget("diacriticalCheckbutton").set_sensitive(enable)
 
     def _say(self, text, stop=False):
         """If the text field is not None, speaks the given text, optionally
@@ -2701,6 +2706,29 @@ class OrcaSetupGUI(orca_gtkbuilder.GtkBuilderWrapper):
         """
         self.prefsDict["enableNavigationKeys"] = widget.get_active()
 
+    def diacriticalKeysChecked(self, widget):
+        """Signal handler for the "toggled" signal for the
+           diacriticalCheckbutton GtkCheckButton widget. The user has
+           [un]checked the 'Enable diacritical keys' checkbox. Set the
+           'enableDiacriticalKeys' preference to the new value.
+
+        Arguments:
+        - widget: the component that generated the signal.
+        """
+        self.prefsDict["enableDiacriticalKeys"] = widget.get_active()
+
+    def echoByCharacterChecked(self, widget):
+        """Signal handler for the "toggled" signal for the
+           echoByCharacterCheckbutton GtkCheckButton widget. The user has
+           [un]checked the 'Enable Echo by Character' checkbox. Set the
+           'enableEchoByCharacter' preference to the new value.
+
+        Arguments:
+        - widget: the component that generated the signal.
+        """
+
+        self.prefsDict["enableEchoByCharacter"] = widget.get_active()
+
     def echoByWordChecked(self, widget):
         """Signal handler for the "toggled" signal for the
            echoByWordCheckbutton GtkCheckButton widget. The user has
diff --git a/src/orca/scripts/apps/gnome-terminal.py b/src/orca/scripts/apps/gnome-terminal.py
index 655641f..05d398c 100644
--- a/src/orca/scripts/apps/gnome-terminal.py
+++ b/src/orca/scripts/apps/gnome-terminal.py
@@ -256,7 +256,21 @@ class Script(default.Script):
             speakThis = True
 
         if matchFound:
-            orca.keyEcho(orca_state.lastInputEvent)
+            echoed = orca.keyEcho(orca_state.lastInputEvent)
+        else:
+            echoed = False
+
+        if not echoed:
+            # We might need to echo this if it is a single character.
+            #
+            speakThis = speakThis \
+                or ((settings.enableEchoByCharacter \
+                     or (settings.enableKeyEcho \
+                         and settings.enablePrintableKeys)) \
+                    and text \
+                    and event.source.getRole() \
+                        != pyatspi.ROLE_PASSWORD_TEXT \
+                    and len(text.decode("UTF-8")) == 1)
 
         if speakThis:
             if text.isupper():
diff --git a/src/orca/settings.py b/src/orca/settings.py
index 8738228..83a05b7 100644
--- a/src/orca/settings.py
+++ b/src/orca/settings.py
@@ -95,6 +95,7 @@ userCustomizableSettings = [
     "speechVerbosityLevel",
     "readTableCellRow",
     "enableSpeechIndentation",
+    "enableEchoByCharacter",
     "enableEchoByWord",
     "enableEchoBySentence",
     "enableKeyEcho",
@@ -104,6 +105,7 @@ userCustomizableSettings = [
     "enableFunctionKeys",
     "enableActionKeys",
     "enableNavigationKeys",
+    "enableDiacriticalKeys",
     "enablePauseBreaks",
     "enableTutorialMessages",
     "enableMnemonicSpeaking",
@@ -532,6 +534,12 @@ magSourceDisplay                 = ''
 #
 magTargetDisplay                 = ''
 
+# if True, enable character echo.
+# Note that it is allowable for both enableEchoByCharacter and enableKeyEcho
+# to be True
+#
+enableEchoByCharacter   = False
+
 # if True, enable word echo.
 # Note that it is allowable for both enableEchoByWord and enableKeyEcho
 # to be True
@@ -574,6 +582,10 @@ enableActionKeys        = True
 #
 enableNavigationKeys    = False
 
+# If True and key echo is enabled, echo Diacritical keys.
+#
+enableDiacriticalKeys   = False
+
 # If True, tutorial strings defined will be spoken.
 #
 enableTutorialMessages = False



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