def nodeLevel(self, obj): """ Determines the level of at which this object is at by using the object attribute 'level'. To be consistent with the default nodeLevel() this value is 0-based (Gecko return is 1-based) """ if obj is None or obj.getRole() == pyatspi.ROLE_HEADING \ or (obj.parent and obj.parent.getRole() == pyatspi.ROLE_MENU): return -1 try: state = obj.getState() except: return -1 else: if state.contains(pyatspi.STATE_DEFUNCT): # Yelp (or perhaps the work-in-progress a11y patch) # seems to be guilty of this. # #print "nodeLevel - obj is defunct", obj debug.println(debug.LEVEL_WARNING, "nodeLevel - obj is defunct") debug.printStack(debug.LEVEL_WARNING) return -1 try: attrs = obj.getAttributes() except: attrs = None if attrs is None: return -1 for attr in attrs: if attr.startswith("level:"): return int(attr[6:]) - 1 return -1
def getObjectsFromEOCs(self, obj, offset=None, boundary=None): """Expands the current object replacing EMBEDDED_OBJECT_CHARACTERS with [obj, startOffset, endOffset, string] tuples. Arguments - obj: the object whose EOCs we need to expand into tuples - offset: the character offset after which - boundary: the pyatspi text boundary type Returns a list of object tuples. """ if not obj: return [] elif boundary and obj.getRole() == pyatspi.ROLE_TABLE: if obj[0] and obj[0].getRole() in [pyatspi.ROLE_CAPTION, pyatspi.ROLE_LIST]: obj = obj[0] else: obj = obj.queryTable().getAccessibleAt(0, 0) if not obj: debug.printStack(debug.LEVEL_WARNING) return [] objects = [] text = self.queryNonEmptyText(obj) if text: if offset == None: offset = max(0, text.caretOffset) if boundary: [string, start, end] = self._getTextAtOffset(obj, offset, boundary) if end == -1: end = text.characterCount else: start = offset end = text.characterCount string = text.getText(start, end) else: string = "" start = 0 end = 1 unicodeText = string objects.append([obj, start, end, unicodeText]) pattern = re.compile(self._script.EMBEDDED_OBJECT_CHARACTER) matches = re.finditer(pattern, unicodeText) offset = 0 for m in matches: # Adjust the last object's endOffset to the last character # before the EOC. # childOffset = m.start(0) + start lastObj = objects[-1] lastObj[2] = childOffset if lastObj[1] == lastObj[2]: # A zero-length object is an indication of something # whose sole contents was an EOC. Delete it from the # list. # objects.pop() else: # Adjust the string to reflect just this segment. # lastObj[3] = unicodeText[offset:m.start(0)] offset = m.start(0) + 1 # Recursively tack on the child's objects. # childIndex = self._script.getChildIndex(obj, childOffset) child = obj[childIndex] objects.extend(self.getObjectsFromEOCs(child, 0, boundary)) # Tack on the remainder of the original object, if any. # if end > childOffset + 1: restOfText = unicodeText[offset:len(unicodeText)] objects.append([obj, childOffset + 1, end, restOfText]) if obj.getRole() in [pyatspi.ROLE_IMAGE, pyatspi.ROLE_TABLE]: # Imagemaps that don't have alternative text won't implement # the text interface, but they will have children (essentially # EOCs) that we need to get. The same is true for tables. # toAdd = [] for child in obj: toAdd.extend(self.getObjectsFromEOCs(child, 0, boundary)) if len(toAdd): if self.isSameObject(objects[-1][0], obj): objects.pop() objects.extend(toAdd) return objects