aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorNeil <nyamatongwe@gmail.com>2025-01-22 21:34:54 +1100
committerNeil <nyamatongwe@gmail.com>2025-01-22 21:34:54 +1100
commit3de9d37c7b8f4501558d309ada718dc52533e94c (patch)
treece87afaff43a86c26c562fefcf0c9a5ea409ef6b /src
parentbac32d7fde0b1d052ac9e926c6b3c96afe39bcfd (diff)
downloadscintilla-mirror-3de9d37c7b8f4501558d309ada718dc52533e94c.tar.gz
Bug [#1224]. Remember selection in undo history. SCI_SETSELECTIONUNDOHISTORY.
Diffstat (limited to 'src')
-rw-r--r--src/CellBuffer.cxx4
-rw-r--r--src/CellBuffer.h1
-rw-r--r--src/Document.cxx48
-rw-r--r--src/Document.h27
-rw-r--r--src/EditModel.cxx59
-rw-r--r--src/EditModel.h41
-rw-r--r--src/Editor.cxx98
-rw-r--r--src/Editor.h7
-rw-r--r--src/Selection.cxx7
-rw-r--r--src/Selection.h2
-rw-r--r--src/UndoHistory.cxx8
-rw-r--r--src/UndoHistory.h1
12 files changed, 295 insertions, 8 deletions
diff --git a/src/CellBuffer.cxx b/src/CellBuffer.cxx
index 643929352..04486d4c6 100644
--- a/src/CellBuffer.cxx
+++ b/src/CellBuffer.cxx
@@ -1075,6 +1075,10 @@ int CellBuffer::UndoSequenceDepth() const noexcept {
return uh->UndoSequenceDepth();
}
+bool CellBuffer::AfterUndoSequenceStart() const noexcept {
+ return uh->AfterUndoSequenceStart();
+}
+
void CellBuffer::AddUndoAction(Sci::Position token, bool mayCoalesce) {
bool startSequence = false;
uh->AppendAction(ActionType::container, token, nullptr, 0, startSequence, mayCoalesce);
diff --git a/src/CellBuffer.h b/src/CellBuffer.h
index d5d9a0467..1b035597a 100644
--- a/src/CellBuffer.h
+++ b/src/CellBuffer.h
@@ -167,6 +167,7 @@ public:
void BeginUndoAction(bool mayCoalesce=false) noexcept;
void EndUndoAction() noexcept;
int UndoSequenceDepth() const noexcept;
+ bool AfterUndoSequenceStart() const noexcept;
void AddUndoAction(Sci::Position token, bool mayCoalesce);
void DeleteUndoHistory() noexcept;
diff --git a/src/Document.cxx b/src/Document.cxx
index ac398fcd3..379a88786 100644
--- a/src/Document.cxx
+++ b/src/Document.cxx
@@ -18,6 +18,7 @@
#include <string_view>
#include <vector>
#include <array>
+#include <map>
#include <forward_list>
#include <optional>
#include <algorithm>
@@ -1320,6 +1321,10 @@ bool Document::DeleteChars(Sci::Position pos, Sci::Position len) {
} else {
enteredModification++;
if (!cb.IsReadOnly()) {
+ if (cb.IsCollectingUndo() && cb.CanRedo()) {
+ // Abandoning some undo actions so truncate any later selections
+ TruncateUndoComments(cb.UndoCurrent());
+ }
NotifyModified(
DocModification(
ModificationFlags::BeforeDelete | ModificationFlags::User,
@@ -1373,6 +1378,10 @@ Sci::Position Document::InsertString(Sci::Position position, const char *s, Sci:
s = insertion.c_str();
insertLength = insertion.length();
}
+ if (cb.IsCollectingUndo() && cb.CanRedo()) {
+ // Abandoning some undo actions so truncate any later selections
+ TruncateUndoComments(cb.UndoCurrent());
+ }
NotifyModified(
DocModification(
ModificationFlags::BeforeInsert | ModificationFlags::User,
@@ -1556,6 +1565,20 @@ Sci::Position Document::Redo() {
return newPos;
}
+void Document::EndUndoAction() noexcept {
+ cb.EndUndoAction();
+ if (UndoSequenceDepth() == 0) {
+ // Broadcast notification to views to allow end of group processing.
+ // NotifyGroupCompleted may throw (for memory exhaustion) but this method
+ // may not as it is called in UndoGroup destructor so ignore exception.
+ try {
+ NotifyGroupCompleted();
+ } catch (...) {
+ // Ignore any exception
+ }
+ }
+}
+
int Document::UndoSequenceDepth() const noexcept {
return cb.UndoSequenceDepth();
}
@@ -2504,6 +2527,25 @@ void Document::SetLexInterface(std::unique_ptr<LexInterface> pLexInterface) noex
pli = std::move(pLexInterface);
}
+void Document::SetViewState(void *view, ViewStateShared pVSS) {
+ viewData[view] = pVSS;
+}
+
+ViewStateShared Document::GetViewState(void *view) const noexcept {
+ const std::map<void *, ViewStateShared>::const_iterator it = viewData.find(view);
+
+ if (it != viewData.end()) {
+ return it->second;
+ }
+ return {};
+}
+
+void Document::TruncateUndoComments(int action) {
+ for (auto &[key, value] : viewData) {
+ value->TruncateUndo(action);
+ }
+}
+
int SCI_METHOD Document::SetLineState(Sci_Position line, int state) {
const int statePrevious = States()->SetLineState(line, state, LinesTotal());
if (state != statePrevious) {
@@ -2700,6 +2742,12 @@ void Document::NotifySavePoint(bool atSavePoint) {
}
}
+void Document::NotifyGroupCompleted() noexcept {
+ for (const WatcherWithUserData &watcher : watchers) {
+ watcher.watcher->NotifyGroupCompleted(this, watcher.userData);
+ }
+}
+
void Document::NotifyModified(DocModification mh) {
if (FlagSet(mh.modificationType, ModificationFlags::InsertText)) {
decorations->InsertSpace(mh.position, mh.length);
diff --git a/src/Document.h b/src/Document.h
index cecf02899..4e18c42d1 100644
--- a/src/Document.h
+++ b/src/Document.h
@@ -172,6 +172,22 @@ public:
bool isEnabled;
};
+// Base class for view state that can be held and transferred without understanding the contents.
+// Declared here but real implementation subclass declared in EditModel
+struct ViewState {
+ ViewState() noexcept = default;
+ // Deleted so ViewState objects can not be copied
+ ViewState(const ViewState &) = delete;
+ ViewState(ViewState &&) = delete;
+ ViewState &operator=(const ViewState &) = delete;
+ ViewState &operator=(ViewState &&) = delete;
+ virtual ~ViewState() noexcept = default;
+
+ virtual void TruncateUndo(int index)=0;
+};
+
+using ViewStateShared = std::shared_ptr<ViewState>;
+
struct LexerReleaser {
// Called by unique_ptr to destroy/free the Resource
void operator()(Scintilla::ILexer5 *pLexer) noexcept {
@@ -304,6 +320,8 @@ private:
std::unique_ptr<RegexSearchBase> regex;
std::unique_ptr<LexInterface> pli;
+ std::map<void *, ViewStateShared>viewData;
+
public:
Scintilla::EndOfLine eolMode;
@@ -396,8 +414,9 @@ public:
}
bool IsCollectingUndo() const noexcept { return cb.IsCollectingUndo(); }
void BeginUndoAction(bool coalesceWithPrior=false) noexcept { cb.BeginUndoAction(coalesceWithPrior); }
- void EndUndoAction() noexcept { cb.EndUndoAction(); }
+ void EndUndoAction() noexcept;
int UndoSequenceDepth() const noexcept;
+ bool AfterUndoSequenceStart() const noexcept { return cb.AfterUndoSequenceStart(); }
void AddUndoAction(Sci::Position token, bool mayCoalesce) { cb.AddUndoAction(token, mayCoalesce); }
void SetSavePoint();
bool IsSavePoint() const noexcept { return cb.IsSavePoint(); }
@@ -534,6 +553,10 @@ public:
LexInterface *GetLexInterface() const noexcept;
void SetLexInterface(std::unique_ptr<LexInterface> pLexInterface) noexcept;
+ void SetViewState(void *view, ViewStateShared pVSS);
+ ViewStateShared GetViewState(void *view) const noexcept;
+ void TruncateUndoComments(int action);
+
int SCI_METHOD SetLineState(Sci_Position line, int state) override;
int SCI_METHOD GetLineState(Sci_Position line) const override;
Sci::Line GetMaxLineState() const noexcept;
@@ -574,6 +597,7 @@ public:
private:
void NotifyModifyAttempt();
void NotifySavePoint(bool atSavePoint);
+ void NotifyGroupCompleted() noexcept;
void NotifyModified(DocModification mh);
};
@@ -664,6 +688,7 @@ public:
virtual void NotifyDeleted(Document *doc, void *userData) noexcept = 0;
virtual void NotifyStyleNeeded(Document *doc, void *userData, Sci::Position endPos) = 0;
virtual void NotifyErrorOccurred(Document *doc, void *userData, Scintilla::Status status) = 0;
+ virtual void NotifyGroupCompleted(Document *doc, void *userData) noexcept = 0;
};
}
diff --git a/src/EditModel.cxx b/src/EditModel.cxx
index 13b3da5b0..60848e6dc 100644
--- a/src/EditModel.cxx
+++ b/src/EditModel.cxx
@@ -58,6 +58,54 @@ using namespace Scintilla::Internal;
Caret::Caret() noexcept :
active(false), on(false), period(500) {}
+SelectionSimple::SelectionSimple(const Selection &sel) {
+ selType = sel.selType;
+ if (sel.IsRectangular()) {
+ // rectangular or thin
+ // Could be large so don't remember each range, just the rectangular bounds then reconstitute when undone
+ rangeRectangular = sel.RectangularCopy();
+ } else {
+ ranges = sel.RangesCopy();
+ }
+ mainRange = sel.Main();
+}
+
+void ModelState::RememberSelectionForUndo(int index, const Selection &sel) {
+ historyForUndo.indexCurrent = index;
+ historyForUndo.ssCurrent = SelectionSimple(sel);
+}
+
+void ModelState::ForgetSelectionForUndo() noexcept {
+ historyForUndo.indexCurrent = -1;
+}
+
+void ModelState::RememberSelectionOntoStack(int index) {
+ if ((historyForUndo.indexCurrent >= 0) && (index == historyForUndo.indexCurrent + 1)) {
+ // Don't overwrite initial selection save if most recent action was coalesced
+ historyForUndo.stack[index] = historyForUndo.ssCurrent;
+ }
+}
+
+void ModelState::RememberSelectionForRedoOntoStack(int index, const Selection &sel) {
+ historyForRedo.stack[index] = SelectionSimple(sel);
+}
+
+const SelectionSimple *ModelState::SelectionFromStack(int index, UndoRedo history) const {
+ const SelectionHistory &sh = history == UndoRedo::undo ? historyForUndo : historyForRedo;
+ std::map<int, SelectionSimple>::const_iterator it = sh.stack.find(index);
+ if (it != sh.stack.end()) {
+ return &it->second;
+ }
+ return {};
+}
+
+void ModelState::TruncateUndo(int index) {
+ std::map<int, SelectionSimple>::iterator itUndo = historyForUndo.stack.find(index);
+ historyForUndo.stack.erase(itUndo, historyForUndo.stack.end());
+ std::map<int, SelectionSimple>::iterator itRedo = historyForRedo.stack.find(index);
+ historyForRedo.stack.erase(itRedo, historyForRedo.stack.end());
+}
+
EditModel::EditModel() : braces{} {
inOverstrike = false;
xOffset = 0;
@@ -131,3 +179,14 @@ InSelection EditModel::LineEndInSelection(Sci::Line lineDoc) const {
int EditModel::GetMark(Sci::Line line) const {
return pdoc->GetMark(line, FlagSet(changeHistoryOption, ChangeHistoryOption::Markers));
}
+
+void EditModel::EnsureModelState() {
+ if (!modelState && rememberingSelectionForUndo) {
+ if (ViewStateShared vss = pdoc->GetViewState(this)) {
+ modelState = std::dynamic_pointer_cast<ModelState>(vss);
+ } else {
+ modelState = std::make_shared<ModelState>();
+ pdoc->SetViewState(this, std::static_pointer_cast<ViewState>(modelState));
+ }
+ }
+}
diff --git a/src/EditModel.h b/src/EditModel.h
index e36d26fb1..716fd5788 100644
--- a/src/EditModel.h
+++ b/src/EditModel.h
@@ -21,6 +21,41 @@ public:
Caret() noexcept;
};
+// Simplified version of selection which won't contain rectangular selection realized
+// into ranges as too much data.
+// Just a type and single range for now.
+
+struct SelectionSimple {
+ std::vector<SelectionRange> ranges;
+ SelectionRange rangeRectangular;
+ size_t mainRange = 0;
+ Selection::SelTypes selType = Selection::SelTypes::stream;
+
+ SelectionSimple() = default;
+ explicit SelectionSimple(const Selection &sel);
+};
+
+enum class UndoRedo { undo, redo };
+
+struct SelectionHistory {
+ int indexCurrent = 0;
+ SelectionSimple ssCurrent;
+ std::map<int, SelectionSimple> stack;
+};
+
+struct ModelState : ViewState {
+ SelectionHistory historyForUndo;
+ SelectionHistory historyForRedo;
+ void RememberSelectionForUndo(int index, const Selection &sel);
+ void ForgetSelectionForUndo() noexcept;
+ void RememberSelectionOntoStack(int index);
+ void RememberSelectionForRedoOntoStack(int index, const Selection &sel);
+ const SelectionSimple *SelectionFromStack(int index, UndoRedo history) const;
+ virtual void TruncateUndo(int index) final;
+};
+
+using ModelStateShared = std::shared_ptr<ModelState>;
+
class EditModel {
public:
bool inOverstrike;
@@ -57,6 +92,10 @@ public:
Document *pdoc;
+ bool rememberingSelectionForUndo = false;
+ bool needRedoRemembered = false;
+ ModelStateShared modelState;
+
EditModel();
// Deleted so EditModel objects can not be copied.
EditModel(const EditModel &) = delete;
@@ -75,6 +114,8 @@ public:
const char *GetFoldDisplayText(Sci::Line lineDoc) const noexcept;
InSelection LineEndInSelection(Sci::Line lineDoc) const;
[[nodiscard]] int GetMark(Sci::Line line) const;
+
+ void EnsureModelState();
};
}
diff --git a/src/Editor.cxx b/src/Editor.cxx
index b60d96643..8a7b15719 100644
--- a/src/Editor.cxx
+++ b/src/Editor.cxx
@@ -926,6 +926,37 @@ void Editor::SetLastXChosen() {
lastXChosen = static_cast<int>(pt.x) + xOffset;
}
+void Editor::RememberSelectionForUndo(int index) {
+ EnsureModelState();
+ if (modelState) {
+ modelState->RememberSelectionForUndo(index, sel);
+ needRedoRemembered = true;
+ // Remember selection at end of processing current message
+ }
+}
+
+void Editor::RememberSelectionOntoStack(int index) {
+ EnsureModelState();
+ if (modelState) {
+ // Is undo currently inside a group?
+ if (!pdoc->AfterUndoSequenceStart()) {
+ // Don't remember selections inside a grouped sequence as can only
+ // unto or redo to the start and end of the group.
+ modelState->RememberSelectionOntoStack(index);
+ }
+ }
+}
+
+void Editor::RememberCurrentSelectionForRedoOntoStack() {
+ if (needRedoRemembered && (pdoc->UndoSequenceDepth() == 0)) {
+ EnsureModelState();
+ if (modelState) {
+ modelState->RememberSelectionForRedoOntoStack(pdoc->UndoCurrent(), sel);
+ needRedoRemembered = false;
+ }
+ }
+}
+
void Editor::ScrollTo(Sci::Line line, bool moveThumb) {
const Sci::Line topLineNew = std::clamp<Sci::Line>(line, 0, MaxScrollPos());
if (topLineNew != topLine) {
@@ -2085,13 +2116,13 @@ void Editor::InsertCharacter(std::string_view sv, CharacterSource charSource) {
}
}
}
+ ThinRectangularRange();
}
if (wrapOccurred) {
SetScrollBars();
SetVerticalScrollPos();
Redraw();
}
- ThinRectangularRange();
// If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
EnsureCaretVisible();
// Avoid blinking during rapid typing:
@@ -2367,22 +2398,43 @@ void Editor::SelectAll() {
Redraw();
}
+void Editor::RestoreSelection(Sci::Position newPos, UndoRedo history) {
+ if (rememberingSelectionForUndo && modelState) {
+ // Undo wants the element after the current as it just undid it
+ const int index = pdoc->UndoCurrent() + (history == UndoRedo::undo ? 1 : 0);
+ const SelectionSimple *pss = modelState->SelectionFromStack(index, history);
+ if (pss) {
+ sel.selType = pss->selType;
+ if (sel.IsRectangular()) {
+ sel.Rectangular() = pss->rangeRectangular;
+ // Reconstitute ranges from rectangular range
+ SetRectangularRange();
+ } else {
+ sel.SetRanges(pss->ranges);
+ }
+ // Unsure if this is safe with SetMain potentially failing if document doesn't appear the same.
+ // Maybe this can occur if the user changes font or wrap mode?
+ sel.SetMain(pss->mainRange);
+ newPos = -1; // Used selection from stack so don't use position returned from undo/redo.
+ }
+ }
+ if (newPos >= 0)
+ SetEmptySelection(newPos);
+ EnsureCaretVisible();
+}
+
void Editor::Undo() {
if (pdoc->CanUndo()) {
InvalidateCaret();
const Sci::Position newPos = pdoc->Undo();
- if (newPos >= 0)
- SetEmptySelection(newPos);
- EnsureCaretVisible();
+ RestoreSelection(newPos, UndoRedo::undo);
}
}
void Editor::Redo() {
if (pdoc->CanRedo()) {
const Sci::Position newPos = pdoc->Redo();
- if (newPos >= 0)
- SetEmptySelection(newPos);
- EnsureCaretVisible();
+ RestoreSelection(newPos, UndoRedo::redo);
}
}
@@ -2458,6 +2510,17 @@ void Editor::NotifyErrorOccurred(Document *, void *, Status status) {
errorStatus = status;
}
+void Editor::NotifyGroupCompleted(Document *, void *) noexcept {
+ // RememberCurrentSelectionForRedoOntoStack may throw (for memory exhaustion)
+ // but this method may not as it is called in UndoGroup destructor so ignore
+ // exception.
+ try {
+ RememberCurrentSelectionForRedoOntoStack();
+ } catch (...) {
+ // Ignore any exception
+ }
+}
+
void Editor::NotifyChar(int ch, CharacterSource charSource) {
NotificationData scn = {};
scn.nmhdr.code = Notification::CharAdded;
@@ -2726,6 +2789,14 @@ void Editor::NotifyModified(Document *, DocModification mh, void *) {
view.llc.Invalidate(LineLayout::ValidLevel::checkTextAndStyle);
}
} else {
+ if (rememberingSelectionForUndo && FlagSet(mh.modificationType, ModificationFlags::User)) {
+ if (FlagSet(mh.modificationType, ModificationFlags::BeforeInsert | ModificationFlags::BeforeDelete)) {
+ RememberSelectionForUndo(pdoc->UndoCurrent());
+ }
+ if (FlagSet(mh.modificationType, ModificationFlags::InsertText | ModificationFlags::DeleteText)) {
+ RememberSelectionOntoStack(pdoc->UndoCurrent());
+ }
+ }
// Move selection and brace highlights
if (FlagSet(mh.modificationType, ModificationFlags::InsertText)) {
sel.MovePositions(true, mh.position, mh.length);
@@ -5460,6 +5531,7 @@ void Editor::SetDocPointer(Document *document) {
pdoc = document;
}
pdoc->AddRef();
+ modelState.reset();
pcs = ContractionStateCreate(pdoc->IsLarge());
// Ensure all positions within document
@@ -8615,6 +8687,13 @@ sptr_t Editor::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) {
case Message::GetChangeHistory:
return static_cast<sptr_t>(changeHistoryOption);
+ case Message::SetSelectionUndoHistory:
+ rememberingSelectionForUndo = wParam;
+ break;
+
+ case Message::GetSelectionUndoHistory:
+ return rememberingSelectionForUndo;
+
case Message::SetExtraAscent:
vs.extraAscent = static_cast<int>(wParam);
InvalidateStyleRedraw();
@@ -9024,6 +9103,11 @@ sptr_t Editor::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) {
default:
return DefWndProc(iMessage, wParam, lParam);
}
+
+ // If there was a change that needs its selection saved and it wasn't explicity saved
+ // then do that here.
+ RememberCurrentSelectionForRedoOntoStack();
+
//Platform::DebugPrintf("end wnd proc\n");
return 0;
}
diff --git a/src/Editor.h b/src/Editor.h
index d95f552ad..655184a31 100644
--- a/src/Editor.h
+++ b/src/Editor.h
@@ -178,6 +178,8 @@ constexpr XYScrollOptions operator|(XYScrollOptions a, XYScrollOptions b) noexce
return static_cast<XYScrollOptions>(static_cast<int>(a) | static_cast<int>(b));
}
+struct SelectionStack;
+
/**
*/
class Editor : public EditModel, public DocWatcher {
@@ -367,6 +369,9 @@ protected: // ScintillaBase subclass needs access to much of Editor
SelectionPosition MovePositionSoVisible(Sci::Position pos, int moveDir);
Point PointMainCaret();
void SetLastXChosen();
+ void RememberSelectionForUndo(int index);
+ void RememberSelectionOntoStack(int index);
+ void RememberCurrentSelectionForRedoOntoStack();
void ScrollTo(Sci::Line line, bool moveThumb=true);
virtual void ScrollText(Sci::Line linesToMove);
@@ -443,6 +448,7 @@ protected: // ScintillaBase subclass needs access to much of Editor
virtual void Paste() = 0;
void Clear();
virtual void SelectAll();
+ void RestoreSelection(Sci::Position newPos, UndoRedo history);
virtual void Undo();
virtual void Redo();
void DelCharBack(bool allowLineStartDeletion);
@@ -477,6 +483,7 @@ protected: // ScintillaBase subclass needs access to much of Editor
void NotifyDeleted(Document *document, void *userData) noexcept override;
void NotifyStyleNeeded(Document *doc, void *userData, Sci::Position endStyleNeeded) override;
void NotifyErrorOccurred(Document *doc, void *userData, Scintilla::Status status) override;
+ void NotifyGroupCompleted(Document *, void *) noexcept override;
void NotifyMacroRecord(Scintilla::Message iMessage, Scintilla::uptr_t wParam, Scintilla::sptr_t lParam);
void ContainerNeedsUpdate(Scintilla::Update flags) noexcept;
diff --git a/src/Selection.cxx b/src/Selection.cxx
index 82614b040..a4531d65b 100644
--- a/src/Selection.cxx
+++ b/src/Selection.cxx
@@ -217,6 +217,10 @@ SelectionRange &Selection::Rectangular() noexcept {
return rangeRectangular;
}
+SelectionRange Selection::RectangularCopy() const noexcept {
+ return rangeRectangular;
+}
+
SelectionSegment Selection::Limits() const noexcept {
PLATFORM_ASSERT(!ranges.empty());
SelectionSegment sr(ranges[0].anchor, ranges[0].caret);
@@ -456,3 +460,6 @@ void Selection::RotateMain() noexcept {
mainRange = (mainRange + 1) % ranges.size();
}
+void Selection::SetRanges(const Ranges &rangesToSet) {
+ ranges = rangesToSet;
+}
diff --git a/src/Selection.h b/src/Selection.h
index 28db77445..c0d785870 100644
--- a/src/Selection.h
+++ b/src/Selection.h
@@ -163,6 +163,7 @@ public:
Sci::Position MainCaret() const noexcept;
Sci::Position MainAnchor() const noexcept;
SelectionRange &Rectangular() noexcept;
+ SelectionRange RectangularCopy() const noexcept;
SelectionSegment Limits() const noexcept;
// This is for when you want to move the caret in response to a
// user direction command - for rectangular selections, use the range
@@ -202,6 +203,7 @@ public:
Ranges RangesCopy() const {
return ranges;
}
+ void SetRanges(const Ranges &rangesToSet);
};
}
diff --git a/src/UndoHistory.cxx b/src/UndoHistory.cxx
index c0d7ba5f2..29a80c6d1 100644
--- a/src/UndoHistory.cxx
+++ b/src/UndoHistory.cxx
@@ -354,6 +354,14 @@ int UndoHistory::UndoSequenceDepth() const noexcept {
return undoSequenceDepth;
}
+bool UndoHistory::AfterUndoSequenceStart() const noexcept {
+ if (currentAction == 0) {
+ return false;
+ }
+ // Count back to last sequence start?
+ return !actions.AtStart(currentAction-1);
+}
+
void UndoHistory::DropUndoSequence() noexcept {
undoSequenceDepth = 0;
}
diff --git a/src/UndoHistory.h b/src/UndoHistory.h
index be639c541..299e28f58 100644
--- a/src/UndoHistory.h
+++ b/src/UndoHistory.h
@@ -102,6 +102,7 @@ public:
void BeginUndoAction(bool mayCoalesce=false) noexcept;
void EndUndoAction() noexcept;
int UndoSequenceDepth() const noexcept;
+ bool AfterUndoSequenceStart() const noexcept;
void DropUndoSequence() noexcept;
void DeleteUndoHistory() noexcept;