[orca/570658-whereami] Begin implementing text where am I code



commit 4ed5b29a1d0c1309c927b32e1ac84f8c019785fd
Author: Willie Walker <william walker sun com>
Date:   Thu Jun 4 18:27:28 2009 -0400

    Begin implementing text where am I code
    
    This moves a lot of stuff to speech_generator.  Seems to work nicely,
    but I'm not ready to throw the switch yet.  Most of the code in
    where_am_I.py can be deleted once more testing has been done.
---
 src/orca/formatting.py       |   12 ++-
 src/orca/speech_generator.py |  202 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 211 insertions(+), 3 deletions(-)

diff --git a/src/orca/formatting.py b/src/orca/formatting.py
index 0696c1b..7215249 100644
--- a/src/orca/formatting.py
+++ b/src/orca/formatting.py
@@ -118,7 +118,9 @@ formatting = {
             },
         pyatspi.ROLE_PASSWORD_TEXT: {
             'focused': 'labelOrName + readOnly + textRole + currentLineText + allTextSelection',
-            'unfocused': 'labelOrName + readOnly + textRole + currentLineText + allTextSelection + mnemonic'
+            'unfocused': 'labelOrName + readOnly + textRole + currentLineText + allTextSelection + mnemonic',
+            'basicWhereAmI': 'label + readOnly + textRole + textContent + anyTextSelection + mnemonic + tutorial',
+            'detailedWhereAmI': 'label + readOnly + textRole + textContentWithAttributes + anyTextSelection + mnemonic + tutorial'
             },
         pyatspi.ROLE_PROGRESS_BAR: {
             'focused': 'percentage',
@@ -204,7 +206,9 @@ formatting = {
             },
         pyatspi.ROLE_TEXT: {
             'focused': 'labelOrName + readOnly + textRole + currentLineText + allTextSelection',
-            'unfocused': 'labelOrName + readOnly + textRole + currentLineText + allTextSelection + mnemonic'
+            'unfocused': 'labelOrName + readOnly + textRole + currentLineText + allTextSelection + mnemonic',
+            'basicWhereAmI': 'label + readOnly + textRole + textContent + anyTextSelection + mnemonic + tutorial',
+            'detailedWhereAmI': 'label + readOnly + textRole + textContentWithAttributes + anyTextSelection + mnemonic + tutorial'
             },
         pyatspi.ROLE_TOGGLE_BUTTON: {
             'focused': 'toggleState',
@@ -217,7 +221,9 @@ formatting = {
             },
         pyatspi.ROLE_PARAGRAPH: {
             'focused': 'labelOrName + readOnly + textRole + currentLineText + allTextSelection',
-            'unfocused': 'labelOrName + readOnly + textRole + currentLineText + allTextSelection + mnemonic'
+            'unfocused': 'labelOrName + readOnly + textRole + currentLineText + allTextSelection + mnemonic',
+            'basicWhereAmI': 'label + readOnly + textRole + textContent + anyTextSelection + mnemonic + tutorial',
+            'detailedWhereAmI': 'label + readOnly + textRole + textContentWithAttributes + anyTextSelection + mnemonic + tutorial'
             },
         pyatspi.ROLE_EMBEDDED: {
             'focused': 'embedded',
diff --git a/src/orca/speech_generator.py b/src/orca/speech_generator.py
index 8b19957..b9bdc70 100644
--- a/src/orca/speech_generator.py
+++ b/src/orca/speech_generator.py
@@ -36,6 +36,7 @@ import orca_state
 import pyatspi
 import rolenames
 import settings
+import text_attribute_names
 
 from orca_i18n import _         # for gettext support
 from orca_i18n import ngettext  # for ngettext support
@@ -95,6 +96,11 @@ class SpeechGenerator:
             name = name[0].lower() + name[1:]
             self._methodsDict[name] = method
 
+        # Something to help us retain things we've computed while
+        # generating speech so we don't need to keep recomputing them.
+        #
+        self._valueCache = {}
+
         # Verify the formatting strings are OK.  This is only
         # for verification and does not effect the function of
         # Orca at all.
@@ -1148,6 +1154,180 @@ class SpeechGenerator:
     #                                                                   #
     #####################################################################
 
+    def _getCharacterAttributes(self,
+                                obj,
+                                text,
+                                textOffset,
+                                lineIndex,
+                                keys=["style", "weight", "underline"]):
+        """Helper function that returns a string containing the
+        given attributes from keys for the given character.
+        """
+        attribStr = ""
+
+        defaultAttributes = text.getDefaultAttributes()
+        attributesDictionary = \
+            self._script.attributeStringToDictionary(defaultAttributes)
+
+        charAttributes = text.getAttributes(textOffset)
+        if charAttributes[0]:
+            charDict = \
+                self._script.attributeStringToDictionary(charAttributes[0])
+            for key in charDict.keys():
+                attributesDictionary[key] = charDict[key]
+
+        if attributesDictionary:
+            for key in keys:
+                localizedKey = text_attribute_names.getTextAttributeName(key)
+                if key in attributesDictionary:
+                    attribute = attributesDictionary[key]
+                    localizedValue = \
+                        text_attribute_names.getTextAttributeName(attribute)
+                    if attribute:
+                        # If it's the 'weight' attribute and greater than 400,
+                        # just speak it as bold, otherwise speak the weight.
+                        #
+                        if key == "weight":
+                            if int(attribute) > 400:
+                                attribStr += " "
+                                # Translators: bold as in the font sense.
+                                #
+                                attribStr += _("bold")
+                        elif key == "underline":
+                            if attribute != "none":
+                                attribStr += " "
+                                attribStr += localizedKey
+                        elif key == "style":
+                            if attribute != "normal":
+                                attribStr += " "
+                                attribStr += localizedValue
+                        else:
+                            attribStr += " "
+                            attribStr += (localizedKey + " " + localizedValue)
+
+            # Also check to see if this is a hypertext link.
+            #
+            if self._script.getLinkIndex(obj, textOffset) >= 0:
+                attribStr += " "
+                # Translators: this indicates that this piece of
+                # text is a hypertext link.
+                #
+                attribStr += _("link")
+
+        return attribStr
+
+    def _getTextInformation(self, obj, **args):
+        """Returns an empty array, but sets up a 'textInformation' attribute
+        in self._valueCache for other methods to use.  The information
+        is either:
+
+        A. if no text on the current line is selected, the current line
+        B. if text is selected, the selected text
+        C. if the current line is blank/empty, 'blank'
+
+        For all the above, we'll get a 'textInformation' entry in
+        self._valueCache that is the following list:
+
+        [textContents, startOffset, endOffset, selected]
+        """
+
+        textObj = obj.queryText()
+        caretOffset = textObj.caretOffset
+        textContents = ""
+        selected = False
+
+        nSelections = textObj.getNSelections()
+
+        [current, other] = self._script.hasTextSelections(obj)
+        if current or other:
+            selected = True
+            [textContents, startOffset, endOffset] = \
+                self._script.getAllSelectedText(obj)
+        else:
+            # Get the line containing the caret
+            #
+            [line, startOffset, endOffset] = textObj.getTextAtOffset(
+                textObj.caretOffset,
+                pyatspi.TEXT_BOUNDARY_LINE_START)
+            if len(line):
+                line = self._script.adjustForRepeats(line)
+                textContents = line
+            else:
+                char = textObj.getTextAtOffset(caretOffset,
+                    pyatspi.TEXT_BOUNDARY_CHAR)
+                if char[0] == "\n" and startOffset == caretOffset \
+                       and settings.speakBlankLines:
+                    # Translators: "blank" is a short word to mean the
+                    # user has navigated to an empty line.
+                    #
+                    textContents = (_("blank"))
+
+        self._valueCache['textInformation'] = \
+            [textContents, startOffset, endOffset, selected]
+
+        return []
+
+    def _getTextContent(self, obj, **args):
+        """Returns an array of strings (and possibly voice and audio
+        specifications) containing the text content.  This requires
+        _getTextInformation to have been called prior to this method.
+        """
+        try:
+            text = obj.queryText()
+        except NotImplementedError:
+            return []
+
+        if not self._valueCache.has_key('textInformation'):
+            self._getTextInformation(obj, **args)
+        [line, startOffset, endOffset, selected] = \
+            self._valueCache['textInformation']
+
+        return [line]
+
+    def _getTextContentWithAttributes(self, obj, **args):
+        """Returns an array of strings (and possibly voice and audio
+        specifications) containing the text content, obtained from the
+        'textInformation' value of self._valueCache, with character
+        attribute information mixed in.  This requires
+        _getTextInformation to have been called prior to this method.
+        """
+        try:
+            text = obj.queryText()
+        except NotImplementedError:
+            return []
+
+        if not self._valueCache.has_key('textInformation'):
+            self._getTextInformation(obj, **args)
+        [line, startOffset, endOffset, selected] = \
+            self._valueCache['textInformation']
+
+        newLine = ""
+        lastAttribs = None
+        textOffset = startOffset
+        for i in range(0, len(line)):
+            attribs = self._getCharacterAttributes(obj, text, textOffset, i)
+            if attribs and attribs != lastAttribs:
+                if newLine:
+                    newLine += " ; "
+                newLine += attribs
+                newLine += " "
+                lastAttribs = attribs
+            newLine += line[i]
+            textOffset += 1
+
+        attribs = self._getCharacterAttributes(obj,
+                                               text,
+                                               startOffset,
+                                               0,
+                                               ["paragraph-style"])
+
+        if attribs:
+            if newLine:
+                newLine += " ; "
+            newLine += attribs
+
+        return [newLine]
+
     def _getCurrentLineText(self, obj, **args ):
         """Returns an array of strings (and possibly voice and audio
         specifications) that represents the current line of text, if
@@ -1165,6 +1345,27 @@ class SpeechGenerator:
         """
         return [self._script.getDisplayedText(obj)]
 
+    def _getAnyTextSelection(self, obj, **args):
+        """Returns an array of strings (and possibly voice and audio
+        specifications) that says if any of the text for the entire
+        object is selected. [[[WDW - I wonder if this string should be
+        moved to settings.py.]]]
+        """
+        result = []
+
+        if not self._valueCache.has_key('textInformation'):
+            self._getTextInformation(obj, **args)
+        [line, startOffset, endOffset, selected] = \
+            self._valueCache['textInformation']
+
+        if selected:
+            # Translators: when the user selects (highlights) text in
+            # a document, Orca lets them know this.
+            #
+            text = C_("text", "selected")
+            result.append(text)
+        return result
+
     def _getAllTextSelection(self, obj, **args):
         """Returns an array of strings (and possibly voice and audio
         specifications) that says if all the text for the entire
@@ -1901,6 +2102,7 @@ class SpeechGenerator:
             # we've been called.
             #
             if not args.get('recursing', False):
+                self._valueCache = {}
                 if args.get('includeContext', True):
                     prefix = self._script.formatting.getPrefix(**args)
                     suffix = self._script.formatting.getSuffix(**args)



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