move undo files around for better organization
This commit is contained in:
@@ -28,8 +28,8 @@
|
||||
#include <QtMath>
|
||||
|
||||
#include "common/qtutils.h"
|
||||
#include "node/nodeundo.h"
|
||||
#include "widget/keyframeview/keyframeviewundo.h"
|
||||
#include "widget/nodeparamview/nodeparamviewundo.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
|
||||
@@ -29,10 +29,10 @@
|
||||
#include "keyframeviewundo.h"
|
||||
#include "node/group/group.h"
|
||||
#include "node/node.h"
|
||||
#include "node/nodeundo.h"
|
||||
#include "node/project/serializer/serializer.h"
|
||||
#include "widget/menu/menu.h"
|
||||
#include "widget/menu/menushared.h"
|
||||
#include "widget/nodeparamview/nodeparamviewundo.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
|
||||
@@ -19,10 +19,12 @@
|
||||
***/
|
||||
|
||||
#include "multicamwidget.h"
|
||||
#include "qshortcut.h"
|
||||
#include "widget/nodeparamview/nodeparamviewundo.h"
|
||||
|
||||
#include <QShortcut>
|
||||
|
||||
#include "node/nodeundo.h"
|
||||
#include "timeline/timelineundosplit.h"
|
||||
#include "widget/timeruler/timeruler.h"
|
||||
#include "widget/timelinewidget/undo/timelineundosplit.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
|
||||
@@ -36,8 +36,6 @@ set(OLIVE_SOURCES
|
||||
widget/nodeparamview/nodeparamviewkeyframecontrol.h
|
||||
widget/nodeparamview/nodeparamviewtextedit.cpp
|
||||
widget/nodeparamview/nodeparamviewtextedit.h
|
||||
widget/nodeparamview/nodeparamviewundo.cpp
|
||||
widget/nodeparamview/nodeparamviewundo.h
|
||||
widget/nodeparamview/nodeparamviewwidgetbridge.cpp
|
||||
widget/nodeparamview/nodeparamviewwidgetbridge.h
|
||||
PARENT_SCOPE
|
||||
|
||||
@@ -26,9 +26,8 @@
|
||||
#include <QScrollBar>
|
||||
#include <QSplitter>
|
||||
|
||||
#include "node/nodeundo.h"
|
||||
#include "node/output/viewer/viewer.h"
|
||||
#include "widget/nodeparamview/nodeparamviewundo.h"
|
||||
#include "widget/nodeview/nodeviewundo.h"
|
||||
#include "widget/timeruler/timeruler.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -25,9 +25,9 @@
|
||||
#include "common/qtutils.h"
|
||||
#include "core.h"
|
||||
#include "node/node.h"
|
||||
#include "node/nodeundo.h"
|
||||
#include "widget/collapsebutton/collapsebutton.h"
|
||||
#include "widget/menu/menu.h"
|
||||
#include "widget/nodeview/nodeviewundo.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
#include "node/block/clip/clip.h"
|
||||
#include "node/factory.h"
|
||||
#include "widget/nodeview/nodeviewundo.h"
|
||||
#include "node/nodeundo.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
|
||||
@@ -27,8 +27,8 @@
|
||||
#include "core.h"
|
||||
#include "dialog/speedduration/speeddurationdialog.h"
|
||||
#include "node/group/group.h"
|
||||
#include "node/nodeundo.h"
|
||||
#include "node/project/sequence/sequence.h"
|
||||
#include "nodeparamviewundo.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -444,7 +444,7 @@ void NodeParamViewItemBody::ArrayAppendClicked()
|
||||
for (auto it=array_ui_.cbegin(); it!=array_ui_.cend(); it++) {
|
||||
if (it.value().append_btn == sender()) {
|
||||
NodeInput real_input = NodeGroup::ResolveInput(NodeInput(it.key().node, it.key().input));
|
||||
Core::instance()->undo_stack()->push(new Node::ArrayInsertCommand(real_input.node(), real_input.input(), real_input.GetArraySize()+1));
|
||||
Core::instance()->undo_stack()->push(new NodeArrayInsertCommand(real_input.node(), real_input.input(), real_input.GetArraySize()+1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -456,7 +456,7 @@ void NodeParamViewItemBody::ArrayInsertClicked()
|
||||
if (it.value().array_insert_btn == sender()) {
|
||||
// Found our input and element
|
||||
NodeInput ic = NodeGroup::ResolveInput(it.key());
|
||||
Core::instance()->undo_stack()->push(new Node::ArrayInsertCommand(ic.node(), ic.input(), ic.element()));
|
||||
Core::instance()->undo_stack()->push(new NodeArrayInsertCommand(ic.node(), ic.input(), ic.element()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -468,7 +468,7 @@ void NodeParamViewItemBody::ArrayRemoveClicked()
|
||||
if (it.value().array_remove_btn == sender()) {
|
||||
// Found our input and element
|
||||
NodeInput ic = NodeGroup::ResolveInput(it.key());
|
||||
Core::instance()->undo_stack()->push(new Node::ArrayRemoveCommand(ic.node(), ic.input(), ic.element()));
|
||||
Core::instance()->undo_stack()->push(new NodeArrayRemoveCommand(ic.node(), ic.input(), ic.element()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "core.h"
|
||||
#include "nodeparamviewundo.h"
|
||||
#include "node/nodeundo.h"
|
||||
#include "ui/icons/icons.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -1,215 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "nodeparamviewundo.h"
|
||||
|
||||
#include "node/node.h"
|
||||
#include "node/project/sequence/sequence.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
NodeParamSetKeyframingCommand::NodeParamSetKeyframingCommand(const NodeInput &input, bool setting) :
|
||||
input_(input),
|
||||
new_setting_(setting)
|
||||
{
|
||||
}
|
||||
|
||||
Project *NodeParamSetKeyframingCommand::GetRelevantProject() const
|
||||
{
|
||||
return input_.node()->project();
|
||||
}
|
||||
|
||||
void NodeParamSetKeyframingCommand::redo()
|
||||
{
|
||||
old_setting_ = input_.IsKeyframing();
|
||||
input_.node()->SetInputIsKeyframing(input_, new_setting_);
|
||||
}
|
||||
|
||||
void NodeParamSetKeyframingCommand::undo()
|
||||
{
|
||||
input_.node()->SetInputIsKeyframing(input_, old_setting_);
|
||||
}
|
||||
|
||||
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) :
|
||||
key_(key),
|
||||
old_value_(old_value),
|
||||
new_value_(new_value)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Project *NodeParamSetKeyframeValueCommand::GetRelevantProject() const
|
||||
{
|
||||
return key_->parent()->project();
|
||||
}
|
||||
|
||||
void NodeParamSetKeyframeValueCommand::redo()
|
||||
{
|
||||
key_->set_value(new_value_);
|
||||
}
|
||||
|
||||
void NodeParamSetKeyframeValueCommand::undo()
|
||||
{
|
||||
key_->set_value(old_value_);
|
||||
}
|
||||
|
||||
NodeParamInsertKeyframeCommand::NodeParamInsertKeyframeCommand(Node* node, NodeKeyframe* keyframe) :
|
||||
input_(node),
|
||||
keyframe_(keyframe)
|
||||
{
|
||||
// Take ownership of the keyframe
|
||||
undo();
|
||||
}
|
||||
|
||||
Project *NodeParamInsertKeyframeCommand::GetRelevantProject() const
|
||||
{
|
||||
return input_->project();
|
||||
}
|
||||
|
||||
void NodeParamInsertKeyframeCommand::redo()
|
||||
{
|
||||
keyframe_->setParent(input_);
|
||||
}
|
||||
|
||||
void NodeParamInsertKeyframeCommand::undo()
|
||||
{
|
||||
keyframe_->setParent(&memory_manager_);
|
||||
}
|
||||
|
||||
NodeParamRemoveKeyframeCommand::NodeParamRemoveKeyframeCommand(NodeKeyframe* keyframe) :
|
||||
input_(keyframe->parent()),
|
||||
keyframe_(keyframe)
|
||||
{
|
||||
}
|
||||
|
||||
Project *NodeParamRemoveKeyframeCommand::GetRelevantProject() const
|
||||
{
|
||||
return input_->project();
|
||||
}
|
||||
|
||||
void NodeParamRemoveKeyframeCommand::redo()
|
||||
{
|
||||
// Removes from input
|
||||
keyframe_->setParent(&memory_manager_);
|
||||
}
|
||||
|
||||
void NodeParamRemoveKeyframeCommand::undo()
|
||||
{
|
||||
keyframe_->setParent(input_);
|
||||
}
|
||||
|
||||
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) :
|
||||
key_(key),
|
||||
old_time_(old_time),
|
||||
new_time_(new_time)
|
||||
{
|
||||
}
|
||||
|
||||
Project *NodeParamSetKeyframeTimeCommand::GetRelevantProject() const
|
||||
{
|
||||
return key_->parent()->project();
|
||||
}
|
||||
|
||||
void NodeParamSetKeyframeTimeCommand::redo()
|
||||
{
|
||||
key_->set_time(new_time_);
|
||||
}
|
||||
|
||||
void NodeParamSetKeyframeTimeCommand::undo()
|
||||
{
|
||||
key_->set_time(old_time_);
|
||||
}
|
||||
|
||||
NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(const NodeKeyframeTrackReference& input, const QVariant &value) :
|
||||
ref_(input),
|
||||
old_value_(ref_.input().node()->GetStandardValue(ref_.input())),
|
||||
new_value_(value)
|
||||
{
|
||||
}
|
||||
|
||||
NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(const NodeKeyframeTrackReference& input, const QVariant &new_value, const QVariant &old_value) :
|
||||
ref_(input),
|
||||
old_value_(old_value),
|
||||
new_value_(new_value)
|
||||
{
|
||||
}
|
||||
|
||||
Project *NodeParamSetStandardValueCommand::GetRelevantProject() const
|
||||
{
|
||||
return ref_.input().node()->project();
|
||||
}
|
||||
|
||||
void NodeParamSetStandardValueCommand::redo()
|
||||
{
|
||||
ref_.input().node()->SetSplitStandardValueOnTrack(ref_, new_value_);
|
||||
}
|
||||
|
||||
void NodeParamSetStandardValueCommand::undo()
|
||||
{
|
||||
ref_.input().node()->SetSplitStandardValueOnTrack(ref_, old_value_);
|
||||
}
|
||||
|
||||
NodeParamArrayAppendCommand::NodeParamArrayAppendCommand(Node *node, const QString &input) :
|
||||
node_(node),
|
||||
input_(input)
|
||||
{
|
||||
}
|
||||
|
||||
Project *NodeParamArrayAppendCommand::GetRelevantProject() const
|
||||
{
|
||||
return node_->project();
|
||||
}
|
||||
|
||||
void NodeParamArrayAppendCommand::redo()
|
||||
{
|
||||
node_->InputArrayAppend(input_);
|
||||
}
|
||||
|
||||
void NodeParamArrayAppendCommand::undo()
|
||||
{
|
||||
node_->InputArrayRemoveLast(input_);
|
||||
}
|
||||
|
||||
void NodeSetValueHintCommand::redo()
|
||||
{
|
||||
old_hint_ = input_.node()->GetValueHintForInput(input_.input(), input_.element());
|
||||
input_.node()->SetValueHintForInput(input_.input(), new_hint_, input_.element());
|
||||
}
|
||||
|
||||
void NodeSetValueHintCommand::undo()
|
||||
{
|
||||
input_.node()->SetValueHintForInput(input_.input(), old_hint_, input_.element());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,239 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef NODEPARAMVIEWUNDO_H
|
||||
#define NODEPARAMVIEWUNDO_H
|
||||
|
||||
#include "node/keyframe.h"
|
||||
#include "node/node.h"
|
||||
#include "node/param.h"
|
||||
#include "undo/undocommand.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class NodeParamSetKeyframingCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
NodeParamSetKeyframingCommand(const NodeInput& input, bool setting);
|
||||
|
||||
virtual Project* GetRelevantProject() const override;
|
||||
|
||||
protected:
|
||||
virtual void redo() override;
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
NodeInput input_;
|
||||
bool new_setting_;
|
||||
bool old_setting_;
|
||||
|
||||
};
|
||||
|
||||
class NodeParamInsertKeyframeCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
NodeParamInsertKeyframeCommand(Node *node, NodeKeyframe* keyframe);
|
||||
|
||||
virtual Project* GetRelevantProject() const override;
|
||||
|
||||
protected:
|
||||
virtual void redo() override;
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
Node* input_;
|
||||
|
||||
NodeKeyframe* keyframe_;
|
||||
|
||||
QObject memory_manager_;
|
||||
|
||||
};
|
||||
|
||||
class NodeParamRemoveKeyframeCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
NodeParamRemoveKeyframeCommand(NodeKeyframe* keyframe);
|
||||
|
||||
virtual Project* GetRelevantProject() const override;
|
||||
|
||||
protected:
|
||||
virtual void redo() override;
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
Node* input_;
|
||||
|
||||
NodeKeyframe* keyframe_;
|
||||
|
||||
QObject memory_manager_;
|
||||
|
||||
};
|
||||
|
||||
class NodeParamSetKeyframeTimeCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
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() override;
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
NodeKeyframe* key_;
|
||||
|
||||
rational old_time_;
|
||||
rational new_time_;
|
||||
|
||||
};
|
||||
|
||||
class NodeParamSetKeyframeValueCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
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() override;
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
NodeKeyframe* key_;
|
||||
|
||||
QVariant old_value_;
|
||||
QVariant new_value_;
|
||||
|
||||
};
|
||||
|
||||
class NodeParamSetStandardValueCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
NodeParamSetStandardValueCommand(const NodeKeyframeTrackReference& input, const QVariant& value);
|
||||
NodeParamSetStandardValueCommand(const NodeKeyframeTrackReference& input, const QVariant& new_value, const QVariant& old_value);
|
||||
|
||||
virtual Project* GetRelevantProject() const override;
|
||||
|
||||
protected:
|
||||
virtual void redo() override;
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
NodeKeyframeTrackReference ref_;
|
||||
|
||||
QVariant old_value_;
|
||||
QVariant new_value_;
|
||||
|
||||
};
|
||||
|
||||
class NodeParamSetSplitStandardValueCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
NodeParamSetSplitStandardValueCommand(const NodeInput& input, const SplitValue& new_value, const SplitValue& old_value) :
|
||||
ref_(input),
|
||||
old_value_(old_value),
|
||||
new_value_(new_value)
|
||||
{}
|
||||
|
||||
NodeParamSetSplitStandardValueCommand(const NodeInput& input, const SplitValue& value) :
|
||||
NodeParamSetSplitStandardValueCommand(input, value, input.node()->GetSplitStandardValue(input.input()))
|
||||
{}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return ref_.node()->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void redo() override
|
||||
{
|
||||
ref_.node()->SetSplitStandardValue(ref_.input(), new_value_, ref_.element());
|
||||
}
|
||||
|
||||
virtual void undo() override
|
||||
{
|
||||
ref_.node()->SetSplitStandardValue(ref_.input(), old_value_, ref_.element());
|
||||
}
|
||||
|
||||
private:
|
||||
NodeInput ref_;
|
||||
|
||||
SplitValue old_value_;
|
||||
SplitValue new_value_;
|
||||
|
||||
};
|
||||
|
||||
class NodeParamArrayAppendCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
NodeParamArrayAppendCommand(Node* node, const QString& input);
|
||||
|
||||
virtual Project* GetRelevantProject() const override;
|
||||
|
||||
protected:
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
Node* node_;
|
||||
|
||||
QString input_;
|
||||
|
||||
};
|
||||
|
||||
class NodeSetValueHintCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
NodeSetValueHintCommand(const NodeInput &input, const Node::ValueHint &hint) :
|
||||
input_(input),
|
||||
new_hint_(hint)
|
||||
{
|
||||
}
|
||||
|
||||
NodeSetValueHintCommand(Node *node, const QString &input, int element, const Node::ValueHint &hint) :
|
||||
NodeSetValueHintCommand(NodeInput(node, input, element), hint)
|
||||
{
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return input_.node()->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
NodeInput input_;
|
||||
|
||||
Node::ValueHint new_hint_;
|
||||
Node::ValueHint old_hint_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // NODEPARAMVIEWUNDO_H
|
||||
@@ -30,10 +30,10 @@
|
||||
#include "core.h"
|
||||
#include "node/group/group.h"
|
||||
#include "node/node.h"
|
||||
#include "node/nodeundo.h"
|
||||
#include "node/project/sequence/sequence.h"
|
||||
#include "nodeparamviewarraywidget.h"
|
||||
#include "nodeparamviewtextedit.h"
|
||||
#include "nodeparamviewundo.h"
|
||||
#include "undo/undostack.h"
|
||||
#include "widget/bezier/bezierwidget.h"
|
||||
#include "widget/colorbutton/colorbutton.h"
|
||||
|
||||
@@ -72,7 +72,7 @@ void NodeTreeView::SetNodes(const QVector<Node *> &nodes)
|
||||
node_item->setCheckState(0, disabled_nodes_.contains(n) ? Qt::Unchecked : Qt::Checked);
|
||||
}
|
||||
node_item->setData(0, kItemType, kItemTypeNode);
|
||||
node_item->setData(0, kItemNodePointer, Node::PtrToValue(n));
|
||||
node_item->setData(0, kItemNodePointer, QtUtils::PtrToValue(n));
|
||||
|
||||
foreach (const QString& input, n->inputs()) {
|
||||
if (n->IsInputHidden(input) || (only_show_keyframable_ && !n->IsInputKeyframable(input))) {
|
||||
@@ -158,7 +158,7 @@ NodeKeyframeTrackReference NodeTreeView::GetSelectedInput()
|
||||
if (item->data(0, kItemType).toInt() == kItemTypeInput) {
|
||||
selected_ref = item->data(0, kItemInputReference).value<NodeKeyframeTrackReference>();
|
||||
} else {
|
||||
selected_ref = NodeKeyframeTrackReference(NodeInput(Node::ValueToPtr<Node>(item->data(0, kItemNodePointer)), QString()));
|
||||
selected_ref = NodeKeyframeTrackReference(NodeInput(QtUtils::ValueToPtr<Node>(item->data(0, kItemNodePointer)), QString()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,7 +232,7 @@ void NodeTreeView::ItemCheckStateChanged(QTreeWidgetItem *item, int column)
|
||||
switch (item->data(0, kItemType).toInt()) {
|
||||
case kItemTypeNode:
|
||||
{
|
||||
Node* n = Node::ValueToPtr<Node>(item->data(0, kItemNodePointer));
|
||||
Node* n = QtUtils::ValueToPtr<Node>(item->data(0, kItemNodePointer));
|
||||
|
||||
if (item->checkState(0) == Qt::Checked) {
|
||||
if (disabled_nodes_.contains(n)) {
|
||||
|
||||
@@ -33,8 +33,6 @@ set(OLIVE_SOURCES
|
||||
widget/nodeview/nodeviewscene.h
|
||||
widget/nodeview/nodeviewtoolbar.cpp
|
||||
widget/nodeview/nodeviewtoolbar.h
|
||||
widget/nodeview/nodeviewundo.cpp
|
||||
widget/nodeview/nodeviewundo.h
|
||||
widget/nodeview/nodewidget.cpp
|
||||
widget/nodeview/nodewidget.h
|
||||
PARENT_SCOPE
|
||||
|
||||
@@ -28,11 +28,11 @@
|
||||
#include <QScrollBar>
|
||||
#include <QToolTip>
|
||||
|
||||
#include "nodeviewundo.h"
|
||||
#include "node/audio/volume/volume.h"
|
||||
#include "node/distort/transform/transformdistortnode.h"
|
||||
#include "node/factory.h"
|
||||
#include "node/group/group.h"
|
||||
#include "node/nodeundo.h"
|
||||
#include "node/project/serializer/serializer.h"
|
||||
#include "node/traverser.h"
|
||||
#include "ui/icons/icons.h"
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
#include <QGraphicsTextItem>
|
||||
|
||||
#include "node/node.h"
|
||||
#include "node/nodeundo.h"
|
||||
#include "nodeviewcommon.h"
|
||||
#include "nodeviewedge.h"
|
||||
#include "nodeviewundo.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
|
||||
@@ -29,9 +29,9 @@
|
||||
#include "common/qtutils.h"
|
||||
#include "config/config.h"
|
||||
#include "core.h"
|
||||
#include "node/nodeundo.h"
|
||||
#include "nodeview.h"
|
||||
#include "nodeviewscene.h"
|
||||
#include "nodeviewundo.h"
|
||||
#include "ui/colorcoding.h"
|
||||
#include "ui/icons/icons.h"
|
||||
#include "window/mainwindow/mainwindow.h"
|
||||
|
||||
@@ -1,290 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "nodeviewundo.h"
|
||||
|
||||
#include "node/project/sequence/sequence.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
NodeEdgeAddCommand::NodeEdgeAddCommand(Node *output, const NodeInput &input) :
|
||||
output_(output),
|
||||
input_(input),
|
||||
remove_command_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
NodeEdgeAddCommand::~NodeEdgeAddCommand()
|
||||
{
|
||||
delete remove_command_;
|
||||
}
|
||||
|
||||
void NodeEdgeAddCommand::redo()
|
||||
{
|
||||
if (input_.IsConnected()) {
|
||||
if (!remove_command_) {
|
||||
remove_command_ = new NodeEdgeRemoveCommand(input_.GetConnectedOutput(), input_);
|
||||
}
|
||||
|
||||
remove_command_->redo_now();
|
||||
}
|
||||
|
||||
Node::ConnectEdge(output_, input_);
|
||||
}
|
||||
|
||||
void NodeEdgeAddCommand::undo()
|
||||
{
|
||||
Node::DisconnectEdge(output_, input_);
|
||||
|
||||
if (remove_command_) {
|
||||
remove_command_->undo_now();
|
||||
}
|
||||
}
|
||||
|
||||
Project *NodeEdgeAddCommand::GetRelevantProject() const
|
||||
{
|
||||
return output_->project();
|
||||
}
|
||||
|
||||
NodeEdgeRemoveCommand::NodeEdgeRemoveCommand(Node *output, const NodeInput &input) :
|
||||
output_(output),
|
||||
input_(input)
|
||||
{
|
||||
}
|
||||
|
||||
void NodeEdgeRemoveCommand::redo()
|
||||
{
|
||||
Node::DisconnectEdge(output_, input_);
|
||||
}
|
||||
|
||||
void NodeEdgeRemoveCommand::undo()
|
||||
{
|
||||
Node::ConnectEdge(output_, input_);
|
||||
}
|
||||
|
||||
Project *NodeEdgeRemoveCommand::GetRelevantProject() const
|
||||
{
|
||||
return output_->project();
|
||||
}
|
||||
|
||||
NodeAddCommand::NodeAddCommand(Project *graph, Node *node) :
|
||||
graph_(graph),
|
||||
node_(node)
|
||||
{
|
||||
// Ensures that when this command is destroyed, if redo() is never called again, the node will be destroyed too
|
||||
node_->setParent(&memory_manager_);
|
||||
}
|
||||
|
||||
void NodeAddCommand::PushToThread(QThread *thread)
|
||||
{
|
||||
memory_manager_.moveToThread(thread);
|
||||
}
|
||||
|
||||
void NodeAddCommand::redo()
|
||||
{
|
||||
node_->setParent(graph_);
|
||||
}
|
||||
|
||||
void NodeAddCommand::undo()
|
||||
{
|
||||
node_->setParent(&memory_manager_);
|
||||
}
|
||||
|
||||
Project *NodeAddCommand::GetRelevantProject() const
|
||||
{
|
||||
return dynamic_cast<Project*>(graph_);
|
||||
}
|
||||
|
||||
void NodeRemoveAndDisconnectCommand::prepare()
|
||||
{
|
||||
command_ = new MultiUndoCommand();
|
||||
|
||||
// If this is a block, remove all links
|
||||
if (node_->HasLinks()) {
|
||||
command_->add_child(new NodeUnlinkAllCommand(node_));
|
||||
}
|
||||
|
||||
// Disconnect everything
|
||||
for (auto it=node_->input_connections().cbegin(); it!=node_->input_connections().cend(); it++) {
|
||||
command_->add_child(new NodeEdgeRemoveCommand(it->second, it->first));
|
||||
}
|
||||
|
||||
for (const Node::OutputConnection& conn : node_->output_connections()) {
|
||||
command_->add_child(new NodeEdgeRemoveCommand(conn.first, conn.second));
|
||||
}
|
||||
|
||||
command_->add_child(new NodeRemovePositionFromAllContextsCommand(node_));
|
||||
}
|
||||
|
||||
void NodeRenameCommand::AddNode(Node *node, const QString &new_name)
|
||||
{
|
||||
nodes_.append(node);
|
||||
new_labels_.append(new_name);
|
||||
old_labels_.append(node->GetLabel());
|
||||
}
|
||||
|
||||
void NodeRenameCommand::redo()
|
||||
{
|
||||
for (int i=0; i<nodes_.size(); i++) {
|
||||
nodes_.at(i)->SetLabel(new_labels_.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
void NodeRenameCommand::undo()
|
||||
{
|
||||
for (int i=0; i<nodes_.size(); i++) {
|
||||
nodes_.at(i)->SetLabel(old_labels_.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
Project *NodeRenameCommand::GetRelevantProject() const
|
||||
{
|
||||
return nodes_.isEmpty() ? nullptr : nodes_.first()->project();
|
||||
}
|
||||
|
||||
NodeOverrideColorCommand::NodeOverrideColorCommand(Node *node, int index) :
|
||||
node_(node),
|
||||
new_index_(index)
|
||||
{
|
||||
}
|
||||
|
||||
Project *NodeOverrideColorCommand::GetRelevantProject() const
|
||||
{
|
||||
return node_->project();
|
||||
}
|
||||
|
||||
void NodeOverrideColorCommand::redo()
|
||||
{
|
||||
old_index_ = node_->GetOverrideColor();
|
||||
node_->SetOverrideColor(new_index_);
|
||||
}
|
||||
|
||||
void NodeOverrideColorCommand::undo()
|
||||
{
|
||||
node_->SetOverrideColor(old_index_);
|
||||
}
|
||||
|
||||
NodeViewDeleteCommand::NodeViewDeleteCommand()
|
||||
{
|
||||
}
|
||||
|
||||
void NodeViewDeleteCommand::AddNode(Node *node, Node *context)
|
||||
{
|
||||
if (ContainsNode(node, context)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Node::ContextPair p = {node, context};
|
||||
nodes_.append(p);
|
||||
|
||||
for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) {
|
||||
if (context->ContextContainsNode(it->second)) {
|
||||
AddEdge(it->second, it->first);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it=node->output_connections().cbegin(); it!=node->output_connections().cend(); it++) {
|
||||
if (context->ContextContainsNode(it->second.node())) {
|
||||
AddEdge(it->first, it->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeViewDeleteCommand::AddEdge(Node *output, const NodeInput &input)
|
||||
{
|
||||
foreach (const Node::OutputConnection &edge, edges_) {
|
||||
if (edge.first == output && edge.second == input) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
edges_.append({output, input});
|
||||
}
|
||||
|
||||
bool NodeViewDeleteCommand::ContainsNode(Node *node, Node *context)
|
||||
{
|
||||
foreach (const Node::ContextPair &pair, nodes_) {
|
||||
if (pair.node == node && pair.context == context) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Project *NodeViewDeleteCommand::GetRelevantProject() const
|
||||
{
|
||||
if (!nodes_.isEmpty()) {
|
||||
return nodes_.first().node->project();
|
||||
}
|
||||
|
||||
if (!edges_.isEmpty()) {
|
||||
return edges_.first().first->project();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void NodeViewDeleteCommand::redo()
|
||||
{
|
||||
foreach (const Node::OutputConnection &edge, edges_) {
|
||||
Node::DisconnectEdge(edge.first, edge.second);
|
||||
}
|
||||
|
||||
foreach (const Node::ContextPair &pair, nodes_) {
|
||||
RemovedNode rn;
|
||||
|
||||
rn.node = pair.node;
|
||||
rn.context = pair.context;
|
||||
rn.pos = rn.context->GetNodePositionInContext(rn.node);
|
||||
|
||||
rn.context->RemoveNodeFromContext(rn.node);
|
||||
|
||||
// If node is no longer in any contexts and is not connected to anything, remove it
|
||||
if (rn.node->parent()->GetNumberOfContextsNodeIsIn(rn.node, true) == 0
|
||||
&& rn.node->input_connections().empty()
|
||||
&& rn.node->output_connections().empty()) {
|
||||
rn.removed_from_graph = rn.node->parent();
|
||||
rn.node->setParent(&memory_manager_);
|
||||
} else {
|
||||
rn.removed_from_graph = nullptr;
|
||||
}
|
||||
|
||||
removed_nodes_.append(rn);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeViewDeleteCommand::undo()
|
||||
{
|
||||
for (auto rn=removed_nodes_.crbegin(); rn!=removed_nodes_.crend(); rn++) {
|
||||
if (rn->removed_from_graph) {
|
||||
rn->node->setParent(rn->removed_from_graph);
|
||||
}
|
||||
|
||||
rn->context->SetNodePositionInContext(rn->node, rn->pos);
|
||||
}
|
||||
removed_nodes_.clear();
|
||||
|
||||
for (auto edge=edges_.crbegin(); edge!=edges_.crend(); edge++) {
|
||||
Node::ConnectEdge(edge->first, edge->second);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,385 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef NODEVIEWUNDO_H
|
||||
#define NODEVIEWUNDO_H
|
||||
|
||||
#include "node/node.h"
|
||||
#include "node/project.h"
|
||||
#include "undo/undocommand.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
/**
|
||||
* @brief An undoable command for disconnecting two NodeParams
|
||||
*
|
||||
* Can be considered a UndoCommand wrapper for NodeParam::DisonnectEdge()/
|
||||
*/
|
||||
class NodeEdgeRemoveCommand : public UndoCommand {
|
||||
public:
|
||||
NodeEdgeRemoveCommand(Node *output, const NodeInput& input);
|
||||
|
||||
virtual Project* GetRelevantProject() const override;
|
||||
|
||||
protected:
|
||||
virtual void redo() override;
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
Node *output_;
|
||||
NodeInput input_;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An undoable command for connecting two NodeParams together
|
||||
*
|
||||
* Can be considered a UndoCommand wrapper for NodeParam::ConnectEdge()/
|
||||
*/
|
||||
class NodeEdgeAddCommand : public UndoCommand {
|
||||
public:
|
||||
NodeEdgeAddCommand(Node *output, const NodeInput& input);
|
||||
|
||||
virtual ~NodeEdgeAddCommand() override;
|
||||
|
||||
virtual Project* GetRelevantProject() const override;
|
||||
|
||||
protected:
|
||||
virtual void redo() override;
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
Node *output_;
|
||||
NodeInput input_;
|
||||
|
||||
NodeEdgeRemoveCommand* remove_command_;
|
||||
|
||||
};
|
||||
|
||||
class NodeAddCommand : public UndoCommand {
|
||||
public:
|
||||
NodeAddCommand(Project* graph, Node* node);
|
||||
|
||||
void PushToThread(QThread* thread);
|
||||
|
||||
virtual Project* GetRelevantProject() const override;
|
||||
|
||||
protected:
|
||||
virtual void redo() override;
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
QObject memory_manager_;
|
||||
|
||||
Project* graph_;
|
||||
Node* node_;
|
||||
};
|
||||
|
||||
class NodeRemoveAndDisconnectCommand : public UndoCommand {
|
||||
public:
|
||||
NodeRemoveAndDisconnectCommand(Node* node) :
|
||||
node_(node),
|
||||
graph_(nullptr),
|
||||
command_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~NodeRemoveAndDisconnectCommand() override
|
||||
{
|
||||
delete command_;
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return graph_;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void prepare() override;
|
||||
|
||||
virtual void redo() override
|
||||
{
|
||||
command_->redo_now();
|
||||
|
||||
graph_ = node_->parent();
|
||||
node_->setParent(&memory_manager_);
|
||||
}
|
||||
|
||||
virtual void undo() override
|
||||
{
|
||||
node_->setParent(graph_);
|
||||
graph_ = nullptr;
|
||||
|
||||
command_->undo_now();
|
||||
}
|
||||
|
||||
private:
|
||||
QObject memory_manager_;
|
||||
|
||||
Node* node_;
|
||||
Project* graph_;
|
||||
|
||||
MultiUndoCommand* command_;
|
||||
|
||||
};
|
||||
|
||||
class NodeRemoveWithExclusiveDependenciesAndDisconnect : public UndoCommand {
|
||||
public:
|
||||
NodeRemoveWithExclusiveDependenciesAndDisconnect(Node* node) :
|
||||
node_(node),
|
||||
command_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~NodeRemoveWithExclusiveDependenciesAndDisconnect() override
|
||||
{
|
||||
delete command_;
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
if (command_) {
|
||||
return static_cast<const NodeRemoveAndDisconnectCommand*>(command_->child(0))->GetRelevantProject();
|
||||
} else {
|
||||
return node_->project();
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void prepare() override
|
||||
{
|
||||
command_ = new MultiUndoCommand();
|
||||
|
||||
command_->add_child(new NodeRemoveAndDisconnectCommand(node_));
|
||||
|
||||
// Remove exclusive dependencies
|
||||
QVector<Node*> deps = node_->GetExclusiveDependencies();
|
||||
foreach (Node* d, deps) {
|
||||
command_->add_child(new NodeRemoveAndDisconnectCommand(d));
|
||||
}
|
||||
}
|
||||
|
||||
virtual void redo() override
|
||||
{
|
||||
command_->redo_now();
|
||||
}
|
||||
|
||||
virtual void undo() override
|
||||
{
|
||||
command_->undo_now();
|
||||
}
|
||||
|
||||
private:
|
||||
Node* node_;
|
||||
MultiUndoCommand* command_;
|
||||
|
||||
};
|
||||
|
||||
class NodeLinkCommand : public UndoCommand {
|
||||
public:
|
||||
NodeLinkCommand(Node* a, Node* b, bool link) :
|
||||
a_(a),
|
||||
b_(b),
|
||||
link_(link)
|
||||
{
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return a_->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void redo() override
|
||||
{
|
||||
if (link_) {
|
||||
done_ = Node::Link(a_, b_);
|
||||
} else {
|
||||
done_ = Node::Unlink(a_, b_);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void undo() override
|
||||
{
|
||||
if (done_) {
|
||||
if (link_) {
|
||||
Node::Unlink(a_, b_);
|
||||
} else {
|
||||
Node::Link(a_, b_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Node* a_;
|
||||
Node* b_;
|
||||
bool link_;
|
||||
bool done_;
|
||||
|
||||
};
|
||||
|
||||
class NodeUnlinkAllCommand : public UndoCommand {
|
||||
public:
|
||||
NodeUnlinkAllCommand(Node* node) :
|
||||
node_(node)
|
||||
{
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return node_->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void redo() override
|
||||
{
|
||||
unlinked_ = node_->links();
|
||||
|
||||
foreach (Node* link, unlinked_) {
|
||||
Node::Unlink(node_, link);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void undo() override
|
||||
{
|
||||
foreach (Node* link, unlinked_) {
|
||||
Node::Link(node_, link);
|
||||
}
|
||||
|
||||
unlinked_.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
Node* node_;
|
||||
|
||||
QVector<Node*> unlinked_;
|
||||
|
||||
};
|
||||
|
||||
class NodeLinkManyCommand : public MultiUndoCommand {
|
||||
public:
|
||||
NodeLinkManyCommand(const QVector<Node*> nodes, bool link) :
|
||||
nodes_(nodes)
|
||||
{
|
||||
foreach (Node* a, nodes_) {
|
||||
foreach (Node* b, nodes_) {
|
||||
if (a != b) {
|
||||
add_child(new NodeLinkCommand(a, b, link));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return nodes_.first()->project();
|
||||
}
|
||||
|
||||
private:
|
||||
QVector<Node*> nodes_;
|
||||
|
||||
};
|
||||
|
||||
class NodeRenameCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
NodeRenameCommand() = default;
|
||||
NodeRenameCommand(Node* node, const QString& new_name)
|
||||
{
|
||||
AddNode(node, new_name);
|
||||
}
|
||||
|
||||
void AddNode(Node* node, const QString& new_name);
|
||||
|
||||
virtual Project * GetRelevantProject() const override;
|
||||
|
||||
protected:
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
QVector<Node*> nodes_;
|
||||
|
||||
QStringList new_labels_;
|
||||
QStringList old_labels_;
|
||||
|
||||
};
|
||||
|
||||
class NodeOverrideColorCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
NodeOverrideColorCommand(Node *node, int index);
|
||||
|
||||
virtual Project * GetRelevantProject() const override;
|
||||
|
||||
protected:
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
Node *node_;
|
||||
|
||||
int old_index_;
|
||||
|
||||
int new_index_;
|
||||
|
||||
};
|
||||
|
||||
class NodeViewDeleteCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
NodeViewDeleteCommand();
|
||||
|
||||
void AddNode(Node *node, Node *context);
|
||||
|
||||
void AddEdge(Node *output, const NodeInput &input);
|
||||
|
||||
bool ContainsNode(Node *node, Node *context);
|
||||
|
||||
virtual Project * GetRelevantProject() const override;
|
||||
|
||||
protected:
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
QVector<Node::ContextPair> nodes_;
|
||||
|
||||
QVector<Node::OutputConnection> edges_;
|
||||
|
||||
struct RemovedNode {
|
||||
Node *node;
|
||||
Node *context;
|
||||
QPointF pos;
|
||||
Project *removed_from_graph;
|
||||
};
|
||||
|
||||
QVector<RemovedNode> removed_nodes_;
|
||||
|
||||
QObject memory_manager_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // NODEVIEWUNDO_H
|
||||
@@ -38,11 +38,9 @@
|
||||
#include "task/taskmanager.h"
|
||||
#include "widget/menu/menu.h"
|
||||
#include "widget/menu/menushared.h"
|
||||
#include "widget/nodeparamview/nodeparamviewundo.h"
|
||||
#include "widget/nodeview/nodeviewundo.h"
|
||||
#include "node/nodeundo.h"
|
||||
#include "window/mainwindow/mainwindow.h"
|
||||
#include "window/mainwindow/mainwindowundo.h"
|
||||
#include "widget/nodeview/nodeviewundo.h"
|
||||
#include "widget/timelinewidget/timelinewidget.h"
|
||||
|
||||
namespace olive {
|
||||
@@ -396,7 +394,7 @@ void ProjectExplorer::ShowContextMenu()
|
||||
} else {
|
||||
foreach (Sequence* i, sequences) {
|
||||
QAction* a = proxy_menu->addAction(tr("For \"%1\"").arg(i->GetLabel()));
|
||||
a->setData(Node::PtrToValue(i));
|
||||
a->setData(QtUtils::PtrToValue(i));
|
||||
}
|
||||
|
||||
connect(proxy_menu, &Menu::triggered, this, &ProjectExplorer::ContextMenuStartProxy);
|
||||
@@ -505,7 +503,7 @@ void ProjectExplorer::OpenContextMenuItemInNewWindow()
|
||||
|
||||
void ProjectExplorer::ContextMenuStartProxy(QAction *a)
|
||||
{
|
||||
Sequence* sequence = Node::ValueToPtr<Sequence>(a->data());
|
||||
Sequence* sequence = QtUtils::ValueToPtr<Sequence>(a->data());
|
||||
|
||||
// To get here, the `context_menu_items_` must be all kFootage
|
||||
foreach (Node* item, context_menu_items_) {
|
||||
|
||||
@@ -26,8 +26,7 @@
|
||||
|
||||
#include "common/qtutils.h"
|
||||
#include "core.h"
|
||||
#include "widget/nodeview/nodeviewundo.h"
|
||||
#include "widget/nodeparamview/nodeparamviewundo.h"
|
||||
#include "node/nodeundo.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
|
||||
@@ -28,8 +28,8 @@
|
||||
#include "core.h"
|
||||
#include "dialog/markerproperties/markerpropertiesdialog.h"
|
||||
#include "node/project/sequence/sequence.h"
|
||||
#include "timeline/timelineundoworkarea.h"
|
||||
#include "widget/timeruler/timeruler.h"
|
||||
#include "widget/timelinewidget/undo/timelineundoworkarea.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
add_subdirectory(trackview)
|
||||
add_subdirectory(tool)
|
||||
add_subdirectory(undo)
|
||||
add_subdirectory(view)
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
|
||||
@@ -30,8 +30,13 @@
|
||||
#include "dialog/sequence/sequence.h"
|
||||
#include "dialog/speedduration/speeddurationdialog.h"
|
||||
#include "node/block/transition/transition.h"
|
||||
#include "node/nodeundo.h"
|
||||
#include "node/project/serializer/serializer.h"
|
||||
#include "task/project/import/import.h"
|
||||
#include "timeline/timelineundogeneral.h"
|
||||
#include "timeline/timelineundopointer.h"
|
||||
#include "timeline/timelineundoripple.h"
|
||||
#include "timeline/timelineundoworkarea.h"
|
||||
#include "tool/add.h"
|
||||
#include "tool/beam.h"
|
||||
#include "tool/edit.h"
|
||||
@@ -47,15 +52,9 @@
|
||||
#include "tool/zoom.h"
|
||||
#include "tool/tool.h"
|
||||
#include "trackview/trackview.h"
|
||||
#include "undo/timelineundogeneral.h"
|
||||
#include "undo/timelineundopointer.h"
|
||||
#include "undo/timelineundoripple.h"
|
||||
#include "undo/timelineundoworkarea.h"
|
||||
#include "widget/menu/menu.h"
|
||||
#include "widget/menu/menushared.h"
|
||||
#include "widget/nodeparamview/nodeparamviewundo.h"
|
||||
#include "widget/nodeparamview/nodeparamview.h"
|
||||
#include "widget/nodeview/nodeviewundo.h"
|
||||
#include "widget/timeruler/timeruler.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -25,9 +25,9 @@
|
||||
#include "node/generator/shape/shapenode.h"
|
||||
#include "node/generator/solid/solid.h"
|
||||
#include "node/generator/text/textv3.h"
|
||||
#include "widget/nodeparamview/nodeparamviewundo.h"
|
||||
#include "node/nodeundo.h"
|
||||
#include "timeline/timelineundopointer.h"
|
||||
#include "widget/timelinewidget/timelinewidget.h"
|
||||
#include "widget/timelinewidget/undo/timelineundopointer.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
|
||||
@@ -34,10 +34,9 @@
|
||||
#include "node/distort/transform/transformdistortnode.h"
|
||||
#include "node/generator/matrix/matrix.h"
|
||||
#include "node/math/math/math.h"
|
||||
#include "node/nodeundo.h"
|
||||
#include "node/project/sequence/sequence.h"
|
||||
#include "widget/nodeparamview/nodeparamviewundo.h"
|
||||
#include "widget/nodeview/nodeviewundo.h"
|
||||
#include "widget/timelinewidget/undo/timelineundopointer.h"
|
||||
#include "timeline/timelineundopointer.h"
|
||||
#include "window/mainwindow/mainwindow.h"
|
||||
#include "window/mainwindow/mainwindowundo.h"
|
||||
|
||||
|
||||
@@ -29,9 +29,9 @@
|
||||
#include "core.h"
|
||||
#include "node/block/gap/gap.h"
|
||||
#include "node/block/transition/transition.h"
|
||||
#include "node/nodeundo.h"
|
||||
#include "pointer.h"
|
||||
#include "widget/nodeview/nodeviewundo.h"
|
||||
#include "widget/timelinewidget/undo/timelineundopointer.h"
|
||||
#include "timeline/timelineundopointer.h"
|
||||
#include "widget/timeruler/timeruler.h"
|
||||
|
||||
namespace olive {
|
||||
@@ -615,7 +615,7 @@ void PointerTool::FinishDrag(TimelineViewMouseEvent *event)
|
||||
// Sort ghosts depending on which ones are trimming, which are moving, and which are sliding
|
||||
foreach (TimelineViewGhostItem* ghost, parent()->GetGhostItems()) {
|
||||
if (ghost->HasBeenAdjusted()) {
|
||||
Block* b = Node::ValueToPtr<Block>(ghost->GetData(TimelineViewGhostItem::kAttachedBlock));
|
||||
Block* b = QtUtils::ValueToPtr<Block>(ghost->GetData(TimelineViewGhostItem::kAttachedBlock));
|
||||
|
||||
if (ghost->GetData(TimelineViewGhostItem::kGhostIsSliding).toBool()) {
|
||||
blocks_sliding.append({ghost, b});
|
||||
@@ -829,7 +829,7 @@ void PointerTool::InitiateDrag(Block *clicked_item, Timeline::MovementMode trim_
|
||||
TimelineViewGhostItem *PointerTool::GetExistingGhostFromBlock(Block *block)
|
||||
{
|
||||
foreach (TimelineViewGhostItem* ghost, parent()->GetGhostItems()) {
|
||||
if (Node::ValueToPtr<Block>(ghost->GetData(TimelineViewGhostItem::kAttachedBlock)) == block) {
|
||||
if (QtUtils::ValueToPtr<Block>(ghost->GetData(TimelineViewGhostItem::kAttachedBlock)) == block) {
|
||||
return ghost;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,10 @@
|
||||
***/
|
||||
|
||||
#include "razor.h"
|
||||
|
||||
#include "node/nodeundo.h"
|
||||
#include "timeline/timelineundosplit.h"
|
||||
#include "widget/timelinewidget/timelinewidget.h"
|
||||
#include "widget/timelinewidget/undo/timelineundosplit.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
|
||||
@@ -21,9 +21,8 @@
|
||||
#include "widget/timelinewidget/timelinewidget.h"
|
||||
|
||||
#include "node/block/gap/gap.h"
|
||||
#include "timeline/timelineundoripple.h"
|
||||
#include "ripple.h"
|
||||
#include "widget/nodeview/nodeviewundo.h"
|
||||
#include "widget/timelinewidget/undo/timelineundoripple.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -105,7 +104,7 @@ void RippleTool::InitiateDrag(Block *clicked_item, Timeline::MovementMode trim_m
|
||||
} else {
|
||||
// Previous is not a gap, we'll have to insert one there ourselves
|
||||
ghost = AddGhostFromNull(block_after_ripple->in(), block_after_ripple->in(), track->ToReference(), trim_mode);
|
||||
ghost->SetData(TimelineViewGhostItem::kReferenceBlock, Node::PtrToValue(block_after_ripple));
|
||||
ghost->SetData(TimelineViewGhostItem::kReferenceBlock, QtUtils::PtrToValue(block_after_ripple));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -128,13 +127,13 @@ void RippleTool::FinishDrag(TimelineViewMouseEvent *event)
|
||||
Track* track = parent()->GetTrackFromReference(ghost->GetTrack());
|
||||
|
||||
TrackListRippleToolCommand::RippleInfo info;
|
||||
Block* b = Node::ValueToPtr<Block>(ghost->GetData(TimelineViewGhostItem::kAttachedBlock));
|
||||
Block* b = QtUtils::ValueToPtr<Block>(ghost->GetData(TimelineViewGhostItem::kAttachedBlock));
|
||||
|
||||
if (b) {
|
||||
info.block = b;
|
||||
info.append_gap = false;
|
||||
} else {
|
||||
info.block = Node::ValueToPtr<Block>(ghost->GetData(TimelineViewGhostItem::kReferenceBlock));
|
||||
info.block = QtUtils::ValueToPtr<Block>(ghost->GetData(TimelineViewGhostItem::kReferenceBlock));
|
||||
info.append_gap = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
#include "widget/timelinewidget/timelinewidget.h"
|
||||
|
||||
#include "node/block/gap/gap.h"
|
||||
#include "node/nodeundo.h"
|
||||
#include "rolling.h"
|
||||
#include "widget/nodeview/nodeviewundo.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
#include "widget/timelinewidget/timelinewidget.h"
|
||||
|
||||
#include "node/block/gap/gap.h"
|
||||
#include "node/nodeundo.h"
|
||||
#include "slide.h"
|
||||
#include "widget/nodeview/nodeviewundo.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
|
||||
@@ -18,13 +18,13 @@
|
||||
|
||||
***/
|
||||
|
||||
#include "widget/timelinewidget/timelinewidget.h"
|
||||
#include "slip.h"
|
||||
|
||||
#include <QToolTip>
|
||||
|
||||
#include "config/config.h"
|
||||
#include "slip.h"
|
||||
#include "widget/timelinewidget/undo/timelineundogeneral.h"
|
||||
#include "timeline/timelineundogeneral.h"
|
||||
#include "widget/timelinewidget/timelinewidget.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -72,7 +72,7 @@ void SlipTool::FinishDrag(TimelineViewMouseEvent *event)
|
||||
|
||||
// Find earliest point to ripple around
|
||||
foreach (TimelineViewGhostItem* ghost, parent()->GetGhostItems()) {
|
||||
Block* b = Node::ValueToPtr<Block>(ghost->GetData(TimelineViewGhostItem::kAttachedBlock));
|
||||
Block* b = QtUtils::ValueToPtr<Block>(ghost->GetData(TimelineViewGhostItem::kAttachedBlock));
|
||||
|
||||
ClipBlock *cb = dynamic_cast<ClipBlock*>(b);
|
||||
if (cb) {
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
|
||||
#include <QDragLeaveEvent>
|
||||
|
||||
#include "widget/nodeview/nodeviewundo.h"
|
||||
#include "widget/timelinewidget/view/timelineviewghostitem.h"
|
||||
#include "widget/timelinewidget/view/timelineviewmouseevent.h"
|
||||
|
||||
|
||||
@@ -23,9 +23,9 @@
|
||||
#include "node/block/transition/crossdissolve/crossdissolvetransition.h"
|
||||
#include "node/block/transition/transition.h"
|
||||
#include "node/factory.h"
|
||||
#include "node/nodeundo.h"
|
||||
#include "timeline/timelineundopointer.h"
|
||||
#include "transition.h"
|
||||
#include "widget/nodeview/nodeviewundo.h"
|
||||
#include "widget/timelinewidget/undo/timelineundopointer.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -65,11 +65,11 @@ void TransitionTool::MousePress(TimelineViewMouseEvent *event)
|
||||
ghost_->SetIn(transition_start_point);
|
||||
ghost_->SetOut(transition_start_point);
|
||||
ghost_->SetMode(trim_mode);
|
||||
ghost_->SetData(TimelineViewGhostItem::kAttachedBlock, Node::PtrToValue(primary));
|
||||
ghost_->SetData(TimelineViewGhostItem::kAttachedBlock, QtUtils::PtrToValue(primary));
|
||||
|
||||
dual_transition_ = (secondary);
|
||||
if (secondary)
|
||||
ghost_->SetData(TimelineViewGhostItem::kReferenceBlock, Node::PtrToValue(secondary));
|
||||
ghost_->SetData(TimelineViewGhostItem::kReferenceBlock, QtUtils::PtrToValue(secondary));
|
||||
|
||||
parent()->AddGhost(ghost_);
|
||||
|
||||
@@ -122,10 +122,10 @@ void TransitionTool::MouseRelease(TimelineViewMouseEvent *event)
|
||||
|
||||
if (dual_transition_) {
|
||||
// Block mouse is hovering over
|
||||
Block* active_block = Node::ValueToPtr<Block>(ghost_->GetData(TimelineViewGhostItem::kAttachedBlock));
|
||||
Block* active_block = QtUtils::ValueToPtr<Block>(ghost_->GetData(TimelineViewGhostItem::kAttachedBlock));
|
||||
|
||||
// Block mouse is next to
|
||||
Block* friend_block = Node::ValueToPtr<Block>(ghost_->GetData(TimelineViewGhostItem::kReferenceBlock));
|
||||
Block* friend_block = QtUtils::ValueToPtr<Block>(ghost_->GetData(TimelineViewGhostItem::kReferenceBlock));
|
||||
|
||||
// Use ghost mode to determine which block is which
|
||||
Block* out_block = (ghost_->GetMode() == Timeline::kTrimIn) ? friend_block : active_block;
|
||||
@@ -141,7 +141,7 @@ void TransitionTool::MouseRelease(TimelineViewMouseEvent *event)
|
||||
command->add_child(new NodeSetPositionCommand(out_block, transition, QPointF(-1, -0.5)));
|
||||
command->add_child(new NodeSetPositionCommand(in_block, transition, QPointF(-1, 0.5)));
|
||||
} else {
|
||||
Block* block_to_transition = Node::ValueToPtr<Block>(ghost_->GetData(TimelineViewGhostItem::kAttachedBlock));
|
||||
Block* block_to_transition = QtUtils::ValueToPtr<Block>(ghost_->GetData(TimelineViewGhostItem::kAttachedBlock));
|
||||
QString transition_input_to_connect;
|
||||
|
||||
if (ghost_->GetMode() == Timeline::kTrimIn) {
|
||||
|
||||
@@ -28,9 +28,9 @@
|
||||
#include <QtMath>
|
||||
|
||||
#include "core.h"
|
||||
#include "timeline/timelineundogeneral.h"
|
||||
#include "ui/icons/icons.h"
|
||||
#include "widget/menu/menu.h"
|
||||
#include "widget/timelinewidget/undo/timelineundogeneral.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
widget/timelinewidget/undo/timelineundocommon.h
|
||||
widget/timelinewidget/undo/timelineundogeneral.cpp
|
||||
widget/timelinewidget/undo/timelineundogeneral.h
|
||||
widget/timelinewidget/undo/timelineundopointer.cpp
|
||||
widget/timelinewidget/undo/timelineundopointer.h
|
||||
widget/timelinewidget/undo/timelineundoripple.cpp
|
||||
widget/timelinewidget/undo/timelineundoripple.h
|
||||
widget/timelinewidget/undo/timelineundosplit.cpp
|
||||
widget/timelinewidget/undo/timelineundosplit.h
|
||||
widget/timelinewidget/undo/timelineundotrack.cpp
|
||||
widget/timelinewidget/undo/timelineundotrack.h
|
||||
widget/timelinewidget/undo/timelineundoworkarea.cpp
|
||||
widget/timelinewidget/undo/timelineundoworkarea.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -1,48 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef TIMELINEUNDOCOMMON_H
|
||||
#define TIMELINEUNDOCOMMON_H
|
||||
|
||||
#include "node/node.h"
|
||||
#include "widget/nodeview/nodeviewundo.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
inline bool NodeCanBeRemoved(Node* n)
|
||||
{
|
||||
return n->output_connections().empty();
|
||||
}
|
||||
|
||||
inline UndoCommand* CreateRemoveCommand(Node* n)
|
||||
{
|
||||
return new NodeRemoveWithExclusiveDependenciesAndDisconnect(n);
|
||||
}
|
||||
|
||||
inline UndoCommand* CreateAndRunRemoveCommand(Node* n)
|
||||
{
|
||||
UndoCommand* command = CreateRemoveCommand(n);
|
||||
command->redo_now();
|
||||
return command;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // TIMELINEUNDOCOMMON_H
|
||||
@@ -1,667 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "timelineundogeneral.h"
|
||||
|
||||
#include "node/block/clip/clip.h"
|
||||
#include "node/block/transition/transition.h"
|
||||
#include "node/factory.h"
|
||||
#include "node/math/math/math.h"
|
||||
#include "node/math/merge/merge.h"
|
||||
#include "timelineundocommon.h"
|
||||
#include "widget/timelinewidget/undo/timelineundotrack.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
//
|
||||
// BlockResizeCommand
|
||||
//
|
||||
void BlockResizeCommand::redo()
|
||||
{
|
||||
old_length_ = block_->length();
|
||||
block_->set_length_and_media_out(new_length_);
|
||||
}
|
||||
|
||||
void BlockResizeCommand::undo()
|
||||
{
|
||||
block_->set_length_and_media_out(old_length_);
|
||||
}
|
||||
|
||||
//
|
||||
// BlockResizeWithMediaInCommand
|
||||
//
|
||||
void BlockResizeWithMediaInCommand::redo()
|
||||
{
|
||||
old_length_ = block_->length();
|
||||
block_->set_length_and_media_in(new_length_);
|
||||
}
|
||||
|
||||
void BlockResizeWithMediaInCommand::undo()
|
||||
{
|
||||
block_->set_length_and_media_in(old_length_);
|
||||
}
|
||||
|
||||
//
|
||||
// BlockSetMediaInCommand
|
||||
//
|
||||
void BlockSetMediaInCommand::redo()
|
||||
{
|
||||
old_media_in_ = block_->media_in();
|
||||
block_->set_media_in(new_media_in_);
|
||||
}
|
||||
|
||||
void BlockSetMediaInCommand::undo()
|
||||
{
|
||||
block_->set_media_in(old_media_in_);
|
||||
}
|
||||
|
||||
//
|
||||
// TimelineAddTrackCommand
|
||||
//
|
||||
TimelineAddTrackCommand::TimelineAddTrackCommand(TrackList *timeline, bool automerge_tracks) :
|
||||
timeline_(timeline),
|
||||
merge_(nullptr),
|
||||
position_command_(nullptr)
|
||||
{
|
||||
// Create new track
|
||||
track_ = new Track();
|
||||
track_->setParent(&memory_manager_);
|
||||
|
||||
// Determine what input to connect it to
|
||||
QString relevant_input;
|
||||
|
||||
if (timeline_->type() == Track::kVideo) {
|
||||
relevant_input = Sequence::kTextureInput;
|
||||
} else if (timeline_->type() == Track::kAudio) {
|
||||
relevant_input = Sequence::kSamplesInput;
|
||||
}
|
||||
|
||||
// If we have an input to connect to, set it as our `direct` connection
|
||||
if (!relevant_input.isEmpty()) {
|
||||
direct_ = NodeInput(timeline_->parent(), relevant_input);
|
||||
|
||||
// If we're automerging and something is already connected, determine if/how to merge it
|
||||
if (automerge_tracks && direct_.IsConnected()) {
|
||||
if (timeline_->type() == Track::kVideo) {
|
||||
// Use merge for video
|
||||
merge_ = new MergeNode();
|
||||
base_ = NodeInput(merge_, MergeNode::kBaseIn);
|
||||
blend_ = NodeInput(merge_, MergeNode::kBlendIn);
|
||||
} else if (timeline_->type() == Track::kAudio) {
|
||||
// Use math (add) for audio
|
||||
merge_ = new MathNode();
|
||||
base_ = NodeInput(merge_, MathNode::kParamAIn);
|
||||
blend_ = NodeInput(merge_, MathNode::kParamBIn);
|
||||
}
|
||||
|
||||
if (merge_) {
|
||||
// If we got created a merge node, ensure it's parented
|
||||
merge_->setParent(&memory_manager_);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineAddTrackCommand::redo()
|
||||
{
|
||||
// Get sequence
|
||||
Sequence* sequence = timeline_->parent();
|
||||
|
||||
// Add track to sequence
|
||||
track_->setParent(timeline_->GetParentGraph());
|
||||
if (timeline_->GetTrackCount() > 0) {
|
||||
track_->SetTrackHeight(timeline_->GetTrackAt(timeline_->GetTrackCount()-1)->GetTrackHeight());
|
||||
}
|
||||
timeline_->ArrayAppend();
|
||||
Node::ConnectEdge(track_, timeline_->track_input(timeline_->ArraySize() - 1));
|
||||
|
||||
qreal position_factor = 0.5;
|
||||
if (timeline_->type() == Track::kVideo) {
|
||||
position_factor = -position_factor;
|
||||
}
|
||||
bool create_pos_command = (!position_command_ && (timeline_->type() == Track::kVideo || timeline_->type() == Track::kAudio));
|
||||
if (create_pos_command) {
|
||||
position_command_ = new MultiUndoCommand();
|
||||
}
|
||||
|
||||
// Add merge if applicable
|
||||
if (merge_) {
|
||||
// Determine what was previously connected
|
||||
Node *previous_connection = direct_.GetConnectedOutput();
|
||||
|
||||
// Add merge to graph
|
||||
merge_->setParent(timeline_->GetParentGraph());
|
||||
|
||||
// Connect merge between what used to be here
|
||||
Node::DisconnectEdge(previous_connection, direct_);
|
||||
Node::ConnectEdge(merge_, direct_);
|
||||
Node::ConnectEdge(previous_connection, base_);
|
||||
Node::ConnectEdge(track_, blend_);
|
||||
|
||||
if (create_pos_command) {
|
||||
position_command_->add_child(new NodeSetPositionCommand(track_, sequence, sequence->GetNodePositionInContext(sequence) + QPointF(-1, -position_factor)));
|
||||
position_command_->add_child(new NodeSetPositionCommand(merge_, sequence, sequence->GetNodePositionInContext(sequence)));
|
||||
position_command_->add_child(new NodeSetPositionAndDependenciesRecursivelyCommand(merge_, sequence, sequence->GetNodePositionInContext(sequence) + QPointF(-1, position_factor * timeline_->GetTrackCount())));
|
||||
}
|
||||
} else if (direct_.IsValid() && !direct_.IsConnected()) {
|
||||
// If no merge, we have a direct connection, and nothing else is connected, connect this
|
||||
Node::ConnectEdge(track_, direct_);
|
||||
|
||||
if (create_pos_command) {
|
||||
// Just position directly next to the context node
|
||||
position_command_->add_child(new NodeSetPositionCommand(track_, sequence, sequence->GetNodePositionInContext(sequence) + QPointF(-1, position_factor)));
|
||||
}
|
||||
}
|
||||
|
||||
// Run position command if we created one
|
||||
if (position_command_) {
|
||||
position_command_->redo_now();
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineAddTrackCommand::undo()
|
||||
{
|
||||
if (position_command_) {
|
||||
position_command_->undo_now();
|
||||
}
|
||||
|
||||
// Remove merge if applicable
|
||||
if (merge_) {
|
||||
Node *previous_connection = base_.GetConnectedOutput();
|
||||
|
||||
Node::DisconnectEdge(track_, blend_);
|
||||
Node::DisconnectEdge(previous_connection, base_);
|
||||
Node::DisconnectEdge(merge_, direct_);
|
||||
Node::ConnectEdge(previous_connection, direct_);
|
||||
|
||||
merge_->setParent(&memory_manager_);
|
||||
} else if (direct_.IsValid() && direct_.GetConnectedOutput() == track_) {
|
||||
Node::DisconnectEdge(track_, direct_);
|
||||
}
|
||||
|
||||
// Remove track
|
||||
Node::DisconnectEdge(track_, timeline_->track_input(timeline_->ArraySize() - 1));
|
||||
timeline_->ArrayRemoveLast();
|
||||
track_->setParent(&memory_manager_);
|
||||
}
|
||||
|
||||
//
|
||||
// TransitionRemoveCommand
|
||||
//
|
||||
void TransitionRemoveCommand::redo()
|
||||
{
|
||||
track_ = block_->track();
|
||||
out_block_ = block_->connected_out_block();
|
||||
in_block_ = block_->connected_in_block();
|
||||
|
||||
Q_ASSERT(out_block_ || in_block_);
|
||||
|
||||
TimeRange invalidate_range(block_->in(), block_->out());
|
||||
|
||||
if (in_block_) {
|
||||
in_block_->set_length_and_media_in(in_block_->length() + block_->in_offset());
|
||||
}
|
||||
|
||||
if (out_block_) {
|
||||
out_block_->set_length_and_media_out(out_block_->length() + block_->out_offset());
|
||||
}
|
||||
|
||||
if (in_block_) {
|
||||
Node::DisconnectEdge(in_block_, NodeInput(block_, TransitionBlock::kInBlockInput));
|
||||
}
|
||||
|
||||
if (out_block_) {
|
||||
Node::DisconnectEdge(out_block_, NodeInput(block_, TransitionBlock::kOutBlockInput));
|
||||
}
|
||||
|
||||
track_->RippleRemoveBlock(block_);
|
||||
|
||||
if (remove_from_graph_) {
|
||||
if (!remove_command_) {
|
||||
remove_command_ = CreateRemoveCommand(block_);
|
||||
}
|
||||
|
||||
remove_command_->redo_now();
|
||||
}
|
||||
}
|
||||
|
||||
void TransitionRemoveCommand::undo()
|
||||
{
|
||||
if (remove_from_graph_) {
|
||||
remove_command_->undo_now();
|
||||
}
|
||||
|
||||
if (in_block_) {
|
||||
track_->InsertBlockBefore(block_, in_block_);
|
||||
} else {
|
||||
track_->InsertBlockAfter(block_, out_block_);
|
||||
}
|
||||
|
||||
if (in_block_) {
|
||||
Node::ConnectEdge(in_block_, NodeInput(block_, TransitionBlock::kInBlockInput));
|
||||
}
|
||||
|
||||
if (out_block_) {
|
||||
Node::ConnectEdge(out_block_, NodeInput(block_, TransitionBlock::kOutBlockInput));
|
||||
}
|
||||
|
||||
// These if statements must be separated because in_offset and out_offset report different things
|
||||
// if only one block is connected vs two. So we have to connect the blocks first before we have
|
||||
// an accurate return value from these offset functions.
|
||||
if (in_block_) {
|
||||
in_block_->set_length_and_media_in(in_block_->length() - block_->in_offset());
|
||||
}
|
||||
|
||||
if (out_block_) {
|
||||
out_block_->set_length_and_media_out(out_block_->length() - block_->out_offset());
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// TrackListInsertGaps
|
||||
//
|
||||
void TrackListInsertGaps::prepare()
|
||||
{
|
||||
// Determine if all tracks will be affected, which will allow us to make some optimizations
|
||||
foreach (Track* track, track_list_->GetTracks()) {
|
||||
if (track->IsLocked()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
working_tracks_.append(track);
|
||||
}
|
||||
|
||||
QVector<Block*> blocks_to_split;
|
||||
QVector<Block*> blocks_to_append_gap_to;
|
||||
QVector<Track*> tracks_to_append_gap_to;
|
||||
|
||||
for (Track* track : qAsConst(working_tracks_)) {
|
||||
for (Block* b : track->Blocks()) {
|
||||
if (dynamic_cast<GapBlock*>(b) && b->in() <= point_ && b->out() >= point_) {
|
||||
// Found a gap at the location
|
||||
gaps_to_extend_.append(b);
|
||||
break;
|
||||
} else if (dynamic_cast<ClipBlock*>(b) && b->out() >= point_) {
|
||||
bool append_gap = true;
|
||||
|
||||
if (b->in() == point_) {
|
||||
// The only reason we should be here is if this block is at the start of the track,
|
||||
// in which case no split needs to occur
|
||||
b = nullptr;
|
||||
} else if (b->out() > point_) {
|
||||
// Block must be split as well as having a gap appended to it
|
||||
blocks_to_split.append(b);
|
||||
} else if (!b->next()) {
|
||||
// At the end of a track, no gap needs to be added at all
|
||||
append_gap = false;
|
||||
}
|
||||
|
||||
if (append_gap) {
|
||||
tracks_to_append_gap_to.append(track);
|
||||
blocks_to_append_gap_to.append(b);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!blocks_to_split.isEmpty()) {
|
||||
split_command_ = new BlockSplitPreservingLinksCommand(blocks_to_split, {point_});
|
||||
}
|
||||
|
||||
for (int i=0; i<blocks_to_append_gap_to.size(); i++) {
|
||||
GapBlock* gap = new GapBlock();
|
||||
gap->set_length_and_media_out(length_);
|
||||
gap->setParent(&memory_manager_);
|
||||
gaps_added_.append({gap, blocks_to_append_gap_to.at(i), tracks_to_append_gap_to.at(i)});
|
||||
}
|
||||
}
|
||||
|
||||
void TrackListInsertGaps::redo()
|
||||
{
|
||||
foreach (Block* gap, gaps_to_extend_) {
|
||||
gap->set_length_and_media_out(gap->length() + length_);
|
||||
}
|
||||
|
||||
if (split_command_) {
|
||||
split_command_->redo_now();
|
||||
}
|
||||
|
||||
foreach (auto add_gap, gaps_added_) {
|
||||
add_gap.gap->setParent(add_gap.track->parent());
|
||||
add_gap.track->InsertBlockAfter(add_gap.gap, add_gap.before);
|
||||
}
|
||||
}
|
||||
|
||||
void TrackListInsertGaps::undo()
|
||||
{
|
||||
// Remove added gaps
|
||||
foreach (auto add_gap, gaps_added_) {
|
||||
add_gap.gap->track()->RippleRemoveBlock(add_gap.gap);
|
||||
add_gap.gap->setParent(&memory_manager_);
|
||||
}
|
||||
|
||||
// Un-split blocks
|
||||
if (split_command_) {
|
||||
split_command_->undo_now();
|
||||
}
|
||||
|
||||
// Restore original length of gaps
|
||||
foreach (Block* gap, gaps_to_extend_) {
|
||||
gap->set_length_and_media_out(gap->length() - length_);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// TrackReplaceBlockWithGapCommand
|
||||
//
|
||||
void TrackReplaceBlockWithGapCommand::redo()
|
||||
{
|
||||
// Determine if this block is connected to any transitions that should also be removed by this operation
|
||||
if (handle_transitions_ && transition_remove_commands_.isEmpty()) {
|
||||
CreateRemoveTransitionCommandIfNecessary(false);
|
||||
CreateRemoveTransitionCommandIfNecessary(true);
|
||||
}
|
||||
for (auto it=transition_remove_commands_.cbegin(); it!=transition_remove_commands_.cend(); it++) {
|
||||
(*it)->redo_now();
|
||||
}
|
||||
|
||||
if (block_->next()) {
|
||||
// Invalidate the range inhabited by this block
|
||||
TimeRange invalidate_range(block_->in(), block_->out());
|
||||
|
||||
// Block has a next, which means it's NOT at the end of the sequence and thus requires a gap
|
||||
rational new_gap_length = block_->length();
|
||||
|
||||
Block* previous = block_->previous();
|
||||
Block* next = block_->next();
|
||||
|
||||
bool previous_is_a_gap = dynamic_cast<GapBlock*>(previous);
|
||||
bool next_is_a_gap = dynamic_cast<GapBlock*>(next);
|
||||
|
||||
if (previous_is_a_gap && next_is_a_gap) {
|
||||
// Clip is preceded and followed by a gap, so we'll merge the two
|
||||
existing_gap_ = static_cast<GapBlock*>(previous);
|
||||
|
||||
existing_merged_gap_ = static_cast<GapBlock*>(next);
|
||||
new_gap_length += existing_merged_gap_->length();
|
||||
track_->RippleRemoveBlock(existing_merged_gap_);
|
||||
existing_merged_gap_->setParent(&memory_manager_);
|
||||
} else if (previous_is_a_gap) {
|
||||
// Extend this gap to fill space left by block
|
||||
existing_gap_ = static_cast<GapBlock*>(previous);
|
||||
} else if (next_is_a_gap) {
|
||||
// Extend this gap to fill space left by block
|
||||
existing_gap_ = static_cast<GapBlock*>(next);
|
||||
}
|
||||
|
||||
if (existing_gap_) {
|
||||
// Extend an existing gap
|
||||
new_gap_length += existing_gap_->length();
|
||||
existing_gap_->set_length_and_media_out(new_gap_length);
|
||||
track_->RippleRemoveBlock(block_);
|
||||
|
||||
existing_gap_precedes_ = (existing_gap_ == previous);
|
||||
} else {
|
||||
// No gap exists to fill this space, create a new one and swap it in
|
||||
if (!our_gap_) {
|
||||
our_gap_ = new GapBlock();
|
||||
our_gap_->set_length_and_media_out(new_gap_length);
|
||||
}
|
||||
|
||||
our_gap_->setParent(track_->parent());
|
||||
track_->ReplaceBlock(block_, our_gap_);
|
||||
}
|
||||
|
||||
} else {
|
||||
// Block is at the end of the track, simply remove it
|
||||
Block* preceding = block_->previous();
|
||||
track_->RippleRemoveBlock(block_);
|
||||
|
||||
// Determine if it's preceded by a gap, and remove that gap if so
|
||||
if (dynamic_cast<GapBlock*>(preceding)) {
|
||||
track_->RippleRemoveBlock(preceding);
|
||||
preceding->setParent(&memory_manager_);
|
||||
|
||||
existing_merged_gap_ = static_cast<GapBlock*>(preceding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TrackReplaceBlockWithGapCommand::undo()
|
||||
{
|
||||
if (our_gap_ || existing_gap_) {
|
||||
if (our_gap_) {
|
||||
|
||||
// We made this gap, simply swap our gap back
|
||||
track_->ReplaceBlock(our_gap_, block_);
|
||||
our_gap_->setParent(&memory_manager_);
|
||||
|
||||
} else {
|
||||
|
||||
// If we're here, assume that we extended an existing gap
|
||||
rational original_gap_length = existing_gap_->length() - block_->length();
|
||||
|
||||
// If we merged two gaps together, restore the second one now
|
||||
if (existing_merged_gap_) {
|
||||
original_gap_length -= existing_merged_gap_->length();
|
||||
existing_merged_gap_->setParent(track_->parent());
|
||||
track_->InsertBlockAfter(existing_merged_gap_, existing_gap_);
|
||||
existing_merged_gap_ = nullptr;
|
||||
}
|
||||
|
||||
// Restore original block
|
||||
if (existing_gap_precedes_) {
|
||||
track_->InsertBlockAfter(block_, existing_gap_);
|
||||
} else {
|
||||
track_->InsertBlockBefore(block_, existing_gap_);
|
||||
}
|
||||
|
||||
// Restore gap's original length
|
||||
existing_gap_->set_length_and_media_out(original_gap_length);
|
||||
|
||||
existing_gap_ = nullptr;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// Our gap and existing gap were both null, our block must have been at the end and thus
|
||||
// required no gap extension/replacement
|
||||
|
||||
// However, we may have removed an unnecessary gap that preceded it
|
||||
if (existing_merged_gap_) {
|
||||
existing_merged_gap_->setParent(track_->parent());
|
||||
track_->AppendBlock(existing_merged_gap_);
|
||||
existing_merged_gap_ = nullptr;
|
||||
}
|
||||
|
||||
// Restore block
|
||||
track_->AppendBlock(block_);
|
||||
|
||||
}
|
||||
|
||||
for (auto it=transition_remove_commands_.crbegin(); it!=transition_remove_commands_.crend(); it++) {
|
||||
(*it)->undo_now();
|
||||
}
|
||||
}
|
||||
|
||||
void TrackReplaceBlockWithGapCommand::CreateRemoveTransitionCommandIfNecessary(bool next)
|
||||
{
|
||||
Block* relevant_block;
|
||||
|
||||
if (next) {
|
||||
relevant_block = block_->next();
|
||||
} else {
|
||||
relevant_block = block_->previous();
|
||||
}
|
||||
|
||||
TransitionBlock* transition_cast_test = dynamic_cast<TransitionBlock*>(relevant_block);
|
||||
|
||||
if (transition_cast_test) {
|
||||
if ((next && transition_cast_test->connected_out_block() == block_ && !transition_cast_test->connected_in_block())
|
||||
|| (!next && transition_cast_test->connected_in_block() == block_ && !transition_cast_test->connected_out_block())) {
|
||||
TransitionRemoveCommand* command = new TransitionRemoveCommand(transition_cast_test, true);
|
||||
transition_remove_commands_.append(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineRemoveTrackCommand::prepare()
|
||||
{
|
||||
list_ = track_->sequence()->track_list(track_->type());
|
||||
|
||||
index_ = list_->GetArrayIndexFromCacheIndex(track_->Index());
|
||||
|
||||
remove_command_ = new NodeRemoveWithExclusiveDependenciesAndDisconnect(track_);
|
||||
}
|
||||
|
||||
void TimelineRemoveTrackCommand::redo()
|
||||
{
|
||||
remove_command_->redo_now();
|
||||
|
||||
list_->parent()->InputArrayRemove(list_->track_input(), index_);
|
||||
}
|
||||
|
||||
void TimelineRemoveTrackCommand::undo()
|
||||
{
|
||||
list_->parent()->InputArrayInsert(list_->track_input(), index_);
|
||||
|
||||
remove_command_->undo_now();
|
||||
}
|
||||
|
||||
void TimelineAddDefaultTransitionCommand::prepare()
|
||||
{
|
||||
for (auto it=clips_.cbegin(); it!=clips_.cend(); it++) {
|
||||
ClipBlock *c = *it;
|
||||
|
||||
// Handle in transition
|
||||
if (clips_.contains(static_cast<ClipBlock*>(c->previous()))) {
|
||||
// Do nothing, assume this will be handled by a dual transition from that clip
|
||||
} else if (dynamic_cast<GapBlock*>(c->previous()) || !c->previous()) {
|
||||
// Create in transition
|
||||
AddTransition(c, kIn);
|
||||
}
|
||||
|
||||
// Handle out transition
|
||||
if (clips_.contains(static_cast<ClipBlock*>(c->next()))) {
|
||||
AddTransition(c, kOutDual);
|
||||
} else if (dynamic_cast<GapBlock*>(c->next()) || !c->next()) {
|
||||
// Create out transition
|
||||
AddTransition(c, kOut);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineAddDefaultTransitionCommand::AddTransition(ClipBlock *c, CreateTransitionMode mode)
|
||||
{
|
||||
if (Track *t = c->track()) {
|
||||
Node *p = nullptr;
|
||||
if (t->type() == Track::kVideo) {
|
||||
p = NodeFactory::CreateFromID(OLIVE_CONFIG("DefaultVideoTransition").toString());
|
||||
} else if (t->type() == Track::kAudio) {
|
||||
p = NodeFactory::CreateFromID(OLIVE_CONFIG("DefaultAudioTransition").toString());
|
||||
}
|
||||
|
||||
rational transition_length = OLIVE_CONFIG("DefaultTransitionLength").value<rational>();
|
||||
|
||||
// Resize original clip
|
||||
switch (mode) {
|
||||
case kIn:
|
||||
ValidateTransitionLength(c, transition_length);
|
||||
|
||||
if (transition_length > 0) {
|
||||
AdjustClipLength(c, transition_length, false);
|
||||
}
|
||||
break;
|
||||
case kOut:
|
||||
ValidateTransitionLength(c, transition_length);
|
||||
|
||||
if (transition_length > 0) {
|
||||
AdjustClipLength(c, transition_length, true);
|
||||
}
|
||||
break;
|
||||
case kOutDual:
|
||||
{
|
||||
rational half_length = transition_length / 2;
|
||||
|
||||
ValidateTransitionLength(static_cast<ClipBlock*>(c->next()), half_length);
|
||||
ValidateTransitionLength(c, half_length);
|
||||
|
||||
transition_length = half_length * 2;
|
||||
|
||||
if (transition_length > 0) {
|
||||
AdjustClipLength(static_cast<ClipBlock*>(c->next()), half_length, false);
|
||||
AdjustClipLength(c, half_length, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (transition_length > 0) {
|
||||
if (TransitionBlock *transition = dynamic_cast<TransitionBlock*>(p)) {
|
||||
transition->set_length_and_media_out(transition_length);
|
||||
|
||||
// Add transition
|
||||
commands_.append(new NodeAddCommand(c->parent(), transition));
|
||||
|
||||
// Insert block
|
||||
Block *insert_after = (mode == kIn) ? c->previous() : c;
|
||||
commands_.append(new TrackInsertBlockAfterCommand(c->track(), transition, insert_after));
|
||||
|
||||
// Connect
|
||||
switch (mode) {
|
||||
case kIn:
|
||||
commands_.append(new NodeEdgeAddCommand(c, NodeInput(transition, TransitionBlock::kInBlockInput)));
|
||||
break;
|
||||
case kOutDual:
|
||||
commands_.append(new NodeEdgeAddCommand(c->next(), NodeInput(transition, TransitionBlock::kInBlockInput)));
|
||||
/* fall through */
|
||||
case kOut:
|
||||
commands_.append(new NodeEdgeAddCommand(c, NodeInput(transition, TransitionBlock::kOutBlockInput)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineAddDefaultTransitionCommand::AdjustClipLength(ClipBlock *c, const rational &transition_length, bool out)
|
||||
{
|
||||
rational cur_len = lengths_.value(c, c->length());
|
||||
rational new_len = cur_len - transition_length;
|
||||
if (out) {
|
||||
commands_.append(new BlockResizeCommand(c, new_len));
|
||||
} else {
|
||||
commands_.append(new BlockResizeWithMediaInCommand(c, new_len));
|
||||
}
|
||||
lengths_.insert(c, new_len);
|
||||
}
|
||||
|
||||
void TimelineAddDefaultTransitionCommand::ValidateTransitionLength(ClipBlock *c, rational &transition_length)
|
||||
{
|
||||
rational cur_len = lengths_.value(c, c->length());
|
||||
rational half_cur_len = cur_len/2;
|
||||
if (transition_length >= half_cur_len) {
|
||||
transition_length = half_cur_len - timebase_;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,418 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef TIMELINEUNDOGENERAL_H
|
||||
#define TIMELINEUNDOGENERAL_H
|
||||
|
||||
#include "config/config.h"
|
||||
#include "node/block/clip/clip.h"
|
||||
#include "node/block/gap/gap.h"
|
||||
#include "node/output/track/track.h"
|
||||
#include "node/output/track/tracklist.h"
|
||||
#include "node/output/viewer/viewer.h"
|
||||
#include "node/project/sequence/sequence.h"
|
||||
#include "timelineundosplit.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class BlockResizeCommand : public UndoCommand {
|
||||
public:
|
||||
BlockResizeCommand(Block* block, rational new_length) :
|
||||
block_(block),
|
||||
new_length_(new_length)
|
||||
{
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return block_->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void redo() override;
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
Block* block_;
|
||||
rational old_length_;
|
||||
rational new_length_;
|
||||
|
||||
};
|
||||
|
||||
class BlockResizeWithMediaInCommand : public UndoCommand {
|
||||
public:
|
||||
BlockResizeWithMediaInCommand(Block* block, rational new_length) :
|
||||
block_(block),
|
||||
new_length_(new_length)
|
||||
{
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const
|
||||
{
|
||||
return block_->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void redo();
|
||||
virtual void undo();
|
||||
|
||||
private:
|
||||
Block* block_;
|
||||
rational old_length_;
|
||||
rational new_length_;
|
||||
|
||||
};
|
||||
|
||||
class BlockSetMediaInCommand : public UndoCommand {
|
||||
public:
|
||||
BlockSetMediaInCommand(ClipBlock* block, rational new_media_in) :
|
||||
block_(block),
|
||||
new_media_in_(new_media_in)
|
||||
{
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const
|
||||
{
|
||||
return block_->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void redo();
|
||||
virtual void undo();
|
||||
|
||||
private:
|
||||
ClipBlock* block_;
|
||||
rational old_media_in_;
|
||||
rational new_media_in_;
|
||||
|
||||
};
|
||||
|
||||
class TimelineAddTrackCommand : public UndoCommand {
|
||||
public:
|
||||
TimelineAddTrackCommand(TrackList *timeline) :
|
||||
TimelineAddTrackCommand(timeline, OLIVE_CONFIG("AutoMergeTracks").toBool())
|
||||
{
|
||||
}
|
||||
|
||||
TimelineAddTrackCommand(TrackList *timeline, bool automerge_tracks);
|
||||
|
||||
static Track* RunImmediately(TrackList *timeline)
|
||||
{
|
||||
TimelineAddTrackCommand c(timeline);
|
||||
c.redo();
|
||||
return c.track();
|
||||
}
|
||||
|
||||
static Track* RunImmediately(TrackList *timeline, bool automerge)
|
||||
{
|
||||
TimelineAddTrackCommand c(timeline, automerge);
|
||||
c.redo();
|
||||
return c.track();
|
||||
}
|
||||
|
||||
virtual ~TimelineAddTrackCommand() override
|
||||
{
|
||||
delete position_command_;
|
||||
}
|
||||
|
||||
Track* track() const
|
||||
{
|
||||
return track_;
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return timeline_->parent()->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
TrackList* timeline_;
|
||||
|
||||
Track* track_;
|
||||
Node* merge_;
|
||||
NodeInput base_;
|
||||
NodeInput blend_;
|
||||
|
||||
NodeInput direct_;
|
||||
|
||||
MultiUndoCommand* position_command_;
|
||||
|
||||
QObject memory_manager_;
|
||||
|
||||
};
|
||||
|
||||
class TimelineRemoveTrackCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
TimelineRemoveTrackCommand(Track *track) :
|
||||
track_(track),
|
||||
remove_command_(nullptr)
|
||||
{}
|
||||
|
||||
virtual ~TimelineRemoveTrackCommand()
|
||||
{
|
||||
delete remove_command_;
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return track_->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void prepare() override;
|
||||
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
Track *track_;
|
||||
|
||||
TrackList *list_;
|
||||
|
||||
int index_;
|
||||
|
||||
UndoCommand *remove_command_;
|
||||
|
||||
};
|
||||
|
||||
class TransitionRemoveCommand : public UndoCommand {
|
||||
public:
|
||||
TransitionRemoveCommand(TransitionBlock* block, bool remove_from_graph) :
|
||||
block_(block),
|
||||
remove_from_graph_(remove_from_graph),
|
||||
remove_command_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return track_->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
TransitionBlock* block_;
|
||||
|
||||
Track* track_;
|
||||
|
||||
Block* out_block_;
|
||||
Block* in_block_;
|
||||
|
||||
bool remove_from_graph_;
|
||||
UndoCommand* remove_command_;
|
||||
|
||||
};
|
||||
|
||||
class TrackReplaceBlockWithGapCommand : public UndoCommand {
|
||||
public:
|
||||
TrackReplaceBlockWithGapCommand(Track* track, Block* block, bool handle_transitions = true) :
|
||||
track_(track),
|
||||
block_(block),
|
||||
existing_gap_(nullptr),
|
||||
existing_merged_gap_(nullptr),
|
||||
our_gap_(nullptr),
|
||||
handle_transitions_(handle_transitions)
|
||||
{
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return block_->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
void CreateRemoveTransitionCommandIfNecessary(bool next);
|
||||
|
||||
Track* track_;
|
||||
Block* block_;
|
||||
|
||||
GapBlock* existing_gap_;
|
||||
GapBlock* existing_merged_gap_;
|
||||
bool existing_gap_precedes_;
|
||||
GapBlock* our_gap_;
|
||||
|
||||
bool handle_transitions_;
|
||||
|
||||
QObject memory_manager_;
|
||||
|
||||
QVector<TransitionRemoveCommand*> transition_remove_commands_;
|
||||
|
||||
};
|
||||
|
||||
class BlockEnableDisableCommand : public UndoCommand {
|
||||
public:
|
||||
BlockEnableDisableCommand(Block* block, bool enabled) :
|
||||
block_(block),
|
||||
old_enabled_(block_->is_enabled()),
|
||||
new_enabled_(enabled)
|
||||
{
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return block_->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void redo() override
|
||||
{
|
||||
block_->set_enabled(new_enabled_);
|
||||
}
|
||||
|
||||
virtual void undo() override
|
||||
{
|
||||
block_->set_enabled(old_enabled_);
|
||||
}
|
||||
|
||||
private:
|
||||
Block* block_;
|
||||
|
||||
bool old_enabled_;
|
||||
|
||||
bool new_enabled_;
|
||||
|
||||
};
|
||||
|
||||
class TrackListInsertGaps : public UndoCommand {
|
||||
public:
|
||||
TrackListInsertGaps(TrackList* track_list, const rational& point, const rational& length) :
|
||||
track_list_(track_list),
|
||||
point_(point),
|
||||
length_(length),
|
||||
split_command_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~TrackListInsertGaps() override
|
||||
{
|
||||
delete split_command_;
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return track_list_->parent()->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void prepare() override;
|
||||
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
TrackList* track_list_;
|
||||
|
||||
rational point_;
|
||||
|
||||
rational length_;
|
||||
|
||||
QVector<Track*> working_tracks_;
|
||||
|
||||
QVector<Block*> gaps_to_extend_;
|
||||
|
||||
struct AddGap {
|
||||
GapBlock* gap;
|
||||
Block* before;
|
||||
Track* track;
|
||||
};
|
||||
|
||||
QVector<AddGap> gaps_added_;
|
||||
|
||||
BlockSplitPreservingLinksCommand* split_command_;
|
||||
|
||||
QObject memory_manager_;
|
||||
|
||||
};
|
||||
|
||||
class TimelineAddDefaultTransitionCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
TimelineAddDefaultTransitionCommand(const QVector<ClipBlock*> &clips, const rational &timebase) :
|
||||
clips_(clips),
|
||||
timebase_(timebase)
|
||||
{}
|
||||
|
||||
virtual ~TimelineAddDefaultTransitionCommand() override
|
||||
{
|
||||
qDeleteAll(commands_);
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return clips_.empty() ? nullptr : clips_.first()->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void prepare() override;
|
||||
|
||||
virtual void redo() override
|
||||
{
|
||||
for (auto it=commands_.cbegin(); it!=commands_.cend(); it++) {
|
||||
(*it)->redo_now();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void undo() override
|
||||
{
|
||||
for (auto it=commands_.crbegin(); it!=commands_.crend(); it++) {
|
||||
(*it)->undo_now();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
enum CreateTransitionMode {
|
||||
kIn,
|
||||
kOut,
|
||||
kOutDual
|
||||
};
|
||||
|
||||
void AddTransition(ClipBlock *c, CreateTransitionMode mode);
|
||||
void AdjustClipLength(ClipBlock *c, const rational &transition_length, bool out);
|
||||
void ValidateTransitionLength(ClipBlock *c, rational &transition_length);
|
||||
|
||||
|
||||
QVector<ClipBlock*> clips_;
|
||||
rational timebase_;
|
||||
QVector<UndoCommand*> commands_;
|
||||
|
||||
QHash<ClipBlock*, rational> lengths_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // TIMELINEUNDOGENERAL_H
|
||||
@@ -1,374 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "timelineundopointer.h"
|
||||
|
||||
#include "node/block/gap/gap.h"
|
||||
#include "node/block/transition/transition.h"
|
||||
#include "node/project.h"
|
||||
#include "timelineundocommon.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
//
|
||||
// BlockTrimCommand
|
||||
//
|
||||
void BlockTrimCommand::redo()
|
||||
{
|
||||
if (doing_nothing_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine how much time to invalidate
|
||||
TimeRange invalidate_range;
|
||||
|
||||
if (mode_ == Timeline::kTrimIn) {
|
||||
invalidate_range = TimeRange(block_->in(), block_->in() + trim_diff_);
|
||||
block_->set_length_and_media_in(new_length_);
|
||||
} else {
|
||||
invalidate_range = TimeRange(block_->out(), block_->out() - trim_diff_);
|
||||
block_->set_length_and_media_out(new_length_);
|
||||
}
|
||||
|
||||
if (needs_adjacent_) {
|
||||
if (we_created_adjacent_) {
|
||||
// Add adjacent and insert it
|
||||
adjacent_->setParent(track_->parent());
|
||||
|
||||
if (mode_ == Timeline::kTrimIn) {
|
||||
track_->InsertBlockBefore(adjacent_, block_);
|
||||
} else {
|
||||
track_->InsertBlockAfter(adjacent_, block_);
|
||||
}
|
||||
} else if (we_removed_adjacent_) {
|
||||
track_->RippleRemoveBlock(adjacent_);
|
||||
|
||||
// It no longer inputs/outputs anything, remove it
|
||||
if (remove_block_from_graph_ && NodeCanBeRemoved(adjacent_)) {
|
||||
if (!deleted_adjacent_command_) {
|
||||
deleted_adjacent_command_ = CreateAndRunRemoveCommand(adjacent_);
|
||||
} else {
|
||||
deleted_adjacent_command_->redo_now();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rational adjacent_length = adjacent_->length() + trim_diff_;
|
||||
|
||||
if (mode_ == Timeline::kTrimIn) {
|
||||
adjacent_->set_length_and_media_out(adjacent_length);
|
||||
} else {
|
||||
adjacent_->set_length_and_media_in(adjacent_length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dynamic_cast<TransitionBlock*>(block_)) {
|
||||
// Whole transition needs to be invalidated
|
||||
invalidate_range = block_->range();
|
||||
}
|
||||
}
|
||||
|
||||
void BlockTrimCommand::undo()
|
||||
{
|
||||
if (doing_nothing_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Will be POSITIVE if trimming shorter and NEGATIVE if trimming longer
|
||||
if (needs_adjacent_) {
|
||||
if (we_created_adjacent_) {
|
||||
// Adjacent is ours, just delete it
|
||||
track_->RippleRemoveBlock(adjacent_);
|
||||
adjacent_->setParent(&memory_manager_);
|
||||
} else {
|
||||
if (we_removed_adjacent_) {
|
||||
if (deleted_adjacent_command_) {
|
||||
// We deleted adjacent, restore it now
|
||||
deleted_adjacent_command_->undo_now();
|
||||
}
|
||||
|
||||
if (mode_ == Timeline::kTrimIn) {
|
||||
track_->InsertBlockBefore(adjacent_, block_);
|
||||
} else {
|
||||
track_->InsertBlockAfter(adjacent_, block_);
|
||||
}
|
||||
} else {
|
||||
rational adjacent_length = adjacent_->length() - trim_diff_;
|
||||
|
||||
if (mode_ == Timeline::kTrimIn) {
|
||||
adjacent_->set_length_and_media_out(adjacent_length);
|
||||
} else {
|
||||
adjacent_->set_length_and_media_in(adjacent_length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TimeRange invalidate_range;
|
||||
|
||||
if (mode_ == Timeline::kTrimIn) {
|
||||
block_->set_length_and_media_in(old_length_);
|
||||
|
||||
invalidate_range = TimeRange(block_->in(), block_->in() + trim_diff_);
|
||||
} else {
|
||||
block_->set_length_and_media_out(old_length_);
|
||||
|
||||
invalidate_range = TimeRange(block_->out(), block_->out() - trim_diff_);
|
||||
}
|
||||
|
||||
if (dynamic_cast<TransitionBlock*>(block_)) {
|
||||
// Whole transition needs to be invalidated
|
||||
invalidate_range = block_->range();
|
||||
}
|
||||
}
|
||||
|
||||
void BlockTrimCommand::prepare()
|
||||
{
|
||||
// Store old length
|
||||
old_length_ = block_->length();
|
||||
|
||||
// Determine if the length isn't changing, in which case we set a flag to do nothing
|
||||
if ((doing_nothing_ = (old_length_ == new_length_))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Will be POSITIVE if trimming shorter and NEGATIVE if trimming longer
|
||||
trim_diff_ = old_length_ - new_length_;
|
||||
|
||||
// Retrieve our adjacent block (or nullptr if none)
|
||||
if (mode_ == Timeline::kTrimIn) {
|
||||
adjacent_ = block_->previous();
|
||||
} else {
|
||||
adjacent_ = block_->next();
|
||||
}
|
||||
|
||||
// Ignore when trimming the out with no adjacent, because the user must have trimmed the end
|
||||
// of the last block in the track, so we don't need to do anything elses
|
||||
needs_adjacent_ = (mode_ == Timeline::kTrimIn || adjacent_);
|
||||
|
||||
if (needs_adjacent_) {
|
||||
// If we're trimming shorter, we need an adjacent, so check if we have a viable one.
|
||||
we_created_adjacent_ = (trim_diff_ > 0 && (!adjacent_ || (!dynamic_cast<GapBlock*>(adjacent_) && !trim_is_a_roll_edit_)));
|
||||
|
||||
if (we_created_adjacent_) {
|
||||
// We shortened but don't have a viable adjacent to lengthen, so we create one
|
||||
adjacent_ = new GapBlock();
|
||||
adjacent_->set_length_and_media_out(trim_diff_);
|
||||
} else {
|
||||
// Determine if we're removing the adjacent
|
||||
rational adjacent_length = adjacent_->length() + trim_diff_;
|
||||
we_removed_adjacent_ = adjacent_length.isNull();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// TrackSlideCommand
|
||||
//
|
||||
void TrackSlideCommand::redo()
|
||||
{
|
||||
// Make sure all movement blocks' old positions are invalidated
|
||||
TimeRange invalidate_range(blocks_.first()->in(), blocks_.last()->out());
|
||||
|
||||
// We will always have an in adjacent if there was a valid slide
|
||||
if (we_created_in_adjacent_) {
|
||||
// We created in adjacent, so all we have to do is insert it
|
||||
in_adjacent_->setParent(track_->parent());
|
||||
track_->InsertBlockBefore(in_adjacent_, blocks_.first());
|
||||
} else if (-movement_ == in_adjacent_->length()) {
|
||||
// Movement will remove in adjacent
|
||||
track_->RippleRemoveBlock(in_adjacent_);
|
||||
|
||||
if (NodeCanBeRemoved(in_adjacent_)) {
|
||||
if (!in_adjacent_remove_command_) {
|
||||
in_adjacent_remove_command_ = CreateRemoveCommand(in_adjacent_);
|
||||
}
|
||||
|
||||
in_adjacent_remove_command_->redo_now();
|
||||
}
|
||||
} else {
|
||||
// Simply resize adjacent
|
||||
in_adjacent_->set_length_and_media_out(in_adjacent_->length() + movement_);
|
||||
}
|
||||
|
||||
// We may not have an out adjacent if the slide was at the end of the track
|
||||
if (out_adjacent_) {
|
||||
if (we_created_out_adjacent_) {
|
||||
// We created out adjacent, so we just have to insert it
|
||||
out_adjacent_->setParent(track_->parent());
|
||||
track_->InsertBlockAfter(out_adjacent_, blocks_.last());
|
||||
} else if (movement_ == out_adjacent_->length()) {
|
||||
// Movement will remove out adjacent
|
||||
track_->RippleRemoveBlock(out_adjacent_);
|
||||
|
||||
if (NodeCanBeRemoved(out_adjacent_)) {
|
||||
if (!out_adjacent_remove_command_) {
|
||||
out_adjacent_remove_command_ = CreateRemoveCommand(out_adjacent_);
|
||||
}
|
||||
|
||||
out_adjacent_remove_command_->redo_now();
|
||||
}
|
||||
} else {
|
||||
// Simply resize adjacent
|
||||
out_adjacent_->set_length_and_media_in(out_adjacent_->length() - movement_);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure all movement blocks' new positions are invalidated
|
||||
invalidate_range.set_range(qMin(invalidate_range.in(), blocks_.first()->in()),
|
||||
qMax(invalidate_range.out(), blocks_.last()->out()));
|
||||
}
|
||||
|
||||
|
||||
void TrackSlideCommand::undo()
|
||||
{
|
||||
// Make sure all movement blocks' old positions are invalidated
|
||||
TimeRange invalidate_range(blocks_.first()->in(), blocks_.last()->out());
|
||||
|
||||
if (we_created_in_adjacent_) {
|
||||
// We created this, so we can remove it now
|
||||
track_->RippleRemoveBlock(in_adjacent_);
|
||||
in_adjacent_->setParent(&memory_manager_);
|
||||
} else if (in_adjacent_remove_command_) {
|
||||
// We removed this, so we can restore it now
|
||||
in_adjacent_remove_command_->undo_now();
|
||||
} else {
|
||||
// Simply resize adjacent
|
||||
in_adjacent_->set_length_and_media_out(in_adjacent_->length() - movement_);
|
||||
}
|
||||
|
||||
if (out_adjacent_) {
|
||||
if (we_created_out_adjacent_) {
|
||||
// We created this, so we can remove it now
|
||||
track_->RippleRemoveBlock(out_adjacent_);
|
||||
out_adjacent_->setParent(&memory_manager_);
|
||||
} else if (out_adjacent_remove_command_) {
|
||||
out_adjacent_remove_command_->undo_now();
|
||||
} else {
|
||||
out_adjacent_->set_length_and_media_in(out_adjacent_->length() + movement_);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure all movement blocks' new positions are invalidated
|
||||
invalidate_range.set_range(qMin(invalidate_range.in(), blocks_.first()->in()),
|
||||
qMax(invalidate_range.out(), blocks_.last()->out()));
|
||||
}
|
||||
|
||||
void TrackSlideCommand::prepare()
|
||||
{
|
||||
if (!in_adjacent_) {
|
||||
in_adjacent_ = new GapBlock();
|
||||
in_adjacent_->set_length_and_media_out(movement_);
|
||||
in_adjacent_->setParent(&memory_manager_);
|
||||
we_created_in_adjacent_ = true;
|
||||
} else {
|
||||
we_created_in_adjacent_ = false;
|
||||
}
|
||||
|
||||
if (!out_adjacent_ && blocks_.last()->next()) {
|
||||
out_adjacent_ = new GapBlock();
|
||||
out_adjacent_->set_length_and_media_out(-movement_);
|
||||
out_adjacent_->setParent(&memory_manager_);
|
||||
we_created_out_adjacent_ = true;
|
||||
} else {
|
||||
we_created_out_adjacent_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// TrackPlaceBlockCommand
|
||||
//
|
||||
TrackPlaceBlockCommand::~TrackPlaceBlockCommand()
|
||||
{
|
||||
delete ripple_remove_command_;
|
||||
qDeleteAll(add_track_commands_);
|
||||
}
|
||||
|
||||
void TrackPlaceBlockCommand::redo()
|
||||
{
|
||||
// Determine if we need to add tracks
|
||||
if (track_index_ >= timeline_->GetTracks().size()) {
|
||||
if (add_track_commands_.isEmpty()) {
|
||||
// First redo, create tracks now
|
||||
add_track_commands_.resize(track_index_ - timeline_->GetTracks().size() + 1);
|
||||
|
||||
for (int i=0; i<add_track_commands_.size(); i++) {
|
||||
add_track_commands_[i] = new TimelineAddTrackCommand(timeline_);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<add_track_commands_.size(); i++) {
|
||||
add_track_commands_.at(i)->redo_now();
|
||||
}
|
||||
}
|
||||
|
||||
Track* track = timeline_->GetTrackAt(track_index_);
|
||||
|
||||
bool append = (in_ >= track->track_length());
|
||||
|
||||
// Check if the placement location is past the end of the timeline
|
||||
if (append) {
|
||||
if (in_ > track->track_length()) {
|
||||
// If so, insert a gap here
|
||||
if (!gap_) {
|
||||
gap_ = new GapBlock();
|
||||
gap_->set_length_and_media_out(in_ - track->track_length());
|
||||
}
|
||||
gap_->setParent(track->parent());
|
||||
track->AppendBlock(gap_);
|
||||
}
|
||||
|
||||
track->AppendBlock(insert_);
|
||||
} else {
|
||||
// Place the Block at this point
|
||||
if (!ripple_remove_command_) {
|
||||
ripple_remove_command_ = new TrackRippleRemoveAreaCommand(track, TimeRange(in_, in_ + insert_->length()));
|
||||
ripple_remove_command_->SetAllowSplittingGaps(true);
|
||||
}
|
||||
|
||||
ripple_remove_command_->redo_now();
|
||||
track->InsertBlockAfter(insert_, ripple_remove_command_->GetInsertionIndex());
|
||||
}
|
||||
}
|
||||
|
||||
void TrackPlaceBlockCommand::undo()
|
||||
{
|
||||
Track* t = timeline_->GetTrackAt(track_index_);
|
||||
|
||||
TimeRange insert_range(insert_->in(), insert_->out());
|
||||
|
||||
// Firstly, remove our insert
|
||||
t->RippleRemoveBlock(insert_);
|
||||
|
||||
if (ripple_remove_command_) {
|
||||
// If we ripple removed, just undo that
|
||||
ripple_remove_command_->undo_now();
|
||||
} else if (gap_) {
|
||||
t->RippleRemoveBlock(gap_);
|
||||
gap_->setParent(&memory_manager_);
|
||||
}
|
||||
|
||||
// Remove tracks if we added them
|
||||
for (int i=add_track_commands_.size()-1; i>=0; i--) {
|
||||
add_track_commands_.at(i)->undo_now();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,206 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef TIMELINEUNDOPOINTER_H
|
||||
#define TIMELINEUNDOPOINTER_H
|
||||
|
||||
#include "node/block/gap/gap.h"
|
||||
#include "node/output/track/track.h"
|
||||
#include "node/output/track/tracklist.h"
|
||||
#include "node/project/sequence/sequence.h"
|
||||
#include "timelineundogeneral.h"
|
||||
#include "timelineundoripple.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
/**
|
||||
* @brief Performs a trim in the timeline that only affects the block and the block adjacent
|
||||
*
|
||||
* Changes the length of one block while also changing the length of the block directly adjacent
|
||||
* to compensate so that the rest of the track is unaffected.
|
||||
*
|
||||
* By default, this will only affect the length of gaps. If the adjacent needs to increase its
|
||||
* length and is not a gap, a gap will be created and inserted to fill that time. This command can
|
||||
* be set to always trim even if the adjacent clip isn't a gap with SetTrimIsARollEdit()
|
||||
*/
|
||||
class BlockTrimCommand : public UndoCommand {
|
||||
public:
|
||||
BlockTrimCommand(Track *track, Block* block, rational new_length, Timeline::MovementMode mode) :
|
||||
track_(track),
|
||||
block_(block),
|
||||
new_length_(new_length),
|
||||
mode_(mode),
|
||||
deleted_adjacent_command_(nullptr),
|
||||
trim_is_a_roll_edit_(false)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~BlockTrimCommand() override
|
||||
{
|
||||
delete deleted_adjacent_command_;
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return track_->project();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set this if the trim should always affect the adjacent clip and not create a gap
|
||||
*/
|
||||
void SetTrimIsARollEdit(bool e)
|
||||
{
|
||||
trim_is_a_roll_edit_ = e;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set whether adjacent blocks set to zero length should be removed from the whole graph
|
||||
*
|
||||
* If an adjacent block's length is set to 0, it's automatically removed from the track. By
|
||||
* default it also gets removed from the whole graph. Set this to FALSE to disable that
|
||||
* functionality.
|
||||
*/
|
||||
void SetRemoveZeroLengthFromGraph(bool e)
|
||||
{
|
||||
remove_block_from_graph_ = e;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void prepare() override;
|
||||
virtual void redo() override;
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
bool doing_nothing_;
|
||||
rational trim_diff_;
|
||||
|
||||
Track* track_;
|
||||
Block* block_;
|
||||
rational old_length_;
|
||||
rational new_length_;
|
||||
Timeline::MovementMode mode_;
|
||||
|
||||
Block* adjacent_;
|
||||
bool needs_adjacent_;
|
||||
bool we_created_adjacent_;
|
||||
bool we_removed_adjacent_;
|
||||
UndoCommand* deleted_adjacent_command_;
|
||||
|
||||
bool trim_is_a_roll_edit_;
|
||||
bool remove_block_from_graph_;
|
||||
|
||||
QObject memory_manager_;
|
||||
|
||||
};
|
||||
|
||||
class TrackSlideCommand : public UndoCommand {
|
||||
public:
|
||||
TrackSlideCommand(Track* track, const QList<Block*>& moving_blocks, Block* in_adjacent, Block* out_adjacent, const rational& movement) :
|
||||
track_(track),
|
||||
blocks_(moving_blocks),
|
||||
movement_(movement),
|
||||
in_adjacent_(in_adjacent),
|
||||
in_adjacent_remove_command_(nullptr),
|
||||
out_adjacent_(out_adjacent),
|
||||
out_adjacent_remove_command_(nullptr)
|
||||
{
|
||||
Q_ASSERT(!movement_.isNull());
|
||||
}
|
||||
|
||||
virtual ~TrackSlideCommand() override
|
||||
{
|
||||
delete in_adjacent_remove_command_;
|
||||
delete out_adjacent_remove_command_;
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return track_->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void prepare() override;
|
||||
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
Track* track_;
|
||||
QList<Block*> blocks_;
|
||||
rational movement_;
|
||||
|
||||
bool we_created_in_adjacent_;
|
||||
Block* in_adjacent_;
|
||||
UndoCommand* in_adjacent_remove_command_;
|
||||
bool we_created_out_adjacent_;
|
||||
Block* out_adjacent_;
|
||||
UndoCommand* out_adjacent_remove_command_;
|
||||
|
||||
QObject memory_manager_;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Destructively places `block` at the in point `start`
|
||||
*
|
||||
* The Block is guaranteed to be placed at the starting point specified. If there are Blocks in this area, they are
|
||||
* either trimmed or removed to make space for this Block. Additionally, if the Block is placed beyond the end of
|
||||
* the Sequence, a GapBlock is inserted to compensate.
|
||||
*/
|
||||
class TrackPlaceBlockCommand : public UndoCommand {
|
||||
public:
|
||||
TrackPlaceBlockCommand(TrackList *timeline, int track, Block* block, rational in) :
|
||||
timeline_(timeline),
|
||||
track_index_(track),
|
||||
in_(in),
|
||||
gap_(nullptr),
|
||||
insert_(block),
|
||||
ripple_remove_command_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~TrackPlaceBlockCommand() override;
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return timeline_->parent()->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
TrackList* timeline_;
|
||||
int track_index_;
|
||||
rational in_;
|
||||
GapBlock* gap_;
|
||||
Block* insert_;
|
||||
QVector<TimelineAddTrackCommand*> add_track_commands_;
|
||||
QObject memory_manager_;
|
||||
TrackRippleRemoveAreaCommand* ripple_remove_command_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // TIMELINEUNDOPOINTER_H
|
||||
@@ -1,521 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "timelineundoripple.h"
|
||||
|
||||
#include "timelineundocommon.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
//
|
||||
// TrackRippleRemoveAreaCommand
|
||||
//
|
||||
TrackRippleRemoveAreaCommand::TrackRippleRemoveAreaCommand(Track* track, const TimeRange& range) :
|
||||
track_(track),
|
||||
range_(range),
|
||||
allow_splitting_gaps_(false),
|
||||
splice_split_command_(nullptr)
|
||||
{
|
||||
trim_out_.block = nullptr;
|
||||
trim_in_.block = nullptr;
|
||||
}
|
||||
|
||||
TrackRippleRemoveAreaCommand::~TrackRippleRemoveAreaCommand()
|
||||
{
|
||||
delete splice_split_command_;
|
||||
qDeleteAll(remove_block_commands_);
|
||||
}
|
||||
|
||||
void TrackRippleRemoveAreaCommand::prepare()
|
||||
{
|
||||
// Determine precisely what will be happening to these tracks
|
||||
Block* first_block = track_->NearestBlockBeforeOrAt(range_.in());
|
||||
|
||||
if (!first_block) {
|
||||
// No blocks at this time, nothing to be done on this track
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine if this first block is getting trimmed or removed
|
||||
bool first_block_is_out_trimmed = first_block->in() < range_.in();
|
||||
bool first_block_is_in_trimmed = first_block->out() > range_.out();
|
||||
|
||||
// Set's the block that any insert command should insert AFTER. If the first block is not
|
||||
// getting out-trimmed, that means first block is either getting removed or in-trimmed, which
|
||||
// means any insert should happen before it
|
||||
insert_previous_ = first_block_is_out_trimmed ? first_block : first_block->previous();
|
||||
|
||||
// If it's getting trimmed, determine if it's actually getting spliced
|
||||
if (first_block_is_out_trimmed && first_block_is_in_trimmed) {
|
||||
if (!allow_splitting_gaps_ && dynamic_cast<GapBlock*>(first_block)) {
|
||||
// As a rule, we don't split gaps, so we just treat it as a trim of the range requested
|
||||
trim_out_ = {first_block,
|
||||
first_block->length(),
|
||||
first_block->length() - range_.length()};
|
||||
} else {
|
||||
// This block is getting spliced, so we'll handle that later
|
||||
splice_split_command_ = new BlockSplitCommand(first_block, range_.in());
|
||||
}
|
||||
} else {
|
||||
// It's just getting trimmed or removed, so we'll append that operation
|
||||
if (first_block_is_out_trimmed) {
|
||||
trim_out_ = {first_block,
|
||||
first_block->length(),
|
||||
first_block->length() - (first_block->out() - range_.in())};
|
||||
} else if (first_block_is_in_trimmed) {
|
||||
// Block is getting in trimmed
|
||||
trim_in_ = {first_block,
|
||||
first_block->length(),
|
||||
first_block->length() - (range_.out() - first_block->in())};
|
||||
} else {
|
||||
// We know for sure this block is within the range so it will be removed
|
||||
removals_.append(RemoveOperation({first_block, first_block->previous()}));
|
||||
}
|
||||
|
||||
// If the first block is getting in trimmed, we're already at the end of our range
|
||||
if (!first_block_is_in_trimmed) {
|
||||
// Loop through the rest of the blocks and determine what to do with those
|
||||
for (Block* next=first_block->next(); next; next=next->next()) {
|
||||
bool trimming = (next->out() > range_.out());
|
||||
|
||||
if (trimming) {
|
||||
trim_in_ = {next,
|
||||
next->length(),
|
||||
next->length() - (range_.out() - next->in())};
|
||||
break;
|
||||
} else {
|
||||
removals_.append(RemoveOperation({next, next->previous()}));
|
||||
|
||||
if (next->out() == range_.out()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TrackRippleRemoveAreaCommand::redo()
|
||||
{
|
||||
if (splice_split_command_) {
|
||||
// We're just splicing
|
||||
splice_split_command_->redo_now();
|
||||
|
||||
// Trim the in of the split
|
||||
Block* split = splice_split_command_->new_block();
|
||||
split->set_length_and_media_in(split->length() - (range_.out() - split->in()));
|
||||
} else {
|
||||
if (trim_out_.block) {
|
||||
trim_out_.block->set_length_and_media_out(trim_out_.new_length);
|
||||
}
|
||||
|
||||
if (trim_in_.block) {
|
||||
trim_in_.block->set_length_and_media_in(trim_in_.new_length);
|
||||
}
|
||||
|
||||
// Perform removals
|
||||
if (!removals_.isEmpty()) {
|
||||
foreach (auto op, removals_) {
|
||||
// Ripple remove them all first
|
||||
track_->RippleRemoveBlock(op.block);
|
||||
}
|
||||
|
||||
// Create undo commands for node removals where possible
|
||||
if (remove_block_commands_.isEmpty()) {
|
||||
foreach (auto op, removals_) {
|
||||
if (NodeCanBeRemoved(op.block)) {
|
||||
remove_block_commands_.append(CreateRemoveCommand(op.block));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (UndoCommand* c, remove_block_commands_) {
|
||||
c->redo_now();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TrackRippleRemoveAreaCommand::undo()
|
||||
{
|
||||
if (splice_split_command_) {
|
||||
splice_split_command_->undo_now();
|
||||
} else {
|
||||
if (trim_out_.block) {
|
||||
trim_out_.block->set_length_and_media_out(trim_out_.old_length);
|
||||
}
|
||||
|
||||
if (trim_in_.block) {
|
||||
trim_in_.block->set_length_and_media_in(trim_in_.old_length);
|
||||
}
|
||||
|
||||
// Un-remove any blocks
|
||||
for (int i=remove_block_commands_.size()-1; i>=0; i--) {
|
||||
remove_block_commands_.at(i)->undo_now();
|
||||
}
|
||||
|
||||
foreach (auto op, removals_) {
|
||||
track_->InsertBlockAfter(op.block, op.before);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// TrackListRippleRemoveAreaCommand
|
||||
//
|
||||
void TrackListRippleRemoveAreaCommand::prepare()
|
||||
{
|
||||
foreach (Track* track, list_->GetTracks()) {
|
||||
if (track->IsLocked()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
TrackRippleRemoveAreaCommand* c = new TrackRippleRemoveAreaCommand(track, range_);
|
||||
commands_.append(c);
|
||||
working_tracks_.append(track);
|
||||
}
|
||||
}
|
||||
|
||||
void TrackListRippleRemoveAreaCommand::redo()
|
||||
{
|
||||
foreach (TrackRippleRemoveAreaCommand* c, commands_) {
|
||||
c->redo_now();
|
||||
}
|
||||
}
|
||||
|
||||
void TrackListRippleRemoveAreaCommand::undo()
|
||||
{
|
||||
foreach (TrackRippleRemoveAreaCommand* c, commands_) {
|
||||
c->undo_now();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// TimelineRippleRemoveAreaCommand
|
||||
//
|
||||
TimelineRippleRemoveAreaCommand::TimelineRippleRemoveAreaCommand(Sequence* timeline, rational in, rational out) :
|
||||
timeline_(timeline)
|
||||
{
|
||||
for (int i=0; i<Track::kCount; i++) {
|
||||
add_child(new TrackListRippleRemoveAreaCommand(timeline->track_list(static_cast<Track::Type>(i)),
|
||||
in,
|
||||
out));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// TrackListRippleToolCommand
|
||||
//
|
||||
TrackListRippleToolCommand::TrackListRippleToolCommand(TrackList* track_list,
|
||||
const QHash<Track*, RippleInfo>& info,
|
||||
const rational& ripple_movement,
|
||||
const Timeline::MovementMode& movement_mode) :
|
||||
track_list_(track_list),
|
||||
info_(info),
|
||||
ripple_movement_(ripple_movement),
|
||||
movement_mode_(movement_mode)
|
||||
{
|
||||
}
|
||||
|
||||
void TrackListRippleToolCommand::ripple(bool redo)
|
||||
{
|
||||
if (info_.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The following variables are used to determine how much of the cache to invalidate
|
||||
|
||||
// If we can shift, we will shift from the latest out before the ripple to the latest out after,
|
||||
// since those sections will be unchanged by this ripple
|
||||
rational pre_latest_out = RATIONAL_MIN;
|
||||
rational post_latest_out = RATIONAL_MIN;
|
||||
|
||||
// Make timeline changes
|
||||
for (auto it=info_.cbegin(); it!=info_.cend(); it++) {
|
||||
Track* track = it.key();
|
||||
const RippleInfo& info = it.value();
|
||||
WorkingData working_data = working_data_.value(track);
|
||||
Block* b = info.block;
|
||||
|
||||
// Generate block length
|
||||
rational new_block_length;
|
||||
rational operation_movement = ripple_movement_;
|
||||
|
||||
if (movement_mode_ == Timeline::kTrimIn) {
|
||||
operation_movement = -operation_movement;
|
||||
}
|
||||
|
||||
if (!redo) {
|
||||
operation_movement = -operation_movement;
|
||||
}
|
||||
|
||||
if (b) {
|
||||
new_block_length = b->length() + operation_movement;
|
||||
}
|
||||
|
||||
rational pre_shift;
|
||||
rational post_shift;
|
||||
|
||||
if (info.append_gap) {
|
||||
|
||||
// Rather than rippling the referenced block, we'll insert a gap and ripple with that
|
||||
GapBlock* gap = working_data.created_gap;
|
||||
|
||||
if (redo) {
|
||||
if (!gap) {
|
||||
gap = new GapBlock();
|
||||
gap->set_length_and_media_out(qAbs(ripple_movement_));
|
||||
working_data.created_gap = gap;
|
||||
}
|
||||
|
||||
gap->setParent(track->parent());
|
||||
track->InsertBlockBefore(gap, b);
|
||||
|
||||
// As an insertion, we will shift from the gap's in to the gap's out
|
||||
pre_shift = gap->in();
|
||||
post_shift = gap->out();
|
||||
working_data.earliest_point_of_change = gap->in();
|
||||
} else {
|
||||
// As a removal, we will shift from the gap's out to the gap's in
|
||||
pre_shift = gap->out();
|
||||
post_shift = gap->in();
|
||||
|
||||
track->RippleRemoveBlock(gap);
|
||||
gap->setParent(&memory_manager_);
|
||||
}
|
||||
|
||||
} else if ((redo && new_block_length.isNull()) || (!redo && !b->track())) {
|
||||
|
||||
// The ripple is the length of this block. We assume that for this to happen, it must have
|
||||
// been a gap that we will now remove.
|
||||
|
||||
if (redo) {
|
||||
// The earliest point changes will happen is at the start of this block
|
||||
working_data.earliest_point_of_change = b->in();
|
||||
|
||||
// As a removal, we will be shifting from the out point to the in point
|
||||
pre_shift = b->out();
|
||||
post_shift = b->in();
|
||||
|
||||
// Remove gap from track and from graph
|
||||
working_data.removed_gap_after = b->previous();
|
||||
track->RippleRemoveBlock(b);
|
||||
b->setParent(&memory_manager_);
|
||||
} else {
|
||||
// Restore gap to graph and track
|
||||
b->setParent(track->parent());
|
||||
track->InsertBlockAfter(b, working_data.removed_gap_after);
|
||||
|
||||
// The earliest point changes will happen is at the start of this block
|
||||
working_data.earliest_point_of_change = b->in();
|
||||
|
||||
// As an insert, we will be shifting from the block's in point to its out point
|
||||
pre_shift = b->in();
|
||||
post_shift = b->out();
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// Store old length
|
||||
working_data.old_length = b->length();
|
||||
|
||||
if (movement_mode_ == Timeline::kTrimIn) {
|
||||
// The earliest point changes will occur is in point of this bloc
|
||||
working_data.earliest_point_of_change = b->in();
|
||||
|
||||
// Undo the trim in inversion we do above, this will still be inverted accurately for
|
||||
// undoing where appropriate
|
||||
rational inverted = -operation_movement;
|
||||
if (inverted > 0) {
|
||||
pre_shift = b->in() + inverted;
|
||||
post_shift = b->in();
|
||||
} else {
|
||||
pre_shift = b->in();
|
||||
post_shift = b->in() - inverted;
|
||||
}
|
||||
|
||||
// Update length
|
||||
b->set_length_and_media_in(new_block_length);
|
||||
} else {
|
||||
// The earliest point changes will occur is the out point if trimming out or the in point
|
||||
// if trimming in
|
||||
working_data.earliest_point_of_change = b->out();
|
||||
|
||||
// The latest out before the ripple is this block's current out point
|
||||
pre_shift = b->out();
|
||||
|
||||
// Update length
|
||||
b->set_length_and_media_out(new_block_length);
|
||||
|
||||
// The latest out after the ripple is this block's out point after the length change
|
||||
post_shift = b->out();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
working_data_.insert(it.key(), working_data);
|
||||
|
||||
pre_latest_out = qMax(pre_latest_out, pre_shift);
|
||||
post_latest_out = qMax(post_latest_out, post_shift);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// TimelineRippleDeleteGapsAtRegionsCommand
|
||||
//
|
||||
void TimelineRippleDeleteGapsAtRegionsCommand::prepare()
|
||||
{
|
||||
int max_gaps = 0;
|
||||
QHash<Track*, QVector<RemovalRequest> > requested_gaps;
|
||||
|
||||
// Convert regions to gaps
|
||||
for (const QPair<Track*, TimeRange> ®ion : qAsConst(regions_)) {
|
||||
Track *track = region.first;
|
||||
const TimeRange &range = region.second;
|
||||
|
||||
GapBlock *gap = dynamic_cast<GapBlock*>(track->NearestBlockBeforeOrAt(range.in()));
|
||||
|
||||
if (gap) {
|
||||
QVector<RemovalRequest> &gaps_on_track = requested_gaps[track];
|
||||
|
||||
RemovalRequest this_req = {gap, range};
|
||||
|
||||
// Insertion sort
|
||||
bool inserted = false;
|
||||
for (int i=0; i<gaps_on_track.size(); i++) {
|
||||
if (gaps_on_track.at(i).range.in() < range.in()) {
|
||||
gaps_on_track.insert(i, this_req);
|
||||
inserted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!inserted) {
|
||||
gaps_on_track.append(this_req);
|
||||
}
|
||||
|
||||
max_gaps = qMax(max_gaps, gaps_on_track.size());
|
||||
} else {
|
||||
qWarning() << "Failed to find corresponding gap to region";
|
||||
}
|
||||
}
|
||||
|
||||
// For each gap on each track, find a corresponding gap on every other track (which may include
|
||||
// a requested gap) to ripple in order to keep everything synchronized
|
||||
QHash<GapBlock*, rational> gap_lengths;
|
||||
for (int gap_index=0; gap_index<max_gaps; gap_index++) {
|
||||
rational earliest_point = RATIONAL_MAX;
|
||||
rational ripple_length = RATIONAL_MAX;
|
||||
rational latest_point = RATIONAL_MIN;
|
||||
|
||||
foreach (const QVector<RemovalRequest> &gaps_on_track, requested_gaps) {
|
||||
if (gap_index < gaps_on_track.size()) {
|
||||
const RemovalRequest &gap = gaps_on_track.at(gap_index);
|
||||
earliest_point = qMin(earliest_point, gap.range.in());
|
||||
ripple_length = qMin(ripple_length, gap.range.length());
|
||||
latest_point = qMax(latest_point, gap.range.out());
|
||||
}
|
||||
}
|
||||
|
||||
// Determine which gaps will be involved in this operation
|
||||
QVector<GapBlock*> gaps;
|
||||
|
||||
foreach (Track* track, timeline_->GetTracks()) {
|
||||
if (track->IsLocked()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const QVector<RemovalRequest> &requested_gaps_on_track = requested_gaps.value(track);
|
||||
GapBlock *gap = nullptr;
|
||||
if (gap_index < requested_gaps_on_track.size()) {
|
||||
// A requested gap was at this index, use it
|
||||
gap = requested_gaps_on_track.at(gap_index).gap;
|
||||
} else {
|
||||
// No requested gap was at this index, find one
|
||||
Block *block = track->NearestBlockAfterOrAt(earliest_point);
|
||||
|
||||
if (block) {
|
||||
// Found a block, test if it's a gap
|
||||
gap = dynamic_cast<GapBlock*>(block);
|
||||
|
||||
if (!gap) {
|
||||
if (block->in() == earliest_point) {
|
||||
if (block->next()) {
|
||||
gap = dynamic_cast<GapBlock*>(block->next());
|
||||
|
||||
if (!gap) {
|
||||
ripple_length = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
gap = dynamic_cast<GapBlock*>(block->previous());
|
||||
|
||||
if (!gap) {
|
||||
ripple_length = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Assume track finishes here and track won't be affected by this operation
|
||||
}
|
||||
}
|
||||
|
||||
if (gap) {
|
||||
gaps.append(gap);
|
||||
|
||||
if (!gap_lengths.contains(gap)) {
|
||||
gap_lengths.insert(gap, gap->length());
|
||||
}
|
||||
|
||||
ripple_length = qMin(ripple_length, gap_lengths.value(gap));
|
||||
}
|
||||
|
||||
if (ripple_length == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ripple_length > 0) {
|
||||
foreach (GapBlock *gap, gaps) {
|
||||
if (gap_lengths.value(gap) == ripple_length) {
|
||||
commands_.append(new TrackRippleRemoveBlockCommand(gap->track(), gap));
|
||||
} else {
|
||||
gap_lengths[gap] -= ripple_length;
|
||||
commands_.append(new BlockResizeCommand(gap, gap_lengths.value(gap)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineRippleDeleteGapsAtRegionsCommand::redo()
|
||||
{
|
||||
for (auto it=commands_.cbegin(); it!=commands_.cend(); it++) {
|
||||
(*it)->redo_now();
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineRippleDeleteGapsAtRegionsCommand::undo()
|
||||
{
|
||||
for (auto it=commands_.crbegin(); it!=commands_.crend(); it++) {
|
||||
(*it)->undo_now();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,259 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef TIMELINEUNDORIPPLE_H
|
||||
#define TIMELINEUNDORIPPLE_H
|
||||
|
||||
#include "node/block/gap/gap.h"
|
||||
#include "node/output/track/track.h"
|
||||
#include "node/output/track/tracklist.h"
|
||||
#include "node/project/sequence/sequence.h"
|
||||
#include "timelineundogeneral.h"
|
||||
#include "timelineundosplit.h"
|
||||
#include "timelineundotrack.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
/**
|
||||
* @brief Clears the area between in and out
|
||||
*
|
||||
* The area between `in` and `out` is guaranteed to be freed. BLocks are trimmed and removed to free this space.
|
||||
* By default, nothing takes this area meaning all subsequent clips are pushed backward, however you can specify
|
||||
* a block to insert at the `in` point. No checking is done to ensure `insert` is the same length as `in` to `out`.
|
||||
*/
|
||||
class TrackRippleRemoveAreaCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
TrackRippleRemoveAreaCommand(Track* track, const TimeRange& range);
|
||||
|
||||
virtual ~TrackRippleRemoveAreaCommand() override;
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return track_->project();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Block to insert after if you want to insert something between this ripple
|
||||
*/
|
||||
Block* GetInsertionIndex() const
|
||||
{
|
||||
return insert_previous_;
|
||||
}
|
||||
|
||||
Block* GetSplicedBlock() const
|
||||
{
|
||||
if (splice_split_command_) {
|
||||
return splice_split_command_->new_block();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SetAllowSplittingGaps(bool e)
|
||||
{
|
||||
allow_splitting_gaps_ = e;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void prepare() override;
|
||||
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
struct TrimOperation {
|
||||
Block* block;
|
||||
rational old_length;
|
||||
rational new_length;
|
||||
};
|
||||
|
||||
struct RemoveOperation {
|
||||
Block* block;
|
||||
Block* before;
|
||||
};
|
||||
|
||||
Track* track_;
|
||||
TimeRange range_;
|
||||
|
||||
TrimOperation trim_out_;
|
||||
QVector<RemoveOperation> removals_;
|
||||
TrimOperation trim_in_;
|
||||
Block* insert_previous_;
|
||||
bool allow_splitting_gaps_;
|
||||
|
||||
BlockSplitCommand* splice_split_command_;
|
||||
QVector<UndoCommand*> remove_block_commands_;
|
||||
|
||||
};
|
||||
|
||||
class TrackListRippleRemoveAreaCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
TrackListRippleRemoveAreaCommand(TrackList* list, rational in, rational out) :
|
||||
list_(list),
|
||||
range_(in, out)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~TrackListRippleRemoveAreaCommand() override
|
||||
{
|
||||
qDeleteAll(commands_);
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return list_->parent()->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void prepare() override;
|
||||
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
TrackList* list_;
|
||||
|
||||
QList<Track*> working_tracks_;
|
||||
|
||||
TimeRange range_;
|
||||
|
||||
QVector<TrackRippleRemoveAreaCommand*> commands_;
|
||||
|
||||
};
|
||||
|
||||
class TimelineRippleRemoveAreaCommand : public MultiUndoCommand
|
||||
{
|
||||
public:
|
||||
TimelineRippleRemoveAreaCommand(Sequence* timeline, rational in, rational out);
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return timeline_->project();
|
||||
}
|
||||
|
||||
private:
|
||||
Sequence* timeline_;
|
||||
|
||||
};
|
||||
|
||||
class TrackListRippleToolCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
struct RippleInfo {
|
||||
Block* block;
|
||||
bool append_gap;
|
||||
};
|
||||
|
||||
TrackListRippleToolCommand(TrackList* track_list,
|
||||
const QHash<Track*, RippleInfo>& info,
|
||||
const rational& ripple_movement,
|
||||
const Timeline::MovementMode& movement_mode);
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return track_list_->parent()->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void redo() override
|
||||
{
|
||||
ripple(true);
|
||||
}
|
||||
|
||||
virtual void undo() override
|
||||
{
|
||||
ripple(false);
|
||||
}
|
||||
|
||||
private:
|
||||
void ripple(bool redo);
|
||||
|
||||
TrackList* track_list_;
|
||||
|
||||
QHash<Track*, RippleInfo> info_;
|
||||
rational ripple_movement_;
|
||||
Timeline::MovementMode movement_mode_;
|
||||
|
||||
struct WorkingData {
|
||||
GapBlock* created_gap = nullptr;
|
||||
Block* removed_gap_after;
|
||||
rational old_length;
|
||||
rational earliest_point_of_change;
|
||||
};
|
||||
|
||||
QHash<Track*, WorkingData> working_data_;
|
||||
|
||||
QObject memory_manager_;
|
||||
|
||||
};
|
||||
|
||||
class TimelineRippleDeleteGapsAtRegionsCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
using RangeList = QVector<QPair<Track*, TimeRange> >;
|
||||
|
||||
TimelineRippleDeleteGapsAtRegionsCommand(Sequence* vo, const RangeList& regions) :
|
||||
timeline_(vo),
|
||||
regions_(regions)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~TimelineRippleDeleteGapsAtRegionsCommand() override
|
||||
{
|
||||
qDeleteAll(commands_);
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return timeline_->project();
|
||||
}
|
||||
|
||||
bool HasCommands() const
|
||||
{
|
||||
return !commands_.isEmpty();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void prepare() override;
|
||||
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
Sequence* timeline_;
|
||||
RangeList regions_;
|
||||
|
||||
QVector<UndoCommand*> commands_;
|
||||
|
||||
struct RemovalRequest {
|
||||
GapBlock *gap;
|
||||
TimeRange range;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // TIMELINEUNDORIPPLE_H
|
||||
@@ -1,179 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "timelineundosplit.h"
|
||||
|
||||
#include "node/block/clip/clip.h"
|
||||
#include "node/block/transition/transition.h"
|
||||
#include "widget/nodeview/nodeviewundo.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
//
|
||||
// BlockSplitCommand
|
||||
//
|
||||
void BlockSplitCommand::prepare()
|
||||
{
|
||||
reconnect_tree_command_ = new MultiUndoCommand();
|
||||
new_block_ = static_cast<Block*>(Node::CopyNodeInGraph(block_, reconnect_tree_command_));
|
||||
}
|
||||
|
||||
void BlockSplitCommand::redo()
|
||||
{
|
||||
old_length_ = block_->length();
|
||||
|
||||
Q_ASSERT(point_ > block_->in() && point_ < block_->out());
|
||||
|
||||
reconnect_tree_command_->redo_now();
|
||||
|
||||
// Determine our new lengths
|
||||
rational new_length = point_ - block_->in();
|
||||
rational new_part_length = block_->out() - point_;
|
||||
|
||||
// Begin an operation
|
||||
Track* track = block_->track();
|
||||
|
||||
// Set lengths
|
||||
block_->set_length_and_media_out(new_length);
|
||||
new_block()->set_length_and_media_in(new_part_length);
|
||||
|
||||
// Insert new block
|
||||
track->InsertBlockAfter(new_block(), block_);
|
||||
|
||||
if (ClipBlock *new_clip = dynamic_cast<ClipBlock*>(new_block_)) {
|
||||
ClipBlock *old_clip = static_cast<ClipBlock*>(block_);
|
||||
new_clip->AddCachePassthroughFrom(old_clip);
|
||||
}
|
||||
|
||||
// If the block had an out transition, we move it to the new block
|
||||
moved_transition_ = NodeInput();
|
||||
|
||||
TransitionBlock* potential_transition = dynamic_cast<TransitionBlock*>(new_block()->next());
|
||||
if (potential_transition) {
|
||||
for (const Node::OutputConnection& output : block_->output_connections()) {
|
||||
if (output.second.node() == potential_transition) {
|
||||
moved_transition_ = NodeInput(potential_transition, TransitionBlock::kOutBlockInput);
|
||||
Node::DisconnectEdge(block_, moved_transition_);
|
||||
Node::ConnectEdge(new_block(), moved_transition_);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BlockSplitCommand::undo()
|
||||
{
|
||||
Track* track = block_->track();
|
||||
|
||||
if (moved_transition_.IsValid()) {
|
||||
Node::DisconnectEdge(new_block(), moved_transition_);
|
||||
Node::ConnectEdge(block_, moved_transition_);
|
||||
}
|
||||
|
||||
block_->set_length_and_media_out(old_length_);
|
||||
track->RippleRemoveBlock(new_block());
|
||||
|
||||
// If we ran a reconnect command, disconnect now
|
||||
reconnect_tree_command_->undo_now();
|
||||
}
|
||||
|
||||
//
|
||||
// BlockSplitPreservingLinksCommand
|
||||
//
|
||||
Block *BlockSplitPreservingLinksCommand::GetSplit(Block *original, int time_index) const
|
||||
{
|
||||
if (time_index >= 0 && time_index < times_.size()) {
|
||||
int original_index = blocks_.indexOf(original);
|
||||
if (original_index != -1) {
|
||||
return splits_.at(time_index).at(original_index);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void BlockSplitPreservingLinksCommand::prepare()
|
||||
{
|
||||
splits_.resize(times_.size());
|
||||
|
||||
for (int i=0;i<times_.size();i++) {
|
||||
const rational& time = times_.at(i);
|
||||
|
||||
// FIXME: I realize this isn't going to work if the times aren't ordered. I'm lazy so rather
|
||||
// than writing in a sorting algorithm here, I'll just put an assert as a reminder
|
||||
// if this ever becomes an issue.
|
||||
Q_ASSERT(i == 0 || time > times_.at(i-1));
|
||||
|
||||
QVector<Block*> splits(blocks_.size());
|
||||
|
||||
for (int j=0;j<blocks_.size();j++) {
|
||||
Block* b = blocks_.at(j);
|
||||
|
||||
if (b->in() < time && b->out() > time) {
|
||||
BlockSplitCommand* split_command = new BlockSplitCommand(b, time);
|
||||
split_command->redo_now();
|
||||
splits.replace(j, split_command->new_block());
|
||||
commands_.append(split_command);
|
||||
} else {
|
||||
splits.replace(j, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
splits_.replace(i, splits);
|
||||
}
|
||||
|
||||
// Now that we've determined all the splits, we can relink everything
|
||||
for (int i=0;i<blocks_.size();i++) {
|
||||
Block* a = blocks_.at(i);
|
||||
|
||||
for (int j=0;j<blocks_.size();j++) {
|
||||
if (i == j) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Block* b = blocks_.at(j);
|
||||
|
||||
if (Block::AreLinked(a, b)) {
|
||||
// These blocks are linked, ensure all the splits are linked too
|
||||
|
||||
foreach (const QVector<Block*>& split_list, splits_) {
|
||||
NodeLinkCommand* blc = new NodeLinkCommand(split_list.at(i), split_list.at(j), true);
|
||||
blc->redo_now();
|
||||
commands_.append(blc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// TrackSplitAtTimeCommand
|
||||
//
|
||||
void TrackSplitAtTimeCommand::prepare()
|
||||
{
|
||||
// Find Block that contains this time
|
||||
Block* b = track_->BlockContainingTime(point_);
|
||||
|
||||
if (b) {
|
||||
command_ = new BlockSplitCommand(b, point_);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef TIMELINEUNDOSPLIT_H
|
||||
#define TIMELINEUNDOSPLIT_H
|
||||
|
||||
#include "node/output/track/track.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class BlockSplitCommand : public UndoCommand {
|
||||
public:
|
||||
BlockSplitCommand(Block* block, rational point) :
|
||||
block_(block),
|
||||
new_block_(nullptr),
|
||||
point_(point),
|
||||
reconnect_tree_command_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~BlockSplitCommand() override
|
||||
{
|
||||
delete reconnect_tree_command_;
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return block_->project();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Access the second block created as a result. Only valid after redo().
|
||||
*/
|
||||
Block* new_block()
|
||||
{
|
||||
return new_block_;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void prepare() override;
|
||||
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
Block* block_;
|
||||
Block* new_block_;
|
||||
|
||||
rational old_length_;
|
||||
rational point_;
|
||||
|
||||
MultiUndoCommand* reconnect_tree_command_;
|
||||
|
||||
NodeInput moved_transition_;
|
||||
|
||||
};
|
||||
|
||||
class BlockSplitPreservingLinksCommand : public UndoCommand {
|
||||
public:
|
||||
BlockSplitPreservingLinksCommand(const QVector<Block *> &blocks, const QList<rational>& times) :
|
||||
blocks_(blocks),
|
||||
times_(times)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~BlockSplitPreservingLinksCommand() override
|
||||
{
|
||||
qDeleteAll(commands_);
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return blocks_.first()->project();
|
||||
}
|
||||
|
||||
Block *GetSplit(Block *original, int time_index) const;
|
||||
|
||||
protected:
|
||||
virtual void prepare() override;
|
||||
|
||||
virtual void redo() override
|
||||
{
|
||||
for (int i=0; i<commands_.size(); i++) {
|
||||
commands_.at(i)->redo_now();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void undo() override
|
||||
{
|
||||
for (int i=commands_.size()-1; i>=0; i--) {
|
||||
commands_.at(i)->undo_now();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
QVector<Block *> blocks_;
|
||||
|
||||
QList<rational> times_;
|
||||
|
||||
QVector<UndoCommand*> commands_;
|
||||
|
||||
QVector< QVector<Block*> > splits_;
|
||||
|
||||
};
|
||||
|
||||
class TrackSplitAtTimeCommand : public UndoCommand {
|
||||
public:
|
||||
TrackSplitAtTimeCommand(Track* track, rational point) :
|
||||
track_(track),
|
||||
point_(point),
|
||||
command_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~TrackSplitAtTimeCommand() override
|
||||
{
|
||||
delete command_;
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return track_->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void prepare() override;
|
||||
|
||||
virtual void redo() override
|
||||
{
|
||||
if (command_) {
|
||||
command_->redo_now();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void undo() override
|
||||
{
|
||||
if (command_) {
|
||||
command_->undo_now();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Track* track_;
|
||||
|
||||
rational point_;
|
||||
|
||||
UndoCommand* command_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // TIMELINEUNDOSPLIT_H
|
||||
@@ -1,25 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "timelineundotrack.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
}
|
||||
@@ -1,167 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef TIMELINEUNDOTRACK_H
|
||||
#define TIMELINEUNDOTRACK_H
|
||||
|
||||
#include "node/output/track/track.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class TrackRippleRemoveBlockCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
TrackRippleRemoveBlockCommand(Track* track, Block* block) :
|
||||
track_(track),
|
||||
block_(block)
|
||||
{
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return track_->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void redo() override
|
||||
{
|
||||
before_ = block_->previous();
|
||||
track_->RippleRemoveBlock(block_);
|
||||
}
|
||||
|
||||
virtual void undo() override
|
||||
{
|
||||
track_->InsertBlockAfter(block_, before_);
|
||||
}
|
||||
|
||||
private:
|
||||
Track* track_;
|
||||
|
||||
Block* block_;
|
||||
|
||||
Block* before_;
|
||||
|
||||
};
|
||||
|
||||
class TrackPrependBlockCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
TrackPrependBlockCommand(Track* track, Block* block) :
|
||||
track_(track),
|
||||
block_(block)
|
||||
{
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return track_->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void redo() override
|
||||
{
|
||||
track_->PrependBlock(block_);
|
||||
}
|
||||
|
||||
virtual void undo() override
|
||||
{
|
||||
track_->RippleRemoveBlock(block_);
|
||||
}
|
||||
|
||||
private:
|
||||
Track* track_;
|
||||
Block* block_;
|
||||
};
|
||||
|
||||
class TrackInsertBlockAfterCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
TrackInsertBlockAfterCommand(Track* track, Block* block, Block* before) :
|
||||
track_(track),
|
||||
block_(block),
|
||||
before_(before)
|
||||
{
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return block_->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void redo() override
|
||||
{
|
||||
track_->InsertBlockAfter(block_, before_);
|
||||
}
|
||||
|
||||
virtual void undo() override
|
||||
{
|
||||
track_->RippleRemoveBlock(block_);
|
||||
}
|
||||
|
||||
private:
|
||||
Track* track_;
|
||||
|
||||
Block* block_;
|
||||
|
||||
Block* before_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Replaces Block `old` with Block `replace`
|
||||
*
|
||||
* Both blocks must have equal lengths.
|
||||
*/
|
||||
class TrackReplaceBlockCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
TrackReplaceBlockCommand(Track* track, Block* old, Block* replace) :
|
||||
track_(track),
|
||||
old_(old),
|
||||
replace_(replace)
|
||||
{
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return track_->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void redo() override
|
||||
{
|
||||
track_->ReplaceBlock(old_, replace_);
|
||||
}
|
||||
|
||||
virtual void undo() override
|
||||
{
|
||||
track_->ReplaceBlock(replace_, old_);
|
||||
}
|
||||
|
||||
private:
|
||||
Track* track_;
|
||||
Block* old_;
|
||||
Block* replace_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // TIMELINEUNDOTRACK_H
|
||||
@@ -1,25 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "timelineundoworkarea.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef TIMELINEUNDOWORKAREA_H
|
||||
#define TIMELINEUNDOWORKAREA_H
|
||||
|
||||
#include "node/project.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class WorkareaSetEnabledCommand : public UndoCommand {
|
||||
public:
|
||||
WorkareaSetEnabledCommand(Project *project, TimelineWorkArea* points, bool enabled) :
|
||||
project_(project),
|
||||
points_(points),
|
||||
old_enabled_(points_->enabled()),
|
||||
new_enabled_(enabled)
|
||||
{
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return project_;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void redo() override
|
||||
{
|
||||
points_->set_enabled(new_enabled_);
|
||||
}
|
||||
|
||||
virtual void undo() override
|
||||
{
|
||||
points_->set_enabled(old_enabled_);
|
||||
}
|
||||
|
||||
private:
|
||||
Project* project_;
|
||||
|
||||
TimelineWorkArea* points_;
|
||||
|
||||
bool old_enabled_;
|
||||
|
||||
bool new_enabled_;
|
||||
|
||||
};
|
||||
|
||||
class WorkareaSetRangeCommand : public UndoCommand {
|
||||
public:
|
||||
WorkareaSetRangeCommand(TimelineWorkArea *workarea, const TimeRange& range, const TimeRange &old_range) :
|
||||
workarea_(workarea),
|
||||
old_range_(old_range),
|
||||
new_range_(range)
|
||||
{
|
||||
}
|
||||
|
||||
WorkareaSetRangeCommand(TimelineWorkArea *workarea, const TimeRange& range) :
|
||||
WorkareaSetRangeCommand(workarea, range, workarea->range())
|
||||
{
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return Project::GetProjectFromObject(workarea_);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void redo() override
|
||||
{
|
||||
workarea_->set_range(new_range_);
|
||||
}
|
||||
|
||||
virtual void undo() override
|
||||
{
|
||||
workarea_->set_range(old_range_);
|
||||
}
|
||||
|
||||
private:
|
||||
TimelineWorkArea *workarea_;
|
||||
|
||||
TimeRange old_range_;
|
||||
|
||||
TimeRange new_range_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // TIMELINEUNDOWORKAREA_H
|
||||
@@ -242,7 +242,7 @@ void TimelineView::drawForeground(QPainter *painter, const QRectF &rect)
|
||||
&& !ghost->IsInvisible()) {
|
||||
int track_index = ghost->GetAdjustedTrack().index();
|
||||
|
||||
Block *attached = Node::ValueToPtr<Block>(ghost->GetData(TimelineViewGhostItem::kAttachedBlock));
|
||||
Block *attached = QtUtils::ValueToPtr<Block>(ghost->GetData(TimelineViewGhostItem::kAttachedBlock));
|
||||
|
||||
if (attached && OLIVE_CONFIG("ShowClipWhileDragging").toBool()) {
|
||||
int adj_track = ghost->GetAdjustedTrack().index();
|
||||
|
||||
@@ -69,7 +69,7 @@ public:
|
||||
ghost->SetMediaIn(static_cast<ClipBlock*>(block)->media_in());
|
||||
}
|
||||
ghost->SetTrack(block->track()->ToReference());
|
||||
ghost->SetData(kAttachedBlock, Node::PtrToValue(block));
|
||||
ghost->SetData(kAttachedBlock, QtUtils::PtrToValue(block));
|
||||
|
||||
if (dynamic_cast<ClipBlock*>(block)) {
|
||||
ghost->can_have_zero_length_ = false;
|
||||
|
||||
@@ -30,10 +30,10 @@
|
||||
#include "core.h"
|
||||
#include "dialog/markerproperties/markerpropertiesdialog.h"
|
||||
#include "node/project/serializer/serializer.h"
|
||||
#include "timeline/timelineundoworkarea.h"
|
||||
#include "widget/colorlabelmenu/colorlabelmenu.h"
|
||||
#include "widget/menu/menushared.h"
|
||||
#include "widget/timebased/timebasedwidget.h"
|
||||
#include "widget/timelinewidget/undo/timelineundoworkarea.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "core.h"
|
||||
#include "node/block/gap/gap.h"
|
||||
#include "node/generator/shape/shapenodebase.h"
|
||||
#include "node/nodeundo.h"
|
||||
#include "node/project.h"
|
||||
#include "panel/multicam/multicampanel.h"
|
||||
#include "panel/panelmanager.h"
|
||||
@@ -45,7 +46,6 @@
|
||||
#include "widget/audiomonitor/audiomonitor.h"
|
||||
#include "widget/menu/menu.h"
|
||||
#include "widget/multicam/multicamdisplay.h"
|
||||
#include "widget/nodeparamview/nodeparamviewundo.h"
|
||||
#include "widget/timelinewidget/tool/add.h"
|
||||
#include "widget/timeruler/timeruler.h"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user