Index: src/orca/Gecko.py
===================================================================
--- src/orca/Gecko.py	(revision 3442)
+++ src/orca/Gecko.py	(working copy)
@@ -74,6 +74,11 @@
 #
 performanceEnhancements = True
 
+# Temporary debugging/testing setting.  If True, use the experimental
+# methods to find the next/previous line.
+#
+useNewLineNav = True
+
 # If True, it tells us to take over caret navigation.  This is something
 # that can be set in user-settings.py:
 #
@@ -4232,6 +4237,13 @@
         - oldLocusOfFocus: Accessible that is the old locus of focus
         - newLocusOfFocus: Accessible that is the new locus of focus
         """
+
+        # Discard our stored line.  [[[TODO - JD: It might be worth
+        # doing a quick check to see if we're still on the same line
+        # before trashing it and getting new line contents.]]]
+        #
+        self._currentLineContents = None
+
         # Sometimes we get different accessibles for the same object.
         #
         if self.isSameObject(oldLocusOfFocus, newLocusOfFocus):
@@ -4342,7 +4354,9 @@
         - offset: the offset within obj
         """
 
-        self._currentLineContents = self.getLineContentsAtOffset(obj, offset)
+        if not self._currentLineContents:
+            self._currentLineContents = self.getLineContentsAtOffset(obj,
+                                                                     offset)
         self.speakContents(self._currentLineContents)
         self.updateBraille(obj)
         self._currentLineContents = None
@@ -7165,27 +7179,31 @@
             else:
                 offset = line[1]
 
-        if singleLine and obj.getRole() == pyatspi.ROLE_TABLE_CELL:
-            containingTable = self.getAncestor(obj,
-                                               [pyatspi.ROLE_TABLE],
-                                               [pyatspi.ROLE_DOCUMENT_FRAME])
-            if containingTable:
-                table = containingTable.queryTable()
-                if obj.parent == containingTable:
-                    index = obj.getIndexInParent()
-                else:
-                    index = obj.parent.getIndexInParent()
-                row = table.getRowAtIndex(index)
-                col = table.getColumnAtIndex(index)
-                if col > 0:
-                    cell = table.getAccessibleAt(row, 0)
-                    cellExtents = cell.queryComponent().getExtents(0)
-                    cellExtents = [cellExtents.x, cellExtents.y,
-                                   cellExtents.width, cellExtents.height]
-                    if self.onSameLine(extents, cellExtents):
-                        obj = cell
-                        offset = 0
+        # Move to the left to see if there's a sibling on this line which
+        # we should include.
+        #
+        index = obj.getIndexInParent()
+        sibling = obj
+        role = obj.getRole()
+        while sibling and index > 0:
+            sibling = obj.parent[index - 1]
+            siblingExtents = self.getExtents(sibling, 0, 1)
 
+            if not self.onSameLine(extents, siblingExtents) or not singleLine:
+                break
+            else:
+                text = self.queryNonEmptyText(sibling)
+                if text:
+                    line = text.getTextAtOffset(offset, boundary)
+                    singleLine = (line[1] == 0) \
+                             and (line[2] == text.characterCount)
+                    if not singleLine:
+                        break
+
+            obj = sibling
+            offset = 0
+            index -= 1
+
         # Get the objects on this line.
         #
         objects = self.getObjectsFromEOCs(obj, offset, boundary)
@@ -7212,7 +7230,6 @@
         elif len(objects) \
              and objects[-1][0].getRole() == pyatspi.ROLE_TABLE_CELL:
             cell = objects[-1][0]
-
         containingTable = self.getAncestor(cell,
                                            [pyatspi.ROLE_TABLE],
                                            [pyatspi.ROLE_DOCUMENT_FRAME])
@@ -7273,7 +7290,8 @@
         #
         if objects and singleLine:
             nextObject = self.findNextObject(objects[-1][0])
-            if nextObject and not nextObject.parent in [obj, objects[-1][0]]:
+            if nextObject \
+              and not nextObject.parent in [obj, objects[-1][0]]:
                 toAdd = self.getObjectsFromEOCs(nextObject, 0, boundary)
                 if toAdd:
                     objExtents = self.getExtents(toAdd[0][0],
@@ -7744,8 +7762,9 @@
         character = self.getCharacterAtOffset(obj, characterOffset)
         if character:
             if self.isAriaWidget() \
-                 or not (obj.getRole() == pyatspi.ROLE_LIST_ITEM \
-                 and not obj.getState().contains(pyatspi.STATE_FOCUSABLE)):
+               or useNewLineNav \
+               or not (obj.getRole() == pyatspi.ROLE_LIST_ITEM \
+                     and not obj.getState().contains(pyatspi.STATE_FOCUSABLE)):
                 obj.queryText().setCaretOffset(characterOffset)
                 mag.magnifyAccessible(None,
                                       obj,
@@ -7844,6 +7863,96 @@
         self.updateBraille(obj)
         self.speakContents(self.getWordContentsAtOffset(obj, startOffset))
 
+    def altFindPreviousLine(self, obj, characterOffset):
+        """Locates the caret offset at the previous line in the document
+        window.
+
+        Arguments:
+        -obj:             the object from which the search should begin
+        -characterOffset: the offset within obj from which the search should
+                          begin
+
+        Returns the [obj, characterOffset] at the beginning of the line.
+        """
+
+        if not obj:
+            [obj, characterOffset] = self.getCaretContext()
+
+        if not obj:
+            obj = self.getLastObject()
+            [obj, characterOffset] = self.findPreviousCaretInOrder(obj, 0)
+
+        if not obj:
+            return [None, -1]
+
+        currentLine = self.getLineContentsAtOffset(obj, characterOffset)
+        [prevObj, prevOffset] = \
+            self.findPreviousCaretInOrder(currentLine[0][0], currentLine[0][1])
+
+        # We need to be sure that we've actually found a new line rather than
+        # a space at the end of the current line.
+        #
+        text = self.queryNonEmptyText(prevObj)
+        if text:
+            line = text.getTextAfterOffset(prevOffset,
+                                           pyatspi.TEXT_BOUNDARY_LINE_START)
+            if not len(line[0].strip()) \
+                   and prevObj.getRole() != pyatspi.ROLE_ENTRY:
+                [prevObj, prevOffset] = \
+                          self.findPreviousCaretInOrder(prevObj, prevOffset)
+            else:
+                prevOffset = line[1]
+
+        prevLine = self.getLineContentsAtOffset(prevObj, prevOffset)
+        if not prevLine:
+            return [None, -1]
+
+        prevObj = prevLine[0][0]
+        prevOffset = prevLine[0][1]
+
+        # Dealing with things like nested tables from the right side is hard.
+        # For now, I'm going to look at the previous caret in order.  If it's
+        # on the same line, we'll give it another go.  In testing, we usually
+        # get it right the first time so this shouldn't be too much of a hit.
+        #
+        [testObj, testOffset] = self.findPreviousCaretInOrder(prevObj,
+                                                              prevOffset)
+        extents1 = self.getExtents(prevObj, prevOffset, prevOffset + 1)
+        extents2 = self.getExtents(testObj, testOffset, testOffset + 1)
+        if self.onSameLine(extents1, extents2):
+            #print "previous line failed"
+            prevLine = self.getLineContentsAtOffset(testObj, testOffset)
+            if not prevLine:
+                return [None, -1]
+
+            prevObj = prevLine[0][0]
+            prevOffset = prevLine[0][1]
+
+        [prevObj, prevOffset] = self.findNextCaretInOrder(prevObj, 
+                                                          prevOffset - 1)
+
+        if not arrowToLineBeginning:
+            extents = self.getExtents(obj, 
+                                      characterOffset, 
+                                      characterOffset + 1)
+            oldX = extents[0]
+            for item in prevLine:
+                extents = self.getExtents(item[0], item[1], item[2])
+                newX1 = extents[0]
+                newX2 = newX1 + extents[2]
+                if newX1 <= oldX <= newX2:
+                    prevObj = item[0]
+                    prevOffset = 0
+                    text = self.queryNonEmptyText(prevObj)
+                    if text:
+                        newY = extents[1] + extents[3] / 2
+                        prevOffset = text.getOffsetAtPoint(oldX, newY, 0)
+                    break
+
+        self._currentLineContents = prevLine
+
+        return [prevObj, prevOffset]
+
     def findPreviousLine(self, obj, characterOffset):
         """Locates the caret offset at the previous line in the document
         window, attempting to preserve horizontal caret position.
@@ -7856,6 +7965,11 @@
         Returns the [obj, characterOffset] at which to position the caret.
         """
 
+        if useNewLineNav:
+            [prevObj, prevOffset] = self.altFindPreviousLine(obj,
+                                                             characterOffset)
+            return [prevObj, prevOffset]
+
         lineExtents = self.getExtents(
             obj, characterOffset, characterOffset + 1)
         try:
@@ -7952,6 +8066,72 @@
 
         return [lastObj, lastCharacterOffset]
 
+    def altFindNextLine(self, obj, characterOffset):
+        """Locates the caret offset at the next line in the document
+        window.
+
+        Arguments:
+        -obj:             the object from which the search should begin
+        -characterOffset: the offset within obj from which the search should
+                          begin
+
+        Returns the [obj, characterOffset] at the beginning of the line.
+        """
+
+        if not obj:
+            [obj, characterOffset] = self.getCaretContext()
+
+        if not obj:
+            obj = self.getDocumentFrame()
+            [obj, characterOffset] = self.findFirstCaretContext(obj, -1)
+
+        if not obj:
+            return [None, -1]
+
+        currentLine = self.getLineContentsAtOffset(obj, characterOffset)
+        [nextObj, nextOffset] = self.findNextCaretInOrder(currentLine[-1][0],
+                                                          currentLine[-1][2])
+
+        # We need to be sure that we've actually found a new line rather than
+        # a space at the end of the current line.
+        #
+        text = self.queryNonEmptyText(nextObj)
+        if text:
+            line = text.getTextAfterOffset(nextOffset,
+                                           pyatspi.TEXT_BOUNDARY_LINE_START)
+            if not len(line[0].strip()) \
+               and nextObj.getRole() != pyatspi.ROLE_ENTRY:
+                [nextObj, nextOffset] = \
+                          self.findNextCaretInOrder(nextObj, nextOffset)
+            else:
+                nextOffset = line[1]
+
+        nextLine = self.getLineContentsAtOffset(nextObj, nextOffset)
+        if not nextLine:
+            return [None, -1]
+
+        if not arrowToLineBeginning:
+            extents = self.getExtents(obj,
+                                      characterOffset, 
+                                      characterOffset + 1)
+            oldX = extents[0]
+            for item in nextLine:
+                extents = self.getExtents(item[0], item[1], item[2])
+                newX1 = extents[0]
+                newX2 = newX1 + extents[2]
+                if newX1 <= oldX <= newX2:
+                    nextObj = item[0]
+                    nextOffset = 0
+                    text = self.queryNonEmptyText(nextObj)
+                    if text:
+                        newY = extents[1] + extents[3] / 2
+                        nextOffset = text.getOffsetAtPoint(oldX, newY, 0)
+                    break
+
+        self._currentLineContents = nextLine
+
+        return [nextObj, nextOffset]
+
     def findNextLine(self, obj, characterOffset):
         """Locates the caret offset at the next line in the document
         window, attempting to preserve horizontal caret position.
@@ -7964,6 +8144,10 @@
         Returns the [obj, characterOffset] at which to position the caret.
         """
 
+        if useNewLineNav:
+            [nextObj, nextOffset] = self.altFindNextLine(obj, characterOffset)
+            return nextObj, nextOffset
+
         lineExtents = self.getExtents(
             obj, characterOffset, characterOffset + 1)
         try:
