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 : #include "notationundostack.h"
24 :
25 : #include "log.h"
26 :
27 : #include "engraving/dom/masterscore.h"
28 : #include "engraving/editing/transaction/transaction.h"
29 : #include "engraving/editing/transaction/undostack.h"
30 :
31 : using namespace mu::notation;
32 : using namespace muse::async;
33 :
34 0 : NotationUndoStack::NotationUndoStack(IGetScore* getScore, Channel<muse::RectF> notationChanged)
35 0 : : m_getScore(getScore), m_notationChanged(notationChanged)
36 : {
37 0 : }
38 :
39 0 : bool NotationUndoStack::canUndo() const
40 : {
41 0 : IF_ASSERT_FAILED(undoStack()) {
42 0 : return false;
43 : }
44 :
45 0 : return undoStack()->canUndo();
46 : }
47 :
48 0 : void NotationUndoStack::undo(mu::engraving::EditData* editData)
49 : {
50 0 : IF_ASSERT_FAILED(score()) {
51 0 : return;
52 : }
53 :
54 0 : transactionManager()->undoRedo(true, editData);
55 :
56 0 : notifyAboutNotationChanged();
57 0 : notifyAboutUndoRedo();
58 0 : notifyAboutStateChanged();
59 : }
60 :
61 0 : bool NotationUndoStack::canRedo() const
62 : {
63 0 : IF_ASSERT_FAILED(undoStack()) {
64 0 : return false;
65 : }
66 :
67 0 : return undoStack()->canRedo();
68 : }
69 :
70 0 : void NotationUndoStack::redo(mu::engraving::EditData* editData)
71 : {
72 0 : IF_ASSERT_FAILED(score()) {
73 0 : return;
74 : }
75 :
76 0 : transactionManager()->undoRedo(false, editData);
77 :
78 0 : notifyAboutNotationChanged();
79 0 : notifyAboutUndoRedo();
80 0 : notifyAboutStateChanged();
81 : }
82 :
83 0 : void NotationUndoStack::undoRedoToIndex(size_t idx, mu::engraving::EditData* editData)
84 : {
85 0 : auto stack = undoStack();
86 :
87 0 : IF_ASSERT_FAILED(stack) {
88 0 : return;
89 : }
90 :
91 0 : if (stack->currentIndex() == idx) {
92 0 : return;
93 : }
94 :
95 0 : while (stack->currentIndex() > idx && stack->canUndo()) {
96 0 : transactionManager()->undoRedo(true, editData);
97 : }
98 0 : while (stack->currentIndex() < idx && stack->canRedo()) {
99 0 : transactionManager()->undoRedo(false, editData);
100 : }
101 :
102 0 : notifyAboutNotationChanged();
103 0 : notifyAboutUndoRedo();
104 0 : notifyAboutStateChanged();
105 : }
106 :
107 0 : void NotationUndoStack::transaction(const muse::TranslatableString& actionName, std::function<void(mu::engraving::Transaction&)> func)
108 : {
109 0 : IF_ASSERT_FAILED(score()) {
110 0 : return;
111 : }
112 :
113 0 : transactionManager()->transaction(actionName, func);
114 :
115 0 : notifyAboutStateChanged();
116 : }
117 :
118 0 : void NotationUndoStack::prepareChanges(const muse::TranslatableString& actionName)
119 : {
120 0 : IF_ASSERT_FAILED(score()) {
121 0 : return;
122 : }
123 :
124 0 : if (isLocked()) {
125 0 : return;
126 : }
127 :
128 0 : transactionManager()->beginTransaction(actionName);
129 : }
130 :
131 0 : void NotationUndoStack::rollbackChanges()
132 : {
133 0 : IF_ASSERT_FAILED(score()) {
134 0 : return;
135 : }
136 :
137 0 : if (isLocked()) {
138 0 : return;
139 : }
140 :
141 0 : transactionManager()->endTransaction(true);
142 : }
143 :
144 0 : void NotationUndoStack::commitChanges()
145 : {
146 0 : IF_ASSERT_FAILED(score()) {
147 0 : return;
148 : }
149 :
150 0 : if (isLocked()) {
151 0 : return;
152 : }
153 :
154 0 : transactionManager()->endTransaction(false);
155 :
156 0 : notifyAboutStateChanged();
157 : }
158 :
159 0 : bool NotationUndoStack::isStackClean() const
160 : {
161 0 : IF_ASSERT_FAILED(undoStack()) {
162 0 : return false;
163 : }
164 :
165 0 : return undoStack()->isClean();
166 : }
167 :
168 0 : void NotationUndoStack::mergeTransactions(size_t startIdx)
169 : {
170 0 : IF_ASSERT_FAILED(undoStack()) {
171 0 : return;
172 : }
173 :
174 0 : undoStack()->mergeTransactions(startIdx);
175 : }
176 :
177 0 : void NotationUndoStack::lock()
178 : {
179 0 : IF_ASSERT_FAILED(undoStack()) {
180 0 : return;
181 : }
182 :
183 0 : undoStack()->setLocked(true);
184 : }
185 :
186 0 : void NotationUndoStack::unlock()
187 : {
188 0 : IF_ASSERT_FAILED(undoStack()) {
189 0 : return;
190 : }
191 :
192 0 : undoStack()->setLocked(false);
193 : }
194 :
195 0 : bool NotationUndoStack::isLocked() const
196 : {
197 0 : return undoStack()->isLocked();
198 : }
199 :
200 0 : const muse::TranslatableString NotationUndoStack::topMostUndoActionName() const
201 : {
202 0 : IF_ASSERT_FAILED(undoStack()) {
203 0 : return {};
204 : }
205 :
206 0 : if (auto action = undoStack()->last()) {
207 0 : return action->actionName();
208 : }
209 :
210 0 : return {};
211 : }
212 :
213 0 : const muse::TranslatableString NotationUndoStack::topMostRedoActionName() const
214 : {
215 0 : IF_ASSERT_FAILED(undoStack()) {
216 0 : return {};
217 : }
218 :
219 0 : if (auto action = undoStack()->next()) {
220 0 : return action->actionName();
221 : }
222 :
223 0 : return {};
224 : }
225 :
226 0 : size_t NotationUndoStack::undoRedoActionCount() const
227 : {
228 0 : IF_ASSERT_FAILED(undoStack()) {
229 0 : return 0;
230 : }
231 :
232 0 : return undoStack()->size();
233 : }
234 :
235 0 : size_t NotationUndoStack::currentStateIndex() const
236 : {
237 0 : IF_ASSERT_FAILED(undoStack()) {
238 0 : return muse::nidx;
239 : }
240 :
241 0 : return undoStack()->currentIndex();
242 : }
243 :
244 0 : const muse::TranslatableString NotationUndoStack::lastActionNameAtIdx(size_t idx) const
245 : {
246 0 : IF_ASSERT_FAILED(undoStack()) {
247 0 : return {};
248 : }
249 :
250 0 : if (auto action = undoStack()->lastAtIndex(idx)) {
251 0 : return action->actionName();
252 : }
253 :
254 0 : return {};
255 : }
256 :
257 0 : muse::async::Notification NotationUndoStack::stackChanged() const
258 : {
259 0 : return m_stackStateChanged;
260 : }
261 :
262 0 : muse::async::Channel<mu::engraving::ScoreChanges> NotationUndoStack::changesChannel() const
263 : {
264 0 : IF_ASSERT_FAILED(score()) {
265 0 : return muse::async::Channel<engraving::ScoreChanges>();
266 : }
267 :
268 0 : return score()->changesChannel();
269 0 : }
270 :
271 0 : Notification NotationUndoStack::undoRedoNotification() const
272 : {
273 0 : return m_undoRedoNotification;
274 : }
275 :
276 0 : mu::engraving::Score* NotationUndoStack::score() const
277 : {
278 0 : return m_getScore->score();
279 : }
280 :
281 0 : mu::engraving::MasterScore* NotationUndoStack::masterScore() const
282 : {
283 0 : return score() ? score()->masterScore() : nullptr;
284 : }
285 :
286 0 : mu::engraving::TransactionManager* NotationUndoStack::transactionManager() const
287 : {
288 0 : return score() ? score()->masterScore()->transactionManager() : nullptr;
289 : }
290 :
291 0 : mu::engraving::UndoStack* NotationUndoStack::undoStack() const
292 : {
293 0 : return score() ? score()->masterScore()->undoStack() : nullptr;
294 : }
295 :
296 0 : void NotationUndoStack::notifyAboutNotationChanged()
297 : {
298 0 : m_notationChanged.send(muse::RectF());
299 0 : }
300 :
301 0 : void NotationUndoStack::notifyAboutStateChanged()
302 : {
303 0 : m_stackStateChanged.notify();
304 0 : }
305 :
306 0 : void NotationUndoStack::notifyAboutUndoRedo()
307 : {
308 0 : m_undoRedoNotification.notify();
309 0 : }
|