Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-Studio-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2021 MuseScore Limited and others
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 :
23 : #pragma once
24 :
25 : #include <map>
26 :
27 : #include "global/containers.h"
28 : #include "global/types/translatablestring.h"
29 :
30 : #include "../../dom/input.h"
31 :
32 : namespace mu::engraving {
33 : class EngravingObject;
34 : class Score;
35 : class Segment;
36 : class Box;
37 :
38 : class EditData;
39 :
40 : class UndoableCommand;
41 : enum class UndoableCommandFilter : unsigned char;
42 : enum class CommandType : signed char;
43 :
44 : class UndoableTransaction
45 : {
46 : public:
47 : UndoableTransaction(Score* s, const muse::TranslatableString& actionName);
48 : ~UndoableTransaction();
49 :
50 : void undo();
51 : void redo();
52 :
53 20940 : void appendCommand(UndoableCommand* cmd) { m_commands.push_back(cmd); }
54 : void append(UndoableTransaction&& other);
55 :
56 : void unwind(bool cleanUp = true);
57 : void cleanup(bool wasDone);
58 :
59 4203 : const std::vector<UndoableCommand*>& commands() const { return m_commands; }
60 1266 : bool empty() const { return m_commands.empty(); }
61 :
62 : bool hasCommandsMatchingFilter(UndoableCommandFilter, const EngravingItem* target) const;
63 : bool hasCommandsNotMatchingFilters(const std::vector<UndoableCommandFilter>& filters, const EngravingItem* target) const;
64 : void removeCommandsMatchingFilter(UndoableCommandFilter f, EngravingItem* target);
65 :
66 : const InputState& undoInputState() const;
67 : const InputState& redoInputState() const;
68 :
69 : struct SelectionInfo {
70 : std::vector<EngravingItem*> elements;
71 : Fraction tickStart;
72 : Fraction tickEnd;
73 : staff_idx_t staffStart = muse::nidx;
74 : staff_idx_t staffEnd = muse::nidx;
75 : Box* startBox = nullptr;
76 : Box* endBox = nullptr;
77 :
78 230 : bool isValid() const { return !elements.empty() || staffStart != muse::nidx; }
79 : };
80 :
81 : const SelectionInfo& undoSelectionInfo() const;
82 : const SelectionInfo& redoSelectionInfo() const;
83 :
84 : void excludeElementFromSelectionInfo(EngravingItem* element);
85 : static bool canRecordSelectedElement(const EngravingItem* e);
86 :
87 : struct ChangesInfo {
88 : ElementTypeSet changedObjectTypes;
89 : std::map<EngravingObject*, std::unordered_set<CommandType> > changedObjects;
90 : StyleIdSet changedStyleIdSet;
91 : PropertyIdSet changedPropertyIdSet;
92 : bool isTextEditing = false;
93 : };
94 :
95 : ChangesInfo changesInfo(bool undo = false) const;
96 : const muse::TranslatableString& actionName() const;
97 :
98 : private:
99 : void appendCommands(UndoableTransaction& other);
100 :
101 : std::vector<UndoableCommand*> m_commands;
102 :
103 : InputState m_undoInputState;
104 : InputState m_redoInputState;
105 : SelectionInfo m_undoSelectionInfo;
106 : SelectionInfo m_redoSelectionInfo;
107 : muse::TranslatableString m_actionName;
108 :
109 : Score* m_score = nullptr;
110 :
111 : static void fillSelectionInfo(SelectionInfo&, const Selection&);
112 : static void applySelectionInfo(const SelectionInfo&, Selection&);
113 : };
114 :
115 : class UndoStack
116 : {
117 : public:
118 : UndoStack();
119 : ~UndoStack();
120 :
121 : bool isLocked() const;
122 : void setLocked(bool locked);
123 :
124 3024 : bool hasActiveTransaction() const { return m_activeTransaction != nullptr; }
125 :
126 : void beginTransaction(Score*, const muse::TranslatableString& actionName);
127 : void endTransaction(bool rollback);
128 :
129 168 : bool canUndo() const { return m_currentIndex > 0; }
130 70 : bool canRedo() const { return m_currentIndex < m_transactions.size(); }
131 1266 : bool isClean() const { return m_cleanState == m_states[m_currentIndex]; }
132 :
133 2 : size_t size() const { return m_transactions.size(); }
134 364 : size_t currentIndex() const { return m_currentIndex; }
135 :
136 6331 : UndoableTransaction* activeTransaction() const { return m_activeTransaction; }
137 :
138 205 : UndoableTransaction* last() const { return m_currentIndex > 0 ? m_transactions[m_currentIndex - 1] : nullptr; }
139 0 : UndoableTransaction* prev() const { return m_currentIndex > 1 ? m_transactions[m_currentIndex - 2] : nullptr; }
140 0 : UndoableTransaction* next() const { return canRedo() ? m_transactions[m_currentIndex] : nullptr; }
141 :
142 : /// Returns the transaction that led to the state with the given `idx`.
143 : /// For further discussion of the indices involved in UndoStack, see:
144 : /// https://github.com/musescore/MuseScore/pull/25389#discussion_r1825782176
145 0 : UndoableTransaction* lastAtIndex(size_t idx) const
146 : {
147 0 : return idx > 0 && idx - 1 < m_transactions.size() ? m_transactions[idx - 1] : nullptr;
148 : }
149 :
150 : void undo(EditData*);
151 : void redo();
152 : void reopen();
153 :
154 : void mergeTransactions(size_t startIdx);
155 1 : void cleanRedoStack() { remove(m_currentIndex); }
156 :
157 : private:
158 : void remove(size_t idx);
159 :
160 : UndoableTransaction* m_activeTransaction = nullptr;
161 : std::vector<UndoableTransaction*> m_transactions;
162 : std::vector<int> m_states;
163 : int m_nextState = 0;
164 : int m_cleanState = 0;
165 : size_t m_currentIndex = 0;
166 : bool m_isLocked = false;
167 : };
168 : }
|