Index: test/harness/utils.py
===================================================================
--- test/harness/utils.py	(revision 3875)
+++ test/harness/utils.py	(working copy)
@@ -21,7 +21,7 @@
 htmlDir = os.path.abspath(fullPath + "/../../html")
 htmlURLPrefix = "file://" + htmlDir + "/"
 
-createDiffs = True
+createDiffs = False
 try:
     # If the difflib module is not found, fall back to generating the
     # old-style EXPECTED/ACTUAL output.
Index: src/orca/default.py
===================================================================
--- src/orca/default.py	(revision 3875)
+++ src/orca/default.py	(working copy)
@@ -985,6 +985,8 @@
             self.onStateChanged
         listeners["object:state-changed:selected"]          = \
             self.onStateChanged
+        listeners["object:text-selection-changed"]          = \
+            self.onTextSelectionChanged
         listeners["object:selection-changed"]               = \
             self.onSelectionChanged
         listeners["object:property-change:accessible-value"] = \
@@ -2081,7 +2083,6 @@
 
             phrase = self.adjustForRepeats(phrase)
             speech.speak(phrase, voice)
-            self.speakTextSelectionState(obj, startOffset, endOffset)
 
     def sayLine(self, obj):
         """Speaks the line of an AccessibleText object that contains the
@@ -2113,7 +2114,6 @@
             line = self.adjustForLinks(obj, line)
             line = self.adjustForRepeats(line)
             speech.speak(line, voice)
-            self.speakTextSelectionState(obj, startOffset, caretOffset)
 
         else:
             # Speak blank line if appropriate. It's necessary to
@@ -2180,7 +2180,6 @@
         word = self.adjustForRepeats(word)
         orca_state.lastWord = word
         speech.speak(word, voice)
-        self.speakTextSelectionState(obj, startOffset, endOffset)
 
     def speakTextIndentation(self, obj, line):
         """Speaks a summary of the number of spaces and/or tabs at the
@@ -2370,7 +2369,6 @@
         else:
             speech.speak(chnames.getCharacterName(character), voice, False)
 
-        self.speakTextSelectionState(obj, startOffset, endOffset)
 
     def presentTooltip(self, obj):
         """
@@ -3227,20 +3225,6 @@
             else:
                 self.sayCharacter(event.source)
 
-        elif (keyString == "A") and isControlKey:
-            # The user has typed Control-A. Check to see if the entire
-            # document has been selected, and if so, let the user know.
-            #
-            charCount = text.characterCount
-            for i in range(0, text.getNSelections()):
-                [startSelOffset, endSelOffset] = text.getSelection(i)
-                if text.caretOffset == 0 and \
-                   startSelOffset == 0 and endSelOffset == charCount:
-                    # Translators: this means the user has selected
-                    # all the text in a document (e.g., Ctrl+a in gedit).
-                    #
-                    speech.speak(_("entire document selected"))
-
     def onCaretMoved(self, event):
         """Called whenever the caret moves.
 
@@ -3651,6 +3635,139 @@
         #    print "FOO INSENSITIVE"
         #    #orca.setLocusOfFocus(event, None)
 
+    def getOffsetsForPhrase(self, obj):
+        """Return the start and end offset for the given phrase
+
+        Arguments:
+        - obj: the Accessible object
+        """
+
+        text = obj.queryText()
+        return [self.pointOfReference["lastCursorPosition"], text.caretOffset]
+
+    def getOffsetsForLine(self, obj):
+        """Return the start and end offset for the given line
+
+        Arguments:
+        - obj: the Accessible object
+        """
+
+        [line, endOffset, startOffset] = self.getTextLineAtCaret(obj)
+        return [startOffset, endOffset]
+
+    def getOffsetsForWord(self, obj):
+        """Return the start and end offset for the given word
+
+        Arguments:
+        - obj: the Accessible object
+        """
+
+        text = obj.queryText()
+        offset = text.caretOffset
+        [word, startOffset, endOffset] = text.getTextAtOffset(offset,
+                                           pyatspi.TEXT_BOUNDARY_WORD_START)
+        return [startOffset, endOffset]
+
+    def getOffsetsForChar(self, obj):
+        """Return the start and end offset for the given character
+
+        Arguments:
+        - obj: the Accessible object
+        """
+
+        text = obj.queryText()
+        offset = text.caretOffset
+
+        mods = orca_state.lastInputEvent.modifiers
+        shiftMask = 1 << pyatspi.MODIFIER_SHIFT
+        if (mods & shiftMask) \
+            and orca_state.lastNonModifierKeyEvent.event_string == "Right":
+            startOffset = offset-1
+            endOffset = offset
+        else:
+            startOffset = offset
+            endOffset = offset+1
+
+        return [startOffset, endOffset]
+
+    def onTextSelectionChanged(self, event):
+        """Called when an object's text selection changes.
+
+        Arguments:
+        - event: the Event
+        """
+
+        if not event or not event.source:
+            return
+
+        obj = event.source
+        try:
+            text = obj.queryText()
+        except:
+            return
+
+        if not isinstance(orca_state.lastInputEvent,
+                          input_event.KeyboardEvent):
+            return
+
+        keyString = orca_state.lastNonModifierKeyEvent.event_string
+        mods = orca_state.lastInputEvent.modifiers
+        isControlKey = mods & (1 << pyatspi.MODIFIER_CONTROL)
+        isShiftKey = mods & (1 << pyatspi.MODIFIER_SHIFT)
+        hasLastPos = self.pointOfReference.has_key("lastCursorPosition")
+
+        if (keyString == "Up") or (keyString == "Down"):
+            if hasLastPos and isShiftKey and not isControlKey:
+                [startOffset, endOffset] = self.getOffsetsForPhrase(obj)
+            else:
+                [startOffset, endOffset] = self.getOffsetsForLine(obj)
+
+        elif (keyString == "Left") or (keyString == "Right"):
+            if hasLastPos and isShiftKey and isControlKey:
+                [startOffset, endOffset] = self.getOffsetsForPhrase(obj)
+            elif isControlKey:
+                [startOffset, endOffset] = self.getOffsetsForWord(obj)
+            else:
+                [startOffset, endOffset] = self.getOffsetsForChar(obj)
+
+        elif keyString == "Page_Up":
+            if hasLastPos and isShiftKey and isControlKey:
+                [startOffset, endOffset] = self.getOffsetsForPhrase(obj)
+            elif isControlKey:
+                [startOffset, endOffset] = self.getOffsetsForChar(obj)
+            else:
+                [startOffset, endOffset] = self.getOffsetsForLine(obj)
+
+        elif keyString == "Page_Down":
+            if hasLastPos and isShiftKey and isControlKey:
+                [startOffset, endOffset] = self.getOffsetsForPhrase(obj)
+            else:
+                [startOffset, endOffset] = self.getOffsetsForLine(obj)
+
+        elif (keyString == "Home") or (keyString == "End"):
+            if hasLastPos and isShiftKey and not isControlKey:
+                [startOffset, endOffset] = self.getOffsetsForPhrase(obj)
+            elif isControlKey:
+                [startOffset, endOffset] = self.getOffsetsForLine(obj)
+            else:
+                [startOffset, endOffset] = self.getOffsetsForChar(obj)
+
+        elif (keyString == "A") and isControlKey:
+            # The user has typed Control-A. Check to see if the entire
+            # document has been selected, and if so, let the user know.
+            #
+            charCount = text.characterCount
+            for i in range(0, text.getNSelections()):
+                [startSelOffset, endSelOffset] = text.getSelection(i)
+                if text.caretOffset == 0 and \
+                   startSelOffset == 0 and endSelOffset == charCount:
+                    # Translators: this means the user has selected
+                    # all the text in a document (e.g., Ctrl+a in gedit).
+                    #
+                    speech.speak(_("entire document selected"))
+
+        self.speakTextSelectionState(obj, startOffset, endOffset)
+
     def onSelectionChanged(self, event):
         """Called when an object's selection changes.
 
