Skip to content

Commit fd18032

Browse files
authored
Merge 23882e6 into 6eda79a
2 parents 6eda79a + 23882e6 commit fd18032

8 files changed

Lines changed: 142 additions & 571 deletions

File tree

source/documentBase.py

Lines changed: 94 additions & 216 deletions
Large diffs are not rendered by default.

source/speech/sayAll.py

Lines changed: 39 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@
44
# Copyright (C) 2006-2021 NV Access Limited, Peter Vágner, Aleksey Sadovoy, Babbage B.V., Bill Dengler,
55
# Julien Cochuyt
66

7-
from abc import ABCMeta, abstractmethod
87
from enum import IntEnum
9-
from typing import Callable, TYPE_CHECKING, Optional
8+
from typing import Callable, TYPE_CHECKING
109
import weakref
1110
import garbageHandler
1211
from logHandler import log
@@ -37,7 +36,6 @@
3736
class CURSOR(IntEnum):
3837
CARET = 0
3938
REVIEW = 1
40-
TABLE = 2
4139

4240

4341
SayAllHandler = None
@@ -97,23 +95,10 @@ def readObjects(self, obj: 'NVDAObjects.NVDAObject'):
9795
self._getActiveSayAll = weakref.ref(reader)
9896
reader.next()
9997

100-
def readText(
101-
self,
102-
cursor: CURSOR,
103-
startPos: Optional[textInfos.TextInfo] = None,
104-
nextLineFunc: Optional[Callable[[textInfos.TextInfo], textInfos.TextInfo]] = None,
105-
shouldUpdateCaret: bool = True,
106-
) -> None:
98+
def readText(self, cursor: CURSOR):
10799
self.lastSayAllMode = cursor
108100
try:
109-
if cursor == CURSOR.CARET:
110-
reader = _CaretTextReader(self)
111-
elif cursor == CURSOR.REVIEW:
112-
reader = _ReviewTextReader(self)
113-
elif cursor == CURSOR.TABLE:
114-
reader = _TableTextReader(self, startPos, nextLineFunc, shouldUpdateCaret)
115-
else:
116-
raise RuntimeError(f"Unknown cursor {cursor}")
101+
reader = _TextReader(self, cursor)
117102
except NotImplementedError:
118103
log.debugWarning("Unable to make reader", exc_info=True)
119104
return
@@ -160,7 +145,7 @@ def stop(self):
160145
self.walker = None
161146

162147

163-
class _TextReader(garbageHandler.TrackedObject, metaclass=ABCMeta):
148+
class _TextReader(garbageHandler.TrackedObject):
164149
"""Manages continuous reading of text.
165150
This is intended for internal use only.
166151
@@ -182,41 +167,35 @@ class _TextReader(garbageHandler.TrackedObject, metaclass=ABCMeta):
182167
"""
183168
MAX_BUFFERED_LINES = 10
184169

185-
def __init__(self, handler: _SayAllHandler):
170+
def __init__(self, handler: _SayAllHandler, cursor: CURSOR):
186171
self.handler = handler
172+
self.cursor = cursor
187173
self.trigger = SayAllProfileTrigger()
188-
self.reader = self.getInitialTextInfo()
174+
self.reader = None
175+
# Start at the cursor.
176+
if cursor == CURSOR.CARET:
177+
try:
178+
self.reader = api.getCaretObject().makeTextInfo(textInfos.POSITION_CARET)
179+
except (NotImplementedError, RuntimeError) as e:
180+
raise NotImplementedError("Unable to make TextInfo: " + str(e))
181+
else:
182+
self.reader = api.getReviewPosition()
189183
# #10899: SayAll profile can't be activated earlier because they may not be anything to read
190184
self.trigger.enter()
191185
self.speakTextInfoState = SayAllHandler._makeSpeakTextInfoState(self.reader.obj)
192186
self.numBufferedLines = 0
193-
self.initialIteration = True
194-
195-
@abstractmethod
196-
def getInitialTextInfo(self) -> textInfos.TextInfo:
197-
...
198-
199-
@abstractmethod
200-
def updateCaret(self, updater: textInfos.TextInfo) -> None:
201-
...
202-
203-
def shouldReadInitialPosition(self) -> bool:
204-
return False
205-
206-
def nextLineImpl(self) -> bool:
207-
"""
208-
Advances cursor to the next reading chunk (e.g. paragraph).
209-
@return: C{True} if advanced successfully, C{False} otherwise.
210-
"""
211-
# Collapse to the end of this line, ready to read the next.
212-
try:
213-
self.reader.collapse(end=True)
214-
except RuntimeError:
215-
# This occurs in Microsoft Word when the range covers the end of the document.
216-
# without this exception to indicate that further collapsing is not possible,
217-
# say all could enter an infinite loop.
218187

219-
return False
188+
def nextLine(self):
189+
if not self.reader:
190+
log.debug("no self.reader")
191+
# We were stopped.
192+
return
193+
if not self.reader.obj:
194+
log.debug("no self.reader.obj")
195+
# The object died, so we should too.
196+
self.finish()
197+
return
198+
bookmark = self.reader.bookmark
220199
# Expand to the current line.
221200
# We use move end rather than expand
222201
# because the user might start in the middle of a line
@@ -232,25 +211,8 @@ def nextLineImpl(self) -> bool:
232211
self.handler.speechWithoutPausesInstance.speakWithoutPauses([cb, EndUtteranceCommand()])
233212
else:
234213
self.finish()
235-
return False
236-
return True
237-
238-
def nextLine(self):
239-
if not self.reader:
240-
log.debug("no self.reader")
241-
# We were stopped.
242-
return
243-
if not self.reader.obj:
244-
log.debug("no self.reader.obj")
245-
# The object died, so we should too.
246-
self.finish()
247214
return
248-
if not self.initialIteration or not self.shouldReadInitialPosition():
249-
if not self.nextLineImpl():
250-
self.finish()
251-
return
252-
self.initialIteration = False
253-
bookmark = self.reader.bookmark
215+
254216
# Copy the speakTextInfoState so that speak callbackCommand
255217
# and its associated callback are using a copy isolated to this specific line.
256218
state = self.speakTextInfoState.copy()
@@ -282,6 +244,14 @@ def _onLineReached(obj=self.reader.obj, state=state):
282244
# Update the textInfo state ready for when speaking the next line.
283245
self.speakTextInfoState = state.copy()
284246

247+
# Collapse to the end of this line, ready to read the next.
248+
try:
249+
self.reader.collapse(end=True)
250+
except RuntimeError:
251+
# This occurs in Microsoft Word when the range covers the end of the document.
252+
# without this exception to indicate that further collapsing is not possible, say all could enter an infinite loop.
253+
self.finish()
254+
return
285255
if not spoke:
286256
# This line didn't include a natural pause, so nothing was spoken.
287257
self.numBufferedLines += 1
@@ -300,7 +270,10 @@ def lineReached(self, obj, bookmark, state):
300270
# We've just started speaking this line, so move the cursor there.
301271
state.updateObj()
302272
updater = obj.makeTextInfo(bookmark)
303-
self.updateCaret(updater)
273+
if self.cursor == CURSOR.CARET:
274+
updater.updateCaret()
275+
if self.cursor != CURSOR.CARET or config.conf["reviewCursor"]["followCaret"]:
276+
api.setReviewPosition(updater, isCaret=self.cursor == CURSOR.CARET)
304277
winKernel.SetThreadExecutionState(winKernel.ES_SYSTEM_REQUIRED)
305278
if self.numBufferedLines == 0:
306279
# This was the last line spoken, so move on.
@@ -342,59 +315,6 @@ def stop(self):
342315
def __del__(self):
343316
self.stop()
344317

345-
346-
class _CaretTextReader(_TextReader):
347-
def getInitialTextInfo(self) -> textInfos.TextInfo:
348-
try:
349-
return api.getCaretObject().makeTextInfo(textInfos.POSITION_CARET)
350-
except (NotImplementedError, RuntimeError) as e:
351-
raise NotImplementedError("Unable to make TextInfo: ", e)
352-
353-
def updateCaret(self, updater: textInfos.TextInfo) -> None:
354-
updater.updateCaret()
355-
if config.conf["reviewCursor"]["followCaret"]:
356-
api.setReviewPosition(updater, isCaret=True)
357-
358-
359-
class _ReviewTextReader(_TextReader):
360-
def getInitialTextInfo(self) -> textInfos.TextInfo:
361-
return api.getReviewPosition()
362-
363-
def updateCaret(self, updater: textInfos.TextInfo) -> None:
364-
api.setReviewPosition(updater, isCaret=False)
365-
366-
367-
class _TableTextReader(_CaretTextReader):
368-
def __init__(
369-
self,
370-
handler: _SayAllHandler,
371-
startPos: Optional[textInfos.TextInfo] = None,
372-
nextLineFunc: Optional[Callable[[textInfos.TextInfo], textInfos.TextInfo]] = None,
373-
shouldUpdateCaret: bool = True,
374-
):
375-
self.startPos = startPos
376-
self.nextLineFunc = nextLineFunc
377-
self.shouldUpdateCaret = shouldUpdateCaret
378-
super().__init__(handler)
379-
380-
def getInitialTextInfo(self) -> textInfos.TextInfo:
381-
return self.startPos or super().getInitialTextInfo()
382-
383-
def nextLineImpl(self) -> bool:
384-
try:
385-
self.reader = self.nextLineFunc(self.reader)
386-
return True
387-
except StopIteration:
388-
return False
389-
390-
def shouldReadInitialPosition(self) -> bool:
391-
return True
392-
393-
def updateCaret(self, updater: textInfos.TextInfo) -> None:
394-
if self.shouldUpdateCaret:
395-
return super().updateCaret(updater)
396-
397-
398318
class SayAllProfileTrigger(config.ProfileTrigger):
399319
"""A configuration profile trigger for when say all is in progress.
400320
"""

source/virtualBuffers/__init__.py

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
# -*- coding: UTF-8 -*-
2-
# A part of NonVisual Desktop Access (NVDA)
3-
# This file is covered by the GNU General Public License.
4-
# See the file COPYING for more details.
5-
# Copyright (C) 2007-2022 NV Access Limited, Peter Vágner
2+
#virtualBuffers/__init__.py
3+
#A part of NonVisual Desktop Access (NVDA)
4+
#This file is covered by the GNU General Public License.
5+
#See the file COPYING for more details.
6+
#Copyright (C) 2007-2017 NV Access Limited, Peter Vágner
67

78
import time
89
import threading
@@ -38,8 +39,6 @@
3839
import treeInterceptorHandler
3940
import watchdog
4041
from abc import abstractmethod
41-
import documentBase
42-
4342

4443
VBufStorage_findDirection_forward=0
4544
VBufStorage_findDirection_back=1
@@ -640,16 +639,7 @@ def _iterTableCells(self, tableID, startPos=None, direction="next", row=None, co
640639
for item in results:
641640
yield item.textInfo
642641

643-
def _getNearestTableCell(
644-
self,
645-
startPos: textInfos.TextInfo,
646-
cell: documentBase._TableCell,
647-
movement: documentBase._Movement,
648-
axis: documentBase._Axis,
649-
) -> textInfos.TextInfo:
650-
tableID, origRow, origCol, origRowSpan, origColSpan = (
651-
cell.tableID, cell.row, cell.col, cell.rowSpan, cell.colSpan
652-
)
642+
def _getNearestTableCell(self, tableID, startPos, origRow, origCol, origRowSpan, origColSpan, movement, axis):
653643
# Determine destination row and column.
654644
destRow = origRow
655645
destCol = origCol

source/virtualBuffers/gecko_ia2.py

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import aria
2424
import config
2525
from NVDAObjects.IAccessible import normalizeIA2TextFormatField, IA2TextTextInfo
26-
import documentBase
2726

2827

2928
def _getNormalizedCurrentAttrs(attrs: textInfos.ControlField) -> typing.Dict[str, typing.Any]:
@@ -547,15 +546,9 @@ def _getTableCellAt(self,tableID,startPos,destRow,destCol):
547546
except (COMError, RuntimeError):
548547
raise LookupError
549548

550-
def _getNearestTableCell(
551-
self,
552-
startPos: textInfos.TextInfo,
553-
cell: documentBase._TableCell,
554-
movement: documentBase._Movement,
555-
axis: documentBase._Axis,
556-
) -> textInfos.TextInfo:
549+
def _getNearestTableCell(self, tableID, startPos, origRow, origCol, origRowSpan, origColSpan, movement, axis):
557550
# Skip the VirtualBuffer implementation as the base BrowseMode implementation is good enough for us here.
558-
return super(VirtualBuffer, self)._getNearestTableCell(startPos, cell, movement, axis)
551+
return super(VirtualBuffer,self)._getNearestTableCell(tableID, startPos, origRow, origCol, origRowSpan, origColSpan, movement, axis)
559552

560553
def _get_documentConstantIdentifier(self):
561554
try:

0 commit comments

Comments
 (0)