Skip to content

Commit 47d1fb0

Browse files
authored
Support for new Kindle functionality introduced in Kindle 1.19, as well as other improvements to Kindle support. (#6638)
1 parent 04282e5 commit 47d1fb0

11 files changed

Lines changed: 411 additions & 87 deletions

File tree

‎source/IAccessibleHandler.py‎

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ class OrderedWinEventLimiter(object):
5252
Only allow one event for one specific object at a time, though push it further forward in time if a duplicate tries to get added. This is true for both generic and focus events.
5353
"""
5454

55-
def __init__(self,maxFocusItems=3):
55+
def __init__(self,maxFocusItems=4):
5656
"""
5757
@param maxFocusItems: the amount of focus changed events allowed to be queued.
5858
@type maxFocusItems: integer
@@ -83,9 +83,6 @@ def addEvent(self,eventID,window,objectID,childID,threadID):
8383
if objectID in (winUser.OBJID_SYSMENU,winUser.OBJID_MENU) and childID==0:
8484
# This is a focus event on a menu bar itself, which is just silly. Ignore it.
8585
return False
86-
#We do not need a focus event on an object if we already got a foreground event for it
87-
if (winUser.EVENT_SYSTEM_FOREGROUND,window,objectID,childID,threadID) in self._focusEventCache:
88-
return False
8986
self._focusEventCache[(eventID,window,objectID,childID,threadID)]=next(self._eventCounter)
9087
return True
9188
elif eventID==winUser.EVENT_SYSTEM_FOREGROUND:

‎source/NVDAObjects/IAccessible/__init__.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ def _getFormatFieldAndOffsets(self,offset,formatConfig,calculateOffsets=True):
199199
log.debugWarning("could not get attributes",exc_info=True)
200200
return textInfos.FormatField(),(self._startOffset,self._endOffset)
201201
formatField=textInfos.FormatField()
202-
if not attribsString and offset>0:
202+
if attribsString is None and offset>0:
203203
try:
204204
attribsString=self.obj.IAccessibleTextObject.attributes(offset-1)[2]
205205
except COMError:

‎source/NVDAObjects/IAccessible/ia2TextMozilla.py‎

Lines changed: 65 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,20 @@ class MozillaCompoundTextInfo(CompoundTextInfo):
5858
def __init__(self, obj, position):
5959
super(MozillaCompoundTextInfo, self).__init__(obj, position)
6060
if isinstance(position, NVDAObject):
61-
# FIXME
62-
position = textInfos.POSITION_CARET
63-
if isinstance(position, self.__class__):
61+
try:
62+
self._start, self._startObj = self._findContentDescendant(position, textInfos.POSITION_FIRST)
63+
self._end, self._endObj = self._findContentDescendant(position, textInfos.POSITION_LAST)
64+
# This is the last character. Move to the end.
65+
self._end.move(textInfos.UNIT_CHARACTER, 1)
66+
except LookupError:
67+
# This might be an embedded object that doesn't support text such as a graphic.
68+
if position not in obj:
69+
raise ValueError("Object %s not in document" % position)
70+
# Use the point where this is embedded.
71+
self._start = self._end = self._getEmbedding(position)
72+
self._startObj = self._endObj = self._start.obj
73+
self._normalizeStartAndEnd()
74+
elif isinstance(position, self.__class__):
6475
self._start = position._start.copy()
6576
self._startObj = position._startObj
6677
if position._end is position._start:
@@ -92,26 +103,9 @@ def __init__(self, obj, position):
92103
self._start = self._end = caretTi
93104
self._startObj = self._endObj = caretObj
94105
elif position == textInfos.POSITION_SELECTION:
95-
# The caret is usually within the selection,
96-
# so start from the caret for better performance/tolerance of server brokenness.
97-
tempTi, tempObj = self._findContentDescendant(obj, textInfos.POSITION_CARET)
98-
try:
99-
tempTi = self._makeRawTextInfo(tempObj, position)
100-
except RuntimeError:
101-
# The caret is just before this object.
102-
# There is never a selection in this case.
103-
pass
104-
else:
105-
if tempTi.isCollapsed:
106-
# No selection, but perhaps the caret is at the start of the next/previous object.
107-
# This happens when you, for example, press shift+rightArrow at the end of a block.
108-
# Try from the root.
109-
rootTi = self._makeRawTextInfo(obj, position)
110-
if not rootTi.isCollapsed:
111-
# There is definitely a selection.
112-
tempTi, tempObj = rootTi, obj
106+
tempTi, tempObj = self._getSelectionBase()
113107
if tempTi.isCollapsed:
114-
# No selection, so use the caret.
108+
# No selection, so return the caret.
115109
self._start = self._end = tempTi
116110
self._startObj = self._endObj = tempObj
117111
else:
@@ -130,6 +124,46 @@ def __init__(self, obj, position):
130124
else:
131125
raise NotImplementedError
132126

127+
def _getSelectionBase(self):
128+
"""Get an NVDAObject and TextInfo somewhere within the selection.
129+
This is just a base point to start from.
130+
It will often be necessary to expand outwards and/or descend to get the complete selection.
131+
"""
132+
# The caret is usually within the selection,
133+
# so start from the caret for better performance/tolerance of server brokenness.
134+
try:
135+
ti, obj = self._findContentDescendant(self.obj, textInfos.POSITION_CARET)
136+
except LookupError:
137+
# No caret.
138+
ti = None
139+
else:
140+
try:
141+
ti = self._makeRawTextInfo(obj, textInfos.POSITION_SELECTION)
142+
except RuntimeError:
143+
# The caret is just before this object.
144+
# There is never a selection in this case.
145+
return ti, obj
146+
else:
147+
if not ti.isCollapsed:
148+
# There was a selection on the caret object.
149+
return ti, obj
150+
# At this point, we're in one of two situations:
151+
# 1. There was no caret.
152+
# This happens for non-navigable text; e.g. Kindle.
153+
# However, this doesn't mean there's no selection.
154+
# 2. There was a caret, but there was no selection on the caret object.
155+
# Perhaps the caret is at the start of the next/previous object.
156+
# This happens when you, for example, press shift+rightArrow at the end of a block.
157+
# Try from the root.
158+
rootTi = self._makeRawTextInfo(self.obj, textInfos.POSITION_SELECTION)
159+
if not rootTi.isCollapsed:
160+
# There is definitely a selection.
161+
return rootTi, self.obj
162+
if ti:
163+
# No selection, but there's a caret, so return that.
164+
return ti, obj
165+
raise RuntimeError("No selection or caret")
166+
133167
def _makeRawTextInfo(self, obj, position):
134168
return _getRawTextInfo(obj)(obj, position)
135169

@@ -201,14 +235,17 @@ def _iterRecursiveText(self, ti, controlStack, formatConfig):
201235
yield item
202236
elif isinstance(item, int): # Embedded object.
203237
embedded = _getEmbedded(ti.obj, item)
238+
notText = _getRawTextInfo(embedded) is NVDAObjectTextInfo
204239
if controlStack is not None:
205240
controlField = self._getControlFieldForObject(embedded)
206241
controlStack.append(controlField)
207242
if controlField:
243+
if notText:
244+
controlField["content"] = embedded.name
208245
controlField["_startOfNode"] = True
209246
yield textInfos.FieldCommand("controlStart", controlField)
210-
if _getRawTextInfo(embedded) is NVDAObjectTextInfo: # No text
211-
yield embedded.basicText
247+
if notText:
248+
yield u" "
212249
else:
213250
for subItem in self._iterRecursiveText(self._makeRawTextInfo(embedded, textInfos.POSITION_ALL), controlStack, formatConfig):
214251
yield subItem
@@ -509,7 +546,10 @@ def move(self, unit, direction, endPoint=None):
509546
# Find the edge of the current unit in the requested direction.
510547
moveTi, moveObj = self._findUnitEndpoints(moveTi, unit, findStart=moveBack, findEnd=not moveBack)
511548

512-
if not moveBack:
549+
if moveBack:
550+
# Collapse to the start of the previous unit.
551+
moveTi.collapse()
552+
else:
513553
# Collapse to the start of the next unit.
514554
moveTi.collapse(end=True)
515555
if moveTi.compareEndPoints(self._makeRawTextInfo(moveObj, textInfos.POSITION_ALL), "endToEnd") == 0:

0 commit comments

Comments
 (0)