new undo system
Also enables undoing when working with node input arrays
This commit is contained in:
@@ -43,6 +43,7 @@ set(OLIVE_SOURCES
|
||||
node/node.cpp
|
||||
node/nodecopypaste.h
|
||||
node/nodecopypaste.cpp
|
||||
node/splitvalue.h
|
||||
node/traverser.h
|
||||
node/traverser.cpp
|
||||
node/value.h
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace olive {
|
||||
|
||||
PolygonGenerator::PolygonGenerator()
|
||||
{
|
||||
points_input_ = new NodeInput(this, QStringLiteral("points_in"), NodeValue::kVec2);
|
||||
points_input_ = new NodeInput(this, QStringLiteral("points_in"), NodeValue::kVec2, QVector2D(0, 0));
|
||||
points_input_->SetIsArray(true);
|
||||
|
||||
color_input_ = new NodeInput(this, QStringLiteral("color_in"), NodeValue::kColor, QVariant::fromValue(Color(1.0, 1.0, 1.0)));
|
||||
|
||||
+137
-76
@@ -23,14 +23,16 @@
|
||||
#include "common/bezier.h"
|
||||
#include "common/lerp.h"
|
||||
#include "common/xmlutils.h"
|
||||
#include "core.h"
|
||||
#include "node.h"
|
||||
#include "project/item/footage/stream.h"
|
||||
#include "widget/nodeview/nodeviewundo.h"
|
||||
|
||||
#define super NodeConnectable
|
||||
|
||||
namespace olive {
|
||||
|
||||
NodeInput::NodeInput(Node* parent, const QString& id, NodeValue::Type type, const QVector<QVariant> &default_value)
|
||||
NodeInput::NodeInput(Node* parent, const QString& id, NodeValue::Type type, const SplitValue &default_value)
|
||||
{
|
||||
Init(parent, id, type, default_value);
|
||||
}
|
||||
@@ -42,7 +44,7 @@ NodeInput::NodeInput(Node *parent, const QString &id, NodeValue::Type type, cons
|
||||
|
||||
NodeInput::NodeInput(Node* parent, const QString &id, NodeValue::Type type)
|
||||
{
|
||||
Init(parent, id, type, QVector<QVariant>());
|
||||
Init(parent, id, type, SplitValue());
|
||||
}
|
||||
|
||||
NodeInput::~NodeInput()
|
||||
@@ -217,7 +219,16 @@ void NodeInput::childEvent(QChildEvent *event)
|
||||
}
|
||||
}
|
||||
|
||||
void NodeInput::Init(Node* parent, const QString &id, NodeValue::Type type, const QVector<QVariant>& default_val)
|
||||
void NodeInput::ClearElement(int index)
|
||||
{
|
||||
GetImmediate(index)->delete_all_keyframes();
|
||||
|
||||
SetIsKeyframing(false, index);
|
||||
|
||||
SetSplitStandardValue(default_value_, index);
|
||||
}
|
||||
|
||||
void NodeInput::Init(Node* parent, const QString &id, NodeValue::Type type, const SplitValue& default_val)
|
||||
{
|
||||
setParent(parent);
|
||||
|
||||
@@ -381,11 +392,25 @@ void NodeInput::SaveImmediate(QXmlStreamWriter* writer, int element) const
|
||||
}
|
||||
}
|
||||
|
||||
void NodeInput::ChangeArraySizeInternal(int size)
|
||||
void NodeInput::ArrayResizeInternal(int size)
|
||||
{
|
||||
array_size_ = size;
|
||||
emit ArraySizeChanged(array_size_);
|
||||
emit ValueChanged(TimeRange(RATIONAL_MIN, RATIONAL_MAX), -1);
|
||||
if (array_size_ != size) {
|
||||
// Update array size
|
||||
if (array_size_ < size) {
|
||||
// Size is larger, create any immediates that don't exist
|
||||
for (int i=subinputs_.size(); i<size; i++) {
|
||||
subinputs_.append(CreateImmediate());
|
||||
}
|
||||
|
||||
// Note that we do not delete any immediates when decreasing size since the user might still
|
||||
// want that data. Therefore it's important to note that array_size_ does NOT necessarily
|
||||
// equal subinputs_.size()
|
||||
}
|
||||
|
||||
array_size_ = size;
|
||||
emit ArraySizeChanged(array_size_);
|
||||
emit ValueChanged(TimeRange(RATIONAL_MIN, RATIONAL_MAX), -1);
|
||||
}
|
||||
}
|
||||
|
||||
NodeInputImmediate *NodeInput::CreateImmediate()
|
||||
@@ -440,79 +465,95 @@ QVector<Node *> NodeInput::GetImmediateDependencies() const
|
||||
return GetDependencies(false, false);
|
||||
}
|
||||
|
||||
void NodeInput::ArrayInsert(int index)
|
||||
void NodeInput::ArrayAppend(bool undoable)
|
||||
{
|
||||
// Add new input
|
||||
subinputs_.insert(index, CreateImmediate());
|
||||
|
||||
ChangeArraySizeInternal(array_size_ + 1);
|
||||
|
||||
// Move connections down
|
||||
std::map<int, Node*> copied_edges = edges();
|
||||
for (auto it=copied_edges.crbegin(); it!=copied_edges.crend(); it++) {
|
||||
if (it->first >= index) {
|
||||
// Disconnect this and reconnect it one element down
|
||||
DisconnectEdge(it->second, this, it->first);
|
||||
ConnectEdge(it->second, this, it->first + 1);
|
||||
}
|
||||
}
|
||||
ArrayResize(ArraySize() + 1, undoable);
|
||||
}
|
||||
|
||||
void NodeInput::ArrayRemove(int index)
|
||||
void NodeInput::ArrayInsert(int index, bool undoable)
|
||||
{
|
||||
// Remove input
|
||||
delete subinputs_.takeAt(index);
|
||||
ChangeArraySizeInternal(array_size_ - 1);
|
||||
if (undoable) {
|
||||
Core::instance()->undo_stack()->push(new ArrayInsertCommand(this, index));
|
||||
} else {
|
||||
// Add new input
|
||||
ArrayResizeInternal(ArraySize() + 1);
|
||||
|
||||
// Move connections up
|
||||
std::map<int, Node*> copied_edges = edges();
|
||||
for (auto it=copied_edges.cbegin(); it!=copied_edges.cend(); it++) {
|
||||
if (it->first >= index) {
|
||||
// Disconnect this and reconnect it one element up if it's not the element being removed
|
||||
DisconnectEdge(it->second, this, it->first);
|
||||
|
||||
if (it->first > index) {
|
||||
ConnectEdge(it->second, this, it->first - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeInput::ArrayPrepend()
|
||||
{
|
||||
ArrayInsert(0);
|
||||
}
|
||||
|
||||
void NodeInput::ArrayResize(int size)
|
||||
{
|
||||
if (array_size_ != size) {
|
||||
// Update array size
|
||||
if (array_size_ < size) {
|
||||
// Size is larger, create any immediates that don't exist
|
||||
for (int i=subinputs_.size(); i<size; i++) {
|
||||
subinputs_.append(CreateImmediate());
|
||||
// Move connections down
|
||||
std::map<int, Node*> copied_edges = edges();
|
||||
for (auto it=copied_edges.crbegin(); it!=copied_edges.crend(); it++) {
|
||||
if (it->first >= index) {
|
||||
// Disconnect this and reconnect it one element down
|
||||
DisconnectEdge(it->second, this, it->first);
|
||||
ConnectEdge(it->second, this, it->first + 1);
|
||||
}
|
||||
}
|
||||
|
||||
ChangeArraySizeInternal(size);
|
||||
|
||||
if (array_size_ > size) {
|
||||
// Decreasing in size, disconnect any extraneous edges
|
||||
for (int i=size; i<array_size_; i++) {
|
||||
try {
|
||||
DisconnectEdge(edges().at(i), this, i);
|
||||
} catch (std::out_of_range&) {}
|
||||
}
|
||||
|
||||
// Note that we do not delete any immediates since the user might still want that data.
|
||||
// Therefore it's important to note that array_size_ does NOT necessarily equal subinputs_.size()
|
||||
// Shift values and keyframes up one element
|
||||
for (int i=ArraySize()-1; i>index; i--) {
|
||||
CopyValuesOfElement(this, this, i-1, i);
|
||||
}
|
||||
|
||||
// Reset value of element we just "inserted"
|
||||
ClearElement(index);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeInput::ArrayRemoveLast()
|
||||
void NodeInput::ArrayRemove(int index, bool undoable)
|
||||
{
|
||||
ArrayResize(ArraySize() - 1);
|
||||
if (undoable) {
|
||||
Core::instance()->undo_stack()->push(new ArrayRemoveCommand(this, index));
|
||||
} else {
|
||||
// Remove input
|
||||
ArrayResizeInternal(ArraySize() - 1);
|
||||
|
||||
// Move connections up
|
||||
std::map<int, Node*> copied_edges = edges();
|
||||
for (auto it=copied_edges.cbegin(); it!=copied_edges.cend(); it++) {
|
||||
if (it->first >= index) {
|
||||
// Disconnect this and reconnect it one element up if it's not the element being removed
|
||||
DisconnectEdge(it->second, this, it->first);
|
||||
|
||||
if (it->first > index) {
|
||||
ConnectEdge(it->second, this, it->first - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Shift values and keyframes down one element
|
||||
for (int i=index; i<ArraySize(); i++) {
|
||||
// Copying ArraySize()+1 is actually legal because immediates are never deleted
|
||||
CopyValuesOfElement(this, this, i+1, i);
|
||||
}
|
||||
|
||||
// Reset value of last element
|
||||
ClearElement(ArraySize());
|
||||
}
|
||||
}
|
||||
|
||||
void NodeInput::ArrayPrepend(bool undoable)
|
||||
{
|
||||
ArrayInsert(0, undoable);
|
||||
}
|
||||
|
||||
void NodeInput::ArrayResize(int size, bool undoable)
|
||||
{
|
||||
if (array_size_ == size) {
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayResizeCommand* c = new ArrayResizeCommand(this, size);
|
||||
|
||||
if (undoable) {
|
||||
Core::instance()->undo_stack()->push(c);
|
||||
} else {
|
||||
c->redo();
|
||||
delete c;
|
||||
}
|
||||
}
|
||||
|
||||
void NodeInput::ArrayRemoveLast(bool undoable)
|
||||
{
|
||||
ArrayResize(ArraySize() - 1, undoable);
|
||||
}
|
||||
|
||||
QVariant NodeInput::GetValueAtTime(const rational &time, int element) const
|
||||
@@ -520,9 +561,9 @@ QVariant NodeInput::GetValueAtTime(const rational &time, int element) const
|
||||
return NodeValue::combine_track_values_into_normal_value(data_type_, GetSplitValuesAtTime(time, element));
|
||||
}
|
||||
|
||||
QVector<QVariant> NodeInput::GetSplitValuesAtTime(const rational &time, int element) const
|
||||
SplitValue NodeInput::GetSplitValuesAtTime(const rational &time, int element) const
|
||||
{
|
||||
QVector<QVariant> vals;
|
||||
SplitValue vals;
|
||||
|
||||
int nb_tracks = GetNumberOfKeyframeTracks();
|
||||
|
||||
@@ -669,26 +710,31 @@ void NodeInput::CopyValues(NodeInput *source, NodeInput *dest, bool include_conn
|
||||
}
|
||||
}
|
||||
|
||||
void NodeInput::CopyValuesOfElement(NodeInput *src, NodeInput *dst, int element)
|
||||
void NodeInput::CopyValuesOfElement(NodeInput *src, NodeInput *dst, int src_element, int dst_element)
|
||||
{
|
||||
if (dst_element >= dst->subinputs_.size()) {
|
||||
qDebug() << "Ignored destination element that was out of array bounds";
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy standard value
|
||||
dst->SetSplitStandardValue(src->GetSplitStandardValue(element), element);
|
||||
dst->SetSplitStandardValue(src->GetSplitStandardValue(src_element), dst_element);
|
||||
|
||||
// Copy keyframes
|
||||
dst->GetImmediate(element)->delete_all_keyframes();
|
||||
foreach (const NodeKeyframeTrack& track, src->GetImmediate(element)->keyframe_tracks()) {
|
||||
dst->GetImmediate(dst_element)->delete_all_keyframes();
|
||||
foreach (const NodeKeyframeTrack& track, src->GetImmediate(src_element)->keyframe_tracks()) {
|
||||
foreach (NodeKeyframe* key, track) {
|
||||
key->copy(dst);
|
||||
key->copy(dst_element, dst);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy keyframing state
|
||||
if (src->IsKeyframable()) {
|
||||
dst->SetIsKeyframing(src->IsKeyframing(element), element);
|
||||
dst->SetIsKeyframing(src->IsKeyframing(src_element), dst_element);
|
||||
}
|
||||
|
||||
// If this is the root of an array, copy the array size
|
||||
if (element == -1) {
|
||||
if (src_element == -1 && dst_element == -1) {
|
||||
dst->ArrayResize(src->ArraySize());
|
||||
}
|
||||
}
|
||||
@@ -839,4 +885,19 @@ uint qHash(const NodeInput::KeyframeTrackReference &ref, uint seed)
|
||||
return qHash(ref.input, seed) ^ qHash(ref.element, seed) ^ qHash(ref.track, seed);
|
||||
}
|
||||
|
||||
Project *NodeInput::ArrayRemoveCommand::GetRelevantProject() const
|
||||
{
|
||||
return input_->parent()->parent()->project();
|
||||
}
|
||||
|
||||
Project *NodeInput::ArrayResizeCommand::GetRelevantProject() const
|
||||
{
|
||||
return input_->parent()->parent()->project();
|
||||
}
|
||||
|
||||
Project *NodeInput::ArrayInsertCommand::GetRelevantProject() const
|
||||
{
|
||||
return input_->parent()->parent()->project();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+155
-20
@@ -26,6 +26,8 @@
|
||||
#include "node/connectable.h"
|
||||
#include "node/inputimmediate.h"
|
||||
#include "node/value.h"
|
||||
#include "splitvalue.h"
|
||||
#include "undo/undocommand.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -47,7 +49,7 @@ public:
|
||||
* saving/loading data from this Node so that parameter order can be changed without issues loading data saved by an
|
||||
* older version. This of course assumes that parameters don't change their ID.
|
||||
*/
|
||||
NodeInput(Node* parent, const QString &id, NodeValue::Type type, const QVector<QVariant>& default_value);
|
||||
NodeInput(Node* parent, const QString &id, NodeValue::Type type, const SplitValue& default_value);
|
||||
NodeInput(Node* parent, const QString &id, NodeValue::Type type, const QVariant& default_value);
|
||||
NodeInput(Node* parent, const QString &id, NodeValue::Type type);
|
||||
|
||||
@@ -176,7 +178,7 @@ public:
|
||||
/**
|
||||
* @brief Get non-keyframed value split into components (the way it's stored)
|
||||
*/
|
||||
const QVector<QVariant>& GetSplitStandardValue(int element = -1) const
|
||||
const SplitValue& GetSplitStandardValue(int element = -1) const
|
||||
{
|
||||
return GetImmediate(element)->get_split_standard_value();
|
||||
}
|
||||
@@ -197,7 +199,7 @@ public:
|
||||
SetSplitStandardValue(NodeValue::split_normal_value_into_track_values(data_type_, value), element);
|
||||
}
|
||||
|
||||
void SetSplitStandardValue(const QVector<QVariant>& value, int element = -1)
|
||||
void SetSplitStandardValue(const SplitValue& value, int element = -1)
|
||||
{
|
||||
GetImmediate(element)->set_split_standard_value(value);
|
||||
|
||||
@@ -231,7 +233,11 @@ public:
|
||||
*/
|
||||
static void CopyValues(NodeInput* source, NodeInput* dest, bool include_connections = true, bool traverse_arrays = true);
|
||||
|
||||
static void CopyValuesOfElement(NodeInput* source, NodeInput* dst, int element);
|
||||
static void CopyValuesOfElement(NodeInput* source, NodeInput* dst, int src_element, int dst_element);
|
||||
static void CopyValuesOfElement(NodeInput* source, NodeInput* dst, int element)
|
||||
{
|
||||
CopyValuesOfElement(source, dst, element, element);
|
||||
}
|
||||
|
||||
QStringList get_combobox_strings() const;
|
||||
|
||||
@@ -244,7 +250,7 @@ public:
|
||||
return NodeValue::combine_track_values_into_normal_value(data_type_, default_value_);
|
||||
}
|
||||
|
||||
const QVector<QVariant>& GetSplitDefaultValue() const
|
||||
const SplitValue& GetSplitDefaultValue() const
|
||||
{
|
||||
return default_value_;
|
||||
}
|
||||
@@ -279,33 +285,36 @@ public:
|
||||
|
||||
void SetIsKeyframing(bool keyframing, int element)
|
||||
{
|
||||
Q_ASSERT(IsKeyframable());
|
||||
if (IsKeyframable()) {
|
||||
qDebug() << "Ignored set keyframing because this input is not keyframable";
|
||||
return;
|
||||
}
|
||||
|
||||
GetImmediate(element)->set_is_keyframing(keyframing);
|
||||
|
||||
emit KeyframeEnableChanged(keyframing, element);
|
||||
}
|
||||
|
||||
void ArrayAppend()
|
||||
{
|
||||
ArrayResize(ArraySize() + 1);
|
||||
}
|
||||
/// Alias for ArrayResize(ArraySize() + 1)
|
||||
void ArrayAppend(bool undoable = false);
|
||||
|
||||
void ArrayInsert(int index);
|
||||
void ArrayInsert(int index, bool undoable = false);
|
||||
|
||||
void ArrayRemove(int index);
|
||||
void ArrayRemove(int index, bool undoable = false);
|
||||
|
||||
void ArrayPrepend();
|
||||
/// Alias for ArrayInsert(0)
|
||||
void ArrayPrepend(bool undoable = false);
|
||||
|
||||
void ArrayResize(int size);
|
||||
void ArrayResize(int size, bool undoable = false);
|
||||
|
||||
/// Alias for ArrayResize(ArraySize() - 1)
|
||||
void ArrayRemoveLast(bool undoable = false);
|
||||
|
||||
int ArraySize() const
|
||||
{
|
||||
return array_size_;
|
||||
}
|
||||
|
||||
void ArrayRemoveLast();
|
||||
|
||||
int GetNumberOfKeyframeTracks() const
|
||||
{
|
||||
return NodeValue::get_number_of_keyframe_tracks(data_type_);
|
||||
@@ -318,7 +327,7 @@ public:
|
||||
*/
|
||||
QVariant GetValueAtTime(const rational& time, int element = -1) const;
|
||||
|
||||
QVector<QVariant> GetSplitValuesAtTime(const rational& time, int element = -1) const;
|
||||
SplitValue GetSplitValuesAtTime(const rational& time, int element = -1) const;
|
||||
|
||||
/**
|
||||
* @brief Calculate the stored value for a specific track
|
||||
@@ -378,13 +387,139 @@ protected:
|
||||
virtual void childEvent(QChildEvent* e) override;
|
||||
|
||||
private:
|
||||
void Init(Node *parent, const QString& id, NodeValue::Type type, const QVector<QVariant> &default_val);
|
||||
class ArrayInsertCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
ArrayInsertCommand(NodeInput* input, int index) :
|
||||
input_(input),
|
||||
index_(index)
|
||||
{
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override;
|
||||
|
||||
virtual void redo() override
|
||||
{
|
||||
input_->ArrayInsert(index_, false);
|
||||
}
|
||||
|
||||
virtual void undo() override
|
||||
{
|
||||
input_->ArrayRemove(index_, false);
|
||||
}
|
||||
|
||||
private:
|
||||
NodeInput* input_;
|
||||
int index_;
|
||||
|
||||
};
|
||||
|
||||
class ArrayRemoveCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
ArrayRemoveCommand(NodeInput* input, int index) :
|
||||
input_(input),
|
||||
index_(index)
|
||||
{
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override;
|
||||
|
||||
protected:
|
||||
virtual void redo() override
|
||||
{
|
||||
// Save immediate data
|
||||
is_keyframing_ = input_->IsKeyframing(index_);
|
||||
standard_value_ = input_->GetSplitStandardValue(index_);
|
||||
keyframes_ = input_->GetImmediate(index_)->keyframe_tracks();
|
||||
input_->GetImmediate(index_)->delete_all_keyframes(&memory_manager_);
|
||||
|
||||
input_->ArrayRemove(index_, false);
|
||||
}
|
||||
|
||||
virtual void undo() override
|
||||
{
|
||||
input_->ArrayInsert(index_, false);
|
||||
|
||||
// Restore keyframes
|
||||
foreach (const NodeKeyframeTrack& track, keyframes_) {
|
||||
foreach (NodeKeyframe* key, track) {
|
||||
key->setParent(input_);
|
||||
}
|
||||
}
|
||||
input_->SetSplitStandardValue(standard_value_, index_);
|
||||
input_->SetIsKeyframing(is_keyframing_, index_);
|
||||
}
|
||||
|
||||
private:
|
||||
NodeInput* input_;
|
||||
int index_;
|
||||
|
||||
SplitValue standard_value_;
|
||||
bool is_keyframing_;
|
||||
QVector<NodeKeyframeTrack> keyframes_;
|
||||
QObject memory_manager_;
|
||||
|
||||
};
|
||||
|
||||
class ArrayResizeCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
ArrayResizeCommand(NodeInput* input, int size) :
|
||||
input_(input),
|
||||
size_(size)
|
||||
{}
|
||||
|
||||
virtual void redo() override
|
||||
{
|
||||
old_size_ = input_->array_size_;
|
||||
|
||||
if (old_size_ > size_) {
|
||||
// Decreasing in size, disconnect any extraneous edges
|
||||
for (int i=size_; i<old_size_; i++) {
|
||||
try {
|
||||
Node* output = input_->edges().at(i);
|
||||
|
||||
removed_connections_.insert(i, output);
|
||||
|
||||
DisconnectEdge(output, input_, i);
|
||||
} catch (std::out_of_range&) {}
|
||||
}
|
||||
}
|
||||
|
||||
input_->ArrayResizeInternal(size_);
|
||||
}
|
||||
|
||||
virtual void undo() override
|
||||
{
|
||||
for (auto it=removed_connections_.cbegin(); it!=removed_connections_.cend(); it++) {
|
||||
ConnectEdge(it.value(), input_, it.key());
|
||||
}
|
||||
removed_connections_.clear();
|
||||
|
||||
input_->ArrayResizeInternal(old_size_);
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override;
|
||||
|
||||
private:
|
||||
NodeInput* input_;
|
||||
int size_;
|
||||
int old_size_;
|
||||
|
||||
QHash<int, Node*> removed_connections_;
|
||||
|
||||
};
|
||||
|
||||
void ClearElement(int index);
|
||||
|
||||
void Init(Node *parent, const QString& id, NodeValue::Type type, const SplitValue &default_val);
|
||||
|
||||
void LoadImmediate(QXmlStreamReader *reader, int element, XMLNodeData& xml_node_data, const QAtomicInt* cancelled);
|
||||
|
||||
void SaveImmediate(QXmlStreamWriter *writer, int element) const;
|
||||
|
||||
void ChangeArraySizeInternal(int size);
|
||||
void ArrayResizeInternal(int size);
|
||||
|
||||
NodeInputImmediate* CreateImmediate();
|
||||
|
||||
@@ -426,7 +561,7 @@ private:
|
||||
|
||||
QVector<NodeInputImmediate*> subinputs_;
|
||||
|
||||
QVector<QVariant> default_value_;
|
||||
SplitValue default_value_;
|
||||
|
||||
/**
|
||||
* @brief Unique identifier of this input within this node
|
||||
|
||||
@@ -106,21 +106,21 @@ void NodeInputDragger::End()
|
||||
return;
|
||||
}
|
||||
|
||||
QUndoCommand* command = new QUndoCommand();
|
||||
MultiUndoCommand* command = new MultiUndoCommand();
|
||||
|
||||
if (input_->IsKeyframing(element_)) {
|
||||
if (drag_created_key_) {
|
||||
// We created a keyframe in this process
|
||||
new NodeParamInsertKeyframeCommand(input_, dragging_key_, command);
|
||||
command->add_child(new NodeParamInsertKeyframeCommand(input_, dragging_key_));
|
||||
}
|
||||
|
||||
// We just set a keyframe's value
|
||||
// We do this even when inserting a keyframe because we don't actually perform an insert in this undo command
|
||||
// so this will ensure the ValueChanged() signal is sent correctly
|
||||
new NodeParamSetKeyframeValueCommand(dragging_key_, end_value_, start_value_, command);
|
||||
command->add_child(new NodeParamSetKeyframeValueCommand(dragging_key_, end_value_, start_value_));
|
||||
} else {
|
||||
// We just set the standard value
|
||||
new NodeParamSetStandardValueCommand(input_, track_, element_, end_value_, start_value_, command);
|
||||
command->add_child(new NodeParamSetStandardValueCommand(input_, track_, element_, end_value_, start_value_));
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->push(command);
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
NodeInputImmediate::NodeInputImmediate(NodeValue::Type type, const QVector<QVariant> &default_val) :
|
||||
NodeInputImmediate::NodeInputImmediate(NodeValue::Type type, const SplitValue &default_val) :
|
||||
keyframing_(false)
|
||||
{
|
||||
int track_size = NodeValue::get_number_of_keyframe_tracks(type);
|
||||
@@ -43,7 +43,7 @@ void NodeInputImmediate::set_standard_value_on_track(const QVariant &value, int
|
||||
standard_value_.replace(track, value);
|
||||
}
|
||||
|
||||
void NodeInputImmediate::set_split_standard_value(const QVector<QVariant> &value)
|
||||
void NodeInputImmediate::set_split_standard_value(const SplitValue &value)
|
||||
{
|
||||
for (int i=0; i<value.size() && i<standard_value_.size(); i++) {
|
||||
standard_value_[i] = value[i];
|
||||
@@ -267,11 +267,15 @@ void NodeInputImmediate::remove_keyframe(NodeKeyframe *key)
|
||||
keyframe_tracks_[key->track()].removeOne(key);
|
||||
}
|
||||
|
||||
void NodeInputImmediate::delete_all_keyframes()
|
||||
void NodeInputImmediate::delete_all_keyframes(QObject* parent)
|
||||
{
|
||||
for (NodeKeyframeTrack& track : keyframe_tracks_) {
|
||||
while (!track.isEmpty()) {
|
||||
delete track.first();
|
||||
if (parent) {
|
||||
track.first()->setParent(parent);
|
||||
} else {
|
||||
delete track.first();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "common/xmlutils.h"
|
||||
#include "node/keyframe.h"
|
||||
#include "node/value.h"
|
||||
#include "splitvalue.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -33,7 +34,7 @@ class NodeInput;
|
||||
class NodeInputImmediate
|
||||
{
|
||||
public:
|
||||
NodeInputImmediate(NodeValue::Type type, const QVector<QVariant>& default_val);
|
||||
NodeInputImmediate(NodeValue::Type type, const SplitValue& default_val);
|
||||
|
||||
/**
|
||||
* @brief Internal insert function, automatically does an insertion sort based on the keyframe's time
|
||||
@@ -42,19 +43,19 @@ public:
|
||||
|
||||
void remove_keyframe(NodeKeyframe* key);
|
||||
|
||||
void delete_all_keyframes();
|
||||
void delete_all_keyframes(QObject *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Get non-keyframed value split into components (the way it's stored)
|
||||
*/
|
||||
const QVector<QVariant>& get_split_standard_value() const
|
||||
const SplitValue& get_split_standard_value() const
|
||||
{
|
||||
return standard_value_;
|
||||
}
|
||||
|
||||
void set_standard_value_on_track(const QVariant &value, int track = 0);
|
||||
|
||||
void set_split_standard_value(const QVector<QVariant>& value);
|
||||
void set_split_standard_value(const SplitValue& value);
|
||||
|
||||
/**
|
||||
* @brief Retrieve a list of keyframe objects for all tracks at a given time
|
||||
@@ -149,7 +150,7 @@ private:
|
||||
/**
|
||||
* @brief Non-keyframed value
|
||||
*/
|
||||
QVector<QVariant> standard_value_;
|
||||
SplitValue standard_value_;
|
||||
|
||||
/**
|
||||
* @brief Internal keyframe array
|
||||
|
||||
@@ -45,14 +45,19 @@ NodeKeyframe::~NodeKeyframe()
|
||||
setParent(nullptr);
|
||||
}
|
||||
|
||||
NodeKeyframe *NodeKeyframe::copy(QObject* parent) const
|
||||
NodeKeyframe *NodeKeyframe::copy(int element, QObject *parent) const
|
||||
{
|
||||
NodeKeyframe* copy = new NodeKeyframe(time_, value_, type_, track_, element_, parent);
|
||||
NodeKeyframe* copy = new NodeKeyframe(time_, value_, type_, track_, element, parent);
|
||||
copy->bezier_control_in_ = bezier_control_in_;
|
||||
copy->bezier_control_out_ = bezier_control_out_;
|
||||
return copy;
|
||||
}
|
||||
|
||||
NodeKeyframe *NodeKeyframe::copy(QObject* parent) const
|
||||
{
|
||||
return copy(element_, parent);
|
||||
}
|
||||
|
||||
NodeInput *NodeKeyframe::parent() const
|
||||
{
|
||||
return static_cast<NodeInput*>(QObject::parent());
|
||||
|
||||
@@ -65,6 +65,7 @@ public:
|
||||
|
||||
virtual ~NodeKeyframe() override;
|
||||
|
||||
NodeKeyframe* copy(int element, QObject* parent = nullptr) const;
|
||||
NodeKeyframe* copy(QObject* parent = nullptr) const;
|
||||
|
||||
NodeInput* parent() const;
|
||||
|
||||
+4
-4
@@ -247,7 +247,7 @@ TimeRange Node::OutputTimeAdjustment(NodeInput *, int, const TimeRange &input_ti
|
||||
return input_time;
|
||||
}
|
||||
|
||||
QVector<Node *> Node::CopyDependencyGraph(const QVector<Node *> &nodes, QUndoCommand* command)
|
||||
QVector<Node *> Node::CopyDependencyGraph(const QVector<Node *> &nodes, MultiUndoCommand *command)
|
||||
{
|
||||
int nb_nodes = nodes.size();
|
||||
|
||||
@@ -263,7 +263,7 @@ QVector<Node *> Node::CopyDependencyGraph(const QVector<Node *> &nodes, QUndoCom
|
||||
// Add to graph
|
||||
NodeGraph* graph = static_cast<NodeGraph*>(nodes.at(i)->parent());
|
||||
if (command) {
|
||||
new NodeAddCommand(graph, c, command);
|
||||
command->add_child(new NodeAddCommand(graph, c));
|
||||
} else {
|
||||
c->setParent(graph);
|
||||
}
|
||||
@@ -277,7 +277,7 @@ QVector<Node *> Node::CopyDependencyGraph(const QVector<Node *> &nodes, QUndoCom
|
||||
return copies;
|
||||
}
|
||||
|
||||
void Node::CopyDependencyGraph(const QVector<Node *> &src, const QVector<Node *> &dst, QUndoCommand *command)
|
||||
void Node::CopyDependencyGraph(const QVector<Node *> &src, const QVector<Node *> &dst, MultiUndoCommand *command)
|
||||
{
|
||||
for (int i=0; i<src.size(); i++) {
|
||||
foreach (NodeInput* input, src.at(i)->inputs()) {
|
||||
@@ -290,7 +290,7 @@ void Node::CopyDependencyGraph(const QVector<Node *> &src, const QVector<Node *>
|
||||
NodeInput* dst_input = dst.at(i)->GetInputWithID(input->id());
|
||||
|
||||
if (command) {
|
||||
new NodeEdgeAddCommand(dst_output, dst_input, it->first, command);
|
||||
command->add_child(new NodeEdgeAddCommand(dst_output, dst_input, it->first));
|
||||
} else {
|
||||
ConnectEdge(dst_output, dst_input, it->first);
|
||||
}
|
||||
|
||||
+2
-2
@@ -368,8 +368,8 @@ public:
|
||||
/**
|
||||
* @brief Clones a set of nodes and connects the new ones the way the old ones were
|
||||
*/
|
||||
static QVector<Node*> CopyDependencyGraph(const QVector<Node*>& nodes, QUndoCommand *command);
|
||||
static void CopyDependencyGraph(const QVector<Node*>& src, const QVector<Node*>& dst, QUndoCommand *command);
|
||||
static QVector<Node*> CopyDependencyGraph(const QVector<Node*>& nodes, MultiUndoCommand *command);
|
||||
static void CopyDependencyGraph(const QVector<Node*>& src, const QVector<Node*>& dst, MultiUndoCommand *command);
|
||||
|
||||
/**
|
||||
* @brief Return whether this Node can be deleted or not
|
||||
|
||||
@@ -56,7 +56,7 @@ void NodeCopyPasteService::CopyNodesToClipboard(const QVector<Node *> &nodes, vo
|
||||
Core::CopyStringToClipboard(copy_str);
|
||||
}
|
||||
|
||||
QVector<Node *> NodeCopyPasteService::PasteNodesFromClipboard(NodeGraph *graph, QUndoCommand* command, void *userdata)
|
||||
QVector<Node *> NodeCopyPasteService::PasteNodesFromClipboard(NodeGraph *graph, MultiUndoCommand* command, void *userdata)
|
||||
{
|
||||
QString clipboard = Core::PasteStringFromClipboard();
|
||||
|
||||
@@ -121,7 +121,7 @@ QVector<Node *> NodeCopyPasteService::PasteNodesFromClipboard(NodeGraph *graph,
|
||||
|
||||
// Add all nodes to graph
|
||||
foreach (Node* n, pasted_nodes) {
|
||||
new NodeAddCommand(graph, n, command);
|
||||
command->add_child(new NodeAddCommand(graph, n));
|
||||
}
|
||||
|
||||
// Make connections
|
||||
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
protected:
|
||||
void CopyNodesToClipboard(const QVector<Node *> &nodes, void* userdata = nullptr);
|
||||
|
||||
QVector<Node*> PasteNodesFromClipboard(NodeGraph *graph, QUndoCommand *command, void* userdata = nullptr);
|
||||
QVector<Node*> PasteNodesFromClipboard(NodeGraph *graph, MultiUndoCommand *command, void* userdata = nullptr);
|
||||
|
||||
virtual void CopyNodesToClipboardInternal(QXmlStreamWriter *writer, void* userdata);
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2020 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef SPLITVALUE_H
|
||||
#define SPLITVALUE_H
|
||||
|
||||
#include <QVariant>
|
||||
#include <QVector>
|
||||
|
||||
namespace olive {
|
||||
|
||||
using SplitValue = QVector<QVariant>;
|
||||
|
||||
}
|
||||
|
||||
#endif // SPLITVALUE_H
|
||||
Reference in New Issue
Block a user