Index: test/keystrokes/oowriter/bug_517048_part2.py
===================================================================
--- test/keystrokes/oowriter/bug_517048_part2.py	(revision 0)
+++ test/keystrokes/oowriter/bug_517048_part2.py	(revision 0)
@@ -0,0 +1,128 @@
+#!/usr/bin/python
+
+"""Tests (part #2) to verify bug #517048 - Orca does not always speak the
+   correct information when navigating and/or selecting text across object
+   boundaries in OOo Writer.
+"""
+
+from macaroon.playback import *
+import utils
+
+sequence = MacroSequence()
+
+###########################################################################
+# Wait for oowriter to startup.
+#
+sequence.append(WaitForWindowActivate("Untitled1 - OpenOffice.org Writer",None))
+
+###########################################################################
+# Create a document containing:
+#
+# This is a test.
+# So is this.
+#
+sequence.append(TypeAction("This is a test."))
+sequence.append(KeyComboAction("Return"))
+sequence.append(WaitForFocus("", acc_role=pyatspi.ROLE_PARAGRAPH))
+sequence.append(TypeAction("So is this."))
+sequence.append(WaitForFocus("", acc_role=pyatspi.ROLE_PARAGRAPH))
+
+###########################################################################
+# Test 9. Press Control-Home to start from the beginning of the document.
+#         Press Shift-Down Arrow to select the current line.
+#
+sequence.append(KeyComboAction("<Control>Home"))
+sequence.append(PauseAction(3000))
+
+sequence.append(utils.StartRecordingAction())
+sequence.append(KeyComboAction("<Shift>Down"))
+sequence.append(PauseAction(3000))
+sequence.append(utils.AssertPresentationAction(
+    "Select the current line from start of document",
+    ["BRAILLE LINE:  'This is a test. $l'",
+     "     VISIBLE:  'This is a test. $l', cursor=1",
+     "BRAILLE LINE:  'soffice Application Untitled1 - OpenOffice.org Writer Frame Untitled1 - OpenOffice.org Writer RootPane ScrollPane Document view So is this. $l'",
+     "     VISIBLE:  'So is this. $l', cursor=1",
+     "SPEECH OUTPUT: 'This is a test.'",
+     "SPEECH OUTPUT: 'selected'"]))
+
+###########################################################################
+# Test 10. Start from the end of the document (by pressing Control+End).
+#          Press Shift-Up Arrow to select a line's worth (thus "So is this."
+#          on the last line is selected, along with "est." at the end of the
+#          previous line).
+#
+sequence.append(KeyComboAction("<Control>End"))
+
+sequence.append(utils.StartRecordingAction())
+sequence.append(KeyComboAction("<Shift>Up"))
+sequence.append(PauseAction(3000))
+sequence.append(utils.AssertPresentationAction(
+    "Select the current line from end of document",
+    ["BUG - should say 'est. so is this. selected'",
+     "BRAILLE LINE:  'So is this. $l'",
+     "     VISIBLE:  'So is this. $l', cursor=1",
+     "BRAILLE LINE:  'soffice Application Untitled1 - OpenOffice.org Writer Frame Untitled1 - OpenOffice.org Writer RootPane ScrollPane Document view This is a test. $l'",
+     "     VISIBLE:  'This is a test. $l', cursor=12",
+     "SPEECH OUTPUT: 'est.'",
+     "SPEECH OUTPUT: 'selected'"]))
+
+###########################################################################
+# Test 11. Press Control-Home to get to the beginning of the document.
+#          Navigate by word by pressing Control-Right Arrow until you 
+#          land on the period.
+#
+sequence.append(KeyComboAction("<Control>Home"))
+sequence.append(KeyComboAction("<Control>Right"))
+sequence.append(KeyComboAction("<Control>Right"))
+sequence.append(KeyComboAction("<Control>Right"))
+
+sequence.append(utils.StartRecordingAction())
+sequence.append(KeyComboAction("<Control>Right"))
+sequence.append(PauseAction(3000))
+sequence.append(utils.AssertPresentationAction(
+    "Navigating by word from start of document",
+    ["BRAILLE LINE:  'soffice Application Untitled1 - OpenOffice.org Writer Frame Untitled1 - OpenOffice.org Writer RootPane ScrollPane Document view This is a test. $l'",
+     "     VISIBLE:  'This is a test. $l', cursor=15",
+     "SPEECH OUTPUT: 'dot'"]))
+
+###########################################################################
+# Adjust the document to now contain:
+#
+# This is a test.
+# So is this.
+# And this.
+#
+sequence.append(KeyComboAction("<Control>End"))
+sequence.append(KeyComboAction("Return"))
+sequence.append(WaitForFocus("", acc_role=pyatspi.ROLE_PARAGRAPH))
+sequence.append(TypeAction("And this."))
+sequence.append(KeyComboAction("Return"))
+sequence.append(WaitForFocus("", acc_role=pyatspi.ROLE_PARAGRAPH))
+
+# Test 12. Press Control-Home to move to the start of the document. Select 
+#          line by line with Shift-Down Arrow. Having selected it all, 
+#          press Shift-Up Arrow twice. "So is this." should just have been
+#          unselected.
+#
+sequence.append(KeyComboAction("<Control>Home"))
+sequence.append(KeyComboAction("<Shift>Down"))
+sequence.append(KeyComboAction("<Shift>Down"))
+sequence.append(KeyComboAction("<Shift>Down"))
+sequence.append(KeyComboAction("<Shift>Up"))
+
+sequence.append(utils.StartRecordingAction())
+sequence.append(KeyComboAction("<Shift>Up"))
+sequence.append(PauseAction(3000))
+sequence.append(utils.AssertPresentationAction(
+    "Unselecting by line from end of document",
+    ["BRAILLE LINE:  'And this. $l'",
+     "     VISIBLE:  'And this. $l', cursor=1",
+     "BRAILLE LINE:  'soffice Application Untitled1 - OpenOffice.org Writer Frame Untitled1 - OpenOffice.org Writer RootPane ScrollPane Document view So is this. $l'",
+     "     VISIBLE:  'So is this. $l', cursor=1",
+     "SPEECH OUTPUT: 'So is this.'",
+     "SPEECH OUTPUT: 'unselected'"]))
+
+sequence.append(utils.AssertionSummaryAction())
+
+sequence.start()
Index: test/keystrokes/oowriter/bug_517048_part1.py
===================================================================
--- test/keystrokes/oowriter/bug_517048_part1.py	(revision 0)
+++ test/keystrokes/oowriter/bug_517048_part1.py	(revision 0)
@@ -0,0 +1,181 @@
+#!/usr/bin/python
+
+"""Tests (part 1) to verify bug #517048 - Orca does not always speak the
+   correct information when navigating and/or selecting text across object
+   boundaries in OOo Writer.
+"""
+
+from macaroon.playback import *
+import utils
+
+sequence = MacroSequence()
+
+###########################################################################
+# Wait for oowriter to startup.
+#
+sequence.append(WaitForWindowActivate("Untitled1 - OpenOffice.org Writer",None))
+
+###########################################################################
+# Create a document containing:
+#
+# This is a test.
+# So is this.
+#
+sequence.append(TypeAction("This is a test."))
+sequence.append(KeyComboAction("Return"))
+sequence.append(WaitForFocus("", acc_role=pyatspi.ROLE_PARAGRAPH))
+sequence.append(TypeAction("So is this."))
+sequence.append(WaitForFocus("", acc_role=pyatspi.ROLE_PARAGRAPH))
+
+###########################################################################
+# Test 1. Position the cursor at the start of the document. Use Shift-Right
+#         Arrow to select character by character. Once you've selected the
+#         period, press Shift+Right Arrow again (selecting just the newline).
+#
+sequence.append(KeyComboAction("<Control>Home"))
+sequence.append(KeyComboAction("<Shift>Right"))
+sequence.append(KeyComboAction("<Shift>Right"))
+sequence.append(KeyComboAction("<Shift>Right"))
+sequence.append(KeyComboAction("<Shift>Right"))
+sequence.append(KeyComboAction("<Shift>Right"))
+sequence.append(KeyComboAction("<Shift>Right"))
+sequence.append(KeyComboAction("<Shift>Right"))
+sequence.append(KeyComboAction("<Shift>Right"))
+sequence.append(KeyComboAction("<Shift>Right"))
+sequence.append(KeyComboAction("<Shift>Right"))
+sequence.append(KeyComboAction("<Shift>Right"))
+sequence.append(KeyComboAction("<Shift>Right"))
+sequence.append(KeyComboAction("<Shift>Right"))
+sequence.append(KeyComboAction("<Shift>Right"))
+sequence.append(KeyComboAction("<Shift>Right"))
+
+sequence.append(utils.StartRecordingAction())
+sequence.append(KeyComboAction("<Shift>Right"))
+sequence.append(PauseAction(3000))
+sequence.append(utils.AssertPresentationAction(
+    "Selecting newline",
+    ["BRAILLE LINE:  'This is a test. $l'",
+     "     VISIBLE:  'This is a test. $l', cursor=1",
+     "BRAILLE LINE:  'soffice Application Untitled1 - OpenOffice.org Writer Frame Untitled1 - OpenOffice.org Writer RootPane ScrollPane Document view So is this. $l'",
+     "     VISIBLE:  'So is this. $l', cursor=1",
+     "SPEECH OUTPUT: 'newline'"]))
+
+###########################################################################
+# Test 2. Press Shift+Left Arrow to unselect the newline.
+#
+sequence.append(utils.StartRecordingAction())
+sequence.append(KeyComboAction("<Shift>Left"))
+sequence.append(PauseAction(3000))
+sequence.append(utils.AssertPresentationAction(
+    "Unselecting newline",
+    ["BRAILLE LINE:  'So is this. $l'",
+     "     VISIBLE:  'So is this. $l', cursor=1",
+     "BRAILLE LINE:  'soffice Application Untitled1 - OpenOffice.org Writer Frame Untitled1 - OpenOffice.org Writer RootPane ScrollPane Document view This is a test. $l'",
+     "     VISIBLE:  'This is a test. $l', cursor=16",
+     "SPEECH OUTPUT: 'newline'"]))
+
+###########################################################################
+# Test 3. Press Control-Home to unselect everything. Then use 
+#         Control-Shift-Right Arrow to select word by word from the beginning
+#         through "test" (i.e. "test" is selected but the "." is not)
+#
+sequence.append(KeyComboAction("<Control>Home"))
+sequence.append(KeyComboAction("<Shift><Control>Right"))
+sequence.append(KeyComboAction("<Shift><Control>Right"))
+sequence.append(KeyComboAction("<Shift><Control>Right"))
+
+sequence.append(utils.StartRecordingAction())
+sequence.append(KeyComboAction("<Shift><Control>Right"))
+sequence.append(PauseAction(3000))
+sequence.append(utils.AssertPresentationAction(
+    "Selecting 'test' by word",
+    ["BRAILLE LINE:  'soffice Application Untitled1 - OpenOffice.org Writer Frame Untitled1 - OpenOffice.org Writer RootPane ScrollPane Document view This is a test. $l'",
+     "     VISIBLE:  'This is a test. $l', cursor=15",
+     "SPEECH OUTPUT: 'test'",
+     "SPEECH OUTPUT: 'selected'"]))
+
+###########################################################################
+# Test 4. Continue to press Control-Shift-Arrow to select forward by word 
+#         to select the period.
+#
+sequence.append(utils.StartRecordingAction())
+sequence.append(KeyComboAction("<Shift><Control>Right"))
+sequence.append(PauseAction(3000))
+sequence.append(utils.AssertPresentationAction(
+    "Selecting period by word",
+    ["BRAILLE LINE:  'soffice Application Untitled1 - OpenOffice.org Writer Frame Untitled1 - OpenOffice.org Writer RootPane ScrollPane Document view This is a test. $l'",
+     "     VISIBLE:  'This is a test. $l', cursor=16",
+     "SPEECH OUTPUT: '.'",
+     "SPEECH OUTPUT: 'selected'"]))
+
+###########################################################################
+# Test 5. Press Control-Shift-Right Arrow to select forward by word to 
+#         select the newline/ paragraph boundary.
+#
+sequence.append(utils.StartRecordingAction())
+sequence.append(KeyComboAction("<Shift><Control>Right"))
+sequence.append(PauseAction(3000))
+sequence.append(utils.AssertPresentationAction(
+    "Selecting newline by word",
+    ["BRAILLE LINE:  'This is a test. $l'",
+     "     VISIBLE:  'This is a test. $l', cursor=1",
+     "BRAILLE LINE:  'soffice Application Untitled1 - OpenOffice.org Writer Frame Untitled1 - OpenOffice.org Writer RootPane ScrollPane Document view So is this. $l'",
+     "     VISIBLE:  'So is this. $l', cursor=1",
+     "SPEECH OUTPUT: 'newline'"]))
+
+###########################################################################
+# Test 6. Press Control-End to move to the end of the document. Press 
+#         Control+Shift+Left to select the period. Then press it again 
+#         to select "this".
+#
+sequence.append(KeyComboAction("<Control>End"))
+sequence.append(KeyComboAction("<Shift><Control>Left"))
+
+sequence.append(utils.StartRecordingAction())
+sequence.append(KeyComboAction("<Shift><Control>Left"))
+sequence.append(PauseAction(3000))
+sequence.append(utils.AssertPresentationAction(
+    "Selecting 'this' by word, from end of document",
+    ["BRAILLE LINE:  'soffice Application Untitled1 - OpenOffice.org Writer Frame Untitled1 - OpenOffice.org Writer RootPane ScrollPane Document view So is this. $l'",
+     "     VISIBLE:  'So is this. $l', cursor=7",
+     "SPEECH OUTPUT: 'this'",
+     "SPEECH OUTPUT: 'selected'"]))
+
+###########################################################################
+# Test 7. Continue pressing Control-Shift-Left Arrow to select backwards
+#         by word. After "So is this." is selected, press
+#         Control-Shift-Left Arrow again.
+#
+sequence.append(KeyComboAction("<Shift><Control>Left"))
+sequence.append(KeyComboAction("<Shift><Control>Left"))
+
+sequence.append(utils.StartRecordingAction())
+sequence.append(KeyComboAction("<Shift><Control>Left"))
+sequence.append(PauseAction(3000))
+sequence.append(utils.AssertPresentationAction(
+    "Selecting newline before 'So', by word, from end of document",
+    ["BRAILLE LINE:  'So is this. $l'",
+     "     VISIBLE:  'So is this. $l', cursor=1",
+     "BRAILLE LINE:  'soffice Application Untitled1 - OpenOffice.org Writer Frame Untitled1 - OpenOffice.org Writer RootPane ScrollPane Document view This is a test. $l'",
+     "     VISIBLE:  'This is a test. $l', cursor=16",
+     "SPEECH OUTPUT: 'newline'"]))
+
+###########################################################################
+# Test 8. Continue pressing Control-Shift-Left Arrow to select backwards 
+#         by word until "test" is selected.
+#
+sequence.append(KeyComboAction("<Shift><Control>Left"))
+
+sequence.append(utils.StartRecordingAction())
+sequence.append(KeyComboAction("<Shift><Control>Left"))
+sequence.append(PauseAction(3000))
+sequence.append(utils.AssertPresentationAction(
+    "Selecting 'test', by word, from end of document",
+    ["BRAILLE LINE:  'soffice Application Untitled1 - OpenOffice.org Writer Frame Untitled1 - OpenOffice.org Writer RootPane ScrollPane Document view This is a test. $l'",
+     "     VISIBLE:  'This is a test. $l', cursor=11",
+     "SPEECH OUTPUT: 'test'",
+     "SPEECH OUTPUT: 'selected'"]))
+
+sequence.append(utils.AssertionSummaryAction())
+
+sequence.start()
Index: src/orca/scripts/StarOffice.py
===================================================================
--- src/orca/scripts/StarOffice.py	(revision 3604)
+++ src/orca/scripts/StarOffice.py	(working copy)
@@ -29,7 +29,6 @@
 import pyatspi
 
 import orca.debug as debug
-import orca.chnames as chnames
 import orca.default as default
 import orca.input_event as input_event
 import orca.braille as braille
@@ -2639,10 +2638,6 @@
         if (event_string == "Up" or event_string == "Down") and not isShiftKey:
             speech.stop()
 
-        # Speak a newline, if appropriate.
-        if self.speakNewLine(event.source):
-            speech.speak(chnames.getCharacterName("\n"), None, False)
-
         # Speak a blank line, if appropriate.
         if self.speakBlankLine(event.source):
             # Translators: "blank" is a short word to mean the
@@ -2652,65 +2647,6 @@
 
         default.Script.onCaretMoved(self, event)
 
-
-    def speakNewLine(self, obj):
-        """Returns True if a newline should be spoken.
-           Otherwise, returns False.
-        """
-
-        # Get the the AccessibleText interface.
-        try:
-            text = obj.queryText()
-        except NotImplementedError:
-            return False
-
-        # Was a left or right-arrow key pressed?
-        if not (orca_state.lastInputEvent and \
-                orca_state.lastInputEvent.__dict__.has_key("event_string")):
-            return False
-
-        lastKey = orca_state.lastNonModifierKeyEvent.event_string
-        if lastKey != "Left" and lastKey != "Right":
-            return False
-
-        # Was a control key pressed?
-        mods = orca_state.lastInputEvent.modifiers
-        isControlKey = mods & (1 << pyatspi.MODIFIER_CONTROL)
-
-        # Get the line containing the caret
-        caretOffset = text.caretOffset
-        line = text.getTextAtOffset(caretOffset, \
-            pyatspi.TEXT_BOUNDARY_LINE_START)
-        lineStart = line[1]
-        lineEnd = line[2]
-
-        if isControlKey:  # control-right-arrow or control-left-arrow
-
-            # Get the word containing the caret.
-            word = text.getTextAtOffset(caretOffset, \
-                pyatspi.TEXT_BOUNDARY_WORD_START)
-            wordStart = word[1]
-            wordEnd = word[2]
-
-            if lastKey == "Right":
-                if wordStart == lineStart:
-                    return True
-            else:
-                if wordEnd == lineEnd:
-                    return True
-
-        else:  # right arrow or left arrow
-
-            if lastKey == "Right":
-                if caretOffset == lineStart:
-                    return True
-            else:
-                if caretOffset == lineEnd:
-                    return True
-
-        return False
-
-
     def speakBlankLine(self, obj):
         """Returns True if a blank line should be spoken.
         Otherwise, returns False.
Index: src/orca/default.py
===================================================================
--- src/orca/default.py	(revision 3605)
+++ src/orca/default.py	(working copy)
@@ -2100,6 +2100,9 @@
             text.getTextAtOffset(offset,
                                  pyatspi.TEXT_BOUNDARY_WORD_START)
 
+        if not len(word):
+            return self.sayCharacter(obj)
+
         # Speak a newline if a control-right-arrow or control-left-arrow
         # was used to cross a line boundary. Handling is different for
         # the two keys since control-right-arrow places the cursor after
@@ -2266,7 +2269,8 @@
         mods = orca_state.lastInputEvent.modifiers
         shiftMask = 1 << pyatspi.MODIFIER_SHIFT
         if (mods & shiftMask) \
-            and orca_state.lastNonModifierKeyEvent.event_string == "Right":
+            and orca_state.lastNonModifierKeyEvent.event_string == "Right" \
+            and offset > 0:
             startOffset = offset-1
             endOffset = offset
         else:
@@ -2294,7 +2298,8 @@
 
         # Handle speaking newlines when the right-arrow key is pressed.
         if orca_state.lastNonModifierKeyEvent.event_string == "Right":
-            if prevChar == "\n":
+            if prevChar == "\n" \
+               or (len(prevChar) > 1 and not len(character)):
                 # The cursor is at the beginning of a line.
                 # Speak a newline.
                 speech.speak(chnames.getCharacterName("\n"), voice, False)
@@ -2315,7 +2320,7 @@
                     # user has navigated to an empty line.
                     #
                     speech.speak(_("blank"), voice, False)
-        else:
+        elif len(character) == 1:
             speech.speak(chnames.getCharacterName(character), voice, False)
 
         self.speakTextSelectionState(obj, startOffset, endOffset)
@@ -3089,18 +3094,74 @@
         mods = orca_state.lastInputEvent.modifiers
         isControlKey = mods & (1 << pyatspi.MODIFIER_CONTROL)
         isShiftKey = mods & (1 << pyatspi.MODIFIER_SHIFT)
-        hasLastPos = self.pointOfReference.has_key("lastCursorPosition")
 
+        lastPos = self.pointOfReference.get("lastCursorPosition")
+        hasLastPos = lastPos and hash(event.source) == hash(lastPos[0])
+
+        selDict = self.pointOfReference.get("lastSelections") or {}
+        try:
+            selList = selDict[hash(event.source)]
+        except:
+            selList = []
+
+            # In OOo, and possibly in Evolution, and for that matter in
+            # Firefox, accessibles seem to be created and destroyed at
+            # will.  As a result, we need to check to see if we have a
+            # match.  We've stored the line.
+            #
+            badKey = None
+            line = text.getTextAtOffset(text.caretOffset,
+                                        pyatspi.TEXT_BOUNDARY_LINE_START)
+            line = line[0]
+            for key in selDict:
+                for sel in selDict[key]:
+                    if line == sel[2]:
+                        # print "NEW ACCESSIBLE FOR SAME OBJECT?"
+                        badKey = key
+                        break
+                if badKey:
+                    break
+
+            if badKey:
+                selList = self.pointOfReference["lastSelections"].pop(badKey)
+                self.pointOfReference["lastSelections"] \
+                                     [hash(event.source)] = selList
+
         if (keyString == "Up") or (keyString == "Down"):
             # If the user has typed Shift-Up or Shift-Down, then we want
             # to speak the text that has just been selected or unselected,
             # otherwise we speak the new line where the text cursor is
             # currently positioned.
             #
-            if hasLastPos and isShiftKey and not isControlKey:
-                self.sayPhrase(event.source, 
-                               self.pointOfReference["lastCursorPosition"],
-                               text.caretOffset)
+            if isShiftKey and not isControlKey:
+                if keyString == "Up":
+                    if text.caretOffset == -1:
+                        # We selected the previous line, but we're still
+                        # officially on this line.
+                        #
+                        selList = []
+
+                    # In a document consisting of multiple text objects,
+                    # if we're unselecting text from the bottom up, we'll
+                    # initially get an event for the line we started on
+                    # whose text is not selected (the state of which was
+                    # announced with the previous key press).  We want to
+                    # ignore this line.
+                    #
+                    hasLastPos = hasLastPos and len(selList) > 0
+
+                if len(selList):
+                    offset = selList[0][1]
+                elif hasLastPos:
+                    offset = lastPos[1]
+
+                if len(selList) or hasLastPos:
+                    self.sayPhrase(event.source, offset, text.caretOffset)
+                elif keyString == "Up" and text.caretOffset != -1:
+                    # We're likely selecting upwards from the bottom.
+                    #
+                    self.sayLine(event.source)
+
             else:
                 self.sayLine(event.source)
 
@@ -3111,13 +3172,33 @@
             # or Control-Right, we speak the current word otherwise we speak
             # the character at the text cursor position.
             #
-            if hasLastPos and isShiftKey and isControlKey:
-                self.sayPhrase(event.source, 
-                               self.pointOfReference["lastCursorPosition"],
-                               text.caretOffset)
+            if isShiftKey and isControlKey:
+                # In a document where multiple text objects are selected,
+                # the user is at the beginning of the line, and text is
+                # being unselected with Control+Shift+Left, the caret
+                # (lastPos) will initially be the line whose first word
+                # was unselected. We don't want to speak that line.
+                #
+                if keyString == "Left" and len(selDict):
+                    hasLastPos = len(selList) > 0 \
+                                 or text.caretOffset != -1
+
+                if hasLastPos:
+                    if text.caretOffset == -1 \
+                       or (text.caretOffset == text.characterCount \
+                           and keyString == "Left"):
+                        # We've just crossed a line boundary and don't want
+                        # to speak the line.
+                        #
+                        self.sayCharacter(event.source)
+                    else:
+                        self.sayPhrase(event.source, 
+                                       lastPos[1],
+                                       text.caretOffset)
+
             elif isControlKey:
                 self.sayWord(event.source)
-            else:
+            elif not (isShiftKey and keyString == "Right") or hasLastPos:
                 self.sayCharacter(event.source)
 
         elif keyString == "Page_Up":
@@ -3129,7 +3210,7 @@
             #
             if hasLastPos and isShiftKey and isControlKey:
                 self.sayPhrase(event.source, 
-                               self.pointOfReference["lastCursorPosition"],
+                               self.pointOfReference["lastCursorPosition"][1],
                                text.caretOffset)
             elif isControlKey:
                 self.sayCharacter(event.source)
@@ -3144,7 +3225,7 @@
             #
             if hasLastPos and isShiftKey and isControlKey:
                 self.sayPhrase(event.source, 
-                               self.pointOfReference["lastCursorPosition"],
+                               self.pointOfReference["lastCursorPosition"][1],
                                text.caretOffset)
             else:
                 self.sayLine(event.source)
@@ -3158,7 +3239,7 @@
             #
             if hasLastPos and isShiftKey and not isControlKey:
                 self.sayPhrase(event.source, 
-                               self.pointOfReference["lastCursorPosition"],
+                               self.pointOfReference["lastCursorPosition"][1],
                                text.caretOffset)
             elif isControlKey:
                 self.sayLine(event.source)
@@ -3179,6 +3260,9 @@
                     #
                     speech.speak(_("entire document selected"))
 
+        self.pointOfReference["lastCursorPosition"] = [hash(event.source),
+                                                       text.caretOffset]
+
     def onCaretMoved(self, event):
         """Called whenever the caret moves.
 
@@ -6655,6 +6739,12 @@
         except:
             debug.printException(debug.LEVEL_FINEST)
 
+        selectionDict = self.pointOfReference.get("lastSelections") or {}
+        try:
+            selectionList = selectionDict.pop(hash(obj))
+        except:
+            selectionList = []
+
         if self.isTextSelected(obj, startOffset, endOffset):
             # Translators: when the user selects (highlights) text in
             # a document, Orca lets them know this.
@@ -6663,32 +6753,41 @@
             #
             speech.speak(Q_("text|selected"), None, False)
         else:
-            if self.pointOfReference.has_key("lastSelections"):
-                for i in xrange(len(self.pointOfReference["lastSelections"])):
-                    startSelOffset = \
-                        self.pointOfReference["lastSelections"][0][0]
-                    endSelOffset = \
-                        self.pointOfReference["lastSelections"][0][1]
-                    if (startOffset >= startSelOffset) \
-                        and (endOffset <= endSelOffset):
-                        # Translators: when the user unselects
-                        # (unhighlights) text in a document, Orca lets
-                        # them know this.
-                        #
-                        # ONLY TRANSLATE THE PART AFTER THE PIPE CHARACTER |
-                        #
-                        speech.speak(Q_("text|unselected"), None, False)
-                        break
+            for i in xrange(len(selectionList)):
+                startSelOffset = selectionList[0][0]
+                endSelOffset = selectionList[0][1]
+                if (startOffset >= startSelOffset) \
+                       and (endOffset <= endSelOffset):
+                    # Translators: when the user unselects
+                    # (unhighlights) text in a document, Orca lets
+                    # them know this.
+                    #
+                    # ONLY TRANSLATE THE PART AFTER THE PIPE CHARACTER |
+                    #
+                    speech.speak(Q_("text|unselected"), None, False)
+                    break
 
-        # Save away the current text cursor position and list of text
-        # selections for next time.
+        # Save the object's list of text selections for next time.
         #
-        self.pointOfReference["lastCursorPosition"] = text.caretOffset
-        self.pointOfReference["lastSelections"] = []
+        selectionList = []
         for i in xrange(text.getNSelections()):
-            self.pointOfReference["lastSelections"].append(
-              text.getSelection(i))
+            [start, end] = text.getSelection(i)
 
+            # Possible bug in OOo? We're not getting the same accessible
+            # for the same functional object. The isSameObject() test
+            # also seems to fail in this case.  So we'll store a line's
+            # worth as a way to do a reality check.
+            #
+            line = text.getTextAtOffset(start,
+                                        pyatspi.TEXT_BOUNDARY_LINE_START)
+            line = line[0]
+            selectionList.append([start, end, line])
+
+        if len(selectionList):
+            selectionDict[hash(obj)] = selectionList
+
+        self.pointOfReference["lastSelections"] = selectionDict
+
     def getURI(self, obj):
         """Return the URI for a given link object.
 
