diff --git a/app/src/timelinecells.cpp b/app/src/timelinecells.cpp index 733e3decfc..8cb57985fa 100644 --- a/app/src/timelinecells.cpp +++ b/app/src/timelinecells.cpp @@ -33,6 +33,7 @@ GNU General Public License for more details. #include "object.h" #include "playbackmanager.h" #include "preferencemanager.h" +#include "undoredomanager.h" #include "timeline.h" #include "cameracontextmenu.h" @@ -1065,8 +1066,15 @@ void TimeLineCells::mouseReleaseEvent(QMouseEvent* event) int posUnderCursor = getFrameNumber(mMousePressX); int offset = frameNumber - posUnderCursor; - currentLayer->moveSelectedFrames(offset); + if (currentLayer->canMoveSelectedFramesToOffset(offset)) { + SAVESTATE_ID saveStateId = mEditor->undoRedo()->createState(UndoRedoRecordType::KEYFRAME_MOVE); + UserSaveState userState; + userState.moveFramesState = MoveFramesSaveState(offset, currentLayer->selectedKeyFramesPositions()); + mEditor->undoRedo()->addUserState(saveStateId, userState); + currentLayer->moveSelectedFrames(offset); + mEditor->undoRedo()->record(saveStateId, tr("Move Frames")); + } mEditor->layers()->notifyAnimationLengthChanged(); emit mEditor->framesModified(); updateContent(); diff --git a/core_lib/src/interface/editor.cpp b/core_lib/src/interface/editor.cpp index e2e5402e9f..696b041904 100644 --- a/core_lib/src/interface/editor.cpp +++ b/core_lib/src/interface/editor.cpp @@ -934,9 +934,15 @@ KeyFrame* Editor::addKeyFrame(const int layerNumber, int frameIndex) const bool ok = layer->addNewKeyFrameAt(frameIndex); Q_ASSERT(ok); // We already ensured that there is no keyframe at frameIndex, so this should always succeed scrubTo(frameIndex); // currentFrameChanged() emit inside. + + SAVESTATE_ID saveStateId = undoRedo()->createState(UndoRedoRecordType::KEYFRAME_ADD); emit frameModified(frameIndex); layers()->notifyAnimationLengthChanged(); - return layer->getKeyFrameAt(frameIndex); + KeyFrame* newFrame = layer->getKeyFrameAt(frameIndex); + + undoRedo()->record(saveStateId, tr("Add frame")); + + return newFrame; } void Editor::removeKey() @@ -956,12 +962,15 @@ void Editor::removeKey() return; } + SAVESTATE_ID saveStateId = undoRedo()->createState(UndoRedoRecordType::KEYFRAME_REMOVE); backup(tr("Remove frame")); deselectAll(); layer->removeKeyFrame(currentFrame()); layers()->notifyAnimationLengthChanged(); emit layers()->currentLayerChanged(layers()->currentLayerIndex()); // trigger timeline repaint. + + undoRedo()->record(saveStateId, tr("Remove frame")); } void Editor::scrubNextKeyFrame() diff --git a/core_lib/src/interface/undoredocommand.cpp b/core_lib/src/interface/undoredocommand.cpp index 8439fd8c54..27d4a34d6b 100644 --- a/core_lib/src/interface/undoredocommand.cpp +++ b/core_lib/src/interface/undoredocommand.cpp @@ -24,6 +24,7 @@ GNU General Public License for more details. #include "layersound.h" #include "layerbitmap.h" #include "layervector.h" +#include "layer.h" #include "editor.h" #include "undoredocommand.h" @@ -38,6 +39,174 @@ UndoRedoCommand::~UndoRedoCommand() { } +KeyFrameRemoveCommand::KeyFrameRemoveCommand(const KeyFrame* undoKeyFrame, + int undoLayerId, + const QString &description, + Editor *editor, + QUndoCommand *parent) : UndoRedoCommand(editor, parent) +{ + this->undoKeyFrame = undoKeyFrame->clone(); + this->undoLayerId = undoLayerId; + + this->redoLayerId = editor->layers()->currentLayer()->id(); + this->redoPosition = editor->currentFrame(); + + setText(description); +} + +KeyFrameRemoveCommand::~KeyFrameRemoveCommand() +{ + delete undoKeyFrame; +} + +void KeyFrameRemoveCommand::undo() +{ + Layer* layer = editor()->layers()->findLayerById(undoLayerId); + if (layer == nullptr) { + // Until we support layer deletion recovery, we mark the command as + // obsolete as soon as it's been + return setObsolete(true); + } + + UndoRedoCommand::undo(); + + layer->addKeyFrame(undoKeyFrame->pos(), undoKeyFrame->clone()); + + emit editor()->frameModified(undoKeyFrame->pos()); + editor()->layers()->notifyAnimationLengthChanged(); + editor()->scrubTo(undoKeyFrame->pos()); +} + +void KeyFrameRemoveCommand::redo() +{ + Layer* layer = editor()->layers()->findLayerById(redoLayerId); + if (layer == nullptr) { + // Until we support layer deletion recovery, we mark the command as + // obsolete as soon as it's been + return setObsolete(true); + } + + UndoRedoCommand::redo(); + + if (isFirstRedo()) { setFirstRedo(false); return; } + + layer->removeKeyFrame(redoPosition); + + emit editor()->frameModified(redoPosition); + editor()->layers()->notifyAnimationLengthChanged(); + editor()->scrubTo(redoPosition); +} + +KeyFrameAddCommand::KeyFrameAddCommand(int undoPosition, + int undoLayerId, + const QString &description, + Editor *editor, + QUndoCommand *parent) + : UndoRedoCommand(editor, parent) +{ + this->undoPosition = undoPosition; + this->undoLayerId = undoLayerId; + + this->redoLayerId = editor->layers()->currentLayer()->id(); + this->redoPosition = editor->currentFrame(); + + setText(description); +} + +KeyFrameAddCommand::~KeyFrameAddCommand() +{ +} + +void KeyFrameAddCommand::undo() +{ + Layer* layer = editor()->layers()->findLayerById(undoLayerId); + if (!layer) { + return setObsolete(true); + } + + UndoRedoCommand::undo(); + + layer->removeKeyFrame(undoPosition); + + emit editor()->frameModified(undoPosition); + editor()->layers()->notifyAnimationLengthChanged(); + editor()->layers()->setCurrentLayer(layer); + editor()->scrubTo(undoPosition); +} + +void KeyFrameAddCommand::redo() +{ + Layer* layer = editor()->layers()->findLayerById(redoLayerId); + if (!layer) { + return setObsolete(true); + } + + UndoRedoCommand::redo(); + + // Ignore automatic redo when added to undo stack + if (isFirstRedo()) { setFirstRedo(false); return; } + + layer->addNewKeyFrameAt(redoPosition); + + emit editor()->frameModified(redoPosition); + editor()->layers()->notifyAnimationLengthChanged(); + editor()->layers()->setCurrentLayer(layer); + editor()->scrubTo(redoPosition); +} + +MoveKeyFramesCommand::MoveKeyFramesCommand(int offset, + QList listOfPositions, + int undoLayerId, + const QString& description, + Editor* editor, + QUndoCommand *parent) + : UndoRedoCommand(editor, parent) +{ + this->frameOffset = offset; + this->positions = listOfPositions; + + this->undoLayerId = undoLayerId; + this->redoLayerId = editor->layers()->currentLayer()->id(); + + setText(description); +} + +void MoveKeyFramesCommand::undo() +{ + Layer* undoLayer = editor()->layers()->findLayerById(undoLayerId); + + if (!undoLayer) { + return setObsolete(true); + } + + UndoRedoCommand::undo(); + + for (int position : qAsConst(positions)) { + undoLayer->moveKeyFrame(position + frameOffset, -frameOffset); + } + + emit editor()->framesModified(); +} + +void MoveKeyFramesCommand::redo() +{ + Layer* redoLayer = editor()->layers()->findLayerById(redoLayerId); + + if (!redoLayer) { + return setObsolete(true); + } + + UndoRedoCommand::redo(); + + // Ignore automatic redo when added to undo stack + if (isFirstRedo()) { setFirstRedo(false); return; } + + for (int position : qAsConst(positions)) { + redoLayer->moveKeyFrame(position, frameOffset); + } + + emit editor()->framesModified(); +} BitmapReplaceCommand::BitmapReplaceCommand(const BitmapImage* undoBitmap, const int undoLayerId, const QString& description, @@ -58,9 +227,13 @@ BitmapReplaceCommand::BitmapReplaceCommand(const BitmapImage* undoBitmap, void BitmapReplaceCommand::undo() { - QUndoCommand::undo(); - Layer* layer = editor()->layers()->findLayerById(undoLayerId); + if (!layer) { + return setObsolete(true); + } + + UndoRedoCommand::undo(); + static_cast(layer)->replaceKeyFrame(&undoBitmap); editor()->scrubTo(undoBitmap.pos()); @@ -68,12 +241,16 @@ void BitmapReplaceCommand::undo() void BitmapReplaceCommand::redo() { - QUndoCommand::redo(); + Layer* layer = editor()->layers()->findLayerById(redoLayerId); + if (!layer) { + return setObsolete(true); + } + + UndoRedoCommand::redo(); // Ignore automatic redo when added to undo stack if (isFirstRedo()) { setFirstRedo(false); return; } - Layer* layer = editor()->layers()->findLayerById(redoLayerId); static_cast(layer)->replaceKeyFrame(&redoBitmap); editor()->scrubTo(redoBitmap.pos()); @@ -98,9 +275,12 @@ VectorReplaceCommand::VectorReplaceCommand(const VectorImage* undoVector, void VectorReplaceCommand::undo() { - QUndoCommand::undo(); - Layer* layer = editor()->layers()->findLayerById(undoLayerId); + if (!layer) { + return setObsolete(true); + } + + UndoRedoCommand::undo(); static_cast(layer)->replaceKeyFrame(&undoVector); @@ -109,13 +289,16 @@ void VectorReplaceCommand::undo() void VectorReplaceCommand::redo() { - QUndoCommand::redo(); + Layer* layer = editor()->layers()->findLayerById(redoLayerId); + if (!layer) { + return setObsolete(true); + } + + UndoRedoCommand::redo(); // Ignore automatic redo when added to undo stack if (isFirstRedo()) { setFirstRedo(false); return; } - Layer* layer = editor()->layers()->findLayerById(redoLayerId); - static_cast(layer)->replaceKeyFrame(&redoVector); editor()->scrubTo(redoVector.pos()); @@ -154,6 +337,7 @@ TransformCommand::TransformCommand(const QRectF& undoSelectionRect, void TransformCommand::undo() { + UndoRedoCommand::undo(); apply(undoSelectionRect, undoTranslation, undoRotationAngle, @@ -165,6 +349,8 @@ void TransformCommand::undo() void TransformCommand::redo() { + UndoRedoCommand::redo(); + // Ignore automatic redo when added to undo stack if (isFirstRedo()) { setFirstRedo(false); return; } diff --git a/core_lib/src/interface/undoredocommand.h b/core_lib/src/interface/undoredocommand.h index 05c4b4766c..26b4fcef74 100644 --- a/core_lib/src/interface/undoredocommand.h +++ b/core_lib/src/interface/undoredocommand.h @@ -24,13 +24,15 @@ GNU General Public License for more details. #include "bitmapimage.h" #include "vectorimage.h" +#include "soundclip.h" +#include "camera.h" +#include "layer.h" class Editor; class UndoRedoManager; class PreferenceManager; class SoundClip; class Camera; -class Layer; class KeyFrame; class TransformCommand; @@ -41,7 +43,7 @@ class UndoRedoCommand : public QUndoCommand ~UndoRedoCommand() override; protected: - Editor* editor() { return mEditor; } + Editor* editor() const { return mEditor; } bool isFirstRedo() const { return mIsFirstRedo; } void setFirstRedo(const bool state) { mIsFirstRedo = state; } @@ -51,6 +53,72 @@ class UndoRedoCommand : public QUndoCommand bool mIsFirstRedo = true; }; +class KeyFrameRemoveCommand : public UndoRedoCommand +{ +public: + KeyFrameRemoveCommand(const KeyFrame* undoKeyFrame, + int undoLayerId, + const QString& description, + Editor* editor, + QUndoCommand* parent = nullptr + ); + ~KeyFrameRemoveCommand(); + + void undo() override; + void redo() override; + +private: + + int undoLayerId = 0; + int redoLayerId = 0; + + KeyFrame* undoKeyFrame = nullptr; + int redoPosition = 0; +}; + +class KeyFrameAddCommand : public UndoRedoCommand +{ +public: + KeyFrameAddCommand(int undoPosition, + int undoLayerId, + const QString& description, + Editor* editor, + QUndoCommand* parent = nullptr); + ~KeyFrameAddCommand(); + + void undo() override; + void redo() override; + +private: + + int undoLayerId = 0; + int redoLayerId = 0; + + int undoPosition = 0; + int redoPosition = 0; +}; + +class MoveKeyFramesCommand : public UndoRedoCommand +{ +public: + MoveKeyFramesCommand(int offset, + QList listOfPositions, + int undoLayerId, + const QString& description, + Editor* editor, + QUndoCommand* parent = nullptr); + + void undo() override; + void redo() override; + +private: + int undoLayerId = 0; + int redoLayerId = 0; + + int frameOffset = 0; + QList positions; +}; + class BitmapReplaceCommand : public UndoRedoCommand { diff --git a/core_lib/src/managers/layermanager.cpp b/core_lib/src/managers/layermanager.cpp index efc55f0910..1fe165f963 100644 --- a/core_lib/src/managers/layermanager.cpp +++ b/core_lib/src/managers/layermanager.cpp @@ -100,7 +100,7 @@ Layer* LayerManager::findLayerByName(QString sName, Layer::LAYER_TYPE type) return object()->findLayerByName(sName, type); } -Layer* LayerManager::findLayerById(int layerId) +Layer* LayerManager::findLayerById(int layerId) const { return object()->findLayerById(layerId); } diff --git a/core_lib/src/managers/layermanager.h b/core_lib/src/managers/layermanager.h index 254ebedb7d..01cc52beb1 100644 --- a/core_lib/src/managers/layermanager.h +++ b/core_lib/src/managers/layermanager.h @@ -44,7 +44,7 @@ class LayerManager : public BaseManager Layer* getLayer(int index); LayerCamera* getCameraLayerBelow(int layerIndex) const; Layer* findLayerByName(QString sName, Layer::LAYER_TYPE type = Layer::UNDEFINED); - Layer* findLayerById(int layerId); + Layer* findLayerById(int layerId) const; Layer* getLastCameraLayer(); int currentLayerIndex(); void setCurrentLayer(int nIndex); diff --git a/core_lib/src/managers/undoredomanager.cpp b/core_lib/src/managers/undoredomanager.cpp index 66e2a9ef2c..507ec0fc6b 100644 --- a/core_lib/src/managers/undoredomanager.cpp +++ b/core_lib/src/managers/undoredomanager.cpp @@ -53,6 +53,7 @@ UndoRedoManager::~UndoRedoManager() { clearStack(); } + clearSaveStates(); qDebug() << "UndoRedoManager: destroyed"; } @@ -92,20 +93,35 @@ Status UndoRedoManager::save(Object* /*o*/) return Status::OK; } -void UndoRedoManager::record(const UndoSaveState*& undoState, const QString& description) +void UndoRedoManager::record(SAVESTATE_ID saveStateId, const QString& description) { - if (!mNewBackupSystemEnabled) { + if (!mSaveStates.contains(saveStateId)) { return; } - if (!undoState) { + UndoSaveState* saveState = mSaveStates.take(saveStateId); + + if (!mNewBackupSystemEnabled && saveState) { + clearState(saveState); return; } - switch (undoState->recordType) + switch (saveState->recordType) { case UndoRedoRecordType::KEYFRAME_MODIFY: { - replaceKeyFrame(*undoState, description); + replaceKeyFrame(*saveState, description); + break; + } + case UndoRedoRecordType::KEYFRAME_REMOVE: { + removeKeyFrame(*saveState, description); + break; + } + case UndoRedoRecordType::KEYFRAME_ADD: { + addKeyFrame(*saveState, description); + break; + } + case UndoRedoRecordType::KEYFRAME_MOVE: { + moveKeyFrames(*saveState, description); break; } default: { @@ -116,10 +132,23 @@ void UndoRedoManager::record(const UndoSaveState*& undoState, const QString& des } } - // The save state has now been used and should be invalidated so we can't use it again. - delete undoState; - undoState = nullptr; + clearState(saveState); +} + +void UndoRedoManager::clearState(UndoSaveState*& state) +{ + if (state) { + delete state; + state = nullptr; + } +} + +void UndoRedoManager::clearSaveStates() +{ + if (mSaveStates.isEmpty()) { return; } + + mSaveStates.clear(); } bool UndoRedoManager::hasUnsavedChanges() const @@ -141,6 +170,24 @@ void UndoRedoManager::pushCommand(QUndoCommand* command) emit didUpdateUndoStack(); } +void UndoRedoManager::removeKeyFrame(const UndoSaveState& undoState, const QString& description) +{ + KeyFrameRemoveCommand* element = new KeyFrameRemoveCommand(undoState.keyframe.get(), + undoState.layerId, + description, + editor()); + pushCommand(element); +} + +void UndoRedoManager::addKeyFrame(const UndoSaveState& undoState, const QString& description) +{ + KeyFrameAddCommand* element = new KeyFrameAddCommand(undoState.currentFrameIndex, + undoState.layerId, + description, + editor()); + pushCommand(element); +} + void UndoRedoManager::replaceKeyFrame(const UndoSaveState& undoState, const QString& description) { if (undoState.layerType == Layer::BITMAP) { @@ -152,6 +199,16 @@ void UndoRedoManager::replaceKeyFrame(const UndoSaveState& undoState, const QStr } } +void UndoRedoManager::moveKeyFrames(const UndoSaveState& undoState, const QString& description) +{ + const MoveFramesSaveState& state = undoState.userState.moveFramesState; + MoveKeyFramesCommand* element = new MoveKeyFramesCommand(state.offset, + state.positions, + undoState.layerId, + description, + editor()); + pushCommand(element); +} void UndoRedoManager::replaceBitmap(const UndoSaveState& undoState, const QString& description) { @@ -161,13 +218,13 @@ void UndoRedoManager::replaceBitmap(const UndoSaveState& undoState, const QStrin description, editor()); - const SelectionSaveState* selectionState = undoState.selectionState.get(); - new TransformCommand(selectionState->bounds, - selectionState->translation, - selectionState->rotationAngle, - selectionState->scaleX, - selectionState->scaleY, - selectionState->anchor, + const SelectionSaveState& selectionState = undoState.selectionState; + new TransformCommand(selectionState.bounds, + selectionState.translation, + selectionState.rotationAngle, + selectionState.scaleX, + selectionState.scaleY, + selectionState.anchor, true, // roundPixels description, editor(), element); @@ -183,54 +240,54 @@ void UndoRedoManager::replaceVector(const UndoSaveState& undoState, const QStrin description, editor()); - const SelectionSaveState* selectionState = undoState.selectionState.get(); - new TransformCommand(selectionState->bounds, - selectionState->translation, - selectionState->rotationAngle, - selectionState->scaleX, - selectionState->scaleY, - selectionState->anchor, + const SelectionSaveState& selectionState = undoState.selectionState; + new TransformCommand(selectionState.bounds, + selectionState.translation, + selectionState.rotationAngle, + selectionState.scaleX, + selectionState.scaleY, + selectionState.anchor, false, // Round pixels description, editor(), element); pushCommand(element); } -const UndoSaveState* UndoRedoManager::state(UndoRedoRecordType recordType) const +SAVESTATE_ID UndoRedoManager::createState(UndoRedoRecordType recordType) { - if (!mNewBackupSystemEnabled) { - return nullptr; - } + int saveStateId = mSaveStateId; + UndoSaveState* state = new UndoSaveState(); + state->recordType = recordType; + initCommonKeyFrameState(state); - switch (recordType) - { - case UndoRedoRecordType::KEYFRAME_MODIFY: { - return savedKeyFrameState(); - default: - return nullptr; - } - } + mSaveStates[saveStateId] = state; + mSaveStateId += 1; + + return saveStateId; } -const UndoSaveState* UndoRedoManager::savedKeyFrameState() const +void UndoRedoManager::addUserState(SAVESTATE_ID saveStateId, const UserSaveState& userState) { - UndoSaveState* undoSaveState = new UndoSaveState(); - undoSaveState->recordType = UndoRedoRecordType::KEYFRAME_MODIFY; + if (!mSaveStates.contains(saveStateId)) { return; } + mSaveStates[saveStateId]->userState = userState; +} +void UndoRedoManager::initCommonKeyFrameState(UndoSaveState* undoSaveState) const +{ const Layer* layer = editor()->layers()->currentLayer(); undoSaveState->layerType = layer->type(); undoSaveState->layerId = layer->id(); + undoSaveState->currentFrameIndex = editor()->currentFrame(); if (layer->type() == Layer::BITMAP || layer->type() == Layer::VECTOR) { auto selectMan = editor()->select(); - undoSaveState->selectionState = std::unique_ptr( new SelectionSaveState( + undoSaveState->selectionState = SelectionSaveState( selectMan->mySelectionRect(), selectMan->myRotation(), selectMan->myScaleX(), selectMan->myScaleY(), selectMan->myTranslation(), - selectMan->currentTransformAnchor()) - ); + selectMan->currentTransformAnchor()); } const int frameIndex = editor()->currentFrame(); @@ -242,8 +299,6 @@ const UndoSaveState* UndoRedoManager::savedKeyFrameState() const { undoSaveState->keyframe = std::unique_ptr(layer->getKeyFrameWhichCovers(frameIndex)->clone()); } - - return undoSaveState; } QAction* UndoRedoManager::createUndoAction(QObject* parent, const QIcon& icon) diff --git a/core_lib/src/managers/undoredomanager.h b/core_lib/src/managers/undoredomanager.h index 84f8d951d1..9d49523a45 100644 --- a/core_lib/src/managers/undoredomanager.h +++ b/core_lib/src/managers/undoredomanager.h @@ -26,6 +26,7 @@ GNU General Public License for more details. #include #include +#include class QAction; class QUndoCommand; @@ -38,13 +39,14 @@ class KeyFrame; class LegacyBackupElement; class UndoRedoCommand; +typedef int SAVESTATE_ID; + /// The undo/redo type which correspond to what is being recorded enum class UndoRedoRecordType { KEYFRAME_MODIFY, // Any modification that involve a keyframe - - // Possible future actions - // KEYFRAME_REMOVE, // Removing a keyframe - // KEYFRAME_ADD, // Adding a keyframe + KEYFRAME_REMOVE, // Removing a keyframe + KEYFRAME_ADD, // Adding a keyframe + KEYFRAME_MOVE, // SCRUB_LAYER, // Scrubbing layer // SCRUB_KEYFRAME, // Scrubbing keyframe INVALID @@ -52,6 +54,7 @@ enum class UndoRedoRecordType { struct SelectionSaveState { + SelectionSaveState() { } SelectionSaveState(const QRectF& rect, const qreal rotationAngle, const qreal scaleX, @@ -75,16 +78,49 @@ struct SelectionSaveState { QPointF anchor; }; +struct MoveFramesSaveState { + + MoveFramesSaveState() { } + MoveFramesSaveState(int offset, + const QList& positions) + { + this->offset = offset; + this->positions = positions; + } + + int offset = 0; + QList positions; +}; + +/// Use this struct to store user related data that will later be added to the backup +/// This struct is meant to be safely shared and stored temporarily, +/// as such don't store ptrs here... +/// All data stored in here should be based on ZII (zero is initialization) principle +/// Only store what you need. +struct UserSaveState { + MoveFramesSaveState moveFramesState = {}; +}; + /// This is the main undo/redo state structure which is meant to populate /// whatever states that needs to be stored temporarily. struct UndoSaveState { - int layerId = 0; - Layer::LAYER_TYPE layerType = Layer::UNDEFINED; - std::unique_ptr keyframe = nullptr; - std::unique_ptr selectionState = nullptr; + ~UndoSaveState() + { + if (recordType != UndoRedoRecordType::INVALID) { + keyframe.reset(); + } + } + // Common data UndoRedoRecordType recordType = UndoRedoRecordType::INVALID; + int layerId = 0; + int currentFrameIndex = 0; + Layer::LAYER_TYPE layerType = Layer::UNDEFINED; + std::unique_ptr keyframe; + SelectionSaveState selectionState = {}; + + UserSaveState userState = {}; }; class UndoRedoManager : public BaseManager @@ -101,19 +137,26 @@ class UndoRedoManager : public BaseManager /** Records the given save state. * The input save state is cleaned up and set to nullptr after use. - * @param undoState The state to record. + * @param SaveStateId The state that will be fetched and recorded based on the input SaveStateId. * @param description The description that will bound to the undo/redo action. */ - void record(const UndoSaveState*& undoState, const QString& description); + void record(SAVESTATE_ID SaveStateId, const QString& description); /** Checks whether there are unsaved changes. * @return true if there are unsaved changes, otherwise false */ bool hasUnsavedChanges() const; - /** Prepares and returns a save state with the given scope. - * @return A struct with state of the given record type */ - const UndoSaveState* state(UndoRedoRecordType recordType) const; + /** Prepares and returns an save state with common data + * @return A UndoSaveState struct with common keyframe data */ + SAVESTATE_ID createState(UndoRedoRecordType recordType); + + /** Adds userState to the saveState found at SaveStateId + * If no record is found matching the id, nothing happens. + * @param SaveStateId The id used to fetch the saveState + * @param userState The data to be inserted onto on the saveState + */ + void addUserState(SAVESTATE_ID SaveStateId, const UserSaveState& userState); QAction* createUndoAction(QObject* parent, const QIcon& icon); QAction* createRedoAction(QObject* parent, const QIcon& icon); @@ -156,15 +199,24 @@ class UndoRedoManager : public BaseManager void replaceBitmap(const UndoSaveState& undoState, const QString& description); void replaceVector(const UndoSaveState& undoState, const QString& description); - const UndoSaveState* savedKeyFrameState() const; + void addKeyFrame(const UndoSaveState& undoState, const QString& description); + void removeKeyFrame(const UndoSaveState& undoState, const QString& description); + void moveKeyFrames(const UndoSaveState& undoState, const QString& description); + + void initCommonKeyFrameState(UndoSaveState* undoSaveState) const; void pushCommand(QUndoCommand* command); + void clearState(UndoSaveState*& state); + void clearSaveStates(); + void legacyUndo(); void legacyRedo(); QUndoStack mUndoStack; + QMap mSaveStates; + // Legacy system int mLegacyBackupIndex = -1; LegacyBackupElement* mLegacyBackupAtSave = nullptr; @@ -173,6 +225,8 @@ class UndoRedoManager : public BaseManager int mLegacyLastModifiedLayer = -1; int mLegacyLastModifiedFrame = -1; + SAVESTATE_ID mSaveStateId = 1; + bool mNewBackupSystemEnabled = false; }; diff --git a/core_lib/src/tool/movetool.cpp b/core_lib/src/tool/movetool.cpp index 690b4fcd09..0c39f45252 100644 --- a/core_lib/src/tool/movetool.cpp +++ b/core_lib/src/tool/movetool.cpp @@ -174,7 +174,7 @@ void MoveTool::pointerMoveEvent(PointerEvent* event) void MoveTool::pointerReleaseEvent(PointerEvent*) { - mEditor->undoRedo()->record(mUndoSaveState, typeName()); + mEditor->undoRedo()->record(mUndoSaveStateId, typeName()); if (mEditor->overlays()->anyOverlayEnabled()) { @@ -224,7 +224,7 @@ void MoveTool::beginInteraction(const QPointF& pos, Qt::KeyboardModifiers keyMod QRectF selectionRect = selectMan->mySelectionRect(); if (!selectionRect.isNull()) { - mUndoSaveState = mEditor->undoRedo()->state(UndoRedoRecordType::KEYFRAME_MODIFY); + mUndoSaveStateId = mEditor->undoRedo()->createState(UndoRedoRecordType::KEYFRAME_MODIFY); mEditor->backup(typeName()); } diff --git a/core_lib/src/tool/movetool.h b/core_lib/src/tool/movetool.h index 21d2b7f1a0..46f4315333 100644 --- a/core_lib/src/tool/movetool.h +++ b/core_lib/src/tool/movetool.h @@ -68,7 +68,7 @@ class MoveTool : public BaseTool MoveMode mPerspMode; QPointF mOffset; - const UndoSaveState* mUndoSaveState = nullptr; + SAVESTATE_ID mUndoSaveStateId = 0; }; #endif diff --git a/core_lib/src/tool/polylinetool.cpp b/core_lib/src/tool/polylinetool.cpp index b18539d88a..aad63255d6 100644 --- a/core_lib/src/tool/polylinetool.cpp +++ b/core_lib/src/tool/polylinetool.cpp @@ -203,11 +203,11 @@ void PolylineTool::pointerDoubleClickEvent(PointerEvent* event) // include the current point before ending the line. mPoints << getCurrentPoint(); - const UndoSaveState* saveState = mEditor->undoRedo()->state(UndoRedoRecordType::KEYFRAME_MODIFY); + SAVESTATE_ID saveStateId = mEditor->undoRedo()->createState(UndoRedoRecordType::KEYFRAME_MODIFY); mEditor->backup(typeName()); endPolyline(mPoints); - mEditor->undoRedo()->record(saveState, typeName()); + mEditor->undoRedo()->record(saveStateId, typeName()); } void PolylineTool::removeLastPolylineSegment() @@ -237,9 +237,9 @@ bool PolylineTool::keyPressEvent(QKeyEvent* event) case Qt::Key_Return: if (mPoints.size() > 0) { - const UndoSaveState* saveState = mEditor->undoRedo()->state(UndoRedoRecordType::KEYFRAME_MODIFY); + SAVESTATE_ID saveStateId = mEditor->undoRedo()->createState(UndoRedoRecordType::KEYFRAME_MODIFY); endPolyline(mPoints); - mEditor->undoRedo()->record(saveState, typeName()); + mEditor->undoRedo()->record(saveStateId, typeName()); return true; } break; diff --git a/core_lib/src/tool/selecttool.cpp b/core_lib/src/tool/selecttool.cpp index 1d6becadb6..09188912fd 100644 --- a/core_lib/src/tool/selecttool.cpp +++ b/core_lib/src/tool/selecttool.cpp @@ -132,7 +132,7 @@ void SelectTool::pointerPressEvent(PointerEvent* event) if (event->button() != Qt::LeftButton) { return; } auto selectMan = mEditor->select(); - mUndoState = mEditor->undoRedo()->state(UndoRedoRecordType::KEYFRAME_MODIFY); + mUndoStateId = mEditor->undoRedo()->createState(UndoRedoRecordType::KEYFRAME_MODIFY); mPressPoint = event->canvasPos(); selectMan->setMoveModeForAnchorInRange(mPressPoint); @@ -193,7 +193,7 @@ void SelectTool::pointerReleaseEvent(PointerEvent* event) keepSelection(currentLayer); } - mEditor->undoRedo()->record(mUndoState, typeName()); + mEditor->undoRedo()->record(mUndoStateId, typeName()); mStartMoveMode = MoveMode::NONE; mSelectionRect = mEditor->select()->mapToSelection(mEditor->select()->mySelectionRect()).boundingRect(); diff --git a/core_lib/src/tool/selecttool.h b/core_lib/src/tool/selecttool.h index 83eeab57c3..d3dc77a3e1 100644 --- a/core_lib/src/tool/selecttool.h +++ b/core_lib/src/tool/selecttool.h @@ -70,7 +70,7 @@ class SelectTool : public BaseTool QPixmap mCursorPixmap = QPixmap(24, 24); - const UndoSaveState* mUndoState = nullptr; + SAVESTATE_ID mUndoStateId = 0; }; #endif diff --git a/core_lib/src/tool/stroketool.cpp b/core_lib/src/tool/stroketool.cpp index ee7b509907..d7b2a6f073 100644 --- a/core_lib/src/tool/stroketool.cpp +++ b/core_lib/src/tool/stroketool.cpp @@ -140,7 +140,7 @@ void StrokeTool::startStroke(PointerEvent::InputType inputType) mStrokePressures << mInterpolator.getPressure(); mCurrentInputType = inputType; - mUndoSaveState = mEditor->undoRedo()->state(UndoRedoRecordType::KEYFRAME_MODIFY); + mUndoSaveStateId = mEditor->undoRedo()->createState(UndoRedoRecordType::KEYFRAME_MODIFY); disableCoalescing(); } @@ -181,7 +181,7 @@ void StrokeTool::endStroke() mEditor->setModified(mEditor->currentLayerIndex(), mEditor->currentFrame()); mScribbleArea->endStroke(); - mEditor->undoRedo()->record(mUndoSaveState, typeName()); + mEditor->undoRedo()->record(mUndoSaveStateId, typeName()); } void StrokeTool::drawStroke() diff --git a/core_lib/src/tool/stroketool.h b/core_lib/src/tool/stroketool.h index 7b1b2fbe90..5ed4448b22 100644 --- a/core_lib/src/tool/stroketool.h +++ b/core_lib/src/tool/stroketool.h @@ -112,7 +112,7 @@ public slots: StrokeInterpolator mInterpolator; - const UndoSaveState* mUndoSaveState = nullptr; + SAVESTATE_ID mUndoSaveStateId = 0; }; #endif // STROKETOOL_H