Pencil2D Animation
Download Community News Docs Contribute
  • Overview
  • Articles
  • Code
  •  
  • Class List
  • Class Index
  • Class Hierarchy
  • Class Members
  • File List
Loading...
Searching...
No Matches
  • core_lib
  • src
  • managers
undoredomanager.cpp
1/*
2
3Pencil2D - Traditional Animation Software
4Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon
5Copyright (C) 2008-2009 Mj Mendoza IV
6Copyright (C) 2012-2020 Matthew Chiawen Chang
7
8This program is free software; you can redistribute it and/or
9modify it under the terms of the GNU General Public License
10as published by the Free Software Foundation; version 2 of the License.
11
12This program is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17*/
18
19#include "object.h"
20#include "editor.h"
21
22#include <QAction>
23#include <QDebug>
24#include <QSettings>
25
26
27#include "preferencemanager.h"
28#include "layermanager.h"
29#include "soundmanager.h"
30#include "undoredomanager.h"
31#include "selectionmanager.h"
32
33#include "undoredocommand.h"
34#include "legacybackupelement.h"
35
36#include "layerbitmap.h"
37#include "layervector.h"
38#include "layersound.h"
39
40
41#include "bitmapimage.h"
42#include "vectorimage.h"
43#include "soundclip.h"
44
45UndoRedoManager::UndoRedoManager(Editor* editor) : BaseManager(editor, "UndoRedoManager")
46{
47 qDebug() << "UndoRedoManager: created";
48}
49
50UndoRedoManager::~UndoRedoManager()
51{
52 if (!mNewBackupSystemEnabled)
53 {
54 clearStack();
55 }
56 clearSaveStates();
57 qDebug() << "UndoRedoManager: destroyed";
58}
59
60bool UndoRedoManager::init()
61{
62 qDebug() << "UndoRedoManager: init";
63
64 mUndoStack.setUndoLimit(editor()->preference()->getInt(SETTING::UNDO_REDO_MAX_STEPS));
65 mNewBackupSystemEnabled = editor()->preference()->isOn(SETTING::NEW_UNDO_REDO_SYSTEM_ON);
66
67 return true;
68}
69
70void UndoRedoManager::onSettingChanged(SETTING setting)
71{
72 if (setting == SETTING::UNDO_REDO_MAX_STEPS) {
73 // The stack needs to be cleared in order to change the undo redo limit
74 clearStack();
75 qDebug() << "updated undo stack limit";
76 mUndoStack.setUndoLimit(editor()->preference()->getInt(SETTING::UNDO_REDO_MAX_STEPS));
77 }
78}
79
80Status UndoRedoManager::load(Object* /*o*/)
81{
82 clearStack();
83 return Status::OK;
84}
85
86Status UndoRedoManager::save(Object* /*o*/)
87{
88 if (mNewBackupSystemEnabled) {
89 mUndoStack.setClean();
90 } else if (!mLegacyBackupList.isEmpty() && mLegacyBackupIndex < mLegacyBackupList.count()) {
91 mLegacyBackupAtSave = mLegacyBackupList[mLegacyBackupIndex];
92 }
93 return Status::OK;
94}
95
96void UndoRedoManager::record(SAVESTATE_ID saveStateId, const QString& description)
97{
98 if (!mSaveStates.contains(saveStateId)) {
99 return;
100 }
101
102 UndoSaveState* saveState = mSaveStates.take(saveStateId);
103 if (!saveState) { return; }
104
105 if (!mNewBackupSystemEnabled) {
106 clearState(saveState);
107 return;
108 }
109
110 switch (saveState->recordType)
111 {
112 case UndoRedoRecordType::KEYFRAME_MODIFY: {
113 replaceKeyFrame(*saveState, description);
114 break;
115 }
116 case UndoRedoRecordType::KEYFRAME_REMOVE: {
117 removeKeyFrame(*saveState, description);
118 break;
119 }
120 case UndoRedoRecordType::KEYFRAME_ADD: {
121 addKeyFrame(*saveState, description);
122 break;
123 }
124 case UndoRedoRecordType::KEYFRAME_MOVE: {
125 moveKeyFrames(*saveState, description);
126 break;
127 }
128 default: {
129 QString reason("Unhandled case for: ");
130 reason.append(description);
131 Q_ASSERT_X(false, "UndoRedoManager::record", qPrintable(reason));
132 break;
133 }
134 }
135
136 // The save state has now been used and should be invalidated so we can't use it again.
137 clearState(saveState);
138}
139
140void UndoRedoManager::clearState(UndoSaveState*& state)
141{
142 if (state) {
143 delete state;
144 state = nullptr;
145 }
146}
147
148void UndoRedoManager::clearSaveStates()
149{
150 for (UndoSaveState* saveState : mSaveStates) {
151 if (saveState) {
152 delete saveState;
153 saveState = nullptr;
154 }
155 }
156 mSaveStates.clear();
157}
158
159bool UndoRedoManager::hasUnsavedChanges() const
160{
161 if (mNewBackupSystemEnabled) {
162 return !mUndoStack.isClean();
163 } else {
164 if (mLegacyBackupIndex >= 0) {
165 return mLegacyBackupAtSave != mLegacyBackupList[mLegacyBackupIndex];
166 }
167 return false;
168 }
169}
170
171void UndoRedoManager::pushCommand(QUndoCommand* command)
172{
173 mUndoStack.push(command);
174
175 emit didUpdateUndoStack();
176}
177
178void UndoRedoManager::removeKeyFrame(const UndoSaveState& undoState, const QString& description)
179{
180 KeyFrameRemoveCommand* element = new KeyFrameRemoveCommand(undoState.keyframe.get(),
181 undoState.layerId,
182 description,
183 editor());
184 pushCommand(element);
185}
186
187void UndoRedoManager::addKeyFrame(const UndoSaveState& undoState, const QString& description)
188{
189 KeyFrameAddCommand* element = new KeyFrameAddCommand(undoState.currentFrameIndex,
190 undoState.layerId,
191 description,
192 editor());
193 pushCommand(element);
194}
195
196void UndoRedoManager::replaceKeyFrame(const UndoSaveState& undoState, const QString& description)
197{
198 if (undoState.layerType == Layer::BITMAP) {
199 replaceBitmap(undoState, description);
200 } else if (undoState.layerType == Layer::VECTOR) {
201 replaceVector(undoState, description);
202 } else {
203 // Implement other cases
204 }
205}
206
207void UndoRedoManager::moveKeyFrames(const UndoSaveState& undoState, const QString& description)
208{
209 const MoveFramesSaveState& state = undoState.userState.moveFramesState;
210 MoveKeyFramesCommand* element = new MoveKeyFramesCommand(state.offset,
211 state.positions,
212 undoState.layerId,
213 description,
214 editor());
215 pushCommand(element);
216}
217
218void UndoRedoManager::replaceBitmap(const UndoSaveState& undoState, const QString& description)
219{
220 if (undoState.keyframe == nullptr || undoState.layerType != Layer::BITMAP) { return; }
221 BitmapReplaceCommand* element = new BitmapReplaceCommand(static_cast<BitmapImage*>(undoState.keyframe.get()),
222 undoState.layerId,
223 description,
224 editor());
225
226 const SelectionSaveState& selectionState = undoState.selectionState;
227 new TransformCommand(selectionState.bounds,
228 selectionState.translation,
229 selectionState.rotationAngle,
230 selectionState.scaleX,
231 selectionState.scaleY,
232 selectionState.anchor,
233 true, // roundPixels
234 description,
235 editor(), element);
236
237 pushCommand(element);
238}
239
240void UndoRedoManager::replaceVector(const UndoSaveState& undoState, const QString& description)
241{
242 if (undoState.keyframe == nullptr || undoState.layerType != Layer::VECTOR) { return; }
243 VectorReplaceCommand* element = new VectorReplaceCommand(static_cast<VectorImage*>(undoState.keyframe.get()),
244 undoState.layerId,
245 description,
246 editor());
247
248 const SelectionSaveState& selectionState = undoState.selectionState;
249 new TransformCommand(selectionState.bounds,
250 selectionState.translation,
251 selectionState.rotationAngle,
252 selectionState.scaleX,
253 selectionState.scaleY,
254 selectionState.anchor,
255 false, // Round pixels
256 description,
257 editor(), element);
258 pushCommand(element);
259}
260
261SAVESTATE_ID UndoRedoManager::createState(UndoRedoRecordType recordType)
262{
263 int saveStateId = mSaveStateId;
264 UndoSaveState* state = new UndoSaveState();
265 state->recordType = recordType;
266 initCommonKeyFrameState(state);
267
268 mSaveStates[saveStateId] = state;
269 mSaveStateId += 1;
270
271 return saveStateId;
272}
273
274void UndoRedoManager::addUserState(SAVESTATE_ID saveStateId, const UserSaveState& userState)
275{
276 if (!mSaveStates.contains(saveStateId)) { return; }
277 mSaveStates[saveStateId]->userState = userState;
278}
279
280void UndoRedoManager::initCommonKeyFrameState(UndoSaveState* undoSaveState) const
281{
282 const Layer* layer = editor()->layers()->currentLayer();
283 undoSaveState->layerType = layer->type();
284 undoSaveState->layerId = layer->id();
285 undoSaveState->currentFrameIndex = editor()->currentFrame();
286
287 if (layer->type() == Layer::BITMAP || layer->type() == Layer::VECTOR) {
288 auto selectMan = editor()->select();
289 undoSaveState->selectionState = SelectionSaveState(
290 selectMan->mySelectionRect(),
291 selectMan->myRotation(),
292 selectMan->myScaleX(),
293 selectMan->myScaleY(),
294 selectMan->myTranslation(),
295 selectMan->currentTransformAnchor());
296 }
297
298 const int frameIndex = editor()->currentFrame();
299 auto keyframe = layer->getKeyFrameWhichCovers(frameIndex);
300 if (keyframe == nullptr)
301 {
302 keyframe = layer->getLastKeyFrameAtPosition(frameIndex);
303 }
304
305 if (keyframe != nullptr) {
306 undoSaveState->keyframe = std::unique_ptr<KeyFrame>(keyframe->clone());
307 }
308}
309
310QAction* UndoRedoManager::createUndoAction(QObject* parent, const QIcon& icon)
311{
312 QAction* undoAction = nullptr;
313 if (mNewBackupSystemEnabled) {
314 undoAction = mUndoStack.createUndoAction(parent);
315 } else {
316 undoAction = new QAction(parent);
317 undoAction->setText(tr("Undo"));
318 undoAction->setDisabled(true);
319 }
320 undoAction->setIcon(icon);
321
322 if (mNewBackupSystemEnabled) {
323 // The new system takes care of this automatically
324 } else {
325 connect(undoAction, &QAction::triggered, this, &UndoRedoManager::legacyUndo);
326 }
327 return undoAction;
328}
329
330QAction* UndoRedoManager::createRedoAction(QObject* parent, const QIcon& icon)
331{
332 QAction* redoAction = nullptr;
333 if (mNewBackupSystemEnabled) {
334 redoAction = mUndoStack.createRedoAction(parent);
335 } else {
336 redoAction = new QAction(parent);
337 redoAction->setText(tr("Redo"));
338 redoAction->setDisabled(true);
339 }
340 redoAction->setIcon(icon);
341
342 if (mNewBackupSystemEnabled) {
343 // The new system takes care of this automatically
344 } else {
345 connect(redoAction, &QAction::triggered, this, &UndoRedoManager::legacyRedo);
346 }
347 return redoAction;
348}
349
350void UndoRedoManager::updateUndoAction(QAction* undoAction)
351{
352 if (mNewBackupSystemEnabled) {
353 // Not used
354 // function can be removed when we have replaced the legacy system
355 } else {
356 if (mLegacyBackupIndex < 0)
357 {
358 undoAction->setText(tr("Undo", "Menu item text"));
359 undoAction->setEnabled(false);
360 qDebug() << undoAction->text();
361 }
362 else
363 {
364 undoAction->setText(QString("%1 %2 %3").arg(tr("Undo", "Menu item text"))
365 .arg(mLegacyBackupIndex + 1)
366 .arg(mLegacyBackupList.at(mLegacyBackupIndex)->undoText));
367 undoAction->setIconText(QString("%1 %2 %3").arg(tr("Undo", "Menu item text"))
368 .arg(mLegacyBackupIndex + 1)
369 .arg(mLegacyBackupList.at(mLegacyBackupIndex)->undoText));
370 undoAction->setEnabled(true);
371 qDebug() << undoAction->text();
372 }
373 }
374}
375
376void UndoRedoManager::updateRedoAction(QAction* redoAction)
377{
378 if (mNewBackupSystemEnabled) {
379 // Not used
380 // function can be removed when we have replaced the legacy system
381 } else {
382 if (mLegacyBackupIndex + 2 < mLegacyBackupList.size())
383 {
384 redoAction->setText(QString("%1 %2 %3").arg(tr("Redo", "Menu item text"))
385 .arg(mLegacyBackupIndex + 2)
386 .arg(mLegacyBackupList.at(mLegacyBackupIndex + 1)->undoText));
387 redoAction->setEnabled(true);
388 }
389 else
390 {
391 redoAction->setText(tr("Redo", "Menu item text"));
392 redoAction->setEnabled(false);
393 }
394 }
395}
396
397void UndoRedoManager::clearStack()
398{
399 if (mNewBackupSystemEnabled) {
400 mUndoStack.clear();
401 } else {
402 mLegacyBackupIndex = -1;
403 while (!mLegacyBackupList.isEmpty())
404 {
405 delete mLegacyBackupList.takeLast();
406 }
407 mLegacyLastModifiedLayer = -1;
408 mLegacyLastModifiedFrame = -1;
409 }
410}
411
412// Legacy backup system
413
414void UndoRedoManager::legacyBackup(const QString& undoText)
415{
416
417 if (mNewBackupSystemEnabled) {
418 return;
419 }
420
421 KeyFrame* frame = nullptr;
422 int currentFrame = editor()->currentFrame();
423 if (mLegacyLastModifiedLayer > -1 && mLegacyLastModifiedFrame > 0)
424 {
425 if (editor()->layers()->currentLayer()->type() == Layer::SOUND)
426 {
427 frame = editor()->layers()->currentLayer()->getKeyFrameWhichCovers(mLegacyLastModifiedFrame);
428 if (frame != nullptr)
429 {
430 legacyBackup(mLegacyLastModifiedLayer, frame->pos(), undoText);
431 }
432 }
433 else
434 {
435 legacyBackup(mLegacyLastModifiedLayer, mLegacyLastModifiedFrame, undoText);
436 }
437 }
438 if (mLegacyLastModifiedLayer != editor()->layers()->currentLayerIndex() || mLegacyLastModifiedFrame != currentFrame)
439 {
440 if (editor()->layers()->currentLayer()->type() == Layer::SOUND)
441 {
442 frame = editor()->layers()->currentLayer()->getKeyFrameWhichCovers(currentFrame);
443
444 if (frame != nullptr)
445 {
446 legacyBackup(editor()->layers()->currentLayerIndex(), frame->pos(), undoText);
447 }
448 }
449 else
450 {
451 legacyBackup(editor()->layers()->currentLayerIndex(), currentFrame, undoText);
452 }
453 }
454}
455
456bool UndoRedoManager::legacyBackup(int backupLayer, int backupFrame, const QString& undoText)
457{
458 if (mNewBackupSystemEnabled) {
459 return false;
460 }
461
462 while (mLegacyBackupList.size() - 1 > mLegacyBackupIndex && !mLegacyBackupList.empty())
463 {
464 delete mLegacyBackupList.takeLast();
465 }
466 while (mLegacyBackupList.size() >= editor()->preference()->getInt(SETTING::UNDO_REDO_MAX_STEPS))
467 {
468 delete mLegacyBackupList.takeFirst();
469 mLegacyBackupIndex--;
470 }
471
472 Layer* layer = editor()->layers()->getLayer(backupLayer);
473 int currentFrame = editor()->currentFrame();
474 if (layer != nullptr)
475 {
476 if (layer->type() == Layer::BITMAP)
477 {
478 BitmapImage* bitmapImage = static_cast<BitmapImage*>(layer->getLastKeyFrameAtPosition(backupFrame));
479 if (currentFrame == 1)
480 {
481 int previous = layer->getPreviousKeyFramePosition(backupFrame);
482 bitmapImage = static_cast<BitmapImage*>(layer->getKeyFrameAt(previous));
483 }
484 if (bitmapImage != nullptr)
485 {
486 BackupLegacyBitmapElement* element = new BackupLegacyBitmapElement(bitmapImage);
487 element->layerId = layer->id();
488 element->layer = backupLayer;
489 element->frame = bitmapImage->pos();
490 element->undoText = undoText;
491 element->somethingSelected = editor()->select()->somethingSelected();
492 element->mySelection = editor()->select()->mySelectionRect();
493 element->rotationAngle = editor()->select()->myRotation();
494 element->scaleX = editor()->select()->myScaleX();
495 element->scaleY = editor()->select()->myScaleY();
496 element->translation = editor()->select()->myTranslation();
497 element->selectionAnchor = editor()->select()->currentTransformAnchor();
498
499 mLegacyBackupList.append(element);
500 mLegacyBackupIndex++;
501 }
502 else
503 {
504 return false;
505 }
506 }
507 else if (layer->type() == Layer::VECTOR)
508 {
509 VectorImage* vectorImage = static_cast<VectorImage*>(layer->getLastKeyFrameAtPosition(backupFrame));
510 if (vectorImage != nullptr)
511 {
512 BackupLegacyVectorElement* element = new BackupLegacyVectorElement(vectorImage);
513 element->layerId = layer->id();
514 element->layer = backupLayer;
515 element->frame = vectorImage->pos();
516 element->undoText = undoText;
517 element->somethingSelected = editor()->select()->somethingSelected();
518 element->mySelection = editor()->select()->mySelectionRect();
519 element->rotationAngle = editor()->select()->myRotation();
520 element->scaleX = editor()->select()->myScaleX();
521 element->scaleY = editor()->select()->myScaleY();
522 element->translation = editor()->select()->myTranslation();
523 element->selectionAnchor = editor()->select()->currentTransformAnchor();
524 mLegacyBackupList.append(element);
525 mLegacyBackupIndex++;
526 }
527 else
528 {
529 return false;
530 }
531 }
532 else if (layer->type() == Layer::SOUND)
533 {
534 int previous = layer->getPreviousKeyFramePosition(backupFrame);
535 KeyFrame* key = layer->getLastKeyFrameAtPosition(backupFrame);
536
537 // in case tracks overlap, get previous frame
538 if (key == nullptr)
539 {
540 KeyFrame* previousKey = layer->getKeyFrameAt(previous);
541 key = previousKey;
542 }
543 if (key != nullptr) {
544 SoundClip* clip = static_cast<SoundClip*>(key);
545 if (clip)
546 {
547 BackupLegacySoundElement* element = new BackupLegacySoundElement(clip);
548 element->layerId = layer->id();
549 element->layer = backupLayer;
550 element->frame = backupFrame;
551 element->undoText = undoText;
552 element->fileName = clip->fileName();
553 element->originalName = clip->soundClipName();
554 mLegacyBackupList.append(element);
555 mLegacyBackupIndex++;
556 }
557 }
558 else
559 {
560 return false;
561 }
562 }
563 }
564
565 emit didUpdateUndoStack();
566
567 return true;
568}
569
570void UndoRedoManager::sanitizeLegacyBackupElementsAfterLayerDeletion(int layerIndex)
571{
572 if (mNewBackupSystemEnabled) {
573 return;
574 }
575
576 for (int i = 0; i < mLegacyBackupList.size(); i++)
577 {
578 LegacyBackupElement *backupElement = mLegacyBackupList[i];
579 BackupLegacyBitmapElement *bitmapElement;
580 BackupLegacyVectorElement *vectorElement;
581 BackupLegacySoundElement *soundElement;
582 switch (backupElement->type())
583 {
584 case LegacyBackupElement::BITMAP_MODIF:
585 bitmapElement = qobject_cast<BackupLegacyBitmapElement*>(backupElement);
586 Q_ASSERT(bitmapElement);
587 if (bitmapElement->layer > layerIndex)
588 {
589 bitmapElement->layer--;
590 continue;
591 }
592 else if (bitmapElement->layer != layerIndex)
593 {
594 continue;
595 }
596 break;
597 case LegacyBackupElement::VECTOR_MODIF:
598 vectorElement = qobject_cast<BackupLegacyVectorElement*>(backupElement);
599 Q_ASSERT(vectorElement);
600 if (vectorElement->layer > layerIndex)
601 {
602 vectorElement->layer--;
603 continue;
604 }
605 else if (vectorElement->layer != layerIndex)
606 {
607 continue;
608 }
609 break;
610 case LegacyBackupElement::SOUND_MODIF:
611 soundElement = qobject_cast<BackupLegacySoundElement*>(backupElement);
612 Q_ASSERT(soundElement);
613 if (soundElement->layer > layerIndex)
614 {
615 soundElement->layer--;
616 continue;
617 }
618 else if (soundElement->layer != layerIndex)
619 {
620 continue;
621 }
622 break;
623 default:
624 Q_UNREACHABLE();
625 }
626 if (i <= mLegacyBackupIndex)
627 {
628 mLegacyBackupIndex--;
629 }
630 delete mLegacyBackupList.takeAt(i);
631 i--;
632 }
633}
634
635void UndoRedoManager::restoreLegacyKey()
636{
637 if (mNewBackupSystemEnabled) {
638 return;
639 }
640
641 LegacyBackupElement* lastBackupElement = mLegacyBackupList[mLegacyBackupIndex];
642
643 Layer* layer = nullptr;
644 int frame = 0;
645 int layerIndex = 0;
646 if (lastBackupElement->type() == LegacyBackupElement::BITMAP_MODIF)
647 {
648 BackupLegacyBitmapElement* lastBackupBitmapElement = static_cast<BackupLegacyBitmapElement*>(lastBackupElement);
649 layerIndex = lastBackupBitmapElement->layer;
650 frame = lastBackupBitmapElement->frame;
651 layer = object()->findLayerById(lastBackupBitmapElement->layerId);
652 editor()->addKeyFrame(layerIndex, frame);
653 dynamic_cast<LayerBitmap*>(layer)->getBitmapImageAtFrame(frame)->paste(&lastBackupBitmapElement->bitmapImage);
654 editor()->setModified(layerIndex, frame);
655 }
656 if (lastBackupElement->type() == LegacyBackupElement::VECTOR_MODIF)
657 {
658 BackupLegacyVectorElement* lastBackupVectorElement = static_cast<BackupLegacyVectorElement*>(lastBackupElement);
659 layerIndex = lastBackupVectorElement->layer;
660 frame = lastBackupVectorElement->frame;
661 layer = object()->findLayerById(layerIndex);
662 editor()->addKeyFrame(layerIndex, frame);
663 dynamic_cast<LayerVector*>(layer)->getVectorImageAtFrame(frame)->paste(lastBackupVectorElement->vectorImage);
664 editor()->setModified(layerIndex, frame);
665 }
666 if (lastBackupElement->type() == LegacyBackupElement::SOUND_MODIF)
667 {
668 QString strSoundFile;
669 BackupLegacySoundElement* lastBackupSoundElement = static_cast<BackupLegacySoundElement*>(lastBackupElement);
670 layerIndex = lastBackupSoundElement->layer;
671 frame = lastBackupSoundElement->frame;
672
673 strSoundFile = lastBackupSoundElement->fileName;
674 if (strSoundFile.isEmpty()) return;
675 KeyFrame* key = editor()->addKeyFrame(layerIndex, frame);
676 SoundClip* clip = dynamic_cast<SoundClip*>(key);
677 if (clip)
678 {
679 Status st = editor()->sound()->loadSound(clip, lastBackupSoundElement->fileName);
680 clip->setSoundClipName(lastBackupSoundElement->originalName);
681 if (!st.ok())
682 {
683 editor()->removeKey();
684 emit editor()->layers()->currentLayerChanged(editor()->layers()->currentLayerIndex()); // trigger timeline repaint.
685 }
686 }
687 }
688}
689
690void UndoRedoManager::legacyUndo()
691{
692 if (!mLegacyBackupList.empty() && mLegacyBackupIndex > -1)
693 {
694 if (mLegacyBackupIndex == mLegacyBackupList.size() - 1)
695 {
696 LegacyBackupElement* lastBackupElement = mLegacyBackupList[mLegacyBackupIndex];
697 if (lastBackupElement->type() == LegacyBackupElement::BITMAP_MODIF)
698 {
699 BackupLegacyBitmapElement* lastBackupBitmapElement = static_cast<BackupLegacyBitmapElement*>(lastBackupElement);
700 if (legacyBackup(lastBackupBitmapElement->layer, lastBackupBitmapElement->frame, "NoOp"))
701 {
702 mLegacyBackupIndex--;
703 }
704 }
705 if (lastBackupElement->type() == LegacyBackupElement::VECTOR_MODIF)
706 {
707 BackupLegacyVectorElement* lastBackupVectorElement = static_cast<BackupLegacyVectorElement*>(lastBackupElement);
708 if (legacyBackup(lastBackupVectorElement->layer, lastBackupVectorElement->frame, "NoOp"))
709 {
710 mLegacyBackupIndex--;
711 }
712 }
713 if (lastBackupElement->type() == LegacyBackupElement::SOUND_MODIF)
714 {
715 BackupLegacySoundElement* lastBackupSoundElement = static_cast<BackupLegacySoundElement*>(lastBackupElement);
716 if (legacyBackup(lastBackupSoundElement->layer, lastBackupSoundElement->frame, "NoOp"))
717 {
718 mLegacyBackupIndex--;
719 }
720 }
721 }
722
723 qDebug() << "Undo" << mLegacyBackupIndex;
724 mLegacyBackupList[mLegacyBackupIndex]->restore(editor());
725 mLegacyBackupIndex--;
726
727 emit didUpdateUndoStack();
728 }
729}
730
731void UndoRedoManager::legacyRedo()
732{
733 if (!mLegacyBackupList.empty() && mLegacyBackupIndex < mLegacyBackupList.size() - 2)
734 {
735 mLegacyBackupIndex++;
736
737 mLegacyBackupList[mLegacyBackupIndex + 1]->restore(editor());
738 emit didUpdateUndoStack();
739 }
740}
741
742void UndoRedoManager::rememberLastModifiedFrame(int layerNumber, int frameNumber)
743{
744 if (mNewBackupSystemEnabled) {
745 // not required
746 } else {
747 mLegacyLastModifiedLayer = layerNumber;
748 mLegacyLastModifiedFrame = frameNumber;
749 }
750}
BackupLegacyBitmapElement
Definition: legacybackupelement.h:48
BackupLegacySoundElement
Definition: legacybackupelement.h:78
BackupLegacyVectorElement
Definition: legacybackupelement.h:63
BaseManager
Definition: basemanager.h:29
BitmapImage
Definition: bitmapimage.h:28
BitmapReplaceCommand
Definition: undoredocommand.h:123
Editor
Definition: editor.h:71
Editor::addKeyFrame
KeyFrame * addKeyFrame(int layerNumber, int frameIndex)
Attempts to create a new keyframe at the given position and layer.
Definition: editor.cpp:904
KeyFrameAddCommand
Definition: undoredocommand.h:80
KeyFrame
Definition: keyframe.h:30
KeyFrameRemoveCommand
Definition: undoredocommand.h:57
LayerBitmap
Definition: layerbitmap.h:26
Layer
Definition: layer.h:33
LayerVector
Definition: layervector.h:26
LegacyBackupElement
Definition: legacybackupelement.h:29
MoveKeyFramesCommand
Definition: undoredocommand.h:102
Object
Definition: object.h:42
SoundClip
Definition: soundclip.h:27
Status
Definition: pencilerror.h:40
TransformCommand
Definition: undoredocommand.h:165
UndoRedoManager::hasUnsavedChanges
bool hasUnsavedChanges() const
Checks whether there are unsaved changes.
Definition: undoredomanager.cpp:159
UndoRedoManager::sanitizeLegacyBackupElementsAfterLayerDeletion
void sanitizeLegacyBackupElementsAfterLayerDeletion(int layerIndex)
Restores integrity of the backup elements after a layer has been deleted.
Definition: undoredomanager.cpp:570
UndoRedoManager::addUserState
void addUserState(SAVESTATE_ID SaveStateId, const UserSaveState &userState)
Adds userState to the saveState found at SaveStateId If no record is found matching the id,...
Definition: undoredomanager.cpp:274
UndoRedoManager::record
void record(SAVESTATE_ID SaveStateId, const QString &description)
Records the given save state.
Definition: undoredomanager.cpp:96
UndoRedoManager::createState
SAVESTATE_ID createState(UndoRedoRecordType recordType)
Prepares and returns an save state with common data.
Definition: undoredomanager.cpp:261
UndoRedoManager::clearStack
void clearStack()
Clears the undo stack.
Definition: undoredomanager.cpp:397
VectorImage
Definition: vectorimage.h:32
VectorReplaceCommand
Definition: undoredocommand.h:144
QAction
QAction::setEnabled
void setEnabled(bool)
QAction::setIcon
void setIcon(const QIcon &icon)
QAction::setIconText
void setIconText(const QString &text)
QAction::setDisabled
void setDisabled(bool b)
QAction::setText
void setText(const QString &text)
QAction::triggered
void triggered(bool checked)
QIcon
QList::append
void append(const T &value)
QList::at
const T & at(int i) const const
QList::count
int count(const T &value) const const
QList::empty
bool empty() const const
QList::isEmpty
bool isEmpty() const const
QList::size
int size() const const
QList::takeAt
T takeAt(int i)
QList::takeFirst
T takeFirst()
QList::takeLast
T takeLast()
QMap::contains
bool contains(const Key &key) const const
QMap::take
T take(const Key &key)
QObject
QObject::connect
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject::parent
QObject * parent() const const
QObject::tr
QString tr(const char *sourceText, const char *disambiguation, int n)
QString
QString::append
QString & append(QChar ch)
QString::isEmpty
bool isEmpty() const const
QUndoCommand
QUndoStack::clear
void clear()
QUndoStack::createRedoAction
QAction * createRedoAction(QObject *parent, const QString &prefix) const const
QUndoStack::createUndoAction
QAction * createUndoAction(QObject *parent, const QString &prefix) const const
QUndoStack::isClean
bool isClean() const const
QUndoStack::push
void push(QUndoCommand *cmd)
QUndoStack::setClean
void setClean()
QUndoStack::setUndoLimit
void setUndoLimit(int limit)
MoveFramesSaveState
Definition: undoredomanager.h:81
SelectionSaveState
Definition: undoredomanager.h:55
UndoSaveState
This is the main undo/redo state structure which is meant to populate whatever states that needs to b...
Definition: undoredomanager.h:106
UserSaveState
Use this struct to store user related data that will later be added to the backup This struct is mean...
Definition: undoredomanager.h:100
Generated on Mon May 4 2026 07:50:47 for Pencil2D by doxygen 1.9.6 based on revision 3ed50cdd696e72315cedf30508d3572536c3876e