Skip to content

Commit

Permalink
Speed ups to ms word object model (#9217)
Browse files Browse the repository at this point in the history
* winword support in nvdaHelper: speed things up a bit by optimizing for only one layout column, remove some debugging code, and turn off screenUpdating while collecting text formatting.

* Winword support in nvdaHelper: fetch editor revision information much more efficiently, similarly to how we fetch comments and spelling errors.

* MS Word object model support: rather than disabling screen updating specifically from within each in-process call, disable it from Python the first time it is required in a core cycle, re-enabling it at the end of the core cycle.

* speech.speakTextInfo: if the unit is paragraph, cell or story, don't fetch costly formatting info such as spelling errors and editor revisions, even if the user has these turned on.

* NVDAHelper MS Word support: add more debug warning log statements.

* Revert "speech.speakTextInfo: if the unit is paragraph, cell or story, don't fetch costly formatting info such as spelling errors and editor revisions, even if the user has these turned on."

This reverts commit 6ea9d0f.

* When navigating by table cell in MS Word, only speak the first line of the next cell, otherwise it may take a very long time to collect all content for the cell.

* When navigating by paragraph, spelling errors and editor revisions will not be announced. It is important that navigating by paragraph is performant as it is used for quick skimming.

* MS Word: Optimize collapse so that end is only fetched if needed.

* Navigating by table cell in MS Word again speaks the entire cell, but editor revisions and spelling errors (like when navigating by paragraph) are no longer spoken for navigating by table cell.

* Only disable spelling errors when speaking paragraphs and table cells. Editor revisions are okay.

* MS Word nvdaHelper support: remove unneeded fetching of application object.

* MS Word support: remove old comment.

* MS Word support: go back to disabling screen updating in-process for now, but do it from a RAAI class so that it can be re-enabled automatically at the end of scope.

* Address review comments.

* Update what's new.
  • Loading branch information
michaelDCurran committed Feb 11, 2019
1 parent 1a45aeb commit 960983e
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 47 deletions.
2 changes: 2 additions & 0 deletions nvdaHelper/remote/WinWord/Constants.h
Expand Up @@ -121,8 +121,10 @@ constexpr int wdDISPID_RANGE_STORYTYPE = 7;
constexpr int wdDISPID_RANGE_STYLE = 151; constexpr int wdDISPID_RANGE_STYLE = 151;
constexpr int wdDISPID_RANGE_TABLES = 50; constexpr int wdDISPID_RANGE_TABLES = 50;
constexpr int wdDISPID_RANGE_TEXT = 0; constexpr int wdDISPID_RANGE_TEXT = 0;
constexpr int wdDISPID_REVISION_RANGE = 3;
constexpr int wdDISPID_REVISION_TYPE = 4; constexpr int wdDISPID_REVISION_TYPE = 4;
constexpr int wdDISPID_REVISIONS_ITEM = 0; constexpr int wdDISPID_REVISIONS_ITEM = 0;
constexpr int wdDISPID_REVISIONS_COUNT = 5;
constexpr int wdDISPID_ROWS_COUNT = 2; constexpr int wdDISPID_ROWS_COUNT = 2;
constexpr int wdDISPID_SECTIONS_COUNT = 2; constexpr int wdDISPID_SECTIONS_COUNT = 2;
constexpr int wdDISPID_SECTIONS_ITEM = 0; constexpr int wdDISPID_SECTIONS_ITEM = 0;
Expand Down
152 changes: 115 additions & 37 deletions nvdaHelper/remote/winword.cpp
Expand Up @@ -58,6 +58,35 @@ constexpr int formatConfig_initialFormatFlags =(formatConfig_reportPage|formatCo
constexpr wchar_t PAGE_BREAK_VALUE = L'\x0c'; constexpr wchar_t PAGE_BREAK_VALUE = L'\x0c';
constexpr wchar_t COLUMN_BREAK_VALUE = L'\x0e'; constexpr wchar_t COLUMN_BREAK_VALUE = L'\x0e';


// A class that disables MS Word screen updating while it is alive.
class ScreenUpdatingDisabler {
private:
IDispatchPtr pDispatchApplication {nullptr};

public:
ScreenUpdatingDisabler(IDispatch* arg_pDispatchApplication) {
if(!arg_pDispatchApplication) {
LOG_ERROR(L"Null application given");
return;
}
HRESULT res=_com_dispatch_raw_propput(arg_pDispatchApplication,wdDISPID_APPLICATION_SCREENUPDATING,VT_BOOL,false);
if(res!=S_OK) {
LOG_ERROR(L"application.screenUpdating false failed with code "<<res);
return;
}
pDispatchApplication=arg_pDispatchApplication;
}

~ScreenUpdatingDisabler() {
if(!pDispatchApplication) return;
HRESULT res=_com_dispatch_raw_propput(pDispatchApplication,wdDISPID_APPLICATION_SCREENUPDATING,VT_BOOL,true);
if(res!=S_OK) {
LOG_ERROR(L"application.screenUpdating true failed with code "<<res);
}
}

};

UINT wm_winword_expandToLine=0; UINT wm_winword_expandToLine=0;
typedef struct { typedef struct {
int offset; int offset;
Expand All @@ -71,12 +100,14 @@ void winword_expandToLine_helper(HWND hwnd, winword_expandToLine_args* args) {
LOG_DEBUGWARNING(L"AccessibleObjectFromWindow failed"); LOG_DEBUGWARNING(L"AccessibleObjectFromWindow failed");
return; return;
} }
IDispatchPtr pDispatchApplication=NULL; IDispatchPtr pDispatchApplication=nullptr;
if(_com_dispatch_raw_propget(pDispatchWindow,wdDISPID_WINDOW_APPLICATION,VT_DISPATCH,&pDispatchApplication)!=S_OK||!pDispatchApplication) { if(_com_dispatch_raw_propget(pDispatchWindow,wdDISPID_WINDOW_APPLICATION,VT_DISPATCH,&pDispatchApplication)!=S_OK||!pDispatchApplication) {
LOG_DEBUGWARNING(L"window.application failed"); LOG_DEBUGWARNING(L"window.application failed");
return; return;
} }
IDispatchPtr pDispatchSelection=NULL; // Disable screen updating until the end of this scope
ScreenUpdatingDisabler sud{pDispatchApplication};
IDispatchPtr pDispatchSelection=nullptr;
if(_com_dispatch_raw_propget(pDispatchWindow,wdDISPID_WINDOW_SELECTION,VT_DISPATCH,&pDispatchSelection)!=S_OK||!pDispatchSelection) { if(_com_dispatch_raw_propget(pDispatchWindow,wdDISPID_WINDOW_SELECTION,VT_DISPATCH,&pDispatchSelection)!=S_OK||!pDispatchSelection) {
LOG_DEBUGWARNING(L"application.selection failed"); LOG_DEBUGWARNING(L"application.selection failed");
return; return;
Expand All @@ -90,8 +121,6 @@ void winword_expandToLine_helper(HWND hwnd, winword_expandToLine_args* args) {
LOG_DEBUGWARNING(L"selection.range failed"); LOG_DEBUGWARNING(L"selection.range failed");
return; return;
} }
//Disable screen updating as we will be moving the selection temporarily
_com_dispatch_raw_propput(pDispatchApplication,wdDISPID_APPLICATION_SCREENUPDATING,VT_BOOL,false);
//Move the selection to the given range //Move the selection to the given range
_com_dispatch_raw_method(pDispatchSelection,wdDISPID_SELECTION_SETRANGE,DISPATCH_METHOD,VT_EMPTY,NULL,L"\x0003\x0003",args->offset,args->offset); _com_dispatch_raw_method(pDispatchSelection,wdDISPID_SELECTION_SETRANGE,DISPATCH_METHOD,VT_EMPTY,NULL,L"\x0003\x0003",args->offset,args->offset);
//Expand the selection to the line //Expand the selection to the line
Expand Down Expand Up @@ -126,8 +155,6 @@ void winword_expandToLine_helper(HWND hwnd, winword_expandToLine_args* args) {
_com_dispatch_raw_method(pDispatchOldSelRange,wdDISPID_RANGE_SELECT,DISPATCH_METHOD,VT_EMPTY,NULL,NULL); _com_dispatch_raw_method(pDispatchOldSelRange,wdDISPID_RANGE_SELECT,DISPATCH_METHOD,VT_EMPTY,NULL,NULL);
//Restore the old selection direction //Restore the old selection direction
_com_dispatch_raw_propput(pDispatchSelection,wdDISPID_SELECTION_STARTISACTIVE,VT_BOOL,startWasActive); _com_dispatch_raw_propput(pDispatchSelection,wdDISPID_SELECTION_STARTISACTIVE,VT_BOOL,startWasActive);
//Reenable screen updating
_com_dispatch_raw_propput(pDispatchApplication,wdDISPID_APPLICATION_SCREENUPDATING,VT_BOOL,true);
} }


BOOL generateFormFieldXML(IDispatch* pDispatchRange, IDispatchPtr pDispatchRangeExpandedToParagraph, wostringstream& XMLStream, int& chunkEnd) { BOOL generateFormFieldXML(IDispatch* pDispatchRange, IDispatchPtr pDispatchRangeExpandedToParagraph, wostringstream& XMLStream, int& chunkEnd) {
Expand Down Expand Up @@ -288,25 +315,6 @@ int generateHeadingXML(IDispatch* pDispatchParagraph, IDispatch* pDispatchParagr
return 1; return 1;
} }


int getRevisionType(IDispatch* pDispatchOrigRange) {
IDispatchPtr pDispatchRange=NULL;
//If range is not duplicated here, revisions collection represents revisions at the start of the range when it was first created
if(_com_dispatch_raw_propget(pDispatchOrigRange,wdDISPID_RANGE_DUPLICATE,VT_DISPATCH,&pDispatchRange)!=S_OK||!pDispatchRange) {
return 0;
}
IDispatchPtr pDispatchRevisions=NULL;
if(_com_dispatch_raw_propget(pDispatchRange,wdDISPID_RANGE_REVISIONS,VT_DISPATCH,&pDispatchRevisions)!=S_OK||!pDispatchRevisions) {
return 0;
}
IDispatchPtr pDispatchRevision=NULL;
if(_com_dispatch_raw_method(pDispatchRevisions,wdDISPID_REVISIONS_ITEM,DISPATCH_METHOD,VT_DISPATCH,&pDispatchRevision,L"\x0003",1)!=S_OK||!pDispatchRevision) {
return 0;
}
long revisionType=0;
_com_dispatch_raw_propget(pDispatchRevision,wdDISPID_REVISION_TYPE,VT_I4,&revisionType);
return revisionType;
}

IDispatchPtr CreateExpandedDuplicate(IDispatch* pDispatchRange, const int expandTo) { IDispatchPtr CreateExpandedDuplicate(IDispatch* pDispatchRange, const int expandTo) {
IDispatchPtr pDispatchRangeDup = nullptr; IDispatchPtr pDispatchRangeDup = nullptr;
auto res = _com_dispatch_raw_propget( pDispatchRange, wdDISPID_RANGE_DUPLICATE, VT_DISPATCH, &pDispatchRangeDup); auto res = _com_dispatch_raw_propget( pDispatchRange, wdDISPID_RANGE_DUPLICATE, VT_DISPATCH, &pDispatchRangeDup);
Expand Down Expand Up @@ -351,6 +359,51 @@ bool collectCommentOffsets(IDispatchPtr pDispatchRange, vector<pair<long,long>>&
return !commentVector.empty(); return !commentVector.empty();
} }


bool collectRevisionOffsets(IDispatchPtr pDispatchRange, vector<tuple<long,long,long>>& revisionsVector) {
IDispatchPtr pDispatchRevisions=nullptr;
HRESULT res=_com_dispatch_raw_propget(pDispatchRange,wdDISPID_RANGE_REVISIONS,VT_DISPATCH,&pDispatchRevisions);
if(res!=S_OK||!pDispatchRevisions) {
LOG_DEBUGWARNING(L"range.revisions failed");
return false;
}
long iVal=0;
_com_dispatch_raw_propget(pDispatchRevisions,wdDISPID_REVISIONS_COUNT,VT_I4,&iVal);
for(int i=1;i<=iVal;++i) {
IDispatchPtr pDispatchRevision=nullptr;
res=_com_dispatch_raw_method(pDispatchRevisions,wdDISPID_REVISIONS_ITEM,DISPATCH_METHOD,VT_DISPATCH,&pDispatchRevision,L"\x0003",i);
if(res!=S_OK||!pDispatchRevision) {
LOG_DEBUGWARNING(L"revisions.item "<<i<<L" failed");
continue;
}
long revisionType=0;
res=_com_dispatch_raw_propget(pDispatchRevision,wdDISPID_REVISION_TYPE,VT_I4,&revisionType);
if(res!=S_OK) {
LOG_DEBUGWARNING(L"revision.type failed");
continue;
}
IDispatchPtr pDispatchRevisionScope=nullptr;
_com_dispatch_raw_propget(pDispatchRevision,wdDISPID_REVISION_RANGE,VT_DISPATCH,&pDispatchRevisionScope);
if(res!=S_OK||!pDispatchRevisionScope) {
LOG_DEBUGWARNING(L"revision.range failed");
continue;
}
long start=0;
res=_com_dispatch_raw_propget(pDispatchRevisionScope,wdDISPID_RANGE_START,VT_I4,&start);
if(res!=S_OK) {
LOG_DEBUGWARNING(L"range.start failed");
continue;
}
long end=0;
res=_com_dispatch_raw_propget(pDispatchRevisionScope,wdDISPID_RANGE_END,VT_I4,&end);
if(res!=S_OK) {
LOG_DEBUGWARNING(L"range.end failed");
continue;
}
revisionsVector.push_back({start,end,revisionType});
}
return !revisionsVector.empty();
}

bool fetchTableInfo(IDispatch* pDispatchTable, bool includeLayoutTables, int* rowCount, int* columnCount, int* nestingLevel) { bool fetchTableInfo(IDispatch* pDispatchTable, bool includeLayoutTables, int* rowCount, int* columnCount, int* nestingLevel) {
IDispatchPtr pDispatchRows=NULL; IDispatchPtr pDispatchRows=NULL;
IDispatchPtr pDispatchColumns=NULL; IDispatchPtr pDispatchColumns=NULL;
Expand Down Expand Up @@ -546,10 +599,6 @@ void generateXMLAttribsForFormatting(IDispatch* pDispatchRange, int startOffset,
} }
} }
} }
if(formatConfig&formatConfig_reportRevisions) {
long revisionType=getRevisionType(pDispatchRange);
formatAttribsStream<<L"wdRevisionType=\""<<revisionType<<L"\" ";
}
if(formatConfig&formatConfig_reportStyle) { if(formatConfig&formatConfig_reportStyle) {
IDispatchPtr pDispatchStyle=NULL; IDispatchPtr pDispatchStyle=NULL;
if(_com_dispatch_raw_propget(pDispatchRange,wdDISPID_RANGE_STYLE,VT_DISPATCH,&pDispatchStyle)==S_OK&&pDispatchStyle) { if(_com_dispatch_raw_propget(pDispatchRange,wdDISPID_RANGE_STYLE,VT_DISPATCH,&pDispatchStyle)==S_OK&&pDispatchStyle) {
Expand Down Expand Up @@ -877,6 +926,14 @@ void detectAndGenerateColumnFormatXML(IDispatchPtr pDispatchRange, wostringstrea
} }
xmlStream <<L"text-column-count=\"" << count << "\" "; xmlStream <<L"text-column-count=\"" << count << "\" ";


// Optimization: If there is only one column, then we can only be in that column.
if(count<=1) {
if(count==1) {
xmlStream <<L"text-column-number=\"" << 1 << "\" ";
}
return;
}

// 2. Get the start position (IN POINTS) of the range. This is relative to the start // 2. Get the start position (IN POINTS) of the range. This is relative to the start
// of the document. // of the document.
auto rangeStart = getStartOfRangeDistanceFromEdgeOfDocument(pDispatchRange); auto rangeStart = getStartOfRangeDistanceFromEdgeOfDocument(pDispatchRange);
Expand Down Expand Up @@ -945,7 +1002,8 @@ void detectAndGenerateColumnFormatXML(IDispatchPtr pDispatchRange, wostringstrea
} }
xmlStream <<L"text-column-number=\"" << columnNumber << "\" "; xmlStream <<L"text-column-number=\"" << columnNumber << "\" ";


// Finally, double check that we calculated the full width of the document /*
The following commented out code to the end of this function can be used to double check that we calculated the full width of the document
float pageWidth = -1.0f; float pageWidth = -1.0f;
res = _com_dispatch_raw_propget( pDispatchPageSetup, wdDISPID_PAGESETUP_PAGEWIDTH, res = _com_dispatch_raw_propget( pDispatchPageSetup, wdDISPID_PAGESETUP_PAGEWIDTH,
VT_R4, &pageWidth); VT_R4, &pageWidth);
Expand All @@ -966,6 +1024,7 @@ void detectAndGenerateColumnFormatXML(IDispatchPtr pDispatchRange, wostringstrea
<< " that some column numbers reported were incorrect." << " that some column numbers reported were incorrect."
<< " pageWidth: " << pageWidth << " colStartPos: " << colStartPos); << " pageWidth: " << pageWidth << " colStartPos: " << colStartPos);
} }
*/
} }


UINT wm_winword_getTextInRange=0; UINT wm_winword_getTextInRange=0;
Expand All @@ -983,8 +1042,15 @@ void winword_getTextInRange_helper(HWND hwnd, winword_getTextInRange_args* args)
LOG_DEBUGWARNING(L"AccessibleObjectFromWindow failed"); LOG_DEBUGWARNING(L"AccessibleObjectFromWindow failed");
return; return;
} }
//Get the current selection IDispatchPtr pDispatchApplication=nullptr;
IDispatchPtr pDispatchSelection=NULL; if(_com_dispatch_raw_propget(pDispatchWindow,wdDISPID_WINDOW_APPLICATION,VT_DISPATCH,&pDispatchApplication)!=S_OK||!pDispatchApplication) {
LOG_DEBUGWARNING(L"window.application failed");
return;
}
// Disable screen updating until the end of this scope
ScreenUpdatingDisabler sud{pDispatchApplication};
//Get the current selection
IDispatchPtr pDispatchSelection=nullptr;
if(_com_dispatch_raw_propget(pDispatchWindow,wdDISPID_WINDOW_SELECTION,VT_DISPATCH,&pDispatchSelection)!=S_OK||!pDispatchSelection) { if(_com_dispatch_raw_propget(pDispatchWindow,wdDISPID_WINDOW_SELECTION,VT_DISPATCH,&pDispatchSelection)!=S_OK||!pDispatchSelection) {
LOG_DEBUGWARNING(L"application.selection failed"); LOG_DEBUGWARNING(L"application.selection failed");
return; return;
Expand Down Expand Up @@ -1048,6 +1114,10 @@ void winword_getTextInRange_helper(HWND hwnd, winword_getTextInRange_args* args)
if(formatConfig&formatConfig_reportComments) { if(formatConfig&formatConfig_reportComments) {
collectCommentOffsets(pDispatchParagraphRange,commentVector); collectCommentOffsets(pDispatchParagraphRange,commentVector);
} }
vector<tuple<long,long,long>> revisionsVector;
if(formatConfig&formatConfig_reportRevisions) {
collectRevisionOffsets(pDispatchParagraphRange,revisionsVector);
}
if(initialFormatConfig&formatConfig_reportHeadings) { if(initialFormatConfig&formatConfig_reportHeadings) {
neededClosingControlTagCount+=generateHeadingXML(pDispatchParagraph,pDispatchParagraphRange,args->startOffset,args->endOffset,XMLStream); neededClosingControlTagCount+=generateHeadingXML(pDispatchParagraph,pDispatchParagraphRange,args->startOffset,args->endOffset,XMLStream);
} }
Expand Down Expand Up @@ -1190,6 +1260,15 @@ void winword_getTextInRange_helper(HWND hwnd, winword_getTextInRange_args* args)
break; break;
} }
} }
for(auto& i: revisionsVector) {
long revStart=get<0>(i);
long revEnd=get<1>(i);
long revType=get<2>(i);
if(!(chunkStartOffset>=revEnd||chunkEndOffset<=revStart)) {
XMLStream<<L"wdRevisionType=\""<<revType<<L"\" ";
break;
}
}
XMLStream<<L">"; XMLStream<<L">";
if(firstLoop) { if(firstLoop) {
formatConfig&=(~formatConfig_reportLists); formatConfig&=(~formatConfig_reportLists);
Expand Down Expand Up @@ -1231,12 +1310,14 @@ void winword_moveByLine_helper(HWND hwnd, winword_moveByLine_args* args) {
LOG_DEBUGWARNING(L"AccessibleObjectFromWindow failed"); LOG_DEBUGWARNING(L"AccessibleObjectFromWindow failed");
return; return;
} }
IDispatchPtr pDispatchApplication=NULL; IDispatchPtr pDispatchApplication=nullptr;
if(_com_dispatch_raw_propget(pDispatchWindow,wdDISPID_WINDOW_APPLICATION,VT_DISPATCH,&pDispatchApplication)!=S_OK||!pDispatchApplication) { if(_com_dispatch_raw_propget(pDispatchWindow,wdDISPID_WINDOW_APPLICATION,VT_DISPATCH,&pDispatchApplication)!=S_OK||!pDispatchApplication) {
LOG_DEBUGWARNING(L"window.application failed"); LOG_DEBUGWARNING(L"window.application failed");
return; return;
} }
IDispatchPtr pDispatchSelection=NULL; // Disable screen updating until the end of this scope
ScreenUpdatingDisabler sud{pDispatchApplication};
IDispatchPtr pDispatchSelection=nullptr;
if(_com_dispatch_raw_propget(pDispatchWindow,wdDISPID_WINDOW_SELECTION,VT_DISPATCH,&pDispatchSelection)!=S_OK||!pDispatchSelection) { if(_com_dispatch_raw_propget(pDispatchWindow,wdDISPID_WINDOW_SELECTION,VT_DISPATCH,&pDispatchSelection)!=S_OK||!pDispatchSelection) {
LOG_DEBUGWARNING(L"application.selection failed"); LOG_DEBUGWARNING(L"application.selection failed");
return; return;
Expand All @@ -1250,8 +1331,6 @@ void winword_moveByLine_helper(HWND hwnd, winword_moveByLine_args* args) {
LOG_DEBUGWARNING(L"selection.range failed"); LOG_DEBUGWARNING(L"selection.range failed");
return; return;
} }
//Disable screen updating as we will be moving the selection temporarily
_com_dispatch_raw_propput(pDispatchApplication,wdDISPID_APPLICATION_SCREENUPDATING,VT_BOOL,false);
//Move the selection to the given range //Move the selection to the given range
_com_dispatch_raw_method(pDispatchSelection,wdDISPID_SELECTION_SETRANGE,DISPATCH_METHOD,VT_EMPTY,NULL,L"\x0003\x0003",args->offset,args->offset); _com_dispatch_raw_method(pDispatchSelection,wdDISPID_SELECTION_SETRANGE,DISPATCH_METHOD,VT_EMPTY,NULL,L"\x0003\x0003",args->offset,args->offset);
// Move the selection by 1 line // Move the selection by 1 line
Expand All @@ -1263,7 +1342,6 @@ void winword_moveByLine_helper(HWND hwnd, winword_moveByLine_args* args) {
//Restore the old selection direction //Restore the old selection direction
_com_dispatch_raw_propput(pDispatchSelection,wdDISPID_SELECTION_STARTISACTIVE,VT_BOOL,startWasActive); _com_dispatch_raw_propput(pDispatchSelection,wdDISPID_SELECTION_STARTISACTIVE,VT_BOOL,startWasActive);
//Reenable screen updating //Reenable screen updating
_com_dispatch_raw_propput(pDispatchApplication,wdDISPID_APPLICATION_SCREENUPDATING,VT_BOOL,true);
} }


LRESULT CALLBACK winword_callWndProcHook(int code, WPARAM wParam, LPARAM lParam) { LRESULT CALLBACK winword_callWndProcHook(int code, WPARAM wParam, LPARAM lParam) {
Expand Down
2 changes: 1 addition & 1 deletion source/NVDAObjects/IAccessible/winword.py
Expand Up @@ -339,7 +339,7 @@ def _moveInTable(self,row=True,forward=True):
ui.message(_("Edge of table")) ui.message(_("Edge of table"))
return False return False
newInfo=WordDocumentTextInfo(self,textInfos.POSITION_CARET,_rangeObj=foundCell) newInfo=WordDocumentTextInfo(self,textInfos.POSITION_CARET,_rangeObj=foundCell)
speech.speakTextInfo(newInfo,reason=controlTypes.REASON_CARET, unit=textInfos.UNIT_PARAGRAPH) speech.speakTextInfo(newInfo,reason=controlTypes.REASON_CARET, unit=textInfos.UNIT_CELL)
newInfo.collapse() newInfo.collapse()
newInfo.updateCaret() newInfo.updateCaret()
return True return True
Expand Down
13 changes: 7 additions & 6 deletions source/NVDAObjects/window/winword.py
Expand Up @@ -927,12 +927,13 @@ def collapse(self,end=False):
if end: if end:
oldEndOffset=self._rangeObj.end oldEndOffset=self._rangeObj.end
self._rangeObj.collapse(wdCollapseEnd if end else wdCollapseStart) self._rangeObj.collapse(wdCollapseEnd if end else wdCollapseStart)
newEndOffset = self._rangeObj.end if end:
# the new endOffset should not have become smaller than the old endOffset, this could cause an infinite loop in newEndOffset = self._rangeObj.end
# a case where you called move end then collapse until the size of the range is no longer being reduced. # the new endOffset should not have become smaller than the old endOffset, this could cause an infinite loop in
# For an example of this see sayAll (specifically readTextHelper_generator in sayAllHandler.py) # a case where you called move end then collapse until the size of the range is no longer being reduced.
if end and newEndOffset < oldEndOffset : # For an example of this see sayAll (specifically readTextHelper_generator in sayAllHandler.py)
raise RuntimeError if newEndOffset < oldEndOffset :
raise RuntimeError


def copy(self): def copy(self):
return WordDocumentTextInfo(self.obj,None,_rangeObj=self._rangeObj) return WordDocumentTextInfo(self.obj,None,_rangeObj=self._rangeObj)
Expand Down
5 changes: 4 additions & 1 deletion source/speech.py
Expand Up @@ -766,10 +766,13 @@ def speakTextInfo(info,useCache=True,formatConfig=None,unit=None,reason=controlT
extraDetail=unit in (textInfos.UNIT_CHARACTER,textInfos.UNIT_WORD) extraDetail=unit in (textInfos.UNIT_CHARACTER,textInfos.UNIT_WORD)
if not formatConfig: if not formatConfig:
formatConfig=config.conf["documentFormatting"] formatConfig=config.conf["documentFormatting"]
formatConfig=formatConfig.copy()
if extraDetail: if extraDetail:
formatConfig=formatConfig.copy()
formatConfig['extraDetail']=True formatConfig['extraDetail']=True
reportIndentation=unit==textInfos.UNIT_LINE and ( formatConfig["reportLineIndentation"] or formatConfig["reportLineIndentationWithTones"]) reportIndentation=unit==textInfos.UNIT_LINE and ( formatConfig["reportLineIndentation"] or formatConfig["reportLineIndentationWithTones"])
# For performance reasons, when navigating by paragraph or table cell, spelling errors will not be announced.
if unit in (textInfos.UNIT_PARAGRAPH,textInfos.UNIT_CELL) and reason is controlTypes.REASON_CARET:
formatConfig['reportSpellingErrors']=False


speechSequence=[] speechSequence=[]
#Fetch the last controlFieldStack, or make a blank one #Fetch the last controlFieldStack, or make a blank one
Expand Down
6 changes: 4 additions & 2 deletions user_docs/en/changes.t2t
Expand Up @@ -30,8 +30,9 @@ What's New in NVDA
- Reporting of text under the mouse has been improved within Microsoft Edge and other UIA applications. (#8370) - Reporting of text under the mouse has been improved within Microsoft Edge and other UIA applications. (#8370)
- When NVDA is started with the `--portable-path` command line parameter, the provided path is automatically filled in when trying to create a portable copy of NVDA using the NVDA menu. (#8623) - When NVDA is started with the `--portable-path` command line parameter, the provided path is automatically filled in when trying to create a portable copy of NVDA using the NVDA menu. (#8623)
- Updated the path to the Norwegian braille table to reflect the standard from the year 2015. (#9170) - Updated the path to the Norwegian braille table to reflect the standard from the year 2015. (#9170)

- When navigating by paragraph (control+arrows) or navigating by table cell (control+alt+arrows), the existence of spelling errors will no longer be announced, even if NVDA is configured to announce these automatically. This is because paragraphs and table cells can be quite large, and calculating spelling errors in some applications can be very costly. (#9217)



== Bug Fixes == == Bug Fixes ==
- OneCore speech synthesizer: On Windows 10 April 2018 and above, large chunks of silence are no longer inserted between speech utterances. (#8985) - OneCore speech synthesizer: On Windows 10 April 2018 and above, large chunks of silence are no longer inserted between speech utterances. (#8985)
- When moving by character in plain text controls (such as Notepad) or browse mode, 32 bit emoji characters consisting of two UTF-16 code points (such as 🤦) will now read properly. (#8782) - When moving by character in plain text controls (such as Notepad) or browse mode, 32 bit emoji characters consisting of two UTF-16 code points (such as 🤦) will now read properly. (#8782)
Expand All @@ -54,6 +55,7 @@ What's New in NVDA
- In Windows 10 October 2018 Update and later, when opening cloud clipboard history with clipboard empty, NVDA will announce clipboard status. (#9112) - In Windows 10 October 2018 Update and later, when opening cloud clipboard history with clipboard empty, NVDA will announce clipboard status. (#9112)
- In Windows 10 October 2018 Update and later, when searching for emojis in emoji panel, NVDA will announce top search result. (#9112) - In Windows 10 October 2018 Update and later, when searching for emojis in emoji panel, NVDA will announce top search result. (#9112)
- NVDA no longer freezes in the mainwindow of Virtualbox 5.2 and above. (#9202) - NVDA no longer freezes in the mainwindow of Virtualbox 5.2 and above. (#9202)
- Responsiveness in Microsoft Word when navigating by line, paragraph or table cell may be significantly improved in some documents. A reminder that for best performance, set Microsoft Word to Draft view with alt+w,e after opening a document. (#9217)




== Changes for Developers == == Changes for Developers ==
Expand Down

0 comments on commit 960983e

Please sign in to comment.