Mini Shell
# Orca
#
# Copyright 2010 Joanmarie Diggs.
#
# 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.
"""Commonly-required utility methods needed by -- and potentially
customized by -- application and toolkit scripts. They have
been pulled out from the scripts because certain scripts had
gotten way too large as a result of including these methods."""
__id__ = "$Id$"
__version__ = "$Revision$"
__date__ = "$Date$"
__copyright__ = "Copyright (c) 2010 Joanmarie Diggs."
__license__ = "LGPL"
import pyatspi
import orca.debug as debug
import orca.script_utilities as script_utilities
#############################################################################
# #
# Utilities #
# #
#############################################################################
class Utilities(script_utilities.Utilities):
def __init__(self, script):
"""Creates an instance of the Utilities class.
Arguments:
- script: the script with which this instance is associated.
"""
script_utilities.Utilities.__init__(self, script)
#########################################################################
# #
# Utilities for finding, identifying, and comparing accessibles #
# #
#########################################################################
def childNodes(self, obj):
"""Gets all of the children that have RELATION_NODE_CHILD_OF pointing
to this expanded table cell. Overridden here because the object
which contains the relation is in a hidden column and thus doesn't
have a column number (necessary for using getAccessibleAt()).
Arguments:
-obj: the Accessible Object
Returns: a list of all the child nodes
"""
if not self._script.chat.isInBuddyList(obj):
return script_utilities.Utilities.childNodes(self, obj)
try:
table = obj.parent.queryTable()
except:
return []
else:
if not obj.getState().contains(pyatspi.STATE_EXPANDED):
return []
nodes = []
index = self.cellIndex(obj)
row = table.getRowAtIndex(index)
col = table.getColumnAtIndex(index + 1)
nodeLevel = self.nodeLevel(obj)
done = False
# Candidates will be in the rows beneath the current row.
# Only check in the current column and stop checking as
# soon as the node level of a candidate is equal or less
# than our current level.
#
for i in range(row+1, table.nRows):
cell = table.getAccessibleAt(i, col)
nodeCell = cell.parent[cell.getIndexInParent() - 1]
relations = nodeCell.getRelationSet()
for relation in relations:
if relation.getRelationType() \
== pyatspi.RELATION_NODE_CHILD_OF:
nodeOf = relation.getTarget(0)
if self.isSameObject(obj, nodeOf):
nodes.append(cell)
else:
currentLevel = self.nodeLevel(nodeOf)
if currentLevel <= nodeLevel:
done = True
break
if done:
break
return nodes
def nodeLevel(self, obj):
"""Determines the node level of this object if it is in a tree
relation, with 0 being the top level node. If this object is
not in a tree relation, then -1 will be returned. Overridden
here because the accessible we need is in a hidden column.
Arguments:
-obj: the Accessible object
"""
if not obj:
return -1
if not self._script.chat.isInBuddyList(obj):
return script_utilities.Utilities.nodeLevel(self, obj)
try:
obj = obj.parent[obj.getIndexInParent() - 1]
except:
return -1
try:
table = obj.parent.queryTable()
except:
return -1
nodes = []
node = obj
done = False
while not done:
relations = node.getRelationSet()
node = None
for relation in relations:
if relation.getRelationType() \
== pyatspi.RELATION_NODE_CHILD_OF:
node = relation.getTarget(0)
break
# We want to avoid situations where something gives us an
# infinite cycle of nodes. Bon Echo has been seen to do
# this (see bug 351847).
#
if (len(nodes) > 100) or nodes.count(node):
debug.println(debug.LEVEL_WARNING,
"pidgin.nodeLevel detected a cycle!!!")
done = True
elif node:
nodes.append(node)
debug.println(debug.LEVEL_FINEST,
"pidgin.nodeLevel %d" % len(nodes))
else:
done = True
return len(nodes) - 1
#########################################################################
# #
# Utilities for working with the accessible text interface #
# #
#########################################################################
#########################################################################
# #
# Miscellaneous Utilities #
# #
#########################################################################
def isZombie(self, obj):
if not super().isZombie(obj):
return False
if obj.getRole() != pyatspi.ROLE_TOGGLE_BUTTON:
return True
msg = 'INFO: Hacking around broken index in parent for %s' % obj
debug.println(debug.LEVEL_INFO, msg, True)
return obj.getIndexInParent() != -1