diff --git a/scintilla/src/Editor.cxx b/scintilla/src/Editor.cxx index aff868295a..12f74b97b4 100644 --- a/scintilla/src/Editor.cxx +++ b/scintilla/src/Editor.cxx @@ -99,6 +99,17 @@ constexpr bool IsLastStep(const DocModification &mh) noexcept { && ((mh.modificationType & finalMask) == finalMask); } +class BatchUpdateGroup { + Editor *editor; +public: + explicit BatchUpdateGroup(Editor *edit) noexcept : editor{edit} { + editor->BeginBatchUpdate(); + } + ~BatchUpdateGroup() { + editor->EndBatchUpdate(); + } +}; + } Timer::Timer() noexcept : @@ -195,6 +206,7 @@ Editor::Editor() { modEventMask = ModificationFlags::EventMaskAll; foldAutomatic = AutomaticFold::None; + batchUpdateDepth = 0; pdoc->AddWatcher(this, nullptr); SetRepresentations(); @@ -6618,11 +6630,21 @@ sptr_t Editor::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) { return pdoc->IsCollectingUndo(); case Message::BeginUndoAction: - pdoc->BeginUndoAction(); + if (wParam == 0) { + pdoc->BeginUndoAction(); + } + if (lParam != 0) { + BeginBatchUpdate(); + } return 0; case Message::EndUndoAction: - pdoc->EndUndoAction(); + if (wParam == 0) { + pdoc->EndUndoAction(); + } + if (lParam != 0) { + EndBatchUpdate(); + } return 0; case Message::GetUndoSequence: @@ -8245,10 +8267,12 @@ sptr_t Editor::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) { case Message::GetCommandEvents: return commandEvents; - case Message::ConvertEOLs: + case Message::ConvertEOLs: { + const BatchUpdateGroup group(this); pdoc->ConvertLineEnds(static_cast(wParam)); SetSelection(sel.MainCaret(), sel.MainAnchor()); // Ensure selection inside document return 0; + } case Message::SetLengthForEncode: lengthForEncode = PositionFromUPtr(wParam); diff --git a/scintilla/src/Editor.h b/scintilla/src/Editor.h index 55009d6e62..cf5d8a0788 100644 --- a/scintilla/src/Editor.h +++ b/scintilla/src/Editor.h @@ -293,6 +293,14 @@ class Editor : public EditModel, public DocWatcher { Scintilla::AutomaticFold foldAutomatic; + int batchUpdateDepth; + struct BatchUpdateSavedState { + ModificationFlags modEventMask; + int actions; + Sci::Line lines; + }; + BatchUpdateSavedState batchUpdateState; + // Wrapping support WrapPending wrapPending; @@ -702,6 +710,8 @@ class Editor : public EditModel, public DocWatcher { bool IsUnicodeMode() const noexcept; // Public so scintilla_send_message can use it. virtual Scintilla::sptr_t WndProc(Scintilla::Message iMessage, Scintilla::uptr_t wParam, Scintilla::sptr_t lParam); + void BeginBatchUpdate() noexcept; + void EndBatchUpdate() noexcept; // Public so scintilla_set_id can use it. int ctrlID; // Public so COM methods for drag and drop can set it. diff --git a/scintilla/win32/ScintillaWin.cxx b/scintilla/win32/ScintillaWin.cxx index 0c35fed2a7..b68a925888 100644 --- a/scintilla/win32/ScintillaWin.cxx +++ b/scintilla/win32/ScintillaWin.cxx @@ -2780,6 +2780,32 @@ void ScintillaWin::NotifyDoubleClick(Point pt, KeyMod modifiers) { MAKELPARAM(point.x, point.y)); } +void Editor::BeginBatchUpdate() noexcept { + ++batchUpdateDepth; + if (batchUpdateDepth == 1) { + batchUpdateState.modEventMask = modEventMask; + modEventMask = ModificationFlags::None; + batchUpdateState.actions = pdoc->UndoActions(); + batchUpdateState.lines = pdoc->LinesTotal(); + ::SendMessage(HwndFromWindow(wMain), WM_SETREDRAW, FALSE, 0); + } +} + +void Editor::EndBatchUpdate() noexcept { + --batchUpdateDepth; + if (batchUpdateDepth == 0) { + modEventMask = batchUpdateState.modEventMask; + ::SendMessage(HwndFromWindow(wMain), WM_SETREDRAW, TRUE, 0); + if (batchUpdateState.actions != pdoc->UndoActions()) { + NotificationData scn = {}; + scn.nmhdr.code = Notification::Modified; + scn.linesAdded = pdoc->LinesTotal() - batchUpdateState.lines; + NotifyParent(scn); + ::InvalidateRect(HwndFromWindow(wMain), nullptr, TRUE); + } + } +} + namespace { class CaseFolderDBCS final : public CaseFolderTable { diff --git a/src/Edit.cpp b/src/Edit.cpp index 4ce69d3d00..44c29f6322 100644 --- a/src/Edit.cpp +++ b/src/Edit.cpp @@ -1816,7 +1816,7 @@ void EditEscapeCChars(HWND hwnd) noexcept { EDITFINDREPLACE * const efr = static_cast(NP2HeapAlloc(sizeof(EDITFINDREPLACE))); efr->hwnd = hwnd; - SciCall_BeginUndoAction(); + SciCall_BeginBatchUpdate(); strcpy(efr->szFind, "\\"); strcpy(efr->szReplace, "\\\\"); @@ -1831,7 +1831,7 @@ void EditEscapeCChars(HWND hwnd) noexcept { EditReplaceAllInSelection(hwnd, efr); NP2HeapFree(efr); - SciCall_EndUndoAction(); + SciCall_EndBatchUpdate(); } //============================================================================= @@ -1849,7 +1849,7 @@ void EditUnescapeCChars(HWND hwnd) noexcept { EDITFINDREPLACE * const efr = static_cast(NP2HeapAlloc(sizeof(EDITFINDREPLACE))); efr->hwnd = hwnd; - SciCall_BeginUndoAction(); + SciCall_BeginBatchUpdate(); strcpy(efr->szFind, "\\\\"); strcpy(efr->szReplace, "\\"); @@ -1864,7 +1864,7 @@ void EditUnescapeCChars(HWND hwnd) noexcept { EditReplaceAllInSelection(hwnd, efr); NP2HeapFree(efr); - SciCall_EndUndoAction(); + SciCall_EndBatchUpdate(); } // XML/HTML predefined entity @@ -1891,7 +1891,7 @@ void EditEscapeXHTMLChars(HWND hwnd) noexcept { EDITFINDREPLACE * const efr = static_cast(NP2HeapAlloc(sizeof(EDITFINDREPLACE))); efr->hwnd = hwnd; - SciCall_BeginUndoAction(); + SciCall_BeginBatchUpdate(); strcpy(efr->szFind, "&"); strcpy(efr->szReplace, "&"); @@ -1924,7 +1924,7 @@ void EditEscapeXHTMLChars(HWND hwnd) noexcept { } NP2HeapFree(efr); - SciCall_EndUndoAction(); + SciCall_EndBatchUpdate(); } //============================================================================= @@ -1942,7 +1942,7 @@ void EditUnescapeXHTMLChars(HWND hwnd) noexcept { EDITFINDREPLACE * const efr = static_cast(NP2HeapAlloc(sizeof(EDITFINDREPLACE))); efr->hwnd = hwnd; - SciCall_BeginUndoAction(); + SciCall_BeginBatchUpdate(); strcpy(efr->szFind, """); strcpy(efr->szReplace, "\""); @@ -1973,7 +1973,7 @@ void EditUnescapeXHTMLChars(HWND hwnd) noexcept { EditReplaceAllInSelection(hwnd, efr); NP2HeapFree(efr); - SciCall_EndUndoAction(); + SciCall_EndBatchUpdate(); } //============================================================================= @@ -5786,7 +5786,6 @@ void EditReplaceAll(HWND hwnd, const EDITFINDREPLACE *lpefr) noexcept { // Show wait cursor... BeginWaitCursor(); - SendMessage(hwnd, WM_SETREDRAW, FALSE, 0); #if 0 StopWatch watch; watch.Start(); @@ -5798,7 +5797,7 @@ void EditReplaceAll(HWND hwnd, const EDITFINDREPLACE *lpefr) noexcept { while (SciCall_FindTextFull(searchFlags, &ttf) >= 0) { ++iCount; if (iCount == 1) { - SciCall_BeginUndoAction(); + SciCall_BeginBatchUpdate(); } SciCall_SetTargetRange(ttf.chrgText.cpMin, ttf.chrgText.cpMax); @@ -5834,11 +5833,9 @@ void EditReplaceAll(HWND hwnd, const EDITFINDREPLACE *lpefr) noexcept { watch.Stop(); watch.ShowLog("EditReplaceAll() time"); #endif - SendMessage(hwnd, WM_SETREDRAW, TRUE, 0); if (iCount) { + SciCall_EndBatchUpdate(); EditEnsureSelectionVisible(); - SciCall_EndUndoAction(); - InvalidateRect(hwnd, nullptr, TRUE); } // Remove wait cursor @@ -5867,7 +5864,6 @@ void EditReplaceAllInSelection(HWND hwnd, const EDITFINDREPLACE *lpefr, EditRepl // Show wait cursor... BeginWaitCursor(); - SendMessage(hwnd, WM_SETREDRAW, FALSE, 0); const bool bRegexStartOfLine = bReplaceRE && (szFind2[0] == '^'); Sci_TextToFindFull ttf = { { SciCall_GetSelectionStart(), SciCall_GetLength() }, szFind2, { 0, 0 } }; @@ -5876,7 +5872,7 @@ void EditReplaceAllInSelection(HWND hwnd, const EDITFINDREPLACE *lpefr, EditRepl if (ttf.chrgText.cpMax <= SciCall_GetSelectionEnd()) { ++iCount; if (iCount == 1 && (flag & EditReplaceAllFlag_UndoGroup) != 0) { - SciCall_BeginUndoAction(); + SciCall_BeginBatchUpdate(); } SciCall_SetTargetRange(ttf.chrgText.cpMin, ttf.chrgText.cpMax); @@ -5911,10 +5907,9 @@ void EditReplaceAllInSelection(HWND hwnd, const EDITFINDREPLACE *lpefr, EditRepl } } - SendMessage(hwnd, WM_SETREDRAW, TRUE, 0); if (iCount) { if ((flag & EditReplaceAllFlag_UndoGroup) != 0) { - SciCall_EndUndoAction(); + SciCall_EndBatchUpdate(); } const Sci_Position iPos = SciCall_GetTargetEnd(); if (SciCall_GetSelectionEnd() < iPos) { @@ -5929,8 +5924,6 @@ void EditReplaceAllInSelection(HWND hwnd, const EDITFINDREPLACE *lpefr, EditRepl EditSelectEx(iAnchorPos, iCurrentPos); } - - InvalidateRect(hwnd, nullptr, TRUE); } // Remove wait cursor diff --git a/src/SciCall.h b/src/SciCall.h index 9e0ae22af8..1a655af2ad 100644 --- a/src/SciCall.h +++ b/src/SciCall.h @@ -260,6 +260,14 @@ inline void SciCall_EndUndoAction() noexcept { SciCall(SCI_ENDUNDOACTION, 0, 0); } +inline void SciCall_BeginBatchUpdate(bool bNoUndoGroup = false) noexcept { + SciCall(SCI_BEGINUNDOACTION, bNoUndoGroup, true); +} + +inline void SciCall_EndBatchUpdate(bool bNoUndoGroup = false) noexcept { + SciCall(SCI_ENDUNDOACTION, bNoUndoGroup, true); +} + inline size_t SciCall_GetUndoActions() noexcept { return SciCall(SCI_GETUNDOACTIONS, 0, 0); }