[orca] Always use the Collection interface for structural navigation and page summary
- From: Joanmarie Diggs <joanied src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [orca] Always use the Collection interface for structural navigation and page summary
- Date: Fri, 21 Dec 2012 15:34:31 +0000 (UTC)
commit c57ebf42ce417b5d3eba5348efbdc16023250cb4
Author: Joanmarie Diggs <jdiggs igalia com>
Date: Fri Dec 21 16:31:36 2012 +0100
Always use the Collection interface for structural navigation and page summary
* The old performace-related bugs were fixed in AT-SPI2
* The collection interface is now enabled via AT-SPI2 on all containers
* The collection interface is faster than the predicate-based alternative
src/orca/scripts/default.py | 11 ++
src/orca/scripts/toolkits/Gecko/script.py | 63 -------
src/orca/settings.py | 4 -
src/orca/structural_navigation.py | 268 +++++++----------------------
4 files changed, 74 insertions(+), 272 deletions(-)
---
diff --git a/src/orca/scripts/default.py b/src/orca/scripts/default.py
index b08380c..06c12bd 100644
--- a/src/orca/scripts/default.py
+++ b/src/orca/scripts/default.py
@@ -1233,6 +1233,17 @@ class Script(script.Script):
return script.Script.processKeyboardEvent(self, keyboardEvent)
+ def getCaretContext(self):
+ obj = orca_state.locusOfFocus
+ try:
+ offset = obj.queryText().caretOffset
+ except NotImplementedError:
+ offset = 0
+ except:
+ offset = -1
+
+ return obj, offset
+
def locusOfFocusChanged(self, event, oldLocusOfFocus, newLocusOfFocus):
"""Called when the visual object with focus changes.
diff --git a/src/orca/scripts/toolkits/Gecko/script.py b/src/orca/scripts/toolkits/Gecko/script.py
index 92e1a66..5e637bc 100644
--- a/src/orca/scripts/toolkits/Gecko/script.py
+++ b/src/orca/scripts/toolkits/Gecko/script.py
@@ -3546,26 +3546,8 @@ class Script(default.Script):
and unvisited links on the page containing obj.
"""
- if _settingsManager.getSetting('useCollection'):
- try:
- summary = self._collectionPageSummary()
- except:
- summary = self._iterativePageSummary(obj)
- else:
- summary = self._iterativePageSummary(obj)
-
- return summary
-
- def _collectionPageSummary(self):
- """Uses the Collection interface to get the quantity of headings,
- forms, tables, visited and unvisited links.
- """
-
docframe = self.utilities.documentFrame()
col = docframe.queryCollection()
- # We will initialize these after the queryCollection() call in case
- # Collection is not supported
- #
headings = 0
forms = 0
tables = 0
@@ -3601,51 +3583,6 @@ class Script(default.Script):
return [headings, forms, tables, vlinks, uvlinks, percentRead]
- def _iterativePageSummary(self, obj):
- """Reads the quantity of headings, forms, tables, visited and
- unvisited links.
- """
-
- headings = 0
- forms = 0
- tables = 0
- vlinks = 0
- uvlinks = 0
- percentRead = None
- nodetotal = 0
- obj_index = None
- currentobj = obj
-
- # Start at the first object after document frame.
- #
- obj = self.utilities.documentFrame()[0]
- while obj:
- nodetotal += 1
- if obj == currentobj:
- obj_index = nodetotal
- role = obj.getRole()
- if role == pyatspi.ROLE_HEADING:
- headings += 1
- elif role == pyatspi.ROLE_FORM:
- forms += 1
- elif role == pyatspi.ROLE_TABLE \
- and not self.utilities.isLayoutOnly(obj):
- tables += 1
- elif role == pyatspi.ROLE_LINK:
- if obj.getState().contains(pyatspi.STATE_VISITED):
- vlinks += 1
- else:
- uvlinks += 1
-
- obj = self.findNextObject(obj)
-
- # Calculate the percentage of the document that has been read.
- #
- if obj_index:
- percentRead = int(obj_index*100/nodetotal)
-
- return [headings, forms, tables, vlinks, uvlinks, percentRead]
-
def guessLabelFromLine(self, obj):
"""Attempts to guess what the label of an unlabeled form control
might be by looking at surrounding contents from the same line.
diff --git a/src/orca/settings.py b/src/orca/settings.py
index fb5a3dd..1540aa0 100644
--- a/src/orca/settings.py
+++ b/src/orca/settings.py
@@ -665,10 +665,6 @@ enableContractedBraille = False
#
brailleContractionTable = ''
-# Use Collection Interface?
-#
-useCollection = True
-
# Whether or not to speak the cell's coordinates when navigating
# from cell to cell in a table.
#
diff --git a/src/orca/structural_navigation.py b/src/orca/structural_navigation.py
index bcc9827..1b3cdb1 100644
--- a/src/orca/structural_navigation.py
+++ b/src/orca/structural_navigation.py
@@ -450,13 +450,6 @@ class StructuralNavigation:
UNVISITED_LINK = "unvisitedLink"
VISITED_LINK = "visitedLink"
- # Whether or not to attempt to use collection. There's no point
- # in bothering if we know that the collection interface has not
- # been implemented in a given app (e.g. StarOffice/OOo) so this
- # variable can be overridden.
- #
- collectionEnabled = settings.useCollection
-
# Roles which are recognized as being a form field. Note that this
# is for the purpose of match rules and predicates and refers to
# AT-SPI roles.
@@ -542,20 +535,10 @@ class StructuralNavigation:
# class for examples of each.)
#
bindings = eval("self._%sBindings()" % name)
+ criteria = eval("self._%sCriteria" % name)
predicate = eval("self._%sPredicate" % name)
presentation = eval("self._%sPresentation" % name)
- # We won't make this assumption for match criteria because
- # the collection interface might not be implemented (e.g.
- # StarOffice/OpenOffice) and/or its use might not be possible
- # or practical for a given StructuralNavigationObject (e.g.
- # matching by text attributes, spatial navigation within tables).
- #
- try:
- criteria = eval("self._%sCriteria" % name)
- except:
- criteria = None
-
return StructuralNavigationObject(self, name, bindings, predicate,
criteria, presentation)
@@ -802,101 +785,60 @@ class StructuralNavigation:
obj = obj or self.getCurrentObject()
- # Yelp is seemingly fond of killing children for sport. Better
- # check for that.
- #
try:
state = obj.getState()
except:
return [None, False]
else:
if state.contains(pyatspi.STATE_DEFUNCT):
- #print "goObject: defunct object", obj
debug.printException(debug.LEVEL_SEVERE)
return [None, False]
- success = False
wrap = settings.wrappedStructuralNavigation
- # Try to find it using Collection first. But don't do this with form
- # fields for now. It's a bit faster moving to the next form field,
- # but not on pages with huge forms (e.g. bugzilla's advanced search
- # page). And due to bug #538680, we definitely don't want to use
- # collection to go to the previous chunk or form field.
- #
- formObjects = [self.BUTTON, self.CHECK_BOX, self.COMBO_BOX,
- self.ENTRY, self.FORM_FIELD, self.RADIO_BUTTON]
-
- criteria = None
- objType = structuralNavigationObject.objType
- if self.collectionEnabled \
- and not objType in formObjects \
- and (isNext or objType != self.CHUNK):
- try:
- document = self._getDocument()
- collection = document.queryCollection()
- if structuralNavigationObject.criteria:
- criteria = structuralNavigationObject.criteria(collection,
- arg)
- except:
- debug.printException(debug.LEVEL_SEVERE)
- else:
- # If the document frame itself contains content and that is
- # our current object, querying the collection interface will
- # result in our starting at the top when looking for the next
- # object rather than the current caret offset. See bug 567984.
- #
- if isNext \
- and self._script.utilities.isSameObject(obj, document):
- criteria = None
-
- if criteria:
- try:
- rule = collection.createMatchRule(criteria.states.raw(),
- criteria.matchStates,
- criteria.objAttrs,
- criteria.matchObjAttrs,
- criteria.roles,
- criteria.matchRoles,
- criteria.interfaces,
- criteria.matchInterfaces,
- criteria.invert)
- if criteria.applyPredicate:
- predicate = structuralNavigationObject.predicate
- else:
- predicate = None
-
- if not isNext:
- [obj, wrapped] = self._findPrevByMatchRule(collection,
- rule,
- wrap,
- obj,
- predicate)
- else:
- [obj, wrapped] = self._findNextByMatchRule(collection,
- rule,
- wrap,
- obj,
- predicate)
- success = True
- collection.freeMatchRule(rule)
- # print "collection", structuralNavigationObject.objType
- except NotImplementedError:
- debug.printException(debug.LEVEL_SEVERE)
- except:
- debug.printException(debug.LEVEL_SEVERE)
- collection.freeMatchRule(rule)
+ document = self._getDocument()
+ collection = document.queryCollection()
+ criteria = structuralNavigationObject.criteria(collection, arg)
+
+ # If the document frame itself contains content and that is
+ # our current object, querying the collection interface will
+ # result in our starting at the top when looking for the next
+ # object rather than the current caret offset. See bug 567984.
+ #
+ if isNext and self._script.utilities.isSameObject(obj, document):
+ pred = self.isAfterDocumentOffset
+ if criteria.applyPredicate:
+ pred = pred and structuralNavigationObject.predicate
+ criteria.applyPredicate = True
+ structuralNavigationObject.predicate = pred
+
+ rule = collection.createMatchRule(criteria.states.raw(),
+ criteria.matchStates,
+ criteria.objAttrs,
+ criteria.matchObjAttrs,
+ criteria.roles,
+ criteria.matchRoles,
+ criteria.interfaces,
+ criteria.matchInterfaces,
+ criteria.invert)
+ if criteria.applyPredicate:
+ predicate = structuralNavigationObject.predicate
+ else:
+ predicate = None
+
+ if not isNext:
+ [obj, wrapped] = self._findPrevByMatchRule(collection,
+ rule,
+ wrap,
+ obj,
+ predicate)
+ else:
+ [obj, wrapped] = self._findNextByMatchRule(collection,
+ rule,
+ wrap,
+ obj,
+ predicate)
+ collection.freeMatchRule(rule)
- # Do it iteratively when Collection failed or is disabled
- #
- if not success:
- pred = structuralNavigationObject.predicate
- if not isNext:
- [obj, wrapped] = self._findPrevByPredicate(pred, wrap,
- obj, arg)
- else:
- [obj, wrapped] = self._findNextByPredicate(pred, wrap,
- obj, arg)
- # print "predicate", structuralNavigationObject.objType
if wrapped:
if not isNext:
# Translators: when the user is attempting to locate a
@@ -930,6 +872,26 @@ class StructuralNavigation:
return orca_state.locusOfFocus
+ def isAfterDocumentOffset(self, obj, arg=None):
+ """Returns True if obj is after the document's caret offset."""
+ document = self._getDocument()
+ try:
+ offset = document.queryText().caretOffset
+ except:
+ return False
+
+ start, end = self._script.utilities.getHyperlinkRange(obj)
+ if start > offset:
+ return True
+
+ try:
+ hypertext = document.queryHypertext()
+ hyperlink = hypertext.getLink(hypertext.getNLinks() - 1)
+ except:
+ return False
+
+ return offset > hyperlink.startIndex
+
def _findPrevByMatchRule(self, collection, matchRule, wrap, currentObj,
predicate=None):
"""Finds the previous object using the given match rule as a
@@ -1073,110 +1035,6 @@ class StructuralNavigation:
return [match, wrapped]
- def _findPrevByPredicate(self, pred, wrap, currentObj=None, arg=None):
- """Finds the caret offset at the beginning of the previous object
- using the given predicate as a pattern to match.
-
- Arguments:
- -pred: a python callable that takes an accessible argument and
- returns true/false based on some match criteria
- -wrap: if True and the top of the document is reached, move
- to the bottom and keep looking.
- -currentObj: the object from which the search should begin
- -arg: an additional value to be passed to the predicate
-
- Returns: [obj, wrapped] where wrapped is a boolean reflecting
- whether wrapping took place.
- """
-
- currentObj = currentObj or self.getCurrentObject()
- document = self._getDocument()
-
- # If the current object is the document itself, find an actual
- # object to use as the starting point. Otherwise we're in
- # danger of skipping over the objects in between our present
- # location and top of the document.
- #
- if self._script.utilities.isSameObject(currentObj, document):
- currentObj = self._findNextObject(currentObj, document)
-
- ancestors = []
- nestableRoles = [pyatspi.ROLE_LIST, pyatspi.ROLE_TABLE]
- obj = currentObj.parent
- while obj:
- ancestors.append(obj)
- obj = obj.parent
-
- obj = self._findPreviousObject(currentObj, document)
- wrapped = obj is None
- match = None
-
- if wrapped:
- obj = self._findLastObject(document)
-
- while obj and not match:
- isNested = (obj != currentObj.parent \
- and currentObj.parent.getRole() == obj.getRole() \
- and obj.getRole() in nestableRoles)
- if (not obj in ancestors or isNested) and pred(obj):
- if wrapped \
- and self._script.utilities.isSameObject(currentObj, obj):
- break
- else:
- match = obj
- else:
- obj = self._findPreviousObject(obj, document)
- if not obj and wrap and not wrapped:
- obj = self._findLastObject(document)
- wrapped = True
-
- return [match, wrapped]
-
- def _findNextByPredicate(self, pred, wrap, currentObj=None, arg=None):
- """Finds the caret offset at the beginning of the next object
- using the given predicate as a pattern to match or not match.
-
- Arguments:
- -pred: a python callable that takes an accessible argument and
- returns true/false based on some match criteria
- -wrap: if True and the bottom of the document is reached, move
- to the top and keep looking.
- -currentObj: the object from which the search should begin
- -arg: an additional value to be passed to the predicate
-
- Returns: [obj, wrapped] where wrapped is a boolean reflecting
- whether wrapping took place.
- """
- currentObj = currentObj or self.getCurrentObject()
- ancestors = []
- obj = currentObj.parent
- while obj:
- ancestors.append(obj)
- obj = obj.parent
-
- document = self._getDocument()
- obj = self._findNextObject(currentObj, document)
- wrapped = obj is None
- match = None
-
- if wrapped:
- [obj, offset] = self._getCaretPosition(document)
-
- while obj and not match:
- if (not obj in ancestors) and pred(obj, arg):
- if wrapped \
- and self._script.utilities.isSameObject(currentObj, obj):
- break
- else:
- match = obj
- else:
- obj = self._findNextObject(obj, document)
- if not obj and wrap and not wrapped:
- [obj, offset] = self._getCaretPosition(document)
- wrapped = True
-
- return [match, wrapped]
-
def _findPreviousObject(self, obj, stopAncestor):
"""Finds the object prior to this one, where the tree we're
dealing with is a DOM and 'prior' means the previous object
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]