orca r4104 - in trunk: . src/orca src/orca/scripts/toolkits/Gecko



Author: joanied
Date: Fri Aug 15 18:41:16 2008
New Revision: 4104
URL: http://svn.gnome.org/viewvc/orca?rev=4104&view=rev

Log:
* src/orca/scripts/toolkits/Gecko/script.py:
  src/orca/default.py:
  src/orca/flat_review.py:
  Work on bug #542833 - Flat review in Thunderbird is largely
  broken.  This part of the fix should stop us from hanging if
  flat review is invoked with a message list with thousands of
  messages and should ensure that we don't review things that
  are not actually on the screen. There is still more work that
  needs to be done on this bug.



Modified:
   trunk/ChangeLog
   trunk/src/orca/default.py
   trunk/src/orca/flat_review.py
   trunk/src/orca/scripts/toolkits/Gecko/script.py

Modified: trunk/src/orca/default.py
==============================================================================
--- trunk/src/orca/default.py	(original)
+++ trunk/src/orca/default.py	Fri Aug 15 18:41:16 2008
@@ -40,6 +40,7 @@
 
 import locale
 import math
+import sys
 import time
 
 import pyatspi
@@ -4517,7 +4518,135 @@
     def pursueForFlatReview(self, obj):
         """Determines if we should look any further at the object
         for flat review."""
-        return obj.getState().contains(pyatspi.STATE_SHOWING)
+
+        try:
+            state = obj.getState()
+        except:
+            debug.printException(debug.LEVEL_WARNING)
+            return False
+        else:
+            return state.contains(pyatspi.STATE_SHOWING)
+
+    def getShowingDescendants(self, parent):
+        """Given a parent that manages its descendants, return a list of
+        Accessible children that are actually showing.  This algorithm
+        was inspired a little by the srw_elements_from_accessible logic
+        in Gnopernicus, and makes the assumption that the children of
+        an object that manages its descendants are arranged in a row
+        and column format.
+
+        Arguments:
+        - parent: The accessible which manages its descendants
+
+        Returns a list of Accessible descendants which are showing.
+        """
+
+        if not parent:
+            return []
+
+        if not parent.getState().contains(pyatspi.STATE_MANAGES_DESCENDANTS) \
+           or parent.childCount <= 50:
+            return []
+
+        try:
+            icomponent = parent.queryComponent()
+        except NotImplementedError:
+            return []
+
+        descendants = []
+
+        parentExtents = icomponent.getExtents(0)
+
+        # [[[TODO: WDW - HACK related to GAIL bug where table column
+        # headers seem to be ignored:
+        # http://bugzilla.gnome.org/show_bug.cgi?id=325809.  The
+        # problem is that this causes getAccessibleAtPoint to return
+        # the cell effectively below the real cell at a given point,
+        # making a mess of everything.  So...we just manually add in
+        # showing headers for now.  The remainder of the logic below
+        # accidentally accounts for this offset, yet it should also
+        # work when bug 325809 is fixed.]]]
+        #
+        try:
+            table = parent.queryTable()
+        except NotImplementedError:
+            table = None
+            
+        if table:
+            for i in range(0, table.nColumns):
+                header = table.getColumnHeader(i)
+                if header:
+                    extents = header.queryComponent().getExtents(0)
+                    stateset = header.getState()
+                    if stateset.contains(pyatspi.STATE_SHOWING) \
+                       and (extents.x >= 0) and (extents.y >= 0) \
+                       and (extents.width > 0) and (extents.height > 0) \
+                       and self.visible(extents.x, extents.y,
+                                        extents.width, extents.height,
+                                        parentExtents.x, parentExtents.y,
+                                        parentExtents.width,
+                                        parentExtents.height):
+                        descendants.append(header)
+
+        # This algorithm goes left to right, top to bottom while attempting
+        # to do *some* optimization over queries.  It could definitely be
+        # improved. The gridSize is a minimal chunk to jump around in the
+        # table.
+        #
+        gridSize = 7
+        currentY = parentExtents.y
+        while currentY < (parentExtents.y + parentExtents.height):
+            currentX = parentExtents.x
+            minHeight = sys.maxint
+            while currentX < (parentExtents.x + parentExtents.width):
+                child = icomponent.getAccessibleAtPoint(currentX, currentY, 0)
+                if child:
+                    extents = child.queryComponent().getExtents(0)
+                    if extents.x >= 0 and extents.y >= 0:
+                        newX = extents.x + extents.width
+                        minHeight = min(minHeight, extents.height)
+                        if not descendants.count(child):
+                            descendants.append(child)
+                    else:
+                        newX = currentX + gridSize
+                else:
+                    newX = currentX + gridSize
+                if newX <= currentX:
+                    currentX += gridSize
+                else:
+                    currentX = newX
+            if minHeight == sys.maxint:
+                minHeight = gridSize
+            currentY += minHeight
+
+        return descendants
+
+    def visible(self,
+                ax, ay, awidth, aheight,
+                bx, by, bwidth, bheight):
+        """Returns true if any portion of region 'a' is in region 'b'
+        """
+        highestBottom = min(ay + aheight, by + bheight)
+        lowestTop = max(ay, by)
+
+        leftMostRightEdge = min(ax + awidth, bx + bwidth)
+        rightMostLeftEdge = max(ax, bx)
+
+        visible = False
+
+        if (lowestTop <= highestBottom) \
+           and (rightMostLeftEdge <= leftMostRightEdge):
+            visible = True
+        elif (aheight == 0):
+            if (awidth == 0):
+                visible = (lowestTop == highestBottom) \
+                          and (leftMostRightEdge == rightMostLeftEdge)
+            else:
+                visible = leftMostRightEdge <= rightMostLeftEdge
+        elif (awidth == 0):
+            visible = (lowestTop <= highestBottom)
+
+        return visible
 
     def getFlatReviewContext(self):
         """Returns the flat review context, creating one if necessary."""

Modified: trunk/src/orca/flat_review.py
==============================================================================
--- trunk/src/orca/flat_review.py	(original)
+++ trunk/src/orca/flat_review.py	Fri Aug 15 18:41:16 2008
@@ -26,7 +26,6 @@
 __license__   = "LGPL"
 
 import re
-import sys
 
 import pyatspi
 import braille
@@ -597,11 +596,6 @@
     WRAP_TOP_BOTTOM = 1 << 1
     WRAP_ALL        = (WRAP_LINE | WRAP_TOP_BOTTOM)
 
-    # A minimal chunk to jump around should we not really know where we
-    # are going.
-    #
-    GRID_SIZE = 7
-
     def __init__(self, script):
         """Create a new Context that will be used for handling flat
         review mode.
@@ -730,34 +724,6 @@
         #
         self.targetCharInfo = None
 
-
-    def visible(self,
-                ax, ay, awidth, aheight,
-                bx, by, bwidth, bheight):
-        """Returns true if any portion of region 'a' is in region 'b'
-        """
-        highestBottom = min(ay + aheight, by + bheight)
-        lowestTop = max(ay, by)
-
-        leftMostRightEdge = min(ax + awidth, bx + bwidth)
-        rightMostLeftEdge = max(ax, bx)
-
-        visible = False
-
-        if (lowestTop <= highestBottom) \
-           and (rightMostLeftEdge <= leftMostRightEdge):
-            visible = True
-        elif (aheight == 0):
-            if (awidth == 0):
-                visible = (lowestTop == highestBottom) \
-                          and (leftMostRightEdge == rightMostLeftEdge)
-            else:
-                visible = leftMostRightEdge <= rightMostLeftEdge
-        elif (awidth == 0):
-            visible = (lowestTop <= highestBottom)
-
-        return visible
-
     def clip(self,
              ax, ay, awidth, aheight,
              bx, by, bwidth, bheight):
@@ -819,9 +785,9 @@
             else:
                 [x, y, width, height] = text.getRangeExtents( \
                           substringStartOffset, substringEndOffset, 0)
-                if self.visible(x, y, width, height,
-                                cliprect.x, cliprect.y,
-                                cliprect.width, cliprect.height):
+                if self.script.visible(x, y, width, height,
+                                       cliprect.x, cliprect.y,
+                                       cliprect.width, cliprect.height):
 
                     anyVisible = True
 
@@ -1094,10 +1060,10 @@
         #
         extents = icomponent.getExtents(0)
 
-        if not self.visible(extents.x, extents.y,
-                            extents.width, extents.height,
-                            cliprect.x, cliprect.y,
-                            cliprect.width, cliprect.height):
+        if not self.script.visible(extents.x, extents.y,
+                                   extents.width, extents.height,
+                                   cliprect.x, cliprect.y,
+                                   cliprect.width, cliprect.height):
             return []
 
         debug.println(
@@ -1143,9 +1109,9 @@
             [width, height] = iimage.getImageSize()
 
             if (width != 0) and (height != 0) \
-                   and self.visible(x, y, width, height,
-                                    cliprect.x, cliprect.y,
-                                    cliprect.width, cliprect.height):
+                   and self.script.visible(x, y, width, height,
+                                           cliprect.x, cliprect.y,
+                                           cliprect.width, cliprect.height):
 
                 clipping = self.clip(x, y, width, height,
                                      cliprect.x, cliprect.y,
@@ -1216,91 +1182,6 @@
 
         return zones
 
-    def getShowingDescendants(self, parent):
-        """Given a parent that manages its descendants, return a list of
-        Accessible children that are actually showing.  This algorithm
-        was inspired a little by the srw_elements_from_accessible logic
-        in Gnopernicus, and makes the assumption that the children of
-        an object that manages its descendants are arranged in a row
-        and column format.
-        """
-
-        if not parent:
-            return []
-
-        try:
-            icomponent = parent.queryComponent()
-        except NotImplementedError:
-            return []
-
-        descendants = []
-
-        parentExtents = icomponent.getExtents(0)
-
-        # [[[TODO: WDW - HACK related to GAIL bug where table column
-        # headers seem to be ignored:
-        # http://bugzilla.gnome.org/show_bug.cgi?id=325809.  The
-        # problem is that this causes getAccessibleAtPoint to return
-        # the cell effectively below the real cell at a given point,
-        # making a mess of everything.  So...we just manually add in
-        # showing headers for now.  The remainder of the logic below
-        # accidentally accounts for this offset, yet it should also
-        # work when bug 325809 is fixed.]]]
-        #
-        try:
-            table = parent.queryTable()
-        except NotImplementedError:
-            table = None
-            
-        if table:
-            for i in range(0, table.nColumns):
-                header = table.getColumnHeader(i)
-                if header:
-                    extents = header.queryComponent().getExtents(0)
-                    stateset = header.getState()
-                    if stateset.contains(pyatspi.STATE_SHOWING) \
-                       and (extents.x >= 0) and (extents.y >= 0) \
-                       and (extents.width > 0) and (extents.height > 0) \
-                       and self.visible(extents.x, extents.y,
-                                        extents.width, extents.height,
-                                        parentExtents.x, parentExtents.y,
-                                        parentExtents.width,
-                                        parentExtents.height):
-                        descendants.append(header)
-
-        # This algorithm goes left to right, top to bottom while attempting
-        # to do *some* optimization over queries.  It could definitely be
-        # improved.
-        #
-        currentY = parentExtents.y
-        while currentY < (parentExtents.y + parentExtents.height):
-            currentX = parentExtents.x
-            minHeight = sys.maxint
-            while currentX < (parentExtents.x + parentExtents.width):
-                child = icomponent.getAccessibleAtPoint(currentX,
-                                                            currentY,
-                                                            0)
-                if child:
-                    extents = child.queryComponent().getExtents(0)
-                    if extents.x >= 0 and extents.y >= 0:
-                        newX = extents.x + extents.width
-                        minHeight = min(minHeight, extents.height)
-                        if not descendants.count(child):
-                            descendants.append(child)
-                    else:
-                        newX = currentX + self.GRID_SIZE
-                else:
-                    newX = currentX + self.GRID_SIZE
-                if newX <= currentX:
-                    currentX += self.GRID_SIZE
-                else:
-                    currentX = newX
-            if minHeight == sys.maxint:
-                minHeight = self.GRID_SIZE
-            currentY += minHeight
-
-        return descendants
-
     def getShowingZones(self, root):
         """Returns a list of all interesting, non-intersecting, regions
         that are drawn on the screen.  Each element of the list is the
@@ -1383,11 +1264,11 @@
                 zones = self.getZonesFromAccessible(root, rootexts)
         except NotImplementedError:
             pass
-        
-        stateset = root.getState()
-        if stateset.contains(pyatspi.STATE_MANAGES_DESCENDANTS) \
-           and (root.childCount > 50):
-            for child in self.getShowingDescendants(root):
+
+        showingDescendants = \
+            self.script.getShowingDescendants(root)
+        if len(showingDescendants):
+            for child in showingDescendants:
                 zones.extend(self.getShowingZones(child))
         else:
             for i in range(0, root.childCount):

Modified: trunk/src/orca/scripts/toolkits/Gecko/script.py
==============================================================================
--- trunk/src/orca/scripts/toolkits/Gecko/script.py	(original)
+++ trunk/src/orca/scripts/toolkits/Gecko/script.py	Fri Aug 15 18:41:16 2008
@@ -3126,23 +3126,122 @@
         """Determines if we should look any further at the object
         for flat review."""
 
-        # [[[TODO: HACK - WDW Gecko has issues about the SHOWING
-        # state of objects, especially those in document frames.
-        # It tells us the content in tabs that are not showing
-        # is actually showing.  See:
-        #
-        # http://bugzilla.gnome.org/show_bug.cgi?id=408071
-        #
-        # To work around this, we do a little extra check.  If
-        # the obj is a document, and it's not the one that
-        # Firefox is currently showing the user, we skip it.
+        # It should be enough to check for STATE_SHOWING, but Gecko seems
+        # to reverse STATE_SHOWING and STATE_VISIBLE, exposing STATE_SHOWING
+        # for objects which are offscreen. So, we'll check for both. See
+        # bug #542833. [[[TODO - JD: We're putting this check in just this
+        # script for now to be on the safe side. Consider for the default
+        # script as well?]]]
         #
-        pursue = default.Script.pursueForFlatReview(self, obj)
-        if pursue and (obj.getRole() == pyatspi.ROLE_DOCUMENT_FRAME):
-            documentFrame = self.getDocumentFrame()
-            pursue = obj == documentFrame
+        try:
+            state = obj.getState()
+        except:
+            debug.printException(debug.LEVEL_WARNING)
+            return False
+        else:
+            return state.contains(pyatspi.STATE_SHOWING) \
+                   and state.contains(pyatspi.STATE_VISIBLE)
+ 
+    def getShowingDescendants(self, parent):
+        """Given an accessible object, returns a list of accessible children
+        that are actually showing/visible/pursable for flat review. We're
+        overriding the default method here primarily to handle enormous
+        tree tables (such as the Thunderbird message list) which do not
+        manage their descendants.
+
+        Arguments:
+        - parent: The accessible which manages its descendants
+
+        Returns a list of Accessible descendants which are showing.
+        """
+
+        if not parent:
+            return []
+
+        # If this object is not a tree table, if it manages its descendants,
+        # or if it doesn't have very many children, let the default script
+        # handle it.
+        #
+        if parent.getRole() != pyatspi.ROLE_TREE_TABLE \
+           or parent.getState().contains(pyatspi.STATE_MANAGES_DESCENDANTS) \
+           or parent.childCount <= 50:
+            return default.Script.getShowingDescendants(self, parent)
+
+        try:
+            table = parent.queryTable()
+        except NotImplementedError:
+            return []
+
+        descendants = []
+
+        # First figure out what columns are visible as there's no point
+        # in examining cells which we know won't be visible.
+        # 
+        visibleColumns = []
+        for i in range(table.nColumns):
+            header = table.getColumnHeader(i)
+            if self.pursueForFlatReview(header):
+                visibleColumns.append(i)
+                descendants.append(header)
+
+        if not len(visibleColumns):
+            return []
+
+        # Now that we know in which columns we can expect to find visible
+        # cells, try to quickly locate a visible row.
+        #
+        startingRow = 0
+
+        # If we have one or more selected items, odds are fairly good
+        # (although not guaranteed) that one of those items happens to
+        # be showing. Failing that, calculate how many rows can fit in
+        # the exposed portion of the tree table and scroll down.
+        #
+        selectedRows = table.getSelectedRows()
+        for row in selectedRows:
+            acc = table.getAccessibleAt(row, visibleColumns[0])
+            if self.pursueForFlatReview(acc):
+                startingRow = row
+                break
+        else:
+            try:
+                tableExtents = parent.queryComponent().getExtents(0)
+                acc = table.getAccessibleAt(0, visibleColumns[0])
+                cellExtents = acc.queryComponent().getExtents(0)
+            except:
+                pass
+            else:
+                rowIncrement = max(1, tableExtents.height / cellExtents.height)
+                for row in range(0, table.nRows, rowIncrement):
+                    acc = table.getAccessibleAt(row, visibleColumns[0])
+                    if acc and self.pursueForFlatReview(acc):
+                        startingRow = row
+                        break
+
+        # Get everything after this point which is visible.
+        #
+        for row in range(startingRow, table.nRows):
+            acc = table.getAccessibleAt(row, visibleColumns[0])
+            if self.pursueForFlatReview(acc):
+                descendants.append(acc)
+                for col in visibleColumns[1:len(visibleColumns)]:
+                    descendants.append(table.getAccessibleAt(row, col))
+            else:
+                break
+
+        # Get everything before this point which is visible.
+        #
+        for row in range(startingRow - 1, -1, -1):
+            acc = table.getAccessibleAt(row, visibleColumns[0])
+            if self.pursueForFlatReview(acc):
+                thisRow = [acc]
+                for col in visibleColumns[1:len(visibleColumns)]:
+                    thisRow.append(table.getAccessibleAt(row, col))
+                descendants[0:0] = thisRow
+            else:
+                break
 
-        return pursue
+        return descendants
 
     def getHeadingLevel(self, obj):
         """Determines the heading level of the given object.  A value



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