diff --git a/app/common/xmlutils.cpp b/app/common/xmlutils.cpp index f068d230a..e243ffcf7 100644 --- a/app/common/xmlutils.cpp +++ b/app/common/xmlutils.cpp @@ -26,14 +26,14 @@ namespace olive { -void XMLConnectNodes(const XMLNodeData &xml_node_data, QUndoCommand *command) +void XMLConnectNodes(const XMLNodeData &xml_node_data, MultiUndoCommand *command) { foreach (const XMLNodeData::SerializedConnection& con, xml_node_data.desired_connections) { Node* out = xml_node_data.node_ptrs.value(con.output); if (out) { if (command) { - new NodeEdgeAddCommand(out, con.input, con.element, command); + command->add_child(new NodeEdgeAddCommand(out, con.input, con.element)); } else { Node::ConnectEdge(out, con.input, con.element); } diff --git a/app/common/xmlutils.h b/app/common/xmlutils.h index d29032320..ea8f43475 100644 --- a/app/common/xmlutils.h +++ b/app/common/xmlutils.h @@ -21,10 +21,10 @@ #ifndef XMLREADLOOP_H #define XMLREADLOOP_H -#include #include #include "project/item/footage/stream.h" +#include "undo/undocommand.h" namespace olive { @@ -63,7 +63,7 @@ struct XMLNodeData { }; -void XMLConnectNodes(const XMLNodeData& xml_node_data, QUndoCommand* command = nullptr); +void XMLConnectNodes(const XMLNodeData& xml_node_data, MultiUndoCommand *command = nullptr); bool XMLReadNextStartElement(QXmlStreamReader* reader); diff --git a/app/core.cpp b/app/core.cpp index dda5e0854..293aaaf26 100644 --- a/app/core.cpp +++ b/app/core.cpp @@ -479,7 +479,7 @@ void Core::ImportTaskComplete(Task* task) { ProjectImportTask* import_task = static_cast(task); - QUndoCommand *command = import_task->GetCommand(); + MultiUndoCommand *command = import_task->GetCommand(); if (import_task->HasInvalidFiles()) { ProjectImportErrorDialog d(import_task->GetInvalidFiles(), main_window_); diff --git a/app/dialog/footageproperties/footageproperties.cpp b/app/dialog/footageproperties/footageproperties.cpp index 5f00f0514..d1e160866 100644 --- a/app/dialog/footageproperties/footageproperties.cpp +++ b/app/dialog/footageproperties/footageproperties.cpp @@ -124,21 +124,19 @@ void FootagePropertiesDialog::accept() { } } - QUndoCommand* command = new QUndoCommand(); + MultiUndoCommand* command = new MultiUndoCommand(); if (footage_->name() != footage_name_field_->text()) { - new FootageChangeCommand(footage_, - footage_name_field_->text(), - command); + command->add_child(new FootageChangeCommand(footage_, + footage_name_field_->text())); } for (int i=0;istreams().size();i++) { bool stream_enabled = (track_list->item(i)->checkState() == Qt::Checked); if (footage_->stream(i)->enabled() != stream_enabled) { - new StreamEnableChangeCommand(footage_->stream(i), - stream_enabled, - command); + command->add_child(new StreamEnableChangeCommand(footage_->stream(i), + stream_enabled)); } } @@ -151,8 +149,7 @@ void FootagePropertiesDialog::accept() { QDialog::accept(); } -FootagePropertiesDialog::FootageChangeCommand::FootageChangeCommand(Footage *footage, const QString &name, QUndoCommand* command) : - UndoCommand(command), +FootagePropertiesDialog::FootageChangeCommand::FootageChangeCommand(Footage *footage, const QString &name) : footage_(footage), new_name_(name) { @@ -163,20 +160,19 @@ Project *FootagePropertiesDialog::FootageChangeCommand::GetRelevantProject() con return footage_->project(); } -void FootagePropertiesDialog::FootageChangeCommand::redo_internal() +void FootagePropertiesDialog::FootageChangeCommand::redo() { old_name_ = footage_->name(); footage_->set_name(new_name_); } -void FootagePropertiesDialog::FootageChangeCommand::undo_internal() +void FootagePropertiesDialog::FootageChangeCommand::undo() { footage_->set_name(old_name_); } -FootagePropertiesDialog::StreamEnableChangeCommand::StreamEnableChangeCommand(Stream *stream, bool enabled, QUndoCommand *command) : - UndoCommand(command), +FootagePropertiesDialog::StreamEnableChangeCommand::StreamEnableChangeCommand(Stream *stream, bool enabled) : stream_(stream), old_enabled_(stream->enabled()), new_enabled_(enabled) @@ -188,12 +184,12 @@ Project *FootagePropertiesDialog::StreamEnableChangeCommand::GetRelevantProject( return stream_->footage()->project(); } -void FootagePropertiesDialog::StreamEnableChangeCommand::redo_internal() +void FootagePropertiesDialog::StreamEnableChangeCommand::redo() { stream_->set_enabled(new_enabled_); } -void FootagePropertiesDialog::StreamEnableChangeCommand::undo_internal() +void FootagePropertiesDialog::StreamEnableChangeCommand::undo() { stream_->set_enabled(old_enabled_); } diff --git a/app/dialog/footageproperties/footageproperties.h b/app/dialog/footageproperties/footageproperties.h index b0284de74..b5b177e64 100644 --- a/app/dialog/footageproperties/footageproperties.h +++ b/app/dialog/footageproperties/footageproperties.h @@ -59,14 +59,12 @@ private: class FootageChangeCommand : public UndoCommand { public: FootageChangeCommand(Footage* footage, - const QString& name, - QUndoCommand *command = nullptr); + const QString& name); virtual Project* GetRelevantProject() const override; - protected: - virtual void redo_internal() override; - virtual void undo_internal() override; + virtual void redo() override; + virtual void undo() override; private: Footage* footage_; @@ -78,14 +76,12 @@ private: class StreamEnableChangeCommand : public UndoCommand { public: StreamEnableChangeCommand(Stream* stream, - bool enabled, - QUndoCommand* command = nullptr); + bool enabled); virtual Project* GetRelevantProject() const override; - protected: - virtual void redo_internal() override; - virtual void undo_internal() override; + virtual void redo() override; + virtual void undo() override; private: Stream* stream_; diff --git a/app/dialog/footageproperties/streamproperties/audiostreamproperties.cpp b/app/dialog/footageproperties/streamproperties/audiostreamproperties.cpp index 586826a02..b11289edf 100644 --- a/app/dialog/footageproperties/streamproperties/audiostreamproperties.cpp +++ b/app/dialog/footageproperties/streamproperties/audiostreamproperties.cpp @@ -27,7 +27,7 @@ AudioStreamProperties::AudioStreamProperties(AudioStream *stream) : { } -void AudioStreamProperties::Accept(QUndoCommand*) +void AudioStreamProperties::Accept(MultiUndoCommand*) { Q_UNUSED(stream_) } diff --git a/app/dialog/footageproperties/streamproperties/audiostreamproperties.h b/app/dialog/footageproperties/streamproperties/audiostreamproperties.h index 5997fdb6b..cc1163786 100644 --- a/app/dialog/footageproperties/streamproperties/audiostreamproperties.h +++ b/app/dialog/footageproperties/streamproperties/audiostreamproperties.h @@ -31,7 +31,7 @@ class AudioStreamProperties : public StreamProperties public: AudioStreamProperties(AudioStream* stream); - virtual void Accept(QUndoCommand* parent) override; + virtual void Accept(MultiUndoCommand* parent) override; private: AudioStream* stream_; diff --git a/app/dialog/footageproperties/streamproperties/streamproperties.h b/app/dialog/footageproperties/streamproperties/streamproperties.h index 677be9b0a..c8457216b 100644 --- a/app/dialog/footageproperties/streamproperties/streamproperties.h +++ b/app/dialog/footageproperties/streamproperties/streamproperties.h @@ -21,10 +21,10 @@ #ifndef STREAMPROPERTIES_H #define STREAMPROPERTIES_H -#include #include #include "common/define.h" +#include "undo/undocommand.h" namespace olive { @@ -33,7 +33,7 @@ class StreamProperties : public QWidget public: StreamProperties(QWidget* parent = nullptr); - virtual void Accept(QUndoCommand*){} + virtual void Accept(MultiUndoCommand*){} virtual bool SanityCheck(){return true;} diff --git a/app/dialog/footageproperties/streamproperties/videostreamproperties.cpp b/app/dialog/footageproperties/streamproperties/videostreamproperties.cpp index 0df54a1e1..69bcde5bf 100644 --- a/app/dialog/footageproperties/streamproperties/videostreamproperties.cpp +++ b/app/dialog/footageproperties/streamproperties/videostreamproperties.cpp @@ -124,7 +124,7 @@ VideoStreamProperties::VideoStreamProperties(VideoStream *stream) : } } -void VideoStreamProperties::Accept(QUndoCommand *parent) +void VideoStreamProperties::Accept(MultiUndoCommand *parent) { QString set_colorspace; @@ -137,12 +137,11 @@ void VideoStreamProperties::Accept(QUndoCommand *parent) || static_cast(video_interlace_combo_->currentIndex()) != stream_->interlacing() || pixel_aspect_combo_->GetPixelAspectRatio() != stream_->pixel_aspect_ratio()) { - new VideoStreamChangeCommand(stream_, - video_premultiply_alpha_ ? video_premultiply_alpha_->isChecked() : stream_->premultiplied_alpha(), - set_colorspace, - static_cast(video_interlace_combo_->currentIndex()), - pixel_aspect_combo_->GetPixelAspectRatio(), - parent); + parent->add_child(new VideoStreamChangeCommand(stream_, + video_premultiply_alpha_ ? video_premultiply_alpha_->isChecked() : stream_->premultiplied_alpha(), + set_colorspace, + static_cast(video_interlace_combo_->currentIndex()), + pixel_aspect_combo_->GetPixelAspectRatio())); } if (stream_->video_type() == VideoStream::kVideoTypeImageSequence) { @@ -153,11 +152,10 @@ void VideoStreamProperties::Accept(QUndoCommand *parent) if (video_stream->start_time() != imgseq_start_time_->GetValue() || video_stream->duration() != new_dur || video_stream->frame_rate() != imgseq_frame_rate_->GetFrameRate()) { - new ImageSequenceChangeCommand(video_stream, - imgseq_start_time_->GetValue(), - new_dur, - imgseq_frame_rate_->GetFrameRate(), - parent); + parent->add_child(new ImageSequenceChangeCommand(video_stream, + imgseq_start_time_->GetValue(), + new_dur, + imgseq_frame_rate_->GetFrameRate())); } } } @@ -181,9 +179,7 @@ VideoStreamProperties::VideoStreamChangeCommand::VideoStreamChangeCommand(VideoS bool premultiplied, QString colorspace, VideoParams::Interlacing interlacing, - const rational &pixel_ar, - QUndoCommand *parent) : - UndoCommand(parent), + const rational &pixel_ar) : stream_(stream), new_premultiplied_(premultiplied), new_colorspace_(colorspace), @@ -197,7 +193,7 @@ Project *VideoStreamProperties::VideoStreamChangeCommand::GetRelevantProject() c return stream_->footage()->project(); } -void VideoStreamProperties::VideoStreamChangeCommand::redo_internal() +void VideoStreamProperties::VideoStreamChangeCommand::redo() { old_premultiplied_ = stream_->premultiplied_alpha(); old_colorspace_ = stream_->colorspace(false); @@ -210,7 +206,7 @@ void VideoStreamProperties::VideoStreamChangeCommand::redo_internal() stream_->set_pixel_aspect_ratio(new_pixel_ar_); } -void VideoStreamProperties::VideoStreamChangeCommand::undo_internal() +void VideoStreamProperties::VideoStreamChangeCommand::undo() { stream_->set_premultiplied_alpha(old_premultiplied_); stream_->set_colorspace(old_colorspace_); @@ -218,8 +214,7 @@ void VideoStreamProperties::VideoStreamChangeCommand::undo_internal() stream_->set_pixel_aspect_ratio(old_pixel_ar_); } -VideoStreamProperties::ImageSequenceChangeCommand::ImageSequenceChangeCommand(VideoStream *video_stream, int64_t start_index, int64_t duration, const rational &frame_rate, QUndoCommand *parent) : - UndoCommand(parent), +VideoStreamProperties::ImageSequenceChangeCommand::ImageSequenceChangeCommand(VideoStream *video_stream, int64_t start_index, int64_t duration, const rational &frame_rate) : video_stream_(video_stream), new_start_index_(start_index), new_duration_(duration), @@ -232,7 +227,7 @@ Project *VideoStreamProperties::ImageSequenceChangeCommand::GetRelevantProject() return video_stream_->footage()->project(); } -void VideoStreamProperties::ImageSequenceChangeCommand::redo_internal() +void VideoStreamProperties::ImageSequenceChangeCommand::redo() { old_start_index_ = video_stream_->start_time(); video_stream_->set_start_time(new_start_index_); @@ -245,7 +240,7 @@ void VideoStreamProperties::ImageSequenceChangeCommand::redo_internal() video_stream_->set_timebase(new_frame_rate_.flipped()); } -void VideoStreamProperties::ImageSequenceChangeCommand::undo_internal() +void VideoStreamProperties::ImageSequenceChangeCommand::undo() { video_stream_->set_start_time(old_start_index_); video_stream_->set_duration(old_duration_); diff --git a/app/dialog/footageproperties/streamproperties/videostreamproperties.h b/app/dialog/footageproperties/streamproperties/videostreamproperties.h index 8087f1949..7ae9412bc 100644 --- a/app/dialog/footageproperties/streamproperties/videostreamproperties.h +++ b/app/dialog/footageproperties/streamproperties/videostreamproperties.h @@ -26,7 +26,6 @@ #include "project/item/footage/videostream.h" #include "streamproperties.h" -#include "undo/undocommand.h" #include "widget/slider/integerslider.h" #include "widget/standardcombos/standardcombos.h" @@ -38,7 +37,7 @@ class VideoStreamProperties : public StreamProperties public: VideoStreamProperties(VideoStream* stream); - virtual void Accept(QUndoCommand* parent) override; + virtual void Accept(MultiUndoCommand *parent) override; virtual bool SanityCheck() override; @@ -89,14 +88,12 @@ private: bool premultiplied, QString colorspace, VideoParams::Interlacing interlacing, - const rational& pixel_ar, - QUndoCommand* parent = nullptr); + const rational& pixel_ar); virtual Project* GetRelevantProject() const override; - protected: - virtual void redo_internal() override; - virtual void undo_internal() override; + virtual void redo() override; + virtual void undo() override; private: VideoStream* stream_; @@ -118,14 +115,12 @@ private: ImageSequenceChangeCommand(VideoStream* video_stream, int64_t start_index, int64_t duration, - const rational& frame_rate, - QUndoCommand* parent = nullptr); + const rational& frame_rate); virtual Project* GetRelevantProject() const override; - protected: - virtual void redo_internal() override; - virtual void undo_internal() override; + virtual void redo() override; + virtual void undo() override; private: VideoStream* video_stream_; diff --git a/app/dialog/keyframeproperties/keyframeproperties.cpp b/app/dialog/keyframeproperties/keyframeproperties.cpp index d0a71e654..4686414b6 100644 --- a/app/dialog/keyframeproperties/keyframeproperties.cpp +++ b/app/dialog/keyframeproperties/keyframeproperties.cpp @@ -194,30 +194,28 @@ KeyframePropertiesDialog::KeyframePropertiesDialog(const QVector void KeyframePropertiesDialog::accept() { - QUndoCommand* command = new QUndoCommand(); + MultiUndoCommand* command = new MultiUndoCommand(); rational new_time = Timecode::timestamp_to_time(time_slider_->GetValue(), timebase_); int new_type = type_select_->currentData().toInt(); foreach (NodeKeyframe* key, keys_) { if (time_slider_->isEnabled() && !time_slider_->IsTristate()) { - new NodeParamSetKeyframeTimeCommand(key, new_time, command); + command->add_child(new NodeParamSetKeyframeTimeCommand(key, new_time)); } if (new_type > -1) { - new KeyframeSetTypeCommand(key, static_cast(new_type), command); + command->add_child(new KeyframeSetTypeCommand(key, static_cast(new_type))); } if (bezier_group_->isEnabled()) { - new KeyframeSetBezierControlPoint(key, - NodeKeyframe::kInHandle, - QPointF(bezier_in_x_slider_->GetValue(), bezier_in_y_slider_->GetValue()), - command); + command->add_child(new KeyframeSetBezierControlPoint(key, + NodeKeyframe::kInHandle, + QPointF(bezier_in_x_slider_->GetValue(), bezier_in_y_slider_->GetValue()))); - new KeyframeSetBezierControlPoint(key, - NodeKeyframe::kOutHandle, - QPointF(bezier_out_x_slider_->GetValue(), bezier_out_y_slider_->GetValue()), - command); + command->add_child(new KeyframeSetBezierControlPoint(key, + NodeKeyframe::kOutHandle, + QPointF(bezier_out_x_slider_->GetValue(), bezier_out_y_slider_->GetValue()))); } } diff --git a/app/dialog/sequence/sequence.cpp b/app/dialog/sequence/sequence.cpp index db9e50615..ed1e823af 100644 --- a/app/dialog/sequence/sequence.cpp +++ b/app/dialog/sequence/sequence.cpp @@ -139,9 +139,7 @@ void SequenceDialog::accept() SequenceDialog::SequenceParamCommand::SequenceParamCommand(Sequence* s, const VideoParams& video_params, const AudioParams &audio_params, - const QString& name, - QUndoCommand* parent) : - UndoCommand(parent), + const QString& name) : sequence_(s), new_video_params_(video_params), new_audio_params_(audio_params), @@ -157,14 +155,14 @@ Project *SequenceDialog::SequenceParamCommand::GetRelevantProject() const return sequence_->project(); } -void SequenceDialog::SequenceParamCommand::redo_internal() +void SequenceDialog::SequenceParamCommand::redo() { sequence_->set_video_params(new_video_params_); sequence_->set_audio_params(new_audio_params_); sequence_->set_name(new_name_); } -void SequenceDialog::SequenceParamCommand::undo_internal() +void SequenceDialog::SequenceParamCommand::undo() { sequence_->set_video_params(old_video_params_); sequence_->set_audio_params(old_audio_params_); diff --git a/app/dialog/sequence/sequence.h b/app/dialog/sequence/sequence.h index 947627642..a8bbff7d5 100644 --- a/app/dialog/sequence/sequence.h +++ b/app/dialog/sequence/sequence.h @@ -38,7 +38,7 @@ namespace olive { * This dialog exposes all the parameters of a Sequence to users allowing them to set up a Sequence however they wish. * A Sequence can be sent to this dialog through the constructor. All fields will be filled using that Sequence's * parameters, allowing the user to view and edit them. Accepting the dialog will apply them back to that Sequence, - * either directly or using a QUndoCommand (see SetUndoable()). + * either directly or using an UndoCommand (see SetUndoable()). * * If creating a new Sequence, the Sequence must still be constructed first before sending it to SequenceDialog. * SequenceDialog does not create any new objects. In most cases when creating a new Sequence, editing its parameters @@ -104,21 +104,19 @@ private: QLineEdit* name_field_; /** - * @brief A QUndoCommand for setting the parameters on a sequence + * @brief An UndoCommand for setting the parameters on a sequence */ class SequenceParamCommand : public UndoCommand { public: SequenceParamCommand(Sequence* s, const VideoParams& video_params, const AudioParams& audio_params, - const QString& name, - QUndoCommand* parent = nullptr); + const QString& name); virtual Project* GetRelevantProject() const override; - protected: - virtual void redo_internal() override; - virtual void undo_internal() override; + virtual void redo() override; + virtual void undo() override; private: Sequence* sequence_; diff --git a/app/node/CMakeLists.txt b/app/node/CMakeLists.txt index 2de2c99fb..70b19a506 100644 --- a/app/node/CMakeLists.txt +++ b/app/node/CMakeLists.txt @@ -43,6 +43,7 @@ set(OLIVE_SOURCES node/node.cpp node/nodecopypaste.h node/nodecopypaste.cpp + node/splitvalue.h node/traverser.h node/traverser.cpp node/value.h diff --git a/app/node/generator/polygon/polygon.cpp b/app/node/generator/polygon/polygon.cpp index ea68861f8..1a9d053e4 100644 --- a/app/node/generator/polygon/polygon.cpp +++ b/app/node/generator/polygon/polygon.cpp @@ -27,7 +27,7 @@ namespace olive { PolygonGenerator::PolygonGenerator() { - points_input_ = new NodeInput(this, QStringLiteral("points_in"), NodeValue::kVec2); + points_input_ = new NodeInput(this, QStringLiteral("points_in"), NodeValue::kVec2, QVector2D(0, 0)); points_input_->SetIsArray(true); color_input_ = new NodeInput(this, QStringLiteral("color_in"), NodeValue::kColor, QVariant::fromValue(Color(1.0, 1.0, 1.0))); diff --git a/app/node/input.cpp b/app/node/input.cpp index e3aaf1630..093099135 100644 --- a/app/node/input.cpp +++ b/app/node/input.cpp @@ -23,14 +23,16 @@ #include "common/bezier.h" #include "common/lerp.h" #include "common/xmlutils.h" +#include "core.h" #include "node.h" #include "project/item/footage/stream.h" +#include "widget/nodeview/nodeviewundo.h" #define super NodeConnectable namespace olive { -NodeInput::NodeInput(Node* parent, const QString& id, NodeValue::Type type, const QVector &default_value) +NodeInput::NodeInput(Node* parent, const QString& id, NodeValue::Type type, const SplitValue &default_value) { Init(parent, id, type, default_value); } @@ -42,7 +44,7 @@ NodeInput::NodeInput(Node *parent, const QString &id, NodeValue::Type type, cons NodeInput::NodeInput(Node* parent, const QString &id, NodeValue::Type type) { - Init(parent, id, type, QVector()); + Init(parent, id, type, SplitValue()); } NodeInput::~NodeInput() @@ -217,7 +219,16 @@ void NodeInput::childEvent(QChildEvent *event) } } -void NodeInput::Init(Node* parent, const QString &id, NodeValue::Type type, const QVector& default_val) +void NodeInput::ClearElement(int index) +{ + GetImmediate(index)->delete_all_keyframes(); + + SetIsKeyframing(false, index); + + SetSplitStandardValue(default_value_, index); +} + +void NodeInput::Init(Node* parent, const QString &id, NodeValue::Type type, const SplitValue& default_val) { setParent(parent); @@ -381,11 +392,25 @@ void NodeInput::SaveImmediate(QXmlStreamWriter* writer, int element) const } } -void NodeInput::ChangeArraySizeInternal(int size) +void NodeInput::ArrayResizeInternal(int size) { - array_size_ = size; - emit ArraySizeChanged(array_size_); - emit ValueChanged(TimeRange(RATIONAL_MIN, RATIONAL_MAX), -1); + if (array_size_ != size) { + // Update array size + if (array_size_ < size) { + // Size is larger, create any immediates that don't exist + for (int i=subinputs_.size(); i NodeInput::GetImmediateDependencies() const return GetDependencies(false, false); } -void NodeInput::ArrayInsert(int index) +void NodeInput::ArrayAppend(bool undoable) { - // Add new input - subinputs_.insert(index, CreateImmediate()); - - ChangeArraySizeInternal(array_size_ + 1); - - // Move connections down - std::map copied_edges = edges(); - for (auto it=copied_edges.crbegin(); it!=copied_edges.crend(); it++) { - if (it->first >= index) { - // Disconnect this and reconnect it one element down - DisconnectEdge(it->second, this, it->first); - ConnectEdge(it->second, this, it->first + 1); - } - } + ArrayResize(ArraySize() + 1, undoable); } -void NodeInput::ArrayRemove(int index) +void NodeInput::ArrayInsert(int index, bool undoable) { - // Remove input - delete subinputs_.takeAt(index); - ChangeArraySizeInternal(array_size_ - 1); + if (undoable) { + Core::instance()->undo_stack()->push(new ArrayInsertCommand(this, index)); + } else { + // Add new input + ArrayResizeInternal(ArraySize() + 1); - // Move connections up - std::map copied_edges = edges(); - for (auto it=copied_edges.cbegin(); it!=copied_edges.cend(); it++) { - if (it->first >= index) { - // Disconnect this and reconnect it one element up if it's not the element being removed - DisconnectEdge(it->second, this, it->first); - - if (it->first > index) { - ConnectEdge(it->second, this, it->first - 1); - } - } - } -} - -void NodeInput::ArrayPrepend() -{ - ArrayInsert(0); -} - -void NodeInput::ArrayResize(int size) -{ - if (array_size_ != size) { - // Update array size - if (array_size_ < size) { - // Size is larger, create any immediates that don't exist - for (int i=subinputs_.size(); i copied_edges = edges(); + for (auto it=copied_edges.crbegin(); it!=copied_edges.crend(); it++) { + if (it->first >= index) { + // Disconnect this and reconnect it one element down + DisconnectEdge(it->second, this, it->first); + ConnectEdge(it->second, this, it->first + 1); } } - ChangeArraySizeInternal(size); - - if (array_size_ > size) { - // Decreasing in size, disconnect any extraneous edges - for (int i=size; iindex; i--) { + CopyValuesOfElement(this, this, i-1, i); } + + // Reset value of element we just "inserted" + ClearElement(index); } } -void NodeInput::ArrayRemoveLast() +void NodeInput::ArrayRemove(int index, bool undoable) { - ArrayResize(ArraySize() - 1); + if (undoable) { + Core::instance()->undo_stack()->push(new ArrayRemoveCommand(this, index)); + } else { + // Remove input + ArrayResizeInternal(ArraySize() - 1); + + // Move connections up + std::map copied_edges = edges(); + for (auto it=copied_edges.cbegin(); it!=copied_edges.cend(); it++) { + if (it->first >= index) { + // Disconnect this and reconnect it one element up if it's not the element being removed + DisconnectEdge(it->second, this, it->first); + + if (it->first > index) { + ConnectEdge(it->second, this, it->first - 1); + } + } + } + + // Shift values and keyframes down one element + for (int i=index; iundo_stack()->push(c); + } else { + c->redo(); + delete c; + } +} + +void NodeInput::ArrayRemoveLast(bool undoable) +{ + ArrayResize(ArraySize() - 1, undoable); } QVariant NodeInput::GetValueAtTime(const rational &time, int element) const @@ -520,9 +561,9 @@ QVariant NodeInput::GetValueAtTime(const rational &time, int element) const return NodeValue::combine_track_values_into_normal_value(data_type_, GetSplitValuesAtTime(time, element)); } -QVector NodeInput::GetSplitValuesAtTime(const rational &time, int element) const +SplitValue NodeInput::GetSplitValuesAtTime(const rational &time, int element) const { - QVector vals; + SplitValue vals; int nb_tracks = GetNumberOfKeyframeTracks(); @@ -669,26 +710,31 @@ void NodeInput::CopyValues(NodeInput *source, NodeInput *dest, bool include_conn } } -void NodeInput::CopyValuesOfElement(NodeInput *src, NodeInput *dst, int element) +void NodeInput::CopyValuesOfElement(NodeInput *src, NodeInput *dst, int src_element, int dst_element) { + if (dst_element >= dst->subinputs_.size()) { + qDebug() << "Ignored destination element that was out of array bounds"; + return; + } + // Copy standard value - dst->SetSplitStandardValue(src->GetSplitStandardValue(element), element); + dst->SetSplitStandardValue(src->GetSplitStandardValue(src_element), dst_element); // Copy keyframes - dst->GetImmediate(element)->delete_all_keyframes(); - foreach (const NodeKeyframeTrack& track, src->GetImmediate(element)->keyframe_tracks()) { + dst->GetImmediate(dst_element)->delete_all_keyframes(); + foreach (const NodeKeyframeTrack& track, src->GetImmediate(src_element)->keyframe_tracks()) { foreach (NodeKeyframe* key, track) { - key->copy(dst); + key->copy(dst_element, dst); } } // Copy keyframing state if (src->IsKeyframable()) { - dst->SetIsKeyframing(src->IsKeyframing(element), element); + dst->SetIsKeyframing(src->IsKeyframing(src_element), dst_element); } // If this is the root of an array, copy the array size - if (element == -1) { + if (src_element == -1 && dst_element == -1) { dst->ArrayResize(src->ArraySize()); } } @@ -839,4 +885,19 @@ uint qHash(const NodeInput::KeyframeTrackReference &ref, uint seed) return qHash(ref.input, seed) ^ qHash(ref.element, seed) ^ qHash(ref.track, seed); } +Project *NodeInput::ArrayRemoveCommand::GetRelevantProject() const +{ + return input_->parent()->parent()->project(); +} + +Project *NodeInput::ArrayResizeCommand::GetRelevantProject() const +{ + return input_->parent()->parent()->project(); +} + +Project *NodeInput::ArrayInsertCommand::GetRelevantProject() const +{ + return input_->parent()->parent()->project(); +} + } diff --git a/app/node/input.h b/app/node/input.h index 0e3c01a5b..0d52f85fd 100644 --- a/app/node/input.h +++ b/app/node/input.h @@ -26,6 +26,8 @@ #include "node/connectable.h" #include "node/inputimmediate.h" #include "node/value.h" +#include "splitvalue.h" +#include "undo/undocommand.h" namespace olive { @@ -47,7 +49,7 @@ public: * saving/loading data from this Node so that parameter order can be changed without issues loading data saved by an * older version. This of course assumes that parameters don't change their ID. */ - NodeInput(Node* parent, const QString &id, NodeValue::Type type, const QVector& default_value); + NodeInput(Node* parent, const QString &id, NodeValue::Type type, const SplitValue& default_value); NodeInput(Node* parent, const QString &id, NodeValue::Type type, const QVariant& default_value); NodeInput(Node* parent, const QString &id, NodeValue::Type type); @@ -176,7 +178,7 @@ public: /** * @brief Get non-keyframed value split into components (the way it's stored) */ - const QVector& GetSplitStandardValue(int element = -1) const + const SplitValue& GetSplitStandardValue(int element = -1) const { return GetImmediate(element)->get_split_standard_value(); } @@ -197,7 +199,7 @@ public: SetSplitStandardValue(NodeValue::split_normal_value_into_track_values(data_type_, value), element); } - void SetSplitStandardValue(const QVector& value, int element = -1) + void SetSplitStandardValue(const SplitValue& value, int element = -1) { GetImmediate(element)->set_split_standard_value(value); @@ -231,7 +233,11 @@ public: */ static void CopyValues(NodeInput* source, NodeInput* dest, bool include_connections = true, bool traverse_arrays = true); - static void CopyValuesOfElement(NodeInput* source, NodeInput* dst, int element); + static void CopyValuesOfElement(NodeInput* source, NodeInput* dst, int src_element, int dst_element); + static void CopyValuesOfElement(NodeInput* source, NodeInput* dst, int element) + { + CopyValuesOfElement(source, dst, element, element); + } QStringList get_combobox_strings() const; @@ -244,7 +250,7 @@ public: return NodeValue::combine_track_values_into_normal_value(data_type_, default_value_); } - const QVector& GetSplitDefaultValue() const + const SplitValue& GetSplitDefaultValue() const { return default_value_; } @@ -279,33 +285,36 @@ public: void SetIsKeyframing(bool keyframing, int element) { - Q_ASSERT(IsKeyframable()); + if (IsKeyframable()) { + qDebug() << "Ignored set keyframing because this input is not keyframable"; + return; + } GetImmediate(element)->set_is_keyframing(keyframing); emit KeyframeEnableChanged(keyframing, element); } - void ArrayAppend() - { - ArrayResize(ArraySize() + 1); - } + /// Alias for ArrayResize(ArraySize() + 1) + void ArrayAppend(bool undoable = false); - void ArrayInsert(int index); + void ArrayInsert(int index, bool undoable = false); - void ArrayRemove(int index); + void ArrayRemove(int index, bool undoable = false); - void ArrayPrepend(); + /// Alias for ArrayInsert(0) + void ArrayPrepend(bool undoable = false); - void ArrayResize(int size); + void ArrayResize(int size, bool undoable = false); + + /// Alias for ArrayResize(ArraySize() - 1) + void ArrayRemoveLast(bool undoable = false); int ArraySize() const { return array_size_; } - void ArrayRemoveLast(); - int GetNumberOfKeyframeTracks() const { return NodeValue::get_number_of_keyframe_tracks(data_type_); @@ -318,7 +327,7 @@ public: */ QVariant GetValueAtTime(const rational& time, int element = -1) const; - QVector GetSplitValuesAtTime(const rational& time, int element = -1) const; + SplitValue GetSplitValuesAtTime(const rational& time, int element = -1) const; /** * @brief Calculate the stored value for a specific track @@ -378,13 +387,139 @@ protected: virtual void childEvent(QChildEvent* e) override; private: - void Init(Node *parent, const QString& id, NodeValue::Type type, const QVector &default_val); + class ArrayInsertCommand : public UndoCommand + { + public: + ArrayInsertCommand(NodeInput* input, int index) : + input_(input), + index_(index) + { + } + + virtual Project* GetRelevantProject() const override; + + virtual void redo() override + { + input_->ArrayInsert(index_, false); + } + + virtual void undo() override + { + input_->ArrayRemove(index_, false); + } + + private: + NodeInput* input_; + int index_; + + }; + + class ArrayRemoveCommand : public UndoCommand + { + public: + ArrayRemoveCommand(NodeInput* input, int index) : + input_(input), + index_(index) + { + } + + virtual Project* GetRelevantProject() const override; + + protected: + virtual void redo() override + { + // Save immediate data + is_keyframing_ = input_->IsKeyframing(index_); + standard_value_ = input_->GetSplitStandardValue(index_); + keyframes_ = input_->GetImmediate(index_)->keyframe_tracks(); + input_->GetImmediate(index_)->delete_all_keyframes(&memory_manager_); + + input_->ArrayRemove(index_, false); + } + + virtual void undo() override + { + input_->ArrayInsert(index_, false); + + // Restore keyframes + foreach (const NodeKeyframeTrack& track, keyframes_) { + foreach (NodeKeyframe* key, track) { + key->setParent(input_); + } + } + input_->SetSplitStandardValue(standard_value_, index_); + input_->SetIsKeyframing(is_keyframing_, index_); + } + + private: + NodeInput* input_; + int index_; + + SplitValue standard_value_; + bool is_keyframing_; + QVector keyframes_; + QObject memory_manager_; + + }; + + class ArrayResizeCommand : public UndoCommand + { + public: + ArrayResizeCommand(NodeInput* input, int size) : + input_(input), + size_(size) + {} + + virtual void redo() override + { + old_size_ = input_->array_size_; + + if (old_size_ > size_) { + // Decreasing in size, disconnect any extraneous edges + for (int i=size_; iedges().at(i); + + removed_connections_.insert(i, output); + + DisconnectEdge(output, input_, i); + } catch (std::out_of_range&) {} + } + } + + input_->ArrayResizeInternal(size_); + } + + virtual void undo() override + { + for (auto it=removed_connections_.cbegin(); it!=removed_connections_.cend(); it++) { + ConnectEdge(it.value(), input_, it.key()); + } + removed_connections_.clear(); + + input_->ArrayResizeInternal(old_size_); + } + + virtual Project* GetRelevantProject() const override; + + private: + NodeInput* input_; + int size_; + int old_size_; + + QHash removed_connections_; + + }; + + void ClearElement(int index); + + void Init(Node *parent, const QString& id, NodeValue::Type type, const SplitValue &default_val); void LoadImmediate(QXmlStreamReader *reader, int element, XMLNodeData& xml_node_data, const QAtomicInt* cancelled); void SaveImmediate(QXmlStreamWriter *writer, int element) const; - void ChangeArraySizeInternal(int size); + void ArrayResizeInternal(int size); NodeInputImmediate* CreateImmediate(); @@ -426,7 +561,7 @@ private: QVector subinputs_; - QVector default_value_; + SplitValue default_value_; /** * @brief Unique identifier of this input within this node diff --git a/app/node/inputdragger.cpp b/app/node/inputdragger.cpp index d71bfbc9c..2060b37d2 100644 --- a/app/node/inputdragger.cpp +++ b/app/node/inputdragger.cpp @@ -106,21 +106,21 @@ void NodeInputDragger::End() return; } - QUndoCommand* command = new QUndoCommand(); + MultiUndoCommand* command = new MultiUndoCommand(); if (input_->IsKeyframing(element_)) { if (drag_created_key_) { // We created a keyframe in this process - new NodeParamInsertKeyframeCommand(input_, dragging_key_, command); + command->add_child(new NodeParamInsertKeyframeCommand(input_, dragging_key_)); } // We just set a keyframe's value // We do this even when inserting a keyframe because we don't actually perform an insert in this undo command // so this will ensure the ValueChanged() signal is sent correctly - new NodeParamSetKeyframeValueCommand(dragging_key_, end_value_, start_value_, command); + command->add_child(new NodeParamSetKeyframeValueCommand(dragging_key_, end_value_, start_value_)); } else { // We just set the standard value - new NodeParamSetStandardValueCommand(input_, track_, element_, end_value_, start_value_, command); + command->add_child(new NodeParamSetStandardValueCommand(input_, track_, element_, end_value_, start_value_)); } Core::instance()->undo_stack()->push(command); diff --git a/app/node/inputimmediate.cpp b/app/node/inputimmediate.cpp index 82f71196b..dd1f083bc 100644 --- a/app/node/inputimmediate.cpp +++ b/app/node/inputimmediate.cpp @@ -27,7 +27,7 @@ namespace olive { -NodeInputImmediate::NodeInputImmediate(NodeValue::Type type, const QVector &default_val) : +NodeInputImmediate::NodeInputImmediate(NodeValue::Type type, const SplitValue &default_val) : keyframing_(false) { int track_size = NodeValue::get_number_of_keyframe_tracks(type); @@ -43,7 +43,7 @@ void NodeInputImmediate::set_standard_value_on_track(const QVariant &value, int standard_value_.replace(track, value); } -void NodeInputImmediate::set_split_standard_value(const QVector &value) +void NodeInputImmediate::set_split_standard_value(const SplitValue &value) { for (int i=0; itrack()].removeOne(key); } -void NodeInputImmediate::delete_all_keyframes() +void NodeInputImmediate::delete_all_keyframes(QObject* parent) { for (NodeKeyframeTrack& track : keyframe_tracks_) { while (!track.isEmpty()) { - delete track.first(); + if (parent) { + track.first()->setParent(parent); + } else { + delete track.first(); + } } } } diff --git a/app/node/inputimmediate.h b/app/node/inputimmediate.h index 4650b9ee6..86694214e 100644 --- a/app/node/inputimmediate.h +++ b/app/node/inputimmediate.h @@ -25,6 +25,7 @@ #include "common/xmlutils.h" #include "node/keyframe.h" #include "node/value.h" +#include "splitvalue.h" namespace olive { @@ -33,7 +34,7 @@ class NodeInput; class NodeInputImmediate { public: - NodeInputImmediate(NodeValue::Type type, const QVector& default_val); + NodeInputImmediate(NodeValue::Type type, const SplitValue& default_val); /** * @brief Internal insert function, automatically does an insertion sort based on the keyframe's time @@ -42,19 +43,19 @@ public: void remove_keyframe(NodeKeyframe* key); - void delete_all_keyframes(); + void delete_all_keyframes(QObject *parent = nullptr); /** * @brief Get non-keyframed value split into components (the way it's stored) */ - const QVector& get_split_standard_value() const + const SplitValue& get_split_standard_value() const { return standard_value_; } void set_standard_value_on_track(const QVariant &value, int track = 0); - void set_split_standard_value(const QVector& value); + void set_split_standard_value(const SplitValue& value); /** * @brief Retrieve a list of keyframe objects for all tracks at a given time @@ -149,7 +150,7 @@ private: /** * @brief Non-keyframed value */ - QVector standard_value_; + SplitValue standard_value_; /** * @brief Internal keyframe array diff --git a/app/node/keyframe.cpp b/app/node/keyframe.cpp index 54ee9ec41..0a49c9868 100644 --- a/app/node/keyframe.cpp +++ b/app/node/keyframe.cpp @@ -45,14 +45,19 @@ NodeKeyframe::~NodeKeyframe() setParent(nullptr); } -NodeKeyframe *NodeKeyframe::copy(QObject* parent) const +NodeKeyframe *NodeKeyframe::copy(int element, QObject *parent) const { - NodeKeyframe* copy = new NodeKeyframe(time_, value_, type_, track_, element_, parent); + NodeKeyframe* copy = new NodeKeyframe(time_, value_, type_, track_, element, parent); copy->bezier_control_in_ = bezier_control_in_; copy->bezier_control_out_ = bezier_control_out_; return copy; } +NodeKeyframe *NodeKeyframe::copy(QObject* parent) const +{ + return copy(element_, parent); +} + NodeInput *NodeKeyframe::parent() const { return static_cast(QObject::parent()); diff --git a/app/node/keyframe.h b/app/node/keyframe.h index 5e1c26132..1659b4e48 100644 --- a/app/node/keyframe.h +++ b/app/node/keyframe.h @@ -65,6 +65,7 @@ public: virtual ~NodeKeyframe() override; + NodeKeyframe* copy(int element, QObject* parent = nullptr) const; NodeKeyframe* copy(QObject* parent = nullptr) const; NodeInput* parent() const; diff --git a/app/node/node.cpp b/app/node/node.cpp index 4af806131..4848f00c7 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -247,7 +247,7 @@ TimeRange Node::OutputTimeAdjustment(NodeInput *, int, const TimeRange &input_ti return input_time; } -QVector Node::CopyDependencyGraph(const QVector &nodes, QUndoCommand* command) +QVector Node::CopyDependencyGraph(const QVector &nodes, MultiUndoCommand *command) { int nb_nodes = nodes.size(); @@ -263,7 +263,7 @@ QVector Node::CopyDependencyGraph(const QVector &nodes, QUndoCom // Add to graph NodeGraph* graph = static_cast(nodes.at(i)->parent()); if (command) { - new NodeAddCommand(graph, c, command); + command->add_child(new NodeAddCommand(graph, c)); } else { c->setParent(graph); } @@ -277,7 +277,7 @@ QVector Node::CopyDependencyGraph(const QVector &nodes, QUndoCom return copies; } -void Node::CopyDependencyGraph(const QVector &src, const QVector &dst, QUndoCommand *command) +void Node::CopyDependencyGraph(const QVector &src, const QVector &dst, MultiUndoCommand *command) { for (int i=0; iinputs()) { @@ -290,7 +290,7 @@ void Node::CopyDependencyGraph(const QVector &src, const QVector NodeInput* dst_input = dst.at(i)->GetInputWithID(input->id()); if (command) { - new NodeEdgeAddCommand(dst_output, dst_input, it->first, command); + command->add_child(new NodeEdgeAddCommand(dst_output, dst_input, it->first)); } else { ConnectEdge(dst_output, dst_input, it->first); } diff --git a/app/node/node.h b/app/node/node.h index a13180741..7ae591793 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -368,8 +368,8 @@ public: /** * @brief Clones a set of nodes and connects the new ones the way the old ones were */ - static QVector CopyDependencyGraph(const QVector& nodes, QUndoCommand *command); - static void CopyDependencyGraph(const QVector& src, const QVector& dst, QUndoCommand *command); + static QVector CopyDependencyGraph(const QVector& nodes, MultiUndoCommand *command); + static void CopyDependencyGraph(const QVector& src, const QVector& dst, MultiUndoCommand *command); /** * @brief Return whether this Node can be deleted or not diff --git a/app/node/nodecopypaste.cpp b/app/node/nodecopypaste.cpp index 5309da2d5..0a64328a4 100644 --- a/app/node/nodecopypaste.cpp +++ b/app/node/nodecopypaste.cpp @@ -56,7 +56,7 @@ void NodeCopyPasteService::CopyNodesToClipboard(const QVector &nodes, vo Core::CopyStringToClipboard(copy_str); } -QVector NodeCopyPasteService::PasteNodesFromClipboard(NodeGraph *graph, QUndoCommand* command, void *userdata) +QVector NodeCopyPasteService::PasteNodesFromClipboard(NodeGraph *graph, MultiUndoCommand* command, void *userdata) { QString clipboard = Core::PasteStringFromClipboard(); @@ -121,7 +121,7 @@ QVector NodeCopyPasteService::PasteNodesFromClipboard(NodeGraph *graph, // Add all nodes to graph foreach (Node* n, pasted_nodes) { - new NodeAddCommand(graph, n, command); + command->add_child(new NodeAddCommand(graph, n)); } // Make connections diff --git a/app/node/nodecopypaste.h b/app/node/nodecopypaste.h index 2048b90eb..cd79c4a4b 100644 --- a/app/node/nodecopypaste.h +++ b/app/node/nodecopypaste.h @@ -37,7 +37,7 @@ public: protected: void CopyNodesToClipboard(const QVector &nodes, void* userdata = nullptr); - QVector PasteNodesFromClipboard(NodeGraph *graph, QUndoCommand *command, void* userdata = nullptr); + QVector PasteNodesFromClipboard(NodeGraph *graph, MultiUndoCommand *command, void* userdata = nullptr); virtual void CopyNodesToClipboardInternal(QXmlStreamWriter *writer, void* userdata); diff --git a/app/node/splitvalue.h b/app/node/splitvalue.h new file mode 100644 index 000000000..3a0147ab7 --- /dev/null +++ b/app/node/splitvalue.h @@ -0,0 +1,33 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2020 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef SPLITVALUE_H +#define SPLITVALUE_H + +#include +#include + +namespace olive { + +using SplitValue = QVector; + +} + +#endif // SPLITVALUE_H diff --git a/app/project/projectviewmodel.cpp b/app/project/projectviewmodel.cpp index b709e460a..b22f71352 100644 --- a/app/project/projectviewmodel.cpp +++ b/app/project/projectviewmodel.cpp @@ -330,9 +330,9 @@ bool ProjectViewModel::dropMimeData(const QMimeData *data, Qt::DropAction action quint64 enabled_streams; // Loop through all data - QUndoCommand* move_command = new QUndoCommand(); + MultiUndoCommand* move_command = new MultiUndoCommand(); - move_command->setText(tr("Move Items")); + move_command->set_name(tr("Move Items")); while (!stream.atEnd()) { stream >> enabled_streams >> r >> item_ptr; @@ -343,9 +343,7 @@ bool ProjectViewModel::dropMimeData(const QMimeData *data, Qt::DropAction action // no-op if (item != drop_location && item->parent() != drop_location && !ItemIsParentOfChild(item, drop_location)) { - MoveItemCommand* mic = new MoveItemCommand(this, item, static_cast(drop_location), move_command); - - Q_UNUSED(mic) + move_command->add_child(new MoveItemCommand(this, item, static_cast(drop_location))); } } @@ -499,16 +497,14 @@ QModelIndex ProjectViewModel::CreateIndexFromItem(Item *item, int column) ProjectViewModel::MoveItemCommand::MoveItemCommand(ProjectViewModel *model, Item *item, - Folder *destination, - QUndoCommand *parent) : - UndoCommand(parent), + Folder *destination) : model_(model), item_(item), destination_(destination) { source_ = static_cast(item->parent()); - setText(QCoreApplication::translate("MoveItemCommand", "Move Item")); + set_name(QCoreApplication::translate("MoveItemCommand", "Move Item")); } Project *ProjectViewModel::MoveItemCommand::GetRelevantProject() const @@ -516,25 +512,24 @@ Project *ProjectViewModel::MoveItemCommand::GetRelevantProject() const return model_->project(); } -void ProjectViewModel::MoveItemCommand::redo_internal() +void ProjectViewModel::MoveItemCommand::redo() { model_->MoveItemInternal(item_, destination_); } -void ProjectViewModel::MoveItemCommand::undo_internal() +void ProjectViewModel::MoveItemCommand::undo() { model_->MoveItemInternal(item_, source_); } -ProjectViewModel::RenameItemCommand::RenameItemCommand(ProjectViewModel* model, Item *item, const QString &name, QUndoCommand *parent) : - UndoCommand(parent), +ProjectViewModel::RenameItemCommand::RenameItemCommand(ProjectViewModel* model, Item *item, const QString &name) : model_(model), item_(item), new_name_(name) { old_name_ = item->name(); - setText(QCoreApplication::translate("RenameItemCommand", "Rename Item")); + set_name(QCoreApplication::translate("RenameItemCommand", "Rename Item")); } Project *ProjectViewModel::RenameItemCommand::GetRelevantProject() const @@ -542,18 +537,17 @@ Project *ProjectViewModel::RenameItemCommand::GetRelevantProject() const return model_->project(); } -void ProjectViewModel::RenameItemCommand::redo_internal() +void ProjectViewModel::RenameItemCommand::redo() { model_->RenameChild(item_, new_name_); } -void ProjectViewModel::RenameItemCommand::undo_internal() +void ProjectViewModel::RenameItemCommand::undo() { model_->RenameChild(item_, old_name_); } -ProjectViewModel::AddItemCommand::AddItemCommand(ProjectViewModel* model, Item* folder, Item* child, QUndoCommand* parent) : - UndoCommand(parent), +ProjectViewModel::AddItemCommand::AddItemCommand(ProjectViewModel* model, Item* folder, Item* child) : model_(model), parent_(folder), child_(child) @@ -571,18 +565,17 @@ Project *ProjectViewModel::AddItemCommand::GetRelevantProject() const return model_->project(); } -void ProjectViewModel::AddItemCommand::redo_internal() +void ProjectViewModel::AddItemCommand::redo() { model_->AddChild(parent_, child_); } -void ProjectViewModel::AddItemCommand::undo_internal() +void ProjectViewModel::AddItemCommand::undo() { model_->RemoveChild(parent_, child_, &memory_manager_); } -ProjectViewModel::RemoveItemCommand::RemoveItemCommand(ProjectViewModel *model, Item *item, QUndoCommand *parent) : - UndoCommand(parent), +ProjectViewModel::RemoveItemCommand::RemoveItemCommand(ProjectViewModel *model, Item *item) : model_(model), item_(item) { @@ -599,12 +592,12 @@ Project *ProjectViewModel::RemoveItemCommand::GetRelevantProject() const return model_->project(); } -void ProjectViewModel::RemoveItemCommand::redo_internal() +void ProjectViewModel::RemoveItemCommand::redo() { model_->RemoveChild(parent_, item_, &memory_manager_); } -void ProjectViewModel::RemoveItemCommand::undo_internal() +void ProjectViewModel::RemoveItemCommand::undo() { model_->AddChild(parent_, item_); } diff --git a/app/project/projectviewmodel.h b/app/project/projectviewmodel.h index 9f790a96a..5b072755b 100644 --- a/app/project/projectviewmodel.h +++ b/app/project/projectviewmodel.h @@ -110,18 +110,17 @@ public: QModelIndex CreateIndexFromItem(Item* item, int column = 0); /** - * @brief A QUndoCommand for moving an item from one folder to another folder + * @brief An UndoCommand for moving an item from one folder to another folder */ class MoveItemCommand : public UndoCommand { public: - MoveItemCommand(ProjectViewModel* model, Item* item, Folder* destination, QUndoCommand* parent = nullptr); + MoveItemCommand(ProjectViewModel* model, Item* item, Folder* destination); virtual Project* GetRelevantProject() const override; - protected: - virtual void redo_internal() override; + virtual void redo() override; - virtual void undo_internal() override; + virtual void undo() override; private: ProjectViewModel* model_; @@ -132,18 +131,17 @@ public: }; /** - * @brief A QUndoCommand for renaming an item + * @brief An UndoCommand for renaming an item */ class RenameItemCommand : public UndoCommand { public: - RenameItemCommand(ProjectViewModel* model, Item* item, const QString& name, QUndoCommand* parent = nullptr); + RenameItemCommand(ProjectViewModel* model, Item* item, const QString& name); virtual Project* GetRelevantProject() const override; - protected: - virtual void redo_internal() override; + virtual void redo() override; - virtual void undo_internal() override; + virtual void undo() override; private: ProjectViewModel* model_; @@ -153,18 +151,17 @@ public: }; /** - * @brief A QUndoCommand for adding an item + * @brief An UndoCommand for adding an item */ class AddItemCommand : public UndoCommand { public: - AddItemCommand(ProjectViewModel* model, Item* folder, Item *child, QUndoCommand* parent = nullptr); + AddItemCommand(ProjectViewModel* model, Item* folder, Item *child); virtual Project* GetRelevantProject() const override; - protected: - virtual void redo_internal() override; + virtual void redo() override; - virtual void undo_internal() override; + virtual void undo() override; private: ProjectViewModel* model_; @@ -179,14 +176,13 @@ public: */ class RemoveItemCommand : public UndoCommand { public: - RemoveItemCommand(ProjectViewModel* model, Item* item, QUndoCommand* parent = nullptr); + RemoveItemCommand(ProjectViewModel* model, Item* item); virtual Project* GetRelevantProject() const override; - protected: - virtual void redo_internal() override; + virtual void redo() override; - virtual void undo_internal() override; + virtual void undo() override; private: ProjectViewModel* model_; @@ -240,8 +236,8 @@ private: * This function will emit a signal indicating that rows are moving, set `destination` as the new parent of `item`, * and then emit a signal that the row has finished moving. * - * It's not recommended to use this function directly in most cases since it does not create a QUndoCommand allowing - * the user to undo the move. Instead this function should primarily be called from QUndoCommands belonging to this + * It's not recommended to use this function directly in most cases since it does not create an UndoCommand allowing + * the user to undo the move. Instead this function should primarily be called from UndoCommands belonging to this * class (e.g. MoveItemCommand). */ void MoveItemInternal(Item* item, Item* destination); diff --git a/app/task/project/import/import.cpp b/app/task/project/import/import.cpp index 691b155bf..911fe05f7 100644 --- a/app/task/project/import/import.cpp +++ b/app/task/project/import/import.cpp @@ -50,7 +50,7 @@ const int &ProjectImportTask::GetFileCount() const bool ProjectImportTask::Run() { - command_ = new QUndoCommand(); + command_ = new MultiUndoCommand(); int imported = 0; @@ -65,7 +65,7 @@ bool ProjectImportTask::Run() } } -void ProjectImportTask::Import(Folder *folder, QFileInfoList import, int &counter, QUndoCommand* parent_command) +void ProjectImportTask::Import(Folder *folder, QFileInfoList import, int &counter, MultiUndoCommand* parent_command) { for (int i=0; iset_name(file_info.fileName()); // Create undoable command that adds the items to the model - new ProjectViewModel::AddItemCommand(model_, - folder, - f, - parent_command); + parent_command->add_child(new ProjectViewModel::AddItemCommand(model_, + folder, + f)); // Recursively follow this path Import(f, entry_list, counter, parent_command); @@ -122,10 +121,9 @@ void ProjectImportTask::Import(Folder *folder, QFileInfoList import, int &counte ValidateImageSequence(footage, import, i); // Create undoable command that adds the items to the model - new ProjectViewModel::AddItemCommand(model_, - folder, - footage, - parent_command); + parent_command->add_child(new ProjectViewModel::AddItemCommand(model_, + folder, + footage)); } else { // Add to list so we can tell the user about it later invalid_files_.append(file_info.absoluteFilePath()); diff --git a/app/task/project/import/import.h b/app/task/project/import/import.h index a8546ffcb..50875597e 100644 --- a/app/task/project/import/import.h +++ b/app/task/project/import/import.h @@ -38,7 +38,7 @@ public: const int& GetFileCount() const; - QUndoCommand* GetCommand() const + MultiUndoCommand* GetCommand() const { return command_; } @@ -57,7 +57,7 @@ protected: virtual bool Run() override; private: - void Import(Folder* folder, QFileInfoList import, int& counter, QUndoCommand *parent_command); + void Import(Folder* folder, QFileInfoList import, int& counter, MultiUndoCommand *parent_command); void ValidateImageSequence(Footage *footage, QFileInfoList &info_list, int index); @@ -67,7 +67,7 @@ private: static int64_t GetImageSequenceLimit(const QString &start_fn, int64_t start, bool up); - QUndoCommand* command_; + MultiUndoCommand* command_; ProjectViewModel* model_; diff --git a/app/undo/undocommand.cpp b/app/undo/undocommand.cpp index db8318c3e..276af8805 100644 --- a/app/undo/undocommand.cpp +++ b/app/undo/undocommand.cpp @@ -24,34 +24,38 @@ namespace olive { -UndoCommand::UndoCommand(QUndoCommand *parent) : - QUndoCommand(parent) +void MultiUndoCommand::redo() { + for (auto it=children_.cbegin(); it!=children_.cend(); it++) { + (*it)->redo_and_set_modified(); + } } -void UndoCommand::redo() +void MultiUndoCommand::undo() { - redo_internal(); - - modified_ = GetRelevantProject()->is_modified(); - GetRelevantProject()->set_modified(true); + for (auto it=children_.crbegin(); it!=children_.crend(); it++) { + (*it)->undo_and_set_modified(); + } } -void UndoCommand::undo() +void UndoCommand::redo_and_set_modified() { - undo_internal(); + redo(); - GetRelevantProject()->set_modified(modified_); + project_ = GetRelevantProject(); + if (project_) { + modified_ = project_->is_modified(); + project_->set_modified(true); + } } -void UndoCommand::redo_internal() +void UndoCommand::undo_and_set_modified() { - QUndoCommand::redo(); -} + undo(); -void UndoCommand::undo_internal() -{ - QUndoCommand::undo(); + if (project_) { + project_->set_modified(modified_); + } } } diff --git a/app/undo/undocommand.h b/app/undo/undocommand.h index 4a4844c05..57ec050dc 100644 --- a/app/undo/undocommand.h +++ b/app/undo/undocommand.h @@ -21,31 +21,83 @@ #ifndef UNDOCOMMAND_H #define UNDOCOMMAND_H -#include +#include +#include +#include #include "common/define.h" -#include "node/graph.h" namespace olive { class Project; -class UndoCommand : public QUndoCommand +class UndoCommand { public: - UndoCommand(QUndoCommand* parent = nullptr); + UndoCommand() = default; + + virtual ~UndoCommand(){} + + DISABLE_COPY_MOVE(UndoCommand) + + virtual void redo() = 0; + virtual void undo() = 0; + + void redo_and_set_modified(); + + void undo_and_set_modified(); + + virtual Project* GetRelevantProject() const = 0; + + const QString& name() const + { + return name_; + } + + void set_name(const QString& name) + { + name_ = name; + } + +private: + bool modified_; + + QString name_; + + Project* project_; + +}; + +class MultiUndoCommand : public UndoCommand +{ +public: + MultiUndoCommand() = default; virtual void redo() override; virtual void undo() override; - virtual Project* GetRelevantProject() const = 0; + virtual Project* GetRelevantProject() const override + { + return nullptr; + } -protected: - virtual void redo_internal(); - virtual void undo_internal(); + void add_child(UndoCommand* command) + { + children_.push_back(command); + } + + int child_count() const + { + return children_.size(); + } + + UndoCommand* child(int i) const + { + return children_[i]; + } private: - bool modified_; + std::vector children_; }; diff --git a/app/undo/undostack.cpp b/app/undo/undostack.cpp index 299f6f695..fa54d1442 100644 --- a/app/undo/undostack.cpp +++ b/app/undo/undostack.cpp @@ -20,15 +20,109 @@ #include "undostack.h" +#include + namespace olive { -void UndoStack::pushIfHasChildren(QUndoCommand *command) +const int UndoStack::kMaxUndoCommands = 200; + +UndoStack::UndoStack() { - if (command->childCount() > 0) { + undo_action_ = new QAction(); + connect(undo_action_, &QAction::triggered, this, &UndoStack::undo); + + redo_action_ = new QAction(); + connect(redo_action_, &QAction::triggered, this, &UndoStack::redo); + + UpdateActions(); +} + +UndoStack::~UndoStack() +{ + clear(); + + delete undo_action_; + delete redo_action_; +} + +void UndoStack::pushIfHasChildren(MultiUndoCommand *command) +{ + if (command->child_count() > 0) { push(command); } else { delete command; } } +void UndoStack::push(UndoCommand *command) +{ + command->redo_and_set_modified(); + commands_.push_back(command); + + if (commands_.size() > kMaxUndoCommands) { + delete commands_.front(); + commands_.pop_front(); + } + + if (CanRedo()) { + for (auto it=undone_commands_.cbegin(); it!=undone_commands_.cend(); it++) { + delete (*it); + } + undone_commands_.clear(); + } + + UpdateActions(); +} + +void UndoStack::undo() +{ + if (CanUndo()) { + // Undo most recently done command + commands_.back()->undo_and_set_modified(); + + // Place at the front of the "undone commands" list + undone_commands_.push_front(commands_.back()); + + // Remove undone command from the commands list + commands_.pop_back(); + + // Update actions + UpdateActions(); + } +} + +void UndoStack::redo() +{ + if (CanRedo()) { + // Redo most recently undone command + undone_commands_.front()->redo_and_set_modified(); + + // Place at the back of the done commands list + commands_.push_back(undone_commands_.front()); + + // Remove done command from undone list + undone_commands_.pop_front(); + + // Update actions + UpdateActions(); + } +} + +void UndoStack::clear() +{ + for (auto it=commands_.cbegin(); it!=commands_.cend(); it++) { + delete (*it); + } + commands_.clear(); +} + +void UndoStack::UpdateActions() +{ + undo_action_->setEnabled(CanUndo()); + redo_action_->setEnabled(CanRedo()); + + undo_action_->setText(QCoreApplication::translate("UndoStack", "Undo %1").arg(CanUndo() ? commands_.back()->name() : QString())); + redo_action_->setText(QCoreApplication::translate("UndoStack", "Redo %1").arg(CanRedo() ? undone_commands_.front()->name() : QString())); +} + } diff --git a/app/undo/undostack.h b/app/undo/undostack.h index f19e98969..60c3aeea5 100644 --- a/app/undo/undostack.h +++ b/app/undo/undostack.h @@ -21,20 +21,70 @@ #ifndef UNDOSTACK_H #define UNDOSTACK_H -#include +#include #include "common/define.h" +#include "undo/undocommand.h" namespace olive { -class UndoStack : public QUndoStack { +class UndoStack : public QObject +{ + Q_OBJECT public: + UndoStack(); + + virtual ~UndoStack() override; + /** * @brief A wrapper for push() that either pushes if the command has children or deletes if not * * This function takes ownership of `command`, and may delete it so it should never be accessed after this call. */ - void pushIfHasChildren(QUndoCommand* command); + void pushIfHasChildren(MultiUndoCommand* command); + + void push(UndoCommand* command); + + void clear(); + + bool CanUndo() const + { + return !commands_.empty(); + } + + bool CanRedo() const + { + return !undone_commands_.empty(); + } + + void UpdateActions(); + + QAction* GetUndoAction() + { + return undo_action_; + } + + QAction* GetRedoAction() + { + return redo_action_; + } + +public slots: + void undo(); + + void redo(); + +private: + static const int kMaxUndoCommands; + + std::list commands_; + + std::list undone_commands_; + + QAction* undo_action_; + + QAction* redo_action_; + }; } diff --git a/app/widget/curvewidget/curvewidget.cpp b/app/widget/curvewidget/curvewidget.cpp index 43b4a99d9..11ff6ee69 100644 --- a/app/widget/curvewidget/curvewidget.cpp +++ b/app/widget/curvewidget/curvewidget.cpp @@ -336,12 +336,12 @@ void CurveWidget::KeyframeTypeButtonTriggered(bool checked) // Ensure only the appropriate button is checked SetKeyframeButtonCheckedFromType(new_type); - QUndoCommand* command = new QUndoCommand(); + MultiUndoCommand* command = new MultiUndoCommand(); foreach (QGraphicsItem* item, selected) { KeyframeViewItem* key_item = static_cast(item); - new KeyframeSetTypeCommand(key_item->key(), new_type, command); + command->add_child(new KeyframeSetTypeCommand(key_item->key(), new_type)); } Core::instance()->undo_stack()->push(command); diff --git a/app/widget/keyframeview/keyframeviewbase.cpp b/app/widget/keyframeview/keyframeviewbase.cpp index b4e5ec4dc..d47cc9237 100644 --- a/app/widget/keyframeview/keyframeviewbase.cpp +++ b/app/widget/keyframeview/keyframeviewbase.cpp @@ -59,13 +59,13 @@ void KeyframeViewBase::Clear() void KeyframeViewBase::DeleteSelected() { - QUndoCommand* command = new QUndoCommand(); + MultiUndoCommand* command = new MultiUndoCommand(); QMap::const_iterator i; for (i=item_map_.constBegin(); i!=item_map_.constEnd(); i++) { if (i.value()->isSelected()) { - new NodeParamRemoveKeyframeCommand(i.key(), command); + command->add_child(new NodeParamRemoveKeyframeCommand(i.key())); } } @@ -307,46 +307,42 @@ void KeyframeViewBase::mouseReleaseEvent(QMouseEvent *event) if (dragging_) { if (dragging_bezier_point_) { - QUndoCommand* command = new QUndoCommand(); + MultiUndoCommand* command = new MultiUndoCommand(); // Create undo command with the current bezier point and the old one - new KeyframeSetBezierControlPoint(dragging_bezier_point_->key(), - dragging_bezier_point_->mode(), - dragging_bezier_point_->key()->bezier_control(dragging_bezier_point_->mode()), - dragging_bezier_point_start_, - command); + command->add_child(new KeyframeSetBezierControlPoint(dragging_bezier_point_->key(), + dragging_bezier_point_->mode(), + dragging_bezier_point_->key()->bezier_control(dragging_bezier_point_->mode()), + dragging_bezier_point_start_)); if (!(event->modifiers() & Qt::ControlModifier)) { auto opposing_type = NodeKeyframe::get_opposing_bezier_type(dragging_bezier_point_->mode()); - new KeyframeSetBezierControlPoint(dragging_bezier_point_->key(), - opposing_type, - dragging_bezier_point_->key()->bezier_control(opposing_type), - dragging_bezier_point_opposing_start_, - command); + command->add_child(new KeyframeSetBezierControlPoint(dragging_bezier_point_->key(), + opposing_type, + dragging_bezier_point_->key()->bezier_control(opposing_type), + dragging_bezier_point_opposing_start_)); } dragging_bezier_point_ = nullptr; Core::instance()->undo_stack()->push(command); } else if (!selected_keys_.isEmpty()) { - QUndoCommand* command = new QUndoCommand(); + MultiUndoCommand* command = new MultiUndoCommand(); foreach (const KeyframeItemAndTime& keypair, selected_keys_) { NodeKeyframe* item = keypair.key->key(); // Commit movement - new NodeParamSetKeyframeTimeCommand(item, - item->time(), - keypair.time, - command); + command->add_child(new NodeParamSetKeyframeTimeCommand(item, + item->time(), + keypair.time)); // Commit value if we're setting a value if (IsYAxisEnabled()) { - new NodeParamSetKeyframeValueCommand(item, - item->value(), - keypair.value, - command); + command->add_child(new NodeParamSetKeyframeValueCommand(item, + item->value(), + keypair.value)); } } @@ -497,11 +493,10 @@ void KeyframeViewBase::ShowContextMenu() new_type = NodeKeyframe::kLinear; } - QUndoCommand* command = new QUndoCommand(); + MultiUndoCommand* command = new MultiUndoCommand(); foreach (QGraphicsItem* item, items) { - new KeyframeSetTypeCommand(static_cast(item)->key(), - new_type, - command); + command->add_child(new KeyframeSetTypeCommand(static_cast(item)->key(), + new_type)); } Core::instance()->undo_stack()->pushIfHasChildren(command); } diff --git a/app/widget/keyframeview/keyframeviewundo.cpp b/app/widget/keyframeview/keyframeviewundo.cpp index 9f109fc80..279186f44 100644 --- a/app/widget/keyframeview/keyframeviewundo.cpp +++ b/app/widget/keyframeview/keyframeviewundo.cpp @@ -26,8 +26,7 @@ namespace olive { -KeyframeSetTypeCommand::KeyframeSetTypeCommand(NodeKeyframe* key, NodeKeyframe::Type type, QUndoCommand *parent) : - UndoCommand(parent), +KeyframeSetTypeCommand::KeyframeSetTypeCommand(NodeKeyframe* key, NodeKeyframe::Type type) : key_(key), old_type_(key->type()), new_type_(type) @@ -39,18 +38,17 @@ Project *KeyframeSetTypeCommand::GetRelevantProject() const return key_->parent()->parent()->parent()->project(); } -void KeyframeSetTypeCommand::redo_internal() +void KeyframeSetTypeCommand::redo() { key_->set_type(new_type_); } -void KeyframeSetTypeCommand::undo_internal() +void KeyframeSetTypeCommand::undo() { key_->set_type(old_type_); } -KeyframeSetBezierControlPoint::KeyframeSetBezierControlPoint(NodeKeyframe* key, NodeKeyframe::BezierType mode, const QPointF& point, QUndoCommand *parent) : - UndoCommand(parent), +KeyframeSetBezierControlPoint::KeyframeSetBezierControlPoint(NodeKeyframe* key, NodeKeyframe::BezierType mode, const QPointF& point) : key_(key), mode_(mode), old_point_(key->bezier_control(mode_)), @@ -58,8 +56,7 @@ KeyframeSetBezierControlPoint::KeyframeSetBezierControlPoint(NodeKeyframe* key, { } -KeyframeSetBezierControlPoint::KeyframeSetBezierControlPoint(NodeKeyframe* key, NodeKeyframe::BezierType mode, const QPointF &new_point, const QPointF &old_point, QUndoCommand *parent) : - UndoCommand(parent), +KeyframeSetBezierControlPoint::KeyframeSetBezierControlPoint(NodeKeyframe* key, NodeKeyframe::BezierType mode, const QPointF &new_point, const QPointF &old_point) : key_(key), mode_(mode), old_point_(old_point), @@ -72,12 +69,12 @@ Project *KeyframeSetBezierControlPoint::GetRelevantProject() const return key_->parent()->parent()->parent()->project(); } -void KeyframeSetBezierControlPoint::redo_internal() +void KeyframeSetBezierControlPoint::redo() { key_->set_bezier_control(mode_, new_point_); } -void KeyframeSetBezierControlPoint::undo_internal() +void KeyframeSetBezierControlPoint::undo() { key_->set_bezier_control(mode_, old_point_); } diff --git a/app/widget/keyframeview/keyframeviewundo.h b/app/widget/keyframeview/keyframeviewundo.h index 97d50fe59..3f5b73261 100644 --- a/app/widget/keyframeview/keyframeviewundo.h +++ b/app/widget/keyframeview/keyframeviewundo.h @@ -28,13 +28,12 @@ namespace olive { class KeyframeSetTypeCommand : public UndoCommand { public: - KeyframeSetTypeCommand(NodeKeyframe* key, NodeKeyframe::Type type, QUndoCommand* parent = nullptr); + KeyframeSetTypeCommand(NodeKeyframe* key, NodeKeyframe::Type type); virtual Project* GetRelevantProject() const override; -protected: - virtual void redo_internal() override; - virtual void undo_internal() override; + virtual void redo() override; + virtual void undo() override; private: NodeKeyframe* key_; @@ -47,14 +46,13 @@ private: class KeyframeSetBezierControlPoint : public UndoCommand { public: - KeyframeSetBezierControlPoint(NodeKeyframe* key, NodeKeyframe::BezierType mode, const QPointF& point, QUndoCommand* parent = nullptr); - KeyframeSetBezierControlPoint(NodeKeyframe* key, NodeKeyframe::BezierType mode, const QPointF& new_point, const QPointF& old_point, QUndoCommand* parent = nullptr); + KeyframeSetBezierControlPoint(NodeKeyframe* key, NodeKeyframe::BezierType mode, const QPointF& point); + KeyframeSetBezierControlPoint(NodeKeyframe* key, NodeKeyframe::BezierType mode, const QPointF& new_point, const QPointF& old_point); virtual Project* GetRelevantProject() const override; -protected: - virtual void redo_internal() override; - virtual void undo_internal() override; + virtual void redo() override; + virtual void undo() override; private: NodeKeyframe* key_; diff --git a/app/widget/menu/menushared.cpp b/app/widget/menu/menushared.cpp index 1589d000d..1ccf5f37d 100644 --- a/app/widget/menu/menushared.cpp +++ b/app/widget/menu/menushared.cpp @@ -118,8 +118,8 @@ void MenuShared::AddItemsForNewMenu(Menu *m) void MenuShared::AddItemsForEditMenu(Menu *m, bool for_clips) { - m->addAction(Core::instance()->undo_stack()->createUndoAction(m)); - m->addAction(Core::instance()->undo_stack()->createRedoAction(m)); + m->addAction(Core::instance()->undo_stack()->GetUndoAction()); + m->addAction(Core::instance()->undo_stack()->GetRedoAction()); m->addSeparator(); diff --git a/app/widget/nodeparamview/nodeparamviewarraywidget.cpp b/app/widget/nodeparamview/nodeparamviewarraywidget.cpp index c49a6d6b0..412c54747 100644 --- a/app/widget/nodeparamview/nodeparamviewarraywidget.cpp +++ b/app/widget/nodeparamview/nodeparamviewarraywidget.cpp @@ -38,6 +38,13 @@ NodeParamViewArrayWidget::NodeParamViewArrayWidget(NodeInput *array, QWidget* pa UpdateCounter(); } +void NodeParamViewArrayWidget::mouseDoubleClickEvent(QMouseEvent *event) +{ + QWidget::mouseDoubleClickEvent(event); + + emit DoubleClicked(); +} + void NodeParamViewArrayWidget::UpdateCounter() { count_lbl_->setText(tr("%1 element(s)").arg(array_->ArraySize())); diff --git a/app/widget/nodeparamview/nodeparamviewarraywidget.h b/app/widget/nodeparamview/nodeparamviewarraywidget.h index 510c15dac..19dae47bd 100644 --- a/app/widget/nodeparamview/nodeparamviewarraywidget.h +++ b/app/widget/nodeparamview/nodeparamviewarraywidget.h @@ -56,6 +56,12 @@ class NodeParamViewArrayWidget : public QWidget public: NodeParamViewArrayWidget(NodeInput* array, QWidget* parent = nullptr); +signals: + void DoubleClicked(); + +protected: + virtual void mouseDoubleClickEvent(QMouseEvent* event) override; + private: NodeInput* array_; diff --git a/app/widget/nodeparamview/nodeparamviewitem.cpp b/app/widget/nodeparamview/nodeparamviewitem.cpp index 4dacb2555..0692c6d63 100644 --- a/app/widget/nodeparamview/nodeparamviewitem.cpp +++ b/app/widget/nodeparamview/nodeparamviewitem.cpp @@ -258,15 +258,14 @@ void NodeParamViewItemBody::CreateWidgets(QGridLayout* layout, NodeInput *input, // Default to collapsed array_collapse_btn->setChecked(false); - // Add data - array_collapse_btn->setProperty("input", Node::PtrToValue(input)); - // Collapse button always goes into column 0 layout->addWidget(array_collapse_btn, row, 0); // Connect signal to show/hide array params when toggled connect(array_collapse_btn, &CollapseButton::toggled, this, &NodeParamViewItemBody::ArrayCollapseBtnPressed); + array_collapse_buttons_.insert(input, array_collapse_btn); + } else { NodeParamViewArrayButton* insert_element_btn = new NodeParamViewArrayButton(NodeParamViewArrayButton::kAdd); @@ -286,6 +285,7 @@ void NodeParamViewItemBody::CreateWidgets(QGridLayout* layout, NodeInput *input, // Create a widget/input bridge for this input ui_objects.widget_bridge = new NodeParamViewWidgetBridge(input, element, this); + connect(ui_objects.widget_bridge, &NodeParamViewWidgetBridge::ArrayWidgetDoubleClicked, this, &NodeParamViewItemBody::ToggleArrayExpanded); // 0 is for the array collapse button, 1 is for the main label, widgets start at 2 const int widget_start = 2; @@ -372,7 +372,7 @@ void NodeParamViewItemBody::SignalAllKeyframes() foreach (const NodeKeyframeTrack& track, input->GetKeyframeTracks(i.key().element)) { foreach (NodeKeyframe* key, track) { - InputAddedKeyframeInternal(input, key); + InputAddedKeyframeInternal(input, i.key().element, key); } } } @@ -406,7 +406,7 @@ void NodeParamViewItemBody::InputKeyframeEnableChanged(bool e, int element) foreach (NodeKeyframe* key, track) { if (e) { // Add a keyframe item for each keyframe - InputAddedKeyframeInternal(input, key); + InputAddedKeyframeInternal(input, element, key); } else { // Remove each keyframe item emit KeyframeRemoved(key); @@ -420,13 +420,13 @@ void NodeParamViewItemBody::InputAddedKeyframe(NodeKeyframe* key) // Get NodeInput that emitted this signal NodeInput* input = static_cast(sender()); - InputAddedKeyframeInternal(input, key); + InputAddedKeyframeInternal(input, key->element(), key); } -void NodeParamViewItemBody::InputAddedKeyframeInternal(NodeInput *input, NodeKeyframe* keyframe) +void NodeParamViewItemBody::InputAddedKeyframeInternal(NodeInput *input, int element, NodeKeyframe* keyframe) { // Find its row in the parameters - QLabel* lbl = input_ui_map_.value(input).main_label; + QLabel* lbl = input_ui_map_.value({input, element}).main_label; // Find label's Y position QPoint lbl_center = lbl->rect().center(); @@ -439,7 +439,7 @@ void NodeParamViewItemBody::InputAddedKeyframeInternal(NodeInput *input, NodeKey void NodeParamViewItemBody::ArrayCollapseBtnPressed(bool checked) { - NodeInput* input = Node::ValueToPtr(sender()->property("input")); + NodeInput* input = array_collapse_buttons_.key(static_cast(sender())); array_ui_.value(input).widget->setVisible(checked); } @@ -486,7 +486,7 @@ void NodeParamViewItemBody::ArrayAppendClicked() { for (auto it=array_ui_.cbegin(); it!=array_ui_.cend(); it++) { if (it.value().append_btn == sender()) { - it.key()->ArrayAppend(); + it.key()->ArrayAppend(true); break; } } @@ -498,7 +498,7 @@ void NodeParamViewItemBody::ArrayInsertClicked() if (it.value().array_insert_btn == sender()) { // Found our input and element const Node::InputConnection& ic = it.key(); - ic.input->ArrayInsert(ic.element); + ic.input->ArrayInsert(ic.element, true); break; } } @@ -510,12 +510,25 @@ void NodeParamViewItemBody::ArrayRemoveClicked() if (it.value().array_remove_btn == sender()) { // Found our input and element const Node::InputConnection& ic = it.key(); - ic.input->ArrayRemove(ic.element); + ic.input->ArrayRemove(ic.element, true); break; } } } +void NodeParamViewItemBody::ToggleArrayExpanded() +{ + NodeParamViewWidgetBridge* bridge = static_cast(sender()); + + for (auto it=input_ui_map_.cbegin(); it!=input_ui_map_.cend(); it++) { + if (it.value().widget_bridge == bridge) { + CollapseButton* b = array_collapse_buttons_.value(it.key().input); + b->setChecked(!b->isChecked()); + return; + } + } +} + NodeParamViewItemBody::InputUI::InputUI() : main_label(nullptr), widget_bridge(nullptr), diff --git a/app/widget/nodeparamview/nodeparamviewitem.h b/app/widget/nodeparamview/nodeparamviewitem.h index d3c6621a7..739ee071f 100644 --- a/app/widget/nodeparamview/nodeparamviewitem.h +++ b/app/widget/nodeparamview/nodeparamviewitem.h @@ -97,7 +97,7 @@ private: void UpdateUIForEdgeConnection(NodeInput* input, int element); - void InputAddedKeyframeInternal(NodeInput* input, NodeKeyframe* keyframe); + void InputAddedKeyframeInternal(NodeInput* input, int element, NodeKeyframe* keyframe); struct InputUI { InputUI(); @@ -121,6 +121,8 @@ private: QHash array_ui_; + QHash array_collapse_buttons_; + /** * @brief The column to place the keyframe controls in * @@ -149,6 +151,8 @@ private slots: void ArrayRemoveClicked(); + void ToggleArrayExpanded(); + }; class NodeParamViewItem : public QDockWidget diff --git a/app/widget/nodeparamview/nodeparamviewkeyframecontrol.cpp b/app/widget/nodeparamview/nodeparamviewkeyframecontrol.cpp index 8b583c963..bfa218d0c 100644 --- a/app/widget/nodeparamview/nodeparamviewkeyframecontrol.cpp +++ b/app/widget/nodeparamview/nodeparamviewkeyframecontrol.cpp @@ -145,7 +145,7 @@ void NodeParamViewKeyframeControl::ToggleKeyframe(bool e) QVector keys = input_->GetKeyframesAtTime(node_time, element_); - QUndoCommand* command = new QUndoCommand(); + MultiUndoCommand* command = new MultiUndoCommand(); int nb_tracks = input_->GetNumberOfKeyframeTracks(); @@ -158,20 +158,19 @@ void NodeParamViewKeyframeControl::ToggleKeyframe(bool e) i, element_); - new NodeParamInsertKeyframeCommand(input_, key, command); + command->add_child(new NodeParamInsertKeyframeCommand(input_, key)); } } else if (!e && !keys.isEmpty()) { // Remove all keyframes at this time foreach (NodeKeyframe* key, keys) { - new NodeParamRemoveKeyframeCommand(key, command); + command->add_child(new NodeParamRemoveKeyframeCommand(key)); if (input_->GetKeyframeTracks(key->track()).size() == 1) { // If this was the last keyframe on this track, set the standard value to the value at this time too - new NodeParamSetStandardValueCommand(input_, - key->track(), - element_, - input_->GetValueAtTimeForTrack(node_time, key->track(), element_), - command); + command->add_child(new NodeParamSetStandardValueCommand(input_, + key->track(), + element_, + input_->GetValueAtTimeForTrack(node_time, key->track(), element_))); } } } @@ -228,11 +227,11 @@ void NodeParamViewKeyframeControl::KeyframeEnableChanged(bool e) return; } - QUndoCommand* command = new QUndoCommand(); + MultiUndoCommand* command = new MultiUndoCommand(); if (e) { // Enable keyframing - new NodeParamSetKeyframingCommand(input_, element_, true, command); + command->add_child(new NodeParamSetKeyframingCommand(input_, element_, true)); // Create one keyframe across all tracks here const QVector& key_vals = input_->GetSplitStandardValue(element_); @@ -244,7 +243,7 @@ void NodeParamViewKeyframeControl::KeyframeEnableChanged(bool e) i, element_); - new NodeParamInsertKeyframeCommand(input_, key, command); + command->add_child(new NodeParamInsertKeyframeCommand(input_, key)); } } else { // Confirm the user wants to clear all keyframes @@ -259,17 +258,17 @@ void NodeParamViewKeyframeControl::KeyframeEnableChanged(bool e) // Delete all keyframes foreach (const NodeKeyframeTrack& track, input_->GetKeyframeTracks(element_)) { for (int i=track.size()-1;i>=0;i--) { - new NodeParamRemoveKeyframeCommand(track.at(i), command); + command->add_child(new NodeParamRemoveKeyframeCommand(track.at(i))); } } // Update standard value for (int i=0;iadd_child(new NodeParamSetStandardValueCommand(input_, i, element_, stored_vals.at(i))); } // Disable keyframing - new NodeParamSetKeyframingCommand(input_, element_, false, command); + command->add_child(new NodeParamSetKeyframingCommand(input_, element_, false)); } else { // Disable action has effectively been ignored diff --git a/app/widget/nodeparamview/nodeparamviewundo.cpp b/app/widget/nodeparamview/nodeparamviewundo.cpp index 95925062c..89ca47444 100644 --- a/app/widget/nodeparamview/nodeparamviewundo.cpp +++ b/app/widget/nodeparamview/nodeparamviewundo.cpp @@ -25,8 +25,7 @@ namespace olive { -NodeParamSetKeyframingCommand::NodeParamSetKeyframingCommand(NodeInput *input, int element, bool setting, QUndoCommand *parent) : - UndoCommand(parent), +NodeParamSetKeyframingCommand::NodeParamSetKeyframingCommand(NodeInput *input, int element, bool setting) : input_(input), setting_(setting), element_(element) @@ -39,26 +38,24 @@ Project *NodeParamSetKeyframingCommand::GetRelevantProject() const return input_->parent()->parent()->project(); } -void NodeParamSetKeyframingCommand::redo_internal() +void NodeParamSetKeyframingCommand::redo() { input_->SetIsKeyframing(setting_, element_); } -void NodeParamSetKeyframingCommand::undo_internal() +void NodeParamSetKeyframingCommand::undo() { input_->SetIsKeyframing(!setting_, element_); } -NodeParamSetKeyframeValueCommand::NodeParamSetKeyframeValueCommand(NodeKeyframe* key, const QVariant& value, QUndoCommand* parent) : - UndoCommand(parent), +NodeParamSetKeyframeValueCommand::NodeParamSetKeyframeValueCommand(NodeKeyframe* key, const QVariant& value) : key_(key), old_value_(key_->value()), new_value_(value) { } -NodeParamSetKeyframeValueCommand::NodeParamSetKeyframeValueCommand(NodeKeyframe* key, const QVariant &new_value, const QVariant &old_value, QUndoCommand *parent) : - UndoCommand(parent), +NodeParamSetKeyframeValueCommand::NodeParamSetKeyframeValueCommand(NodeKeyframe* key, const QVariant &new_value, const QVariant &old_value) : key_(key), old_value_(old_value), new_value_(new_value) @@ -71,18 +68,17 @@ Project *NodeParamSetKeyframeValueCommand::GetRelevantProject() const return key_->parent()->parent()->parent()->project(); } -void NodeParamSetKeyframeValueCommand::redo_internal() +void NodeParamSetKeyframeValueCommand::redo() { key_->set_value(new_value_); } -void NodeParamSetKeyframeValueCommand::undo_internal() +void NodeParamSetKeyframeValueCommand::undo() { key_->set_value(old_value_); } -NodeParamInsertKeyframeCommand::NodeParamInsertKeyframeCommand(NodeInput *input, NodeKeyframe* keyframe, QUndoCommand* parent) : - UndoCommand(parent), +NodeParamInsertKeyframeCommand::NodeParamInsertKeyframeCommand(NodeInput *input, NodeKeyframe* keyframe) : input_(input), keyframe_(keyframe) { @@ -94,18 +90,17 @@ Project *NodeParamInsertKeyframeCommand::GetRelevantProject() const return input_->parent()->parent()->project(); } -void NodeParamInsertKeyframeCommand::redo_internal() +void NodeParamInsertKeyframeCommand::redo() { keyframe_->setParent(input_); } -void NodeParamInsertKeyframeCommand::undo_internal() +void NodeParamInsertKeyframeCommand::undo() { keyframe_->setParent(&memory_manager_); } -NodeParamRemoveKeyframeCommand::NodeParamRemoveKeyframeCommand(NodeKeyframe* keyframe, QUndoCommand *parent) : - UndoCommand(parent), +NodeParamRemoveKeyframeCommand::NodeParamRemoveKeyframeCommand(NodeKeyframe* keyframe) : input_(keyframe->parent()), keyframe_(keyframe) { @@ -116,27 +111,25 @@ Project *NodeParamRemoveKeyframeCommand::GetRelevantProject() const return input_->parent()->parent()->project(); } -void NodeParamRemoveKeyframeCommand::redo_internal() +void NodeParamRemoveKeyframeCommand::redo() { // Removes from input keyframe_->setParent(&memory_manager_); } -void NodeParamRemoveKeyframeCommand::undo_internal() +void NodeParamRemoveKeyframeCommand::undo() { keyframe_->setParent(input_); } -NodeParamSetKeyframeTimeCommand::NodeParamSetKeyframeTimeCommand(NodeKeyframe* key, const rational &time, QUndoCommand *parent) : - UndoCommand(parent), +NodeParamSetKeyframeTimeCommand::NodeParamSetKeyframeTimeCommand(NodeKeyframe* key, const rational &time) : key_(key), old_time_(key->time()), new_time_(time) { } -NodeParamSetKeyframeTimeCommand::NodeParamSetKeyframeTimeCommand(NodeKeyframe* key, const rational &new_time, const rational &old_time, QUndoCommand *parent) : - UndoCommand(parent), +NodeParamSetKeyframeTimeCommand::NodeParamSetKeyframeTimeCommand(NodeKeyframe* key, const rational &new_time, const rational &old_time) : key_(key), old_time_(old_time), new_time_(new_time) @@ -148,18 +141,17 @@ Project *NodeParamSetKeyframeTimeCommand::GetRelevantProject() const return key_->parent()->parent()->parent()->project(); } -void NodeParamSetKeyframeTimeCommand::redo_internal() +void NodeParamSetKeyframeTimeCommand::redo() { key_->set_time(new_time_); } -void NodeParamSetKeyframeTimeCommand::undo_internal() +void NodeParamSetKeyframeTimeCommand::undo() { key_->set_time(old_time_); } -NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(NodeInput *input, int track, int element, const QVariant &value, QUndoCommand *parent) : - UndoCommand(parent), +NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(NodeInput *input, int track, int element, const QVariant &value) : input_(input), element_(element), track_(track), @@ -168,8 +160,7 @@ NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(NodeInput *in { } -NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(NodeInput *input, int track, int element, const QVariant &new_value, const QVariant &old_value, QUndoCommand *parent) : - UndoCommand(parent), +NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(NodeInput *input, int track, int element, const QVariant &new_value, const QVariant &old_value) : input_(input), element_(element), track_(track), @@ -183,14 +174,19 @@ Project *NodeParamSetStandardValueCommand::GetRelevantProject() const return input_->parent()->parent()->project(); } -void NodeParamSetStandardValueCommand::redo_internal() +void NodeParamSetStandardValueCommand::redo() { input_->SetStandardValueOnTrack(new_value_, track_, element_); } -void NodeParamSetStandardValueCommand::undo_internal() +void NodeParamSetStandardValueCommand::undo() { input_->SetStandardValueOnTrack(old_value_, track_, element_); } +Project *NodeParamArrayInsertCommand::GetRelevantProject() const +{ + return input_->parent()->parent()->project(); +} + } diff --git a/app/widget/nodeparamview/nodeparamviewundo.h b/app/widget/nodeparamview/nodeparamviewundo.h index ad15c9892..8f28600eb 100644 --- a/app/widget/nodeparamview/nodeparamviewundo.h +++ b/app/widget/nodeparamview/nodeparamviewundo.h @@ -26,15 +26,15 @@ namespace olive { -class NodeParamSetKeyframingCommand : public UndoCommand { +class NodeParamSetKeyframingCommand : public UndoCommand +{ public: - NodeParamSetKeyframingCommand(NodeInput* input, int element, bool setting, QUndoCommand* parent = nullptr); + NodeParamSetKeyframingCommand(NodeInput* input, int element, bool setting); virtual Project* GetRelevantProject() const override; -protected: - virtual void redo_internal() override; - virtual void undo_internal() override; + virtual void redo() override; + virtual void undo() override; private: NodeInput* input_; @@ -43,15 +43,15 @@ private: }; -class NodeParamInsertKeyframeCommand : public UndoCommand { +class NodeParamInsertKeyframeCommand : public UndoCommand +{ public: - NodeParamInsertKeyframeCommand(NodeInput* input, NodeKeyframe* keyframe, QUndoCommand *parent = nullptr); + NodeParamInsertKeyframeCommand(NodeInput* input, NodeKeyframe* keyframe); virtual Project* GetRelevantProject() const override; -protected: - virtual void redo_internal() override; - virtual void undo_internal() override; + virtual void redo() override; + virtual void undo() override; private: NodeInput* input_; @@ -62,15 +62,15 @@ private: }; -class NodeParamRemoveKeyframeCommand : public UndoCommand { +class NodeParamRemoveKeyframeCommand : public UndoCommand +{ public: - NodeParamRemoveKeyframeCommand(NodeKeyframe* keyframe, QUndoCommand *parent = nullptr); + NodeParamRemoveKeyframeCommand(NodeKeyframe* keyframe); virtual Project* GetRelevantProject() const override; -protected: - virtual void redo_internal() override; - virtual void undo_internal() override; + virtual void redo() override; + virtual void undo() override; private: NodeInput* input_; @@ -81,16 +81,16 @@ private: }; -class NodeParamSetKeyframeTimeCommand : public UndoCommand { +class NodeParamSetKeyframeTimeCommand : public UndoCommand +{ public: - NodeParamSetKeyframeTimeCommand(NodeKeyframe* key, const rational& time, QUndoCommand* parent = nullptr); - NodeParamSetKeyframeTimeCommand(NodeKeyframe* key, const rational& new_time, const rational& old_time, QUndoCommand* parent = nullptr); + NodeParamSetKeyframeTimeCommand(NodeKeyframe* key, const rational& time); + NodeParamSetKeyframeTimeCommand(NodeKeyframe* key, const rational& new_time, const rational& old_time); virtual Project* GetRelevantProject() const override; -protected: - virtual void redo_internal() override; - virtual void undo_internal() override; + virtual void redo() override; + virtual void undo() override; private: NodeKeyframe* key_; @@ -100,16 +100,16 @@ private: }; -class NodeParamSetKeyframeValueCommand : public UndoCommand { +class NodeParamSetKeyframeValueCommand : public UndoCommand +{ public: - NodeParamSetKeyframeValueCommand(NodeKeyframe* key, const QVariant& value, QUndoCommand* parent = nullptr); - NodeParamSetKeyframeValueCommand(NodeKeyframe* key, const QVariant& new_value, const QVariant& old_value, QUndoCommand* parent = nullptr); + NodeParamSetKeyframeValueCommand(NodeKeyframe* key, const QVariant& value); + NodeParamSetKeyframeValueCommand(NodeKeyframe* key, const QVariant& new_value, const QVariant& old_value); virtual Project* GetRelevantProject() const override; -protected: - virtual void redo_internal() override; - virtual void undo_internal() override; + virtual void redo() override; + virtual void undo() override; private: NodeKeyframe* key_; @@ -119,16 +119,16 @@ private: }; -class NodeParamSetStandardValueCommand : public UndoCommand { +class NodeParamSetStandardValueCommand : public UndoCommand +{ public: - NodeParamSetStandardValueCommand(NodeInput* input, int track, int element, const QVariant& value, QUndoCommand* parent = nullptr); - NodeParamSetStandardValueCommand(NodeInput* input, int track, int element, const QVariant& new_value, const QVariant& old_value, QUndoCommand* parent = nullptr); + NodeParamSetStandardValueCommand(NodeInput* input, int track, int element, const QVariant& value); + NodeParamSetStandardValueCommand(NodeInput* input, int track, int element, const QVariant& new_value, const QVariant& old_value); virtual Project* GetRelevantProject() const override; -protected: - virtual void redo_internal() override; - virtual void undo_internal() override; + virtual void redo() override; + virtual void undo() override; private: NodeInput* input_; @@ -140,6 +140,33 @@ private: }; +class NodeParamArrayInsertCommand : public UndoCommand +{ +public: + NodeParamArrayInsertCommand(NodeInput* input, int index) : + input_(input), + index_(index) + { + } + + virtual Project* GetRelevantProject() const override; + + virtual void redo() override + { + input_->ArrayInsert(index_); + } + + virtual void undo() override + { + input_->ArrayRemove(index_); + } + +private: + NodeInput* input_; + int index_; + +}; + } #endif // NODEPARAMVIEWUNDO_H diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp index a9e13dc65..f8e70ded4 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp @@ -70,6 +70,7 @@ void NodeParamViewWidgetBridge::CreateWidgets() if (input_->IsArray() && element_ == -1) { NodeParamViewArrayWidget* w = new NodeParamViewArrayWidget(input_); + connect(w, &NodeParamViewArrayWidget::DoubleClicked, this, &NodeParamViewWidgetBridge::ArrayWidgetDoubleClicked); widgets_.append(w); } else { @@ -190,14 +191,14 @@ void NodeParamViewWidgetBridge::CreateWidgets() void NodeParamViewWidgetBridge::SetInputValue(const QVariant &value, int track) { - QUndoCommand* command = new QUndoCommand(); + MultiUndoCommand* command = new MultiUndoCommand(); SetInputValueInternal(value, track, command); Core::instance()->undo_stack()->pushIfHasChildren(command); } -void NodeParamViewWidgetBridge::SetInputValueInternal(const QVariant &value, int track, QUndoCommand *command) +void NodeParamViewWidgetBridge::SetInputValueInternal(const QVariant &value, int track, MultiUndoCommand *command) { rational node_time = GetCurrentTimeAsNodeTime(); @@ -205,7 +206,7 @@ void NodeParamViewWidgetBridge::SetInputValueInternal(const QVariant &value, int NodeKeyframe* existing_key = input_->GetKeyframeAtTimeOnTrack(node_time, track, element_); if (existing_key) { - new NodeParamSetKeyframeValueCommand(existing_key, value, command); + command->add_child(new NodeParamSetKeyframeValueCommand(existing_key, value)); } else { // No existing key, create a new one NodeKeyframe* new_key = new NodeKeyframe(node_time, @@ -214,10 +215,10 @@ void NodeParamViewWidgetBridge::SetInputValueInternal(const QVariant &value, int track, element_); - new NodeParamInsertKeyframeCommand(input_, new_key, command); + command->add_child(new NodeParamInsertKeyframeCommand(input_, new_key)); } } else { - new NodeParamSetStandardValueCommand(input_, track, element_, value, command); + command->add_child(new NodeParamSetStandardValueCommand(input_, track, element_, value)); } } @@ -231,13 +232,11 @@ void NodeParamViewWidgetBridge::ProcessSlider(SliderBase *slider, const QVariant // While we're dragging, we block the input's normal signalling and create our own if (!dragger_.IsStarted()) { - dragger_.Start(input_, node_time, slider_track); + dragger_.Start(input_, node_time, slider_track, element_); } dragger_.Drag(value); - //input_->parentNode()->InvalidateVisible(input_, input_); - } else if (dragger_.IsStarted()) { // We were dragging and just stopped @@ -245,8 +244,10 @@ void NodeParamViewWidgetBridge::ProcessSlider(SliderBase *slider, const QVariant dragger_.End(); } else { + // No drag was involved, we can just push the value SetInputValue(value, slider_track); + } } @@ -321,7 +322,7 @@ void NodeParamViewWidgetBridge::WidgetCallback() // Sender is a ColorButton ManagedColor c = static_cast(sender())->GetColor(); - QUndoCommand* command = new QUndoCommand(); + MultiUndoCommand* command = new MultiUndoCommand(); SetInputValueInternal(c.red(), 0, command); SetInputValueInternal(c.green(), 1, command); @@ -394,7 +395,7 @@ void NodeParamViewWidgetBridge::CreateSliders(int count) void NodeParamViewWidgetBridge::UpdateWidgetValues() { - if (input_->IsArray()) { + if (input_->IsArray() && element_ == -1) { return; } @@ -428,7 +429,7 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues() } case NodeValue::kVec2: { - QVector2D vec2 = input_->GetValueAtTime(node_time, -1).value(); + QVector2D vec2 = input_->GetValueAtTime(node_time, element_).value(); QVector2D offset = input_->property("offset").value(); static_cast(widgets_.at(0))->SetValue(static_cast(vec2.x() + offset.x())); @@ -510,9 +511,11 @@ rational NodeParamViewWidgetBridge::GetCurrentTimeAsNodeTime() const return GetAdjustedTime(GetTimeTarget(), input_->parent(), time_, true); } -void NodeParamViewWidgetBridge::InputValueChanged(const TimeRange &range) +void NodeParamViewWidgetBridge::InputValueChanged(const TimeRange &range, int element) { - if (!dragger_.IsStarted() && range.in() <= time_ && range.out() >= time_) { + if (element == element_ + && !dragger_.IsStarted() + && range.in() <= time_ && range.out() >= time_) { // We'll need to update the widgets because the values have changed on our current time UpdateWidgetValues(); } diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.h b/app/widget/nodeparamview/nodeparamviewwidgetbridge.h index 48aad2ccf..b3a1c0736 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.h +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.h @@ -47,12 +47,15 @@ public: const QList& widgets() const; +signals: + void ArrayWidgetDoubleClicked(); + private: void CreateWidgets(); void SetInputValue(const QVariant& value, int track); - void SetInputValueInternal(const QVariant& value, int track, QUndoCommand* command); + void SetInputValueInternal(const QVariant& value, int track, MultiUndoCommand *command); void ProcessSlider(SliderBase* slider, const QVariant& value); @@ -77,7 +80,7 @@ private: private slots: void WidgetCallback(); - void InputValueChanged(const TimeRange& range); + void InputValueChanged(const TimeRange& range, int element); void PropertyChanged(const QString& key, const QVariant& value); diff --git a/app/widget/nodeview/nodeview.cpp b/app/widget/nodeview/nodeview.cpp index e8f88f2f5..46ad9fe91 100644 --- a/app/widget/nodeview/nodeview.cpp +++ b/app/widget/nodeview/nodeview.cpp @@ -118,13 +118,13 @@ void NodeView::DeleteSelected() return; } - QUndoCommand* command = new QUndoCommand(); + MultiUndoCommand* command = new MultiUndoCommand(); { QVector selected_edges = scene_.GetSelectedEdges(); foreach (NodeViewEdge* edge, selected_edges) { - new NodeEdgeRemoveCommand(edge->output(), edge->input(), edge->element(), command); + command->add_child(new NodeEdgeRemoveCommand(edge->output(), edge->input(), edge->element())); } } @@ -141,7 +141,7 @@ void NodeView::DeleteSelected() if (!selected_nodes.isEmpty()) { foreach (Node* node, selected_nodes) { - new NodeRemoveAndDisconnectCommand(node, command); + command->add_child(new NodeRemoveAndDisconnectCommand(node)); } } } @@ -292,7 +292,7 @@ void NodeView::Paste() return; } - QUndoCommand* command = new QUndoCommand(); + MultiUndoCommand* command = new MultiUndoCommand(); QVector pasted_nodes = PasteNodesFromClipboard(graph_, command); @@ -315,7 +315,7 @@ void NodeView::Duplicate() return; } - QUndoCommand* command = new QUndoCommand(); + MultiUndoCommand* command = new MultiUndoCommand(); QVector duplicated_nodes = Node::CopyDependencyGraph(selected, command); @@ -536,14 +536,14 @@ void NodeView::mouseReleaseEvent(QMouseEvent *event) if (drop_edge_) { // We have everything we need to place the node in between - QUndoCommand* command = new QUndoCommand(); + MultiUndoCommand* command = new MultiUndoCommand(); // Remove old edge - new NodeEdgeRemoveCommand(drop_edge_->output(), drop_edge_->input(), drop_edge_->element(), command); + command->add_child(new NodeEdgeRemoveCommand(drop_edge_->output(), drop_edge_->input(), drop_edge_->element())); // Place new edges - new NodeEdgeAddCommand(drop_edge_->output(), drop_input_, -1, command); - new NodeEdgeAddCommand(dropping_node, drop_edge_->input(), drop_edge_->element(), command); + command->add_child(new NodeEdgeAddCommand(drop_edge_->output(), drop_input_, -1)); + command->add_child(new NodeEdgeAddCommand(dropping_node, drop_edge_->input(), drop_edge_->element())); Core::instance()->undo_stack()->push(command); } diff --git a/app/widget/nodeview/nodeviewitem.h b/app/widget/nodeview/nodeviewitem.h index 9985dbceb..349dfdff9 100644 --- a/app/widget/nodeview/nodeviewitem.h +++ b/app/widget/nodeview/nodeviewitem.h @@ -24,7 +24,6 @@ #include #include #include -#include #include #include "node/node.h" diff --git a/app/widget/nodeview/nodeviewundo.cpp b/app/widget/nodeview/nodeviewundo.cpp index f7f6449ce..9e57694e9 100644 --- a/app/widget/nodeview/nodeviewundo.cpp +++ b/app/widget/nodeview/nodeviewundo.cpp @@ -25,22 +25,41 @@ namespace olive { -NodeEdgeAddCommand::NodeEdgeAddCommand(Node *output, NodeInput *input, int element, QUndoCommand *parent) : - UndoCommand(parent), +NodeEdgeAddCommand::NodeEdgeAddCommand(Node *output, NodeInput *input, int element) : output_(output), input_(input), - element_(element) + element_(element), + remove_command_(nullptr) { } -void NodeEdgeAddCommand::redo_internal() +NodeEdgeAddCommand::~NodeEdgeAddCommand() { + delete remove_command_; +} + +void NodeEdgeAddCommand::redo() +{ + if (input_->IsConnected(element_)) { + if (!remove_command_) { + remove_command_ = new NodeEdgeRemoveCommand(input_->GetConnectedNode(element_), + input_, + element_); + } + + remove_command_->redo(); + } + Node::ConnectEdge(output_, input_, element_); } -void NodeEdgeAddCommand::undo_internal() +void NodeEdgeAddCommand::undo() { Node::DisconnectEdge(output_, input_, element_); + + if (remove_command_) { + remove_command_->undo(); + } } Project *NodeEdgeAddCommand::GetRelevantProject() const @@ -48,20 +67,19 @@ Project *NodeEdgeAddCommand::GetRelevantProject() const return output_->parent()->project(); } -NodeEdgeRemoveCommand::NodeEdgeRemoveCommand(Node *output, NodeInput *input, int element, QUndoCommand *parent) : - UndoCommand(parent), +NodeEdgeRemoveCommand::NodeEdgeRemoveCommand(Node *output, NodeInput *input, int element) : output_(output), input_(input), element_(element) { } -void NodeEdgeRemoveCommand::redo_internal() +void NodeEdgeRemoveCommand::redo() { Node::DisconnectEdge(output_, input_, element_); } -void NodeEdgeRemoveCommand::undo_internal() +void NodeEdgeRemoveCommand::undo() { Node::ConnectEdge(output_, input_, element_); } @@ -71,8 +89,7 @@ Project *NodeEdgeRemoveCommand::GetRelevantProject() const return output_->parent()->project(); } -NodeAddCommand::NodeAddCommand(NodeGraph *graph, Node *node, QUndoCommand *parent) : - UndoCommand(parent), +NodeAddCommand::NodeAddCommand(NodeGraph *graph, Node *node) : graph_(graph), node_(node) { @@ -80,12 +97,12 @@ NodeAddCommand::NodeAddCommand(NodeGraph *graph, Node *node, QUndoCommand *paren node_->setParent(&memory_manager_); } -void NodeAddCommand::redo_internal() +void NodeAddCommand::redo() { node_->setParent(graph_); } -void NodeAddCommand::undo_internal() +void NodeAddCommand::undo() { node_->setParent(&memory_manager_); } @@ -95,8 +112,7 @@ Project *NodeAddCommand::GetRelevantProject() const return graph_->project(); } -NodeCopyInputsCommand::NodeCopyInputsCommand(Node *src, Node *dest, bool include_connections, QUndoCommand *parent) : - QUndoCommand(parent), +NodeCopyInputsCommand::NodeCopyInputsCommand(Node *src, Node *dest, bool include_connections) : src_(src), dest_(dest), include_connections_(include_connections) @@ -110,21 +126,21 @@ void NodeCopyInputsCommand::redo() void NodeRemoveAndDisconnectCommand::prep() { - command_ = new QUndoCommand(); + command_ = new MultiUndoCommand(); // If this is a block, remove all links if (node_->HasLinks()) { - new NodeUnlinkAllCommand(node_, command_); + command_->add_child(new NodeUnlinkAllCommand(node_)); } // Disconnect everything foreach (const Node::InputConnection& conn, node_->edges()) { - new NodeEdgeRemoveCommand(node_, conn.input, conn.element, command_); + command_->add_child(new NodeEdgeRemoveCommand(node_, conn.input, conn.element)); } foreach (NodeInput* input, node_->inputs()) { for (auto it=input->edges().cbegin(); it!=input->edges().cend(); it++) { - new NodeEdgeRemoveCommand(it->second, input, it->first, command_); + command_->add_child(new NodeEdgeRemoveCommand(it->second, input, it->first)); } } } diff --git a/app/widget/nodeview/nodeviewundo.h b/app/widget/nodeview/nodeviewundo.h index a653e64fa..19c117654 100644 --- a/app/widget/nodeview/nodeviewundo.h +++ b/app/widget/nodeview/nodeviewundo.h @@ -21,8 +21,6 @@ #ifndef NODEVIEWUNDO_H #define NODEVIEWUNDO_H -#include - #include "node/graph.h" #include "node/node.h" #include "undo/undocommand.h" @@ -30,19 +28,18 @@ namespace olive { /** - * @brief An undoable command for connecting two NodeParams together + * @brief An undoable command for disconnecting two NodeParams * - * Can be considered a QUndoCommand wrapper for NodeParam::ConnectEdge()/ + * Can be considered a UndoCommand wrapper for NodeParam::DisonnectEdge()/ */ -class NodeEdgeAddCommand : public UndoCommand { +class NodeEdgeRemoveCommand : public UndoCommand { public: - NodeEdgeAddCommand(Node* output, NodeInput* input, int element, QUndoCommand* parent = nullptr); + NodeEdgeRemoveCommand(Node* output, NodeInput* input, int element); virtual Project* GetRelevantProject() const override; -protected: - virtual void redo_internal() override; - virtual void undo_internal() override; + virtual void redo() override; + virtual void undo() override; private: Node* output_; @@ -52,36 +49,38 @@ private: }; /** - * @brief An undoable command for disconnecting two NodeParams + * @brief An undoable command for connecting two NodeParams together * - * Can be considered a QUndoCommand wrapper for NodeParam::DisonnectEdge()/ + * Can be considered a UndoCommand wrapper for NodeParam::ConnectEdge()/ */ -class NodeEdgeRemoveCommand : public UndoCommand { +class NodeEdgeAddCommand : public UndoCommand { public: - NodeEdgeRemoveCommand(Node* output, NodeInput* input, int element, QUndoCommand* parent = nullptr); + NodeEdgeAddCommand(Node* output, NodeInput* input, int element); + + virtual ~NodeEdgeAddCommand() override; virtual Project* GetRelevantProject() const override; -protected: - virtual void redo_internal() override; - virtual void undo_internal() override; + virtual void redo() override; + virtual void undo() override; private: Node* output_; NodeInput* input_; int element_; + NodeEdgeRemoveCommand* remove_command_; + }; class NodeAddCommand : public UndoCommand { public: - NodeAddCommand(NodeGraph* graph, Node* node, QUndoCommand* parent = nullptr); + NodeAddCommand(NodeGraph* graph, Node* node); virtual Project* GetRelevantProject() const override; -protected: - virtual void redo_internal() override; - virtual void undo_internal() override; + virtual void redo() override; + virtual void undo() override; private: QObject memory_manager_; @@ -92,8 +91,7 @@ private: class NodeRemoveAndDisconnectCommand : public UndoCommand { public: - NodeRemoveAndDisconnectCommand(Node* node, QUndoCommand* parent = nullptr) : - UndoCommand(parent), + NodeRemoveAndDisconnectCommand(Node* node) : node_(node), graph_(nullptr), command_(nullptr), @@ -115,8 +113,7 @@ public: } } -protected: - virtual void redo_internal() override + virtual void redo() override { if (!prepped_) { prep(); @@ -129,7 +126,7 @@ protected: node_->setParent(&memory_manager_); } - virtual void undo_internal() override + virtual void undo() override { node_->setParent(graph_); graph_ = nullptr; @@ -145,7 +142,7 @@ private: Node* node_; NodeGraph* graph_; - QUndoCommand* command_; + MultiUndoCommand* command_; bool prepped_; @@ -153,8 +150,7 @@ private: class NodeRemoveWithExclusiveDependenciesAndDisconnect : public UndoCommand { public: - NodeRemoveWithExclusiveDependenciesAndDisconnect(Node* node, QUndoCommand* parent = nullptr) : - UndoCommand(parent), + NodeRemoveWithExclusiveDependenciesAndDisconnect(Node* node) : node_(node), command_(nullptr), prepped_(false) @@ -175,8 +171,7 @@ public: } } -protected: - virtual void redo_internal() override + virtual void redo() override { if (!prepped_) { prep(); @@ -186,7 +181,7 @@ protected: command_->redo(); } - virtual void undo_internal() override + virtual void undo() override { command_->undo(); } @@ -194,33 +189,35 @@ protected: private: void prep() { - command_ = new QUndoCommand(); + command_ = new MultiUndoCommand(); - new NodeRemoveAndDisconnectCommand(node_, command_); + command_->add_child(new NodeRemoveAndDisconnectCommand(node_)); // Remove exclusive dependencies QVector deps = node_->GetExclusiveDependencies(); foreach (Node* d, deps) { - new NodeRemoveAndDisconnectCommand(d, command_); + command_->add_child(new NodeRemoveAndDisconnectCommand(d)); } } Node* node_; - QUndoCommand* command_; + MultiUndoCommand* command_; bool prepped_; }; -class NodeCopyInputsCommand : public QUndoCommand { +class NodeCopyInputsCommand : public UndoCommand { public: NodeCopyInputsCommand(Node* src, Node* dest, - bool include_connections, - QUndoCommand* parent = nullptr); + bool include_connections); -protected: virtual void redo() override; + virtual void undo() override {} + + virtual Project* GetRelevantProject() const override {return nullptr;} + private: Node* src_; @@ -232,8 +229,7 @@ private: class NodeLinkCommand : public UndoCommand { public: - NodeLinkCommand(Node* a, Node* b, bool link, QUndoCommand* parent = nullptr) : - UndoCommand(parent), + NodeLinkCommand(Node* a, Node* b, bool link) : a_(a), b_(b), link_(link) @@ -245,8 +241,7 @@ public: return a_->parent()->project(); } -protected: - virtual void redo_internal() override + virtual void redo() override { if (link_) { done_ = Node::Link(a_, b_); @@ -255,7 +250,7 @@ protected: } } - virtual void undo_internal() override + virtual void undo() override { if (done_) { if (link_) { @@ -276,8 +271,7 @@ private: class NodeUnlinkAllCommand : public UndoCommand { public: - NodeUnlinkAllCommand(Node* node, QUndoCommand* parent = nullptr) : - UndoCommand(parent), + NodeUnlinkAllCommand(Node* node) : node_(node) { } @@ -287,8 +281,7 @@ public: return node_->parent()->project(); } -protected: - virtual void redo_internal() override + virtual void redo() override { unlinked_ = node_->links(); @@ -297,7 +290,7 @@ protected: } } - virtual void undo_internal() override + virtual void undo() override { foreach (Node* link, unlinked_) { Node::Link(node_, link); @@ -313,16 +306,15 @@ private: }; -class NodeLinkManyCommand : public UndoCommand { +class NodeLinkManyCommand : public MultiUndoCommand { public: - NodeLinkManyCommand(const QVector nodes, bool link, QUndoCommand* parent = nullptr) : - UndoCommand(parent), + NodeLinkManyCommand(const QVector nodes, bool link) : nodes_(nodes) { foreach (Node* a, nodes_) { foreach (Node* b, nodes_) { if (a != b) { - new NodeLinkCommand(a, b, link, this); + add_child(new NodeLinkCommand(a, b, link)); } } } diff --git a/app/widget/projectexplorer/projectexplorer.cpp b/app/widget/projectexplorer/projectexplorer.cpp index 1b39e4a04..b07ea05ff 100644 --- a/app/widget/projectexplorer/projectexplorer.cpp +++ b/app/widget/projectexplorer/projectexplorer.cpp @@ -571,7 +571,7 @@ void ProjectExplorer::DeleteSelected() return; } - QUndoCommand* command = new QUndoCommand(); + MultiUndoCommand* command = new MultiUndoCommand(); foreach (Item* item, selected) { // Verify whether this item is in use anywhere @@ -629,7 +629,7 @@ void ProjectExplorer::DeleteSelected() if (msgbox.clickedButton() == offline_btn || msgbox.clickedButton() == delete_clip_btn) { // For safety, even if we're deleting clips, we'll offline the footage nodes too - new OfflineFootageCommand(footage_nodes, command); + command->add_child(new OfflineFootageCommand(footage_nodes)); } @@ -676,7 +676,7 @@ void ProjectExplorer::DeleteSelected() break; } - new ProjectViewModel::RemoveItemCommand(&model_, item, command); + command->add_child(new ProjectViewModel::RemoveItemCommand(&model_, item)); } Core::instance()->undo_stack()->pushIfHasChildren(command); diff --git a/app/widget/projectexplorer/projectexplorerundo.h b/app/widget/projectexplorer/projectexplorerundo.h index ef98e4bb3..19ec360c0 100644 --- a/app/widget/projectexplorer/projectexplorerundo.h +++ b/app/widget/projectexplorer/projectexplorerundo.h @@ -31,8 +31,7 @@ namespace olive { */ class OfflineFootageCommand : public UndoCommand { public: - OfflineFootageCommand(const QVector& media, QUndoCommand* parent = nullptr) : - UndoCommand(parent) + OfflineFootageCommand(const QVector& media) { foreach (MediaInput* i, media) { stream_data_.insert(i, i->stream()); @@ -46,15 +45,14 @@ public: return project_; } -protected: - virtual void redo_internal() override + virtual void redo() override { for (auto it=stream_data_.cbegin(); it!=stream_data_.cend(); it++) { it.key()->SetStream(nullptr); } } - virtual void undo_internal() override + virtual void undo() override { for (auto it=stream_data_.cbegin(); it!=stream_data_.cend(); it++) { it.key()->SetStream(it.value()); diff --git a/app/widget/timebased/timebasedwidget.cpp b/app/widget/timebased/timebasedwidget.cpp index 505e99027..68c506c33 100644 --- a/app/widget/timebased/timebasedwidget.cpp +++ b/app/widget/timebased/timebasedwidget.cpp @@ -21,7 +21,6 @@ #include "timebasedwidget.h" #include -#include #include "common/autoscroll.h" #include "common/timecodefunctions.h" @@ -435,11 +434,11 @@ void TimeBasedWidget::SetPoint(Timeline::MovementMode m, const rational& time) return; } - QUndoCommand* command = new QUndoCommand(); + MultiUndoCommand* command = new MultiUndoCommand(); // Enable workarea if it isn't already enabled if (!points_->workarea()->enabled()) { - new WorkareaSetEnabledCommand(GetTimelinePointsProject(), points_, true, command); + command->add_child(new WorkareaSetEnabledCommand(GetTimelinePointsProject(), points_, true)); } // Determine our new range @@ -464,7 +463,7 @@ void TimeBasedWidget::SetPoint(Timeline::MovementMode m, const rational& time) } // Set workarea - new WorkareaSetRangeCommand(GetTimelinePointsProject(), points_, TimeRange(in_point, out_point), command); + command->add_child(new WorkareaSetRangeCommand(GetTimelinePointsProject(), points_, TimeRange(in_point, out_point))); Core::instance()->undo_stack()->push(command); } @@ -647,12 +646,12 @@ Project *TimeBasedWidget::MarkerAddCommand::GetRelevantProject() const return project_; } -void TimeBasedWidget::MarkerAddCommand::redo_internal() +void TimeBasedWidget::MarkerAddCommand::redo() { added_marker_ = marker_list_->AddMarker(range_, name_); } -void TimeBasedWidget::MarkerAddCommand::undo_internal() +void TimeBasedWidget::MarkerAddCommand::undo() { marker_list_->RemoveMarker(added_marker_); } diff --git a/app/widget/timebased/timebasedwidget.h b/app/widget/timebased/timebasedwidget.h index 1905d294c..083af2f6c 100644 --- a/app/widget/timebased/timebasedwidget.h +++ b/app/widget/timebased/timebasedwidget.h @@ -152,9 +152,8 @@ private: virtual Project* GetRelevantProject() const override; - protected: - virtual void redo_internal() override; - virtual void undo_internal() override; + virtual void redo() override; + virtual void undo() override; private: Project* project_; diff --git a/app/widget/timelinewidget/timelineundo.h b/app/widget/timelinewidget/timelineundo.h index 0bfac1994..88b3fc5d3 100644 --- a/app/widget/timelinewidget/timelineundo.h +++ b/app/widget/timelinewidget/timelineundo.h @@ -21,8 +21,6 @@ #ifndef TIMELINEUNDOABLE_H #define TIMELINEUNDOABLE_H -#include - #include "config/config.h" #include "core.h" #include "node/block/block.h" @@ -46,22 +44,21 @@ inline bool NodeCanBeRemoved(Node* n) return n->edges().empty(); } -inline QUndoCommand* CreateRemoveCommand(Node* n) +inline UndoCommand* CreateRemoveCommand(Node* n) { return new NodeRemoveWithExclusiveDependenciesAndDisconnect(n); } -inline QUndoCommand* CreateAndRunRemoveCommand(Node* n) +inline UndoCommand* CreateAndRunRemoveCommand(Node* n) { - QUndoCommand* command = CreateRemoveCommand(n); + UndoCommand* command = CreateRemoveCommand(n); command->redo(); return command; } class BlockResizeCommand : public UndoCommand { public: - BlockResizeCommand(Block* block, rational new_length, QUndoCommand* parent = nullptr) : - UndoCommand(parent), + BlockResizeCommand(Block* block, rational new_length) : block_(block), new_length_(new_length) { @@ -72,14 +69,13 @@ public: return block_->parent()->project(); } -protected: - virtual void redo_internal() override + virtual void redo() override { old_length_ = block_->length(); block_->set_length_and_media_out(new_length_); } - virtual void undo_internal() override + virtual void undo() override { block_->set_length_and_media_out(old_length_); } @@ -93,8 +89,7 @@ private: class BlockResizeWithMediaInCommand : public UndoCommand { public: - BlockResizeWithMediaInCommand(Block* block, rational new_length, QUndoCommand* parent = nullptr) : - UndoCommand(parent), + BlockResizeWithMediaInCommand(Block* block, rational new_length) : block_(block), new_length_(new_length) { @@ -105,14 +100,13 @@ public: return block_->parent()->project(); } -protected: - virtual void redo_internal() override + virtual void redo() override { old_length_ = block_->length(); block_->set_length_and_media_in(new_length_); } - virtual void undo_internal() override + virtual void undo() override { block_->set_length_and_media_in(old_length_); } @@ -135,8 +129,7 @@ private: */ class BlockTrimCommand : public UndoCommand { public: - BlockTrimCommand(Track *track, Block* block, rational new_length, Timeline::MovementMode mode, QUndoCommand* command = nullptr) : - UndoCommand(command), + BlockTrimCommand(Track *track, Block* block, rational new_length, Timeline::MovementMode mode) : prepped_(false), track_(track), block_(block), @@ -177,8 +170,7 @@ public: remove_block_from_graph_ = e; } -protected: - virtual void redo_internal() override + virtual void redo() override { if (!prepped_) { prep(); @@ -245,7 +237,7 @@ protected: track_->InvalidateCache(invalidate_range, track_->block_input()); } - virtual void undo_internal() override + virtual void undo() override { if (doing_nothing_) { return; @@ -360,7 +352,7 @@ private: bool needs_adjacent_; bool we_created_adjacent_; bool we_removed_adjacent_; - QUndoCommand* deleted_adjacent_command_; + UndoCommand* deleted_adjacent_command_; bool trim_is_a_roll_edit_; bool remove_block_from_graph_; @@ -371,8 +363,7 @@ private: class BlockSetMediaInCommand : public UndoCommand { public: - BlockSetMediaInCommand(Block* block, rational new_media_in, QUndoCommand* parent = nullptr) : - UndoCommand(parent), + BlockSetMediaInCommand(Block* block, rational new_media_in) : block_(block), new_media_in_(new_media_in) { @@ -383,14 +374,13 @@ public: return block_->parent()->project(); } -protected: - virtual void redo_internal() override + virtual void redo() override { old_media_in_ = block_->media_in(); block_->set_media_in(new_media_in_); } - virtual void undo_internal() override + virtual void undo() override { block_->set_media_in(old_media_in_); } @@ -403,8 +393,7 @@ private: class TrackRippleRemoveBlockCommand : public UndoCommand { public: - TrackRippleRemoveBlockCommand(Track* track, Block* block, QUndoCommand* parent = nullptr) : - UndoCommand(parent), + TrackRippleRemoveBlockCommand(Track* track, Block* block) : track_(track), block_(block) { @@ -415,14 +404,13 @@ public: return track_->parent()->project(); } -protected: - virtual void redo_internal() override + virtual void redo() override { before_ = block_->previous(); track_->RippleRemoveBlock(block_); } - virtual void undo_internal() override + virtual void undo() override { track_->InsertBlockAfter(block_, before_); } @@ -438,8 +426,7 @@ private: class TrackPrependBlockCommand : public UndoCommand { public: - TrackPrependBlockCommand(Track* track, Block* block, QUndoCommand* parent = nullptr) : - UndoCommand(parent), + TrackPrependBlockCommand(Track* track, Block* block) : track_(track), block_(block) { @@ -450,13 +437,12 @@ public: return track_->parent()->project(); } -protected: - virtual void redo_internal() override + virtual void redo() override { track_->PrependBlock(block_); } - virtual void undo_internal() override + virtual void undo() override { track_->RippleRemoveBlock(block_); } @@ -468,8 +454,7 @@ private: class TrackInsertBlockAfterCommand : public UndoCommand { public: - TrackInsertBlockAfterCommand(Track* track, Block* block, Block* before, QUndoCommand* parent = nullptr) : - UndoCommand(parent), + TrackInsertBlockAfterCommand(Track* track, Block* block, Block* before) : track_(track), block_(block), before_(before) @@ -481,13 +466,12 @@ public: return block_->parent()->project(); } -protected: - virtual void redo_internal() override + virtual void redo() override { track_->InsertBlockAfter(block_, before_); } - virtual void undo_internal() override + virtual void undo() override { track_->RippleRemoveBlock(block_); } @@ -502,8 +486,7 @@ private: class BlockSplitCommand : public UndoCommand { public: - BlockSplitCommand(Block* block, rational point, QUndoCommand* parent = nullptr) : - UndoCommand(parent), + BlockSplitCommand(Block* block, rational point) : block_(block), point_(point), reconnect_tree_command_(nullptr) @@ -528,8 +511,7 @@ public: return static_cast(added_nodes_.first()); } -protected: - virtual void redo_internal() override + virtual void redo() override { old_length_ = block_->length(); @@ -571,8 +553,8 @@ protected: if (!reconnect_tree_command_) { if (copy_dependencies_too) { // Create equivalent connections among our copied dependency tree - reconnect_tree_command_ = new QUndoCommand(); - Node::CopyDependencyGraph(src_nodes_, added_nodes_, reconnect_tree_command_); + reconnect_tree_command_ = new MultiUndoCommand(); + Node::CopyDependencyGraph(src_nodes_, added_nodes_, static_cast(reconnect_tree_command_)); } else { reconnect_tree_command_ = new NodeCopyInputsCommand(block_, new_block(), true); } @@ -613,7 +595,7 @@ protected: track->EndOperation(); } - virtual void undo_internal() override + virtual void undo() override { Track* track = block_->track(); @@ -646,7 +628,7 @@ private: QObject memory_manager_; - QUndoCommand* reconnect_tree_command_; + UndoCommand* reconnect_tree_command_; NodeInput* moved_transition_; @@ -657,8 +639,7 @@ private: class BlockSplitPreservingLinksCommand : public UndoCommand { public: - BlockSplitPreservingLinksCommand(const QVector &blocks, const QList& times, QUndoCommand* parent = nullptr) : - UndoCommand(parent), + BlockSplitPreservingLinksCommand(const QVector &blocks, const QList& times) : blocks_(blocks), times_(times) { @@ -674,8 +655,7 @@ public: return blocks_.first()->parent()->project(); } -protected: - virtual void redo_internal() override + virtual void redo() override { if (commands_.isEmpty()) { QVector< QVector > split_blocks(times_.size()); @@ -736,7 +716,7 @@ protected: } } - virtual void undo_internal() override + virtual void undo() override { for (int i=commands_.size()-1; i>=0; i--) { commands_.at(i)->undo(); @@ -748,14 +728,13 @@ private: QList times_; - QVector commands_; + QVector commands_; }; class TrackSplitAtTimeCommand : public UndoCommand { public: - TrackSplitAtTimeCommand(Track* track, rational point, QUndoCommand* parent = nullptr) : - UndoCommand(parent), + TrackSplitAtTimeCommand(Track* track, rational point) : prepped_(false), track_(track), point_(point), @@ -773,8 +752,7 @@ public: return track_->parent()->project(); } -protected: - virtual void redo_internal() override + virtual void redo() override { if (!prepped_) { // Find Block that contains this time @@ -792,7 +770,7 @@ protected: } } - virtual void undo_internal() override + virtual void undo() override { if (command_) { command_->undo(); @@ -806,7 +784,7 @@ private: rational point_; - QUndoCommand* command_; + UndoCommand* command_; }; @@ -819,8 +797,7 @@ private: */ class TrackRippleRemoveAreaCommand : public UndoCommand { public: - TrackRippleRemoveAreaCommand(Track* track, const TimeRange& range, QUndoCommand* parent = nullptr) : - UndoCommand(parent), + TrackRippleRemoveAreaCommand(Track* track, const TimeRange& range) : prepped_(false), track_(track), range_(range), @@ -858,8 +835,7 @@ public: return nullptr; } -protected: - virtual void redo_internal() override + virtual void redo() override { if (!prepped_) { prep(); @@ -900,7 +876,7 @@ protected: } } - foreach (QUndoCommand* c, remove_block_commands_) { + foreach (UndoCommand* c, remove_block_commands_) { c->redo(); } } @@ -911,7 +887,7 @@ protected: track_->InvalidateCache(TimeRange(range_.in(), RATIONAL_MAX)); } - virtual void undo_internal() override + virtual void undo() override { // Begin operations track_->BeginOperation(); @@ -1028,14 +1004,13 @@ private: Block* insert_previous_; BlockSplitCommand* splice_split_command_; - QVector remove_block_commands_; + QVector remove_block_commands_; }; class TrackListRippleRemoveAreaCommand : public UndoCommand { public: - TrackListRippleRemoveAreaCommand(TrackList* list, rational in, rational out, QUndoCommand* parent = nullptr) : - UndoCommand(parent), + TrackListRippleRemoveAreaCommand(TrackList* list, rational in, rational out) : list_(list), in_(in), out_(out) @@ -1052,8 +1027,7 @@ public: return static_cast(list_->parent())->parent()->project(); } -protected: - virtual void redo_internal() override + virtual void redo() override { // Code that's only run on the first redo if (commands_.isEmpty()) { @@ -1096,7 +1070,7 @@ protected: } } - virtual void undo_internal() override + virtual void undo() override { if (all_tracks_unlocked_) { // We can optimize here by simply shifting the whole cache forward instead of re-caching @@ -1138,17 +1112,15 @@ private: }; -class TimelineRippleRemoveAreaCommand : public UndoCommand { +class TimelineRippleRemoveAreaCommand : public MultiUndoCommand { public: - TimelineRippleRemoveAreaCommand(ViewerOutput* timeline, rational in, rational out, QUndoCommand* parent = nullptr) : - UndoCommand(parent), + TimelineRippleRemoveAreaCommand(ViewerOutput* timeline, rational in, rational out) : timeline_(timeline) { for (int i=0; itrack_list(static_cast(i)), - in, - out, - this); + add_child(new TrackListRippleRemoveAreaCommand(timeline->track_list(static_cast(i)), + in, + out)); } } @@ -1173,8 +1145,7 @@ public: const QHash& info, const rational& ripple_movement, const Timeline::MovementMode& movement_mode, - QUndoCommand* parent = nullptr) : - UndoCommand(parent), + UndoCommand* parent = nullptr) : track_list_(track_list), info_(info), ripple_movement_(ripple_movement), @@ -1188,13 +1159,12 @@ public: return static_cast(track_list_->parent())->parent()->project(); } -protected: - virtual void redo_internal() override + virtual void redo() override { ripple(true); } - virtual void undo_internal() override + virtual void undo() override { ripple(false); } @@ -1385,8 +1355,7 @@ private: class TimelineAddTrackCommand : public UndoCommand { public: - TimelineAddTrackCommand(TrackList *timeline, QUndoCommand* command = nullptr) : - UndoCommand(command), + TimelineAddTrackCommand(TrackList *timeline) : timeline_(timeline) { track_ = new Track(); @@ -1415,8 +1384,7 @@ public: return timeline_->GetParentGraph()->project(); } -protected: - virtual void redo_internal() override + virtual void redo() override { // Add track track_->setParent(timeline_->GetParentGraph()); @@ -1444,7 +1412,7 @@ protected: } } - virtual void undo_internal() override + virtual void undo() override { // Remove merge if applicable if (merge_) { @@ -1490,8 +1458,7 @@ private: */ class TrackPlaceBlockCommand : public UndoCommand { public: - TrackPlaceBlockCommand(TrackList *timeline, int track, Block* block, rational in, QUndoCommand* parent = nullptr) : - UndoCommand(parent), + TrackPlaceBlockCommand(TrackList *timeline, int track, Block* block, rational in) : timeline_(timeline), track_index_(track), in_(in), @@ -1512,8 +1479,7 @@ public: return timeline_->GetParentGraph()->project(); } -protected: - virtual void redo_internal() override + virtual void redo() override { // Determine if we need to add tracks if (track_index_ >= timeline_->GetTracks().size()) { @@ -1560,7 +1526,7 @@ protected: } } - virtual void undo_internal() override + virtual void undo() override { Track* t = timeline_->GetTrackAt(track_index_); @@ -1600,8 +1566,7 @@ private: */ class TrackReplaceBlockCommand : public UndoCommand { public: - TrackReplaceBlockCommand(Track* track, Block* old, Block* replace, QUndoCommand* parent = nullptr) : - UndoCommand(parent), + TrackReplaceBlockCommand(Track* track, Block* old, Block* replace) : track_(track), old_(old), replace_(replace) @@ -1613,13 +1578,12 @@ public: return track_->parent()->project(); } -protected: - virtual void redo_internal() override + virtual void redo() override { track_->ReplaceBlock(old_, replace_); } - virtual void undo_internal() override + virtual void undo() override { track_->ReplaceBlock(replace_, old_); } @@ -1633,8 +1597,7 @@ private: class TrackReplaceBlockWithGapCommand : public UndoCommand { public: - TrackReplaceBlockWithGapCommand(Track* track, Block* block, QUndoCommand* command = nullptr) : - UndoCommand(command), + TrackReplaceBlockWithGapCommand(Track* track, Block* block) : track_(track), block_(block), existing_gap_(nullptr), @@ -1648,8 +1611,7 @@ public: return block_->parent()->project(); } -protected: - virtual void redo_internal() override + virtual void redo() override { track_->BeginOperation(); @@ -1721,7 +1683,7 @@ protected: track_->InvalidateCache(invalidate_range, track_->block_input()); } - virtual void undo_internal() override + virtual void undo() override { track_->BeginOperation(); @@ -1793,8 +1755,7 @@ private: class TimelineRippleDeleteGapsAtRegionsCommand : public UndoCommand { public: - TimelineRippleDeleteGapsAtRegionsCommand(ViewerOutput* vo, const TimeRangeList& regions, QUndoCommand* parent = nullptr) : - UndoCommand(parent), + TimelineRippleDeleteGapsAtRegionsCommand(ViewerOutput* vo, const TimeRangeList& regions) : timeline_(vo), regions_(regions) { @@ -1810,8 +1771,7 @@ public: return timeline_->parent()->project(); } -protected: - virtual void redo_internal() override + virtual void redo() override { if (commands_.isEmpty()) { foreach (const TimeRange& range, regions_) { @@ -1849,12 +1809,12 @@ protected: } } - foreach (QUndoCommand* c, commands_) { + foreach (UndoCommand* c, commands_) { c->redo(); } } - virtual void undo_internal() override + virtual void undo() override { for (int i=commands_.size()-1;i>=0;i--) { commands_.at(i)->undo(); @@ -1865,14 +1825,13 @@ private: ViewerOutput* timeline_; TimeRangeList regions_; - QVector commands_; + QVector commands_; }; class WorkareaSetEnabledCommand : public UndoCommand { public: - WorkareaSetEnabledCommand(Project *project, TimelinePoints* points, bool enabled, QUndoCommand* parent = nullptr) : - UndoCommand(parent), + WorkareaSetEnabledCommand(Project *project, TimelinePoints* points, bool enabled) : project_(project), points_(points), old_enabled_(points_->workarea()->enabled()), @@ -1885,13 +1844,12 @@ public: return project_; } -protected: - virtual void redo_internal() override + virtual void redo() override { points_->workarea()->set_enabled(new_enabled_); } - virtual void undo_internal() override + virtual void undo() override { points_->workarea()->set_enabled(old_enabled_); } @@ -1909,8 +1867,7 @@ private: class WorkareaSetRangeCommand : public UndoCommand { public: - WorkareaSetRangeCommand(Project *project, TimelinePoints* points, const TimeRange& range, QUndoCommand* parent = nullptr) : - UndoCommand(parent), + WorkareaSetRangeCommand(Project *project, TimelinePoints* points, const TimeRange& range) : project_(project), points_(points), old_range_(points_->workarea()->range()), @@ -1923,13 +1880,12 @@ public: return project_; } -protected: - virtual void redo_internal() override + virtual void redo() override { points_->workarea()->set_range(new_range_); } - virtual void undo_internal() override + virtual void undo() override { points_->workarea()->set_range(old_range_); } @@ -1947,8 +1903,7 @@ private: class BlockEnableDisableCommand : public UndoCommand { public: - BlockEnableDisableCommand(Block* block, bool enabled, QUndoCommand* parent = nullptr) : - UndoCommand(parent), + BlockEnableDisableCommand(Block* block, bool enabled) : block_(block), old_enabled_(block_->is_enabled()), new_enabled_(enabled) @@ -1960,13 +1915,12 @@ public: return block_->parent()->project(); } -protected: - virtual void redo_internal() override + virtual void redo() override { block_->set_enabled(new_enabled_); } - virtual void undo_internal() override + virtual void undo() override { block_->set_enabled(old_enabled_); } @@ -1982,8 +1936,7 @@ private: class TrackSlideCommand : public UndoCommand { public: - TrackSlideCommand(Track* track, const QList& moving_blocks, Block* in_adjacent, Block* out_adjacent, const rational& movement, QUndoCommand* parent = nullptr) : - UndoCommand(parent), + TrackSlideCommand(Track* track, const QList& moving_blocks, Block* in_adjacent, Block* out_adjacent, const rational& movement) : prepped_(false), track_(track), blocks_(moving_blocks), @@ -2007,8 +1960,7 @@ public: return track_->parent()->project(); } -protected: - virtual void redo_internal() override + virtual void redo() override { if (!prepped_) { prep(); @@ -2073,7 +2025,7 @@ protected: track_->InvalidateCache(invalidate_range, track_->block_input()); } - virtual void undo_internal() override + virtual void undo() override { // Make sure all movement blocks' old positions are invalidated TimeRange invalidate_range(blocks_.first()->in(), blocks_.last()->out()); @@ -2145,10 +2097,10 @@ private: bool we_created_in_adjacent_; Block* in_adjacent_; - QUndoCommand* in_adjacent_remove_command_; + UndoCommand* in_adjacent_remove_command_; bool we_created_out_adjacent_; Block* out_adjacent_; - QUndoCommand* out_adjacent_remove_command_; + UndoCommand* out_adjacent_remove_command_; QObject memory_manager_; @@ -2156,8 +2108,7 @@ private: class TrackListInsertGaps : public UndoCommand { public: - TrackListInsertGaps(TrackList* track_list, const rational& point, const rational& length, QUndoCommand* parent = nullptr) : - UndoCommand(parent), + TrackListInsertGaps(TrackList* track_list, const rational& point, const rational& length) : prepped_(false), track_list_(track_list), point_(point), @@ -2176,8 +2127,7 @@ public: return static_cast(track_list_->parent())->parent()->project(); } -protected: - virtual void redo_internal() override + virtual void redo() override { if (!prepped_) { prep(); @@ -2221,7 +2171,7 @@ protected: } } - virtual void undo_internal() override + virtual void undo() override { if (all_tracks_unlocked_) { // Optimize by shifting over since we have a constant amount of time being inserted @@ -2340,8 +2290,7 @@ private: class TransitionRemoveCommand : public UndoCommand { public: - TransitionRemoveCommand(TransitionBlock* block, QUndoCommand *parent = nullptr) : - UndoCommand(parent), + TransitionRemoveCommand(TransitionBlock* block, UndoCommand *parent = nullptr) : block_(block) { } @@ -2351,8 +2300,7 @@ public: return track_->parent()->project(); } -protected: - virtual void redo_internal() override + virtual void redo() override { track_ = block_->track(); out_block_ = block_->connected_out_block(); @@ -2387,7 +2335,7 @@ protected: track_->InvalidateCache(invalidate_range, track_->block_input()); } - virtual void undo_internal() override + virtual void undo() override { track_->BeginOperation(); diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index 60da89cc9..a90c46ce3 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -421,7 +421,7 @@ void TimelineWidget::SplitAtPlayhead() void TimelineWidget::ReplaceBlocksWithGaps(const QVector &blocks, bool remove_from_graph, - QUndoCommand *command) + MultiUndoCommand *command) { foreach (Block* b, blocks) { if (b->type() == Block::kGap) { @@ -432,10 +432,10 @@ void TimelineWidget::ReplaceBlocksWithGaps(const QVector &blocks, Track* original_track = b->track(); - new TrackReplaceBlockWithGapCommand(original_track, b, command); + command->add_child(new TrackReplaceBlockWithGapCommand(original_track, b)); if (remove_from_graph) { - new NodeRemoveWithExclusiveDependenciesAndDisconnect(b, command); + command->add_child(new NodeRemoveWithExclusiveDependenciesAndDisconnect(b)); } } } @@ -454,7 +454,7 @@ void TimelineWidget::DeleteSelected(bool ripple) return; } - QUndoCommand* command = new QUndoCommand(); + MultiUndoCommand* command = new MultiUndoCommand(); QVector clips_to_delete; QVector transitions_to_delete; @@ -469,16 +469,16 @@ void TimelineWidget::DeleteSelected(bool ripple) // For transitions, remove them but extend their attached blocks to fill their place foreach (TransitionBlock* transition, transitions_to_delete) { - new TransitionRemoveCommand(transition, command); + command->add_child(new TransitionRemoveCommand(transition)); - new NodeRemoveWithExclusiveDependenciesAndDisconnect(transition, command); + command->add_child(new NodeRemoveWithExclusiveDependenciesAndDisconnect(transition)); } // Replace clips with gaps (effectively deleting them) ReplaceBlocksWithGaps(clips_to_delete, true, command); // Remove all selections - new SetSelectionsCommand(this, TimelineWidgetSelections(), GetSelections(), command); + command->add_child(new SetSelectionsCommand(this, TimelineWidgetSelections(), GetSelections())); // Insert ripple command now that it's all cleaned up gaps if (ripple) { @@ -488,7 +488,7 @@ void TimelineWidget::DeleteSelected(bool ripple) range_list.insert(TimeRange(b->in(), b->out())); } - new TimelineRippleDeleteGapsAtRegionsCommand(GetConnectedNode(), range_list, command); + command->add_child(new TimelineRippleDeleteGapsAtRegionsCommand(GetConnectedNode(), range_list)); } Core::instance()->undo_stack()->pushIfHasChildren(command); @@ -593,7 +593,7 @@ void TimelineWidget::Paste(bool insert) return; } - QUndoCommand* command = new QUndoCommand(); + MultiUndoCommand* command = new MultiUndoCommand(); QVector paste_data; QVector pasted = PasteNodesFromClipboard(GetConnectedNode()->parent(), command, &paste_data); @@ -614,11 +614,10 @@ void TimelineWidget::Paste(bool insert) foreach (const BlockPasteData& bpd, paste_data) { qDebug() << "Placing" << bpd.block; - new TrackPlaceBlockCommand(GetConnectedNode()->track_list(bpd.track_type), - bpd.track_index, - bpd.block, - paste_start + bpd.in, - command); + command->add_child(new TrackPlaceBlockCommand(GetConnectedNode()->track_list(bpd.track_type), + bpd.track_index, + bpd.block, + paste_start + bpd.in)); } Core::instance()->undo_stack()->pushIfHasChildren(command); @@ -632,14 +631,13 @@ void TimelineWidget::DeleteInToOut(bool ripple) return; } - QUndoCommand* command = new QUndoCommand(); + MultiUndoCommand* command = new MultiUndoCommand(); if (ripple) { - new TimelineRippleRemoveAreaCommand(GetConnectedNode(), - GetConnectedTimelinePoints()->workarea()->in(), - GetConnectedTimelinePoints()->workarea()->out(), - command); + command->add_child(new TimelineRippleRemoveAreaCommand(GetConnectedNode(), + GetConnectedTimelinePoints()->workarea()->in(), + GetConnectedTimelinePoints()->workarea()->out())); } else { QVector unlocked_tracks = GetConnectedNode()->GetUnlockedTracks(); @@ -649,23 +647,20 @@ void TimelineWidget::DeleteInToOut(bool ripple) gap->set_length_and_media_out(GetConnectedTimelinePoints()->workarea()->length()); - new NodeAddCommand(static_cast(track->parent()), - gap, - command); + command->add_child(new NodeAddCommand(static_cast(track->parent()), + gap)); - new TrackPlaceBlockCommand(GetConnectedNode()->track_list(track->type()), - track->Index(), - gap, - GetConnectedTimelinePoints()->workarea()->in(), - command); + command->add_child(new TrackPlaceBlockCommand(GetConnectedNode()->track_list(track->type()), + track->Index(), + gap, + GetConnectedTimelinePoints()->workarea()->in())); } } // Clear workarea after this - new WorkareaSetEnabledCommand(GetTimelinePointsProject(), - GetConnectedTimelinePoints(), - false, - command); + command->add_child(new WorkareaSetEnabledCommand(GetTimelinePointsProject(), + GetConnectedTimelinePoints(), + false)); if (ripple) { SetTimeAndSignal(Timecode::time_to_timestamp(GetConnectedTimelinePoints()->workarea()->in(), @@ -683,12 +678,11 @@ void TimelineWidget::ToggleSelectedEnabled() return; } - QUndoCommand* command = new QUndoCommand(); + MultiUndoCommand* command = new MultiUndoCommand(); foreach (Block* i, items) { - new BlockEnableDisableCommand(i, - !i->is_enabled(), - command); + command->add_child(new BlockEnableDisableCommand(i, + !i->is_enabled())); } Core::instance()->undo_stack()->pushIfHasChildren(command); @@ -701,13 +695,12 @@ void TimelineWidget::SetColorLabel(int index) } } -void TimelineWidget::InsertGapsAt(const rational &earliest_point, const rational &insert_length, QUndoCommand *command) +void TimelineWidget::InsertGapsAt(const rational &earliest_point, const rational &insert_length, MultiUndoCommand *command) { for (int i=0;itrack_list(static_cast(i)), - earliest_point, - insert_length, - command); + command->add_child(new TrackListInsertGaps(GetConnectedNode()->track_list(static_cast(i)), + earliest_point, + insert_length)); } } @@ -1238,7 +1231,7 @@ void TimelineWidget::EditTo(Timeline::MovementMode mode) return; } - QUndoCommand* command = new QUndoCommand(); + MultiUndoCommand* command = new MultiUndoCommand(); foreach (const Timeline::EditToInfo& info, tracks) { if (info.nearest_block @@ -1253,11 +1246,10 @@ void TimelineWidget::EditTo(Timeline::MovementMode mode) } new_len = info.nearest_block->length() - new_len; - new BlockTrimCommand(info.track, - info.nearest_block, - new_len, - mode, - command); + command->add_child(new BlockTrimCommand(info.track, + info.nearest_block, + new_len, + mode)); } } diff --git a/app/widget/timelinewidget/timelinewidget.h b/app/widget/timelinewidget/timelinewidget.h index 6d5b19960..8a1b51fb7 100644 --- a/app/widget/timelinewidget/timelinewidget.h +++ b/app/widget/timelinewidget/timelinewidget.h @@ -104,7 +104,7 @@ public: void RestoreSplitterState(const QByteArray& state); - static void ReplaceBlocksWithGaps(const QVector &blocks, bool remove_from_graph, QUndoCommand* command); + static void ReplaceBlocksWithGaps(const QVector &blocks, bool remove_from_graph, MultiUndoCommand *command); /** * @brief Retrieve the QGraphicsItem at a particular scene position @@ -136,7 +136,7 @@ public: return ghost_items_; } - void InsertGapsAt(const rational& time, const rational& length, QUndoCommand* command); + void InsertGapsAt(const rational& time, const rational& length, MultiUndoCommand *command); void StartRubberBandSelect(const QPoint& global_cursor_start); void MoveRubberBandSelect(bool enable_selecting, bool select_links); @@ -197,17 +197,15 @@ public: */ void SignalDeselectedAllBlocks(); - class SetSelectionsCommand : public QUndoCommand { + class SetSelectionsCommand : public UndoCommand { public: - SetSelectionsCommand(TimelineWidget* timeline, const TimelineWidgetSelections& now, const TimelineWidgetSelections& old, QUndoCommand* parent = nullptr) : - QUndoCommand(parent), + SetSelectionsCommand(TimelineWidget* timeline, const TimelineWidgetSelections& now, const TimelineWidgetSelections& old) : timeline_(timeline), old_(old), now_(now) { } - protected: virtual void redo() override { timeline_->SetSelections(now_); @@ -218,6 +216,8 @@ public: timeline_->SetSelections(old_); } + virtual Project* GetRelevantProject() const override {return nullptr;} + private: TimelineWidget* timeline_; TimelineWidgetSelections old_; diff --git a/app/widget/timelinewidget/tool/add.cpp b/app/widget/timelinewidget/tool/add.cpp index d7c6e5830..89b2992e7 100644 --- a/app/widget/timelinewidget/tool/add.cpp +++ b/app/widget/timelinewidget/tool/add.cpp @@ -91,7 +91,7 @@ void AddTool::MouseRelease(TimelineViewMouseEvent *event) if (ghost_) { if (!ghost_->GetAdjustedLength().isNull()) { - QUndoCommand* command = new QUndoCommand(); + MultiUndoCommand* command = new MultiUndoCommand(); ClipBlock* clip = new ClipBlock(); clip->set_length_and_media_out(ghost_->GetAdjustedLength()); @@ -99,15 +99,13 @@ void AddTool::MouseRelease(TimelineViewMouseEvent *event) NodeGraph* graph = static_cast(parent()->GetConnectedNode()->parent()); - new NodeAddCommand(graph, - clip, - command); + command->add_child(new NodeAddCommand(graph, + clip)); - new TrackPlaceBlockCommand(parent()->GetConnectedNode()->track_list(track.type()), - track.index(), - clip, - ghost_->GetAdjustedIn(), - command); + command->add_child(new TrackPlaceBlockCommand(parent()->GetConnectedNode()->track_list(track.type()), + track.index(), + clip, + ghost_->GetAdjustedIn())); switch (Core::instance()->GetSelectedAddableObject()) { case olive::Tool::kAddableEmpty: @@ -117,22 +115,20 @@ void AddTool::MouseRelease(TimelineViewMouseEvent *event) { Node* solid = new SolidGenerator(); - new NodeAddCommand(graph, - solid, - command); + command->add_child(new NodeAddCommand(graph, + solid)); - new NodeEdgeAddCommand(solid, clip->texture_input(), -1, command); + command->add_child(new NodeEdgeAddCommand(solid, clip->texture_input(), -1)); break; } case olive::Tool::kAddableTitle: { Node* text = new TextGenerator(); - new NodeAddCommand(graph, - text, - command); + command->add_child(new NodeAddCommand(graph, + text)); - new NodeEdgeAddCommand(text, clip->texture_input(), -1, command); + command->add_child(new NodeEdgeAddCommand(text, clip->texture_input(), -1)); break; } case olive::Tool::kAddableBars: diff --git a/app/widget/timelinewidget/tool/import.cpp b/app/widget/timelinewidget/tool/import.cpp index 9ee4c8fd8..3064feedf 100644 --- a/app/widget/timelinewidget/tool/import.cpp +++ b/app/widget/timelinewidget/tool/import.cpp @@ -306,7 +306,7 @@ void ImportTool::PrepGhosts(const rational& frame, const int& track_index) void ImportTool::DropGhosts(bool insert) { - QUndoCommand* command = new QUndoCommand(); + MultiUndoCommand* command = new MultiUndoCommand(); NodeGraph* dst_graph = nullptr; ViewerOutput* viewer_node = nullptr; @@ -383,10 +383,9 @@ void ImportTool::DropGhosts(bool insert) if (sequence_is_valid) { new_sequence->add_default_nodes(); - new ProjectViewModel::AddItemCommand(Core::instance()->GetActiveProjectModel(), - Core::instance()->GetSelectedFolderInActiveProject(), - new_sequence, - command); + command->add_child(new ProjectViewModel::AddItemCommand(Core::instance()->GetActiveProjectModel(), + Core::instance()->GetSelectedFolderInActiveProject(), + new_sequence)); FootageToGhosts(0, dragged_footage_, new_sequence->video_params().time_base(), 0); @@ -422,44 +421,43 @@ void ImportTool::DropGhosts(bool insert) clip->set_media_in(ghost->GetMediaIn()); clip->set_length_and_media_out(ghost->GetLength()); clip->SetLabel(footage_stream->footage()->name()); - new NodeAddCommand(dst_graph, clip, command); + command->add_child(new NodeAddCommand(dst_graph, clip)); switch (footage_stream->type()) { case Stream::kVideo: { MediaInput* video_input = new MediaInput(); video_input->SetStream(footage_stream); - new NodeAddCommand(dst_graph, video_input, command); + command->add_child(new NodeAddCommand(dst_graph, video_input)); TransformDistortNode* transform = new TransformDistortNode(); - new NodeAddCommand(dst_graph, transform, command); + command->add_child(new NodeAddCommand(dst_graph, transform)); - new NodeEdgeAddCommand(video_input, transform->texture_input(), -1, command); - new NodeEdgeAddCommand(transform, clip->texture_input(), -1, command); + command->add_child(new NodeEdgeAddCommand(video_input, transform->texture_input(), -1)); + command->add_child(new NodeEdgeAddCommand(transform, clip->texture_input(), -1)); break; } case Stream::kAudio: { MediaInput* audio_input = new MediaInput(); audio_input->SetStream(footage_stream); - new NodeAddCommand(dst_graph, audio_input, command); + command->add_child(new NodeAddCommand(dst_graph, audio_input)); VolumeNode* volume_node = new VolumeNode(); - new NodeAddCommand(dst_graph, volume_node, command); + command->add_child(new NodeAddCommand(dst_graph, volume_node)); - new NodeEdgeAddCommand(audio_input, volume_node->samples_input(), -1, command); - new NodeEdgeAddCommand(volume_node, clip->texture_input(), -1, command); + command->add_child(new NodeEdgeAddCommand(audio_input, volume_node->samples_input(), -1)); + command->add_child(new NodeEdgeAddCommand(volume_node, clip->texture_input(), -1)); break; } default: break; } - new TrackPlaceBlockCommand(viewer_node->track_list(ghost->GetAdjustedTrack().type()), - ghost->GetAdjustedTrack().index(), - clip, - ghost->GetAdjustedIn(), - command); + command->add_child(new TrackPlaceBlockCommand(viewer_node->track_list(ghost->GetAdjustedTrack().type()), + ghost->GetAdjustedTrack().index(), + clip, + ghost->GetAdjustedIn())); block_items.replace(i, clip); diff --git a/app/widget/timelinewidget/tool/pointer.cpp b/app/widget/timelinewidget/tool/pointer.cpp index 5c39f7951..0d189c92e 100644 --- a/app/widget/timelinewidget/tool/pointer.cpp +++ b/app/widget/timelinewidget/tool/pointer.cpp @@ -563,7 +563,7 @@ void PointerTool::FinishDrag(TimelineViewMouseEvent *event) return; } - QUndoCommand* command = new QUndoCommand(); + MultiUndoCommand* command = new MultiUndoCommand(); if (!blocks_trimming.isEmpty()) { foreach (const GhostBlockPair& p, blocks_trimming) { @@ -574,10 +574,11 @@ void PointerTool::FinishDrag(TimelineViewMouseEvent *event) BlockTrimCommand* c = new BlockTrimCommand(parent()->GetTrackFromReference(ghost->GetAdjustedTrack()), p.block, ghost->GetAdjustedLength(), - ghost->GetMode(), - command); + ghost->GetMode()); c->SetTrimIsARollEdit(ghost->GetData(TimelineViewGhostItem::kTrimIsARollEdit).toBool()); + + command->add_child(c); } } @@ -590,7 +591,7 @@ void PointerTool::FinishDrag(TimelineViewMouseEvent *event) } else { new_sel.TrimOut(reference_ghost->GetOutAdjustment()); } - new TimelineWidget::SetSelectionsCommand(parent(), new_sel, parent()->GetSelections(), command); + command->add_child(new TimelineWidget::SetSelectionsCommand(parent(), new_sel, parent()->GetSelections())); } } @@ -632,11 +633,10 @@ void PointerTool::FinishDrag(TimelineViewMouseEvent *event) } else { copy = block->copy(); - new NodeAddCommand(static_cast(block->parent()), - copy, - command); + command->add_child(new NodeAddCommand(static_cast(block->parent()), + copy)); - new NodeCopyInputsCommand(block, copy, true, command); + command->add_child(new NodeCopyInputsCommand(block, copy, true)); } // Place the copy instead of the original block @@ -644,18 +644,17 @@ void PointerTool::FinishDrag(TimelineViewMouseEvent *event) } const Track::Reference& track_ref = p.ghost->GetAdjustedTrack(); - new TrackPlaceBlockCommand(parent()->GetConnectedNode()->track_list(track_ref.type()), - track_ref.index(), - block, - p.ghost->GetAdjustedIn(), - command); + command->add_child(new TrackPlaceBlockCommand(parent()->GetConnectedNode()->track_list(track_ref.type()), + track_ref.index(), + block, + p.ghost->GetAdjustedIn())); } // Adjust selections TimelineWidgetSelections new_sel = parent()->GetSelections(); new_sel.ShiftTime(blocks_moving.first().ghost->GetInAdjustment()); new_sel.ShiftTracks(drag_track_type_, blocks_moving.first().ghost->GetTrackAdjustment()); - new TimelineWidget::SetSelectionsCommand(parent(), new_sel, parent()->GetSelections(), command); + command->add_child(new TimelineWidget::SetSelectionsCommand(parent(), new_sel, parent()->GetSelections())); } if (!blocks_sliding.isEmpty()) { @@ -706,18 +705,17 @@ void PointerTool::FinishDrag(TimelineViewMouseEvent *event) if (!movement.isNull()) { QHash >::const_iterator i; for (i=slide_info.constBegin(); i!=slide_info.constEnd(); i++) { - new TrackSlideCommand(parent()->GetTrackFromReference(i.key()), + command->add_child(new TrackSlideCommand(parent()->GetTrackFromReference(i.key()), i.value(), in_adjacents.value(i.key()), out_adjacents.value(i.key()), - movement, - command); + movement)); } // Adjust selections TimelineWidgetSelections new_sel = parent()->GetSelections(); new_sel.ShiftTime(movement); - new TimelineWidget::SetSelectionsCommand(parent(), new_sel, parent()->GetSelections(), command); + command->add_child(new TimelineWidget::SetSelectionsCommand(parent(), new_sel, parent()->GetSelections())); } } diff --git a/app/widget/timelinewidget/tool/ripple.cpp b/app/widget/timelinewidget/tool/ripple.cpp index 46c94abca..61360c0a2 100644 --- a/app/widget/timelinewidget/tool/ripple.cpp +++ b/app/widget/timelinewidget/tool/ripple.cpp @@ -132,7 +132,7 @@ void RippleTool::FinishDrag(TimelineViewMouseEvent *event) info_list[track->type()].insert(track, info); } - QUndoCommand* command = new QUndoCommand(); + MultiUndoCommand* command = new MultiUndoCommand(); rational movement; @@ -144,15 +144,14 @@ void RippleTool::FinishDrag(TimelineViewMouseEvent *event) for (int i=0;iGetConnectedNode()->track_list(static_cast(i)), - info_list.at(i), - movement, - drag_movement_mode(), - command); + command->add_child(new TrackListRippleToolCommand(parent()->GetConnectedNode()->track_list(static_cast(i)), + info_list.at(i), + movement, + drag_movement_mode())); } } - if (command->childCount() > 0) { + if (command->child_count() > 0) { TimelineWidgetSelections new_sel = parent()->GetSelections(); TimelineViewGhostItem* reference_ghost = parent()->GetGhostItems().first(); if (drag_movement_mode() == Timeline::kTrimIn) { @@ -160,7 +159,7 @@ void RippleTool::FinishDrag(TimelineViewMouseEvent *event) } else { new_sel.TrimOut(reference_ghost->GetOutAdjustment()); } - new TimelineWidget::SetSelectionsCommand(parent(), new_sel, parent()->GetSelections(), command); + command->add_child(new TimelineWidget::SetSelectionsCommand(parent(), new_sel, parent()->GetSelections())); Core::instance()->undo_stack()->push(command); } else { diff --git a/app/widget/timelinewidget/tool/slip.cpp b/app/widget/timelinewidget/tool/slip.cpp index 1570791df..78619ec1c 100644 --- a/app/widget/timelinewidget/tool/slip.cpp +++ b/app/widget/timelinewidget/tool/slip.cpp @@ -68,13 +68,13 @@ void SlipTool::FinishDrag(TimelineViewMouseEvent *event) { Q_UNUSED(event) - QUndoCommand* command = new QUndoCommand(); + MultiUndoCommand* command = new MultiUndoCommand(); // Find earliest point to ripple around foreach (TimelineViewGhostItem* ghost, parent()->GetGhostItems()) { Block* b = Node::ValueToPtr(ghost->GetData(TimelineViewGhostItem::kAttachedBlock)); - new BlockSetMediaInCommand(b, ghost->GetAdjustedMediaIn(), command); + command->add_child(new BlockSetMediaInCommand(b, ghost->GetAdjustedMediaIn())); } Core::instance()->undo_stack()->pushIfHasChildren(command); diff --git a/app/widget/timelinewidget/tool/tool.cpp b/app/widget/timelinewidget/tool/tool.cpp index 598d7fb8a..0550122e2 100644 --- a/app/widget/timelinewidget/tool/tool.cpp +++ b/app/widget/timelinewidget/tool/tool.cpp @@ -128,7 +128,7 @@ void TimelineTool::GetGhostData(rational *earliest_point, rational *latest_point } } -void TimelineTool::InsertGapsAtGhostDestination(QUndoCommand *command) +void TimelineTool::InsertGapsAtGhostDestination(olive::MultiUndoCommand *command) { rational earliest_point, latest_point; diff --git a/app/widget/timelinewidget/tool/tool.h b/app/widget/timelinewidget/tool/tool.h index b22869d1e..ac69eefdc 100644 --- a/app/widget/timelinewidget/tool/tool.h +++ b/app/widget/timelinewidget/tool/tool.h @@ -75,7 +75,7 @@ protected: void GetGhostData(rational *earliest_point, rational *latest_point); - void InsertGapsAtGhostDestination(QUndoCommand* command); + void InsertGapsAtGhostDestination(MultiUndoCommand* command); QVector snap_points_; diff --git a/app/widget/timelinewidget/tool/transition.cpp b/app/widget/timelinewidget/tool/transition.cpp index 57d33d8a6..541f2707a 100644 --- a/app/widget/timelinewidget/tool/transition.cpp +++ b/app/widget/timelinewidget/tool/transition.cpp @@ -120,18 +120,16 @@ void TransitionTool::MouseRelease(TimelineViewMouseEvent *event) transition = static_cast(NodeFactory::CreateFromID(Core::instance()->GetSelectedTransition())); } - QUndoCommand* command = new QUndoCommand(); + MultiUndoCommand* command = new MultiUndoCommand(); // Place transition in place - new NodeAddCommand(static_cast(parent()->GetConnectedNode()->parent()), - transition, - command); + command->add_child(new NodeAddCommand(static_cast(parent()->GetConnectedNode()->parent()), + transition)); - new TrackPlaceBlockCommand(parent()->GetConnectedNode()->track_list(track.type()), - track.index(), - transition, - ghost_->GetAdjustedIn(), - command); + command->add_child(new TrackPlaceBlockCommand(parent()->GetConnectedNode()->track_list(track.type()), + track.index(), + transition, + ghost_->GetAdjustedIn())); if (dual_transition_) { transition->set_length_and_media_out(ghost_->GetAdjustedLength()); @@ -148,15 +146,13 @@ void TransitionTool::MouseRelease(TimelineViewMouseEvent *event) Block* in_block = (ghost_->GetMode() == Timeline::kTrimIn) ? active_block : friend_block; // Connect block to transition - new NodeEdgeAddCommand(out_block, - transition->out_block_input(), - -1, - command); + command->add_child(new NodeEdgeAddCommand(out_block, + transition->out_block_input(), + -1)); - new NodeEdgeAddCommand(in_block, - transition->in_block_input(), - -1, - command); + command->add_child(new NodeEdgeAddCommand(in_block, + transition->in_block_input(), + -1)); } else { Block* block_to_transition = Node::ValueToPtr(ghost_->GetData(TimelineViewGhostItem::kAttachedBlock)); NodeInput* transition_input_to_connect; @@ -170,10 +166,9 @@ void TransitionTool::MouseRelease(TimelineViewMouseEvent *event) } // Connect block to transition - new NodeEdgeAddCommand(block_to_transition, - transition_input_to_connect, - -1, - command); + command->add_child(new NodeEdgeAddCommand(block_to_transition, + transition_input_to_connect, + -1)); } Core::instance()->undo_stack()->push(command); diff --git a/app/window/mainwindow/mainmenu.cpp b/app/window/mainwindow/mainmenu.cpp index a5d8c1dc2..f387f65f7 100644 --- a/app/window/mainwindow/mainmenu.cpp +++ b/app/window/mainwindow/mainmenu.cpp @@ -75,10 +75,10 @@ MainMenu::MainMenu(MainWindow *parent) : // edit_menu_ = new Menu(this); - edit_undo_item_ = Core::instance()->undo_stack()->createUndoAction(this); + edit_undo_item_ = Core::instance()->undo_stack()->GetUndoAction(); Menu::ConformItem(edit_undo_item_, "undo", "Ctrl+Z"); edit_menu_->addAction(edit_undo_item_); - edit_redo_item_ = Core::instance()->undo_stack()->createRedoAction(this); + edit_redo_item_ = Core::instance()->undo_stack()->GetRedoAction(); Menu::ConformItem(edit_redo_item_, "redo", "Ctrl+Shift+Z"); edit_menu_->addAction(edit_redo_item_);