# Orca # # Copyright 2005-2008 Sun Microsystems Inc. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the # Free Software Foundation, Inc., Franklin Street, Fifth Floor, # Boston MA 02110-1301 USA. """Custom structural navigation for the Gecko toolkit.""" __id__ = "$Id$" __version__ = "$Revision$" __date__ = "$Date$" __copyright__ = "Copyright (c) 2005-2008 Sun Microsystems Inc." __license__ = "LGPL" import pyatspi import orca.structural_navigation as structural_navigation import orca.settings as settings ######################################################################## # # # Custom Structural Navigation # # # ######################################################################## class GeckoStructuralNavigation(structural_navigation.StructuralNavigation): def __init__(self, script, enabledTypes, enabled): """Gecko specific Structural Navigation.""" structural_navigation.StructuralNavigation.__init__(self, script, enabledTypes, enabled) ##################################################################### # # # Methods for finding and moving to objects # # # ##################################################################### def getCurrentObject(self): """Returns the current object -- normally, the locusOfFocus. But in the case of Gecko, that doesn't always work. """ [obj, offset] = self._script.getCaretContext() return obj 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 in a linear presentation sense. Arguments: -obj: the object where to start. -stopAncestor: the ancestor at which the search should stop """ return self._script.findPreviousObject(obj, stopAncestor) def _findNextObject(self, obj, stopAncestor): """Finds the object after to this one, where the tree we're dealing with is a DOM and 'next' means the next object in a linear presentation sense. Arguments: -obj: the object where to start. -stopAncestor: the ancestor at which the search should stop """ return self._script.findNextObject(obj, stopAncestor) def _findLastObject(self, ancestor): """Returns the last object in ancestor. Arguments: - ancestor: the accessible object whose last (child) object is sought. """ return self._script.getLastObject(ancestor) def _getDocument(self): """Returns the document or other object in which the object of interest is contained. """ return self._script.utilities.documentFrame() def _isInDocument(self, obj): """Returns True of the object is inside of the document.""" return self._script.inDocumentContent(obj) def _getCaretPosition(self, obj): """Returns the [obj, characterOffset] where the caret should be positioned. """ obj, offset = self._script.findFirstCaretContext(obj, 0) if not obj: return obj, offset if obj.getRole() == pyatspi.ROLE_SECTION \ and not self._script.utilities.queryNonEmptyText(obj): obj, offset = self._script.findNextCaretInOrder(obj, offset) # If it's an anchor, look for the first object of use. # See bug #591592. # if obj.getRole() == pyatspi.ROLE_LINK \ and not obj.getState().contains(pyatspi.STATE_FOCUSABLE): obj, offset = self._script.findNextCaretInOrder(obj, offset) return obj, offset def _setCaretPosition(self, obj, characterOffset): """Sets the caret at the specified offset within obj.""" self._script.setCaretPosition(obj, characterOffset) def _isUselessObject(self, obj): """Returns true if the given object is an obj that doesn't have any meaning associated with it. """ return self._script.isUselessObject(obj) ##################################################################### # # # Methods for presenting objects # # # ##################################################################### def _presentLine(self, obj, offset): """Presents the first line of the object to the user.""" self._script.presentLine(obj, offset) def _presentObject(self, obj, offset): """Presents the entire object to the user.""" if obj.getRole() == pyatspi.ROLE_LINK: try: obj.queryComponent().grabFocus() except: pass self._script.updateBraille(obj) contents = self._script.getObjectContentsAtOffset(obj, offset) self._script.speakContents(contents) ######################################################################### # # # Objects # # # ######################################################################### ######################## # # # Chunks/Large Objects # # # ######################## def _chunkPredicate(self, obj, arg=None): """The predicate to be used for verifying that the object obj is a chunk. Arguments: - obj: the accessible object under consideration. - arg: an optional argument which may need to be included in the criteria (e.g. the level of a heading). """ role = obj.getRole() if not role in self.OBJECT_ROLES: return False embeddedObjectChar = self._script.EMBEDDED_OBJECT_CHARACTER if role in [pyatspi.ROLE_LIST, pyatspi.ROLE_TABLE]: # These roles are often serving as containers. We want to see # if what they contain is a bunch of text (as opposed to a # bunch of links or other embedded objects). As for lists: # We only care about those of the (un)ordered variety. Form # field lists are not chunks. # if not obj.getState().contains(pyatspi.STATE_FOCUSABLE): charCount = 0 for child in obj: try: text = child.queryText() except: text = None if not text: continue string = text.getText(0, -1) if not string.count(embeddedObjectChar): charCount += text.characterCount if charCount > settings.largeObjectTextLength: return True return False else: # We're going to have to take a guess. It's probably a big # chunk of text if it contains at least the number of characters # specified by largeObjectTextLength, AND # - Guess #1: No more than 5% of the object's total characters # are EOCs, OR # - Guess #2: No more than 0.5% of the object's initial n # characters are EOCs, where n is the largeObjectTextLength. # try: text = obj.queryText() except: return False if text \ and text.characterCount > settings.largeObjectTextLength: string = text.getText(0, -1).decode("UTF-8") eocs = float(string.count(embeddedObjectChar)) if eocs/text.characterCount < 0.05: # print "Guess #1", string, eocs/text.characterCount return True else: string = string[0:settings.largeObjectTextLength] eocs = float(string.count(embeddedObjectChar)) # print "Guess #2", string, eocs/len(string) return eocs/len(string) < 0.005 ######################## # # # Entries # # # ######################## def _entryPredicate(self, obj, arg=None): """The predicate to be used for verifying that the object obj is an entry. Arguments: - obj: the accessible object under consideration. - arg: an optional argument which may need to be included in the criteria (e.g. the level of a heading). """ isMatch = False if self._script.utilities.isEntry(obj) \ or self._script.utilities.isPasswordText(obj): state = obj.getState() isMatch = state.contains(pyatspi.STATE_FOCUSABLE) \ and state.contains(pyatspi.STATE_SENSITIVE) \ and state.contains(pyatspi.STATE_EDITABLE) return isMatch ######################## # # # Form Fields # # # ######################## def _formFieldPredicate(self, obj, arg=None): """The predicate to be used for verifying that the object obj is a form field. Arguments: - obj: the accessible object under consideration. - arg: an optional argument which may need to be included in the criteria (e.g. the level of a heading). """ isMatch = False if obj and obj.getRole() in self.FORM_ROLES: state = obj.getState() isMatch = state.contains(pyatspi.STATE_FOCUSABLE) \ and state.contains(pyatspi.STATE_SENSITIVE) if obj.getRole() == pyatspi.ROLE_DOCUMENT_FRAME: isMatch = isMatch and state.contains(pyatspi.STATE_EDITABLE) return isMatch