nodeparamview: implement copy/pasting values
This commit is contained in:
+88
-24
@@ -1064,7 +1064,7 @@ Node *Node::CopyNodeAndDependencyGraphMinusItemsInternal(QMap<Node*, Node*>& cre
|
||||
}
|
||||
|
||||
// Copy values to the clone
|
||||
command->add_child(new NodeCopyInputsCommand(node, copy, false));
|
||||
CopyInputs(node, copy, false, command);
|
||||
|
||||
// Go through input connections and copy if non-item and connect if item
|
||||
for (auto it=node->input_connections_.cbegin(); it!=node->input_connections_.cend(); it++) {
|
||||
@@ -1110,7 +1110,7 @@ Node *Node::CopyNodeInGraph(Node *node, MultiUndoCommand *command)
|
||||
|
||||
command->add_child(new NodeAddCommand(static_cast<NodeGraph*>(node->parent()), copy));
|
||||
|
||||
command->add_child(new NodeCopyInputsCommand(node, copy, true));
|
||||
CopyInputs(node, copy, true, command);
|
||||
|
||||
const PositionMap &map = node->GetContextPositions();
|
||||
for (auto it=map.cbegin(); it!=map.cend(); it++) {
|
||||
@@ -1359,7 +1359,7 @@ void Node::Hash(QCryptographicHash &hash, const NodeGlobals &globals, const Vide
|
||||
}
|
||||
}
|
||||
|
||||
void Node::CopyInputs(const Node *source, Node *destination, bool include_connections)
|
||||
void Node::CopyInputs(const Node *source, Node *destination, bool include_connections, MultiUndoCommand *command)
|
||||
{
|
||||
Q_ASSERT(source->id() == destination->id());
|
||||
|
||||
@@ -1369,76 +1369,119 @@ void Node::CopyInputs(const Node *source, Node *destination, bool include_connec
|
||||
// passthroughs correctly.
|
||||
Q_ASSERT(destination->HasInputWithID(input));
|
||||
|
||||
CopyInput(source, destination, input, include_connections, true);
|
||||
CopyInput(source, destination, input, include_connections, true, command);
|
||||
}
|
||||
|
||||
destination->SetLabel(source->GetLabel());
|
||||
destination->SetOverrideColor(source->GetOverrideColor());
|
||||
if (command) {
|
||||
command->add_child(new NodeRenameCommand(destination, source->GetLabel()));
|
||||
} else {
|
||||
destination->SetLabel(source->GetLabel());
|
||||
}
|
||||
|
||||
if (command) {
|
||||
command->add_child(new NodeOverrideColorCommand(destination, source->GetOverrideColor()));
|
||||
} else {
|
||||
destination->SetOverrideColor(source->GetOverrideColor());
|
||||
}
|
||||
}
|
||||
|
||||
void Node::CopyInput(const Node *src, Node *dst, const QString &input, bool include_connections, bool traverse_arrays)
|
||||
void Node::CopyInput(const Node *src, Node *dst, const QString &input, bool include_connections, bool traverse_arrays, MultiUndoCommand *command)
|
||||
{
|
||||
Q_ASSERT(src->id() == dst->id());
|
||||
|
||||
CopyValuesOfElement(src, dst, input, -1);
|
||||
CopyValuesOfElement(src, dst, input, -1, command);
|
||||
|
||||
// Copy array size
|
||||
if (src->InputIsArray(input) && traverse_arrays) {
|
||||
int src_array_sz = src->InputArraySize(input);
|
||||
|
||||
for (int i=0; i<src_array_sz; i++) {
|
||||
CopyValuesOfElement(src, dst, input, i);
|
||||
CopyValuesOfElement(src, dst, input, i, command);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy connections
|
||||
if (include_connections) {
|
||||
if (traverse_arrays) {
|
||||
// Copy all connections
|
||||
for (auto it=src->input_connections().cbegin(); it!=src->input_connections().cend(); it++) {
|
||||
ConnectEdge(it->second, NodeInput(dst, input, it->first.element()));
|
||||
// Copy all connections
|
||||
for (auto it=src->input_connections().cbegin(); it!=src->input_connections().cend(); it++) {
|
||||
if (!traverse_arrays && it->first.element() != -1) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// Just copy the primary connection (at -1)
|
||||
if (src->IsInputConnected(input)) {
|
||||
ConnectEdge(src->GetConnectedOutput(input), NodeInput(dst, input));
|
||||
|
||||
auto conn_output = it->second;
|
||||
NodeInput conn_input(dst, input, it->first.element());
|
||||
|
||||
if (command) {
|
||||
command->add_child(new NodeEdgeAddCommand(conn_output, conn_input));
|
||||
} else {
|
||||
ConnectEdge(conn_output, conn_input);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Node::CopyValuesOfElement(const Node *src, Node *dst, const QString &input, int src_element, int dst_element)
|
||||
void Node::CopyValuesOfElement(const Node *src, Node *dst, const QString &input, int src_element, int dst_element, MultiUndoCommand *command)
|
||||
{
|
||||
if (dst_element >= dst->GetInternalInputArraySize(input)) {
|
||||
qDebug() << "Ignored destination element that was out of array bounds";
|
||||
return;
|
||||
}
|
||||
|
||||
NodeInput dst_input(dst, input, dst_element);
|
||||
|
||||
// Copy standard value
|
||||
dst->SetSplitStandardValue(input, src->GetSplitStandardValue(input, src_element), dst_element);
|
||||
SplitValue standard = src->GetSplitStandardValue(input, src_element);
|
||||
if (command) {
|
||||
command->add_child(new NodeParamSetSplitStandardValueCommand(dst_input, standard));
|
||||
} else {
|
||||
dst->SetSplitStandardValue(input, standard, dst_element);
|
||||
}
|
||||
|
||||
// Copy keyframes
|
||||
if (NodeInputImmediate *immediate = dst->GetImmediate(input, dst_element)) {
|
||||
immediate->delete_all_keyframes();
|
||||
if (command) {
|
||||
command->add_child(new ImmediateRemoveAllKeyframesCommand(immediate));
|
||||
} else {
|
||||
immediate->delete_all_keyframes();
|
||||
}
|
||||
}
|
||||
|
||||
foreach (const NodeKeyframeTrack& track, src->GetImmediate(input, src_element)->keyframe_tracks()) {
|
||||
foreach (NodeKeyframe* key, track) {
|
||||
key->copy(dst_element, dst);
|
||||
NodeKeyframe *copy = key->copy(dst_element, command ? nullptr : dst);
|
||||
if (command) {
|
||||
command->add_child(new NodeParamInsertKeyframeCommand(dst, copy));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy keyframing state
|
||||
if (src->IsInputKeyframable(input)) {
|
||||
dst->SetInputIsKeyframing(input, src->IsInputKeyframing(input, src_element), dst_element);
|
||||
bool is_keying = src->IsInputKeyframing(input, src_element);
|
||||
if (command) {
|
||||
command->add_child(new NodeParamSetKeyframingCommand(dst_input, is_keying));
|
||||
} else {
|
||||
dst->SetInputIsKeyframing(input, is_keying, dst_element);
|
||||
}
|
||||
}
|
||||
|
||||
// If this is the root of an array, copy the array size
|
||||
if (src_element == -1 && dst_element == -1) {
|
||||
dst->ArrayResizeInternal(input, src->InputArraySize(input));
|
||||
int array_sz = src->InputArraySize(input);
|
||||
if (command) {
|
||||
command->add_child(new Node::ArrayResizeCommand(dst, input, array_sz));
|
||||
} else {
|
||||
dst->ArrayResizeInternal(input, array_sz);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy value hint
|
||||
dst->SetValueHintForInput(input, src->GetValueHintForInput(input, src_element), dst_element);
|
||||
Node::ValueHint vh = src->GetValueHintForInput(input, src_element);
|
||||
if (command) {
|
||||
command->add_child(new NodeSetValueHintCommand(dst_input, vh));
|
||||
} else {
|
||||
dst->SetValueHintForInput(input, vh, dst_element);
|
||||
}
|
||||
}
|
||||
|
||||
bool Node::CanBeDeleted() const
|
||||
@@ -2151,4 +2194,25 @@ void NodeSetPositionAndDependenciesRecursivelyCommand::move_recursively(Node *no
|
||||
}
|
||||
}
|
||||
|
||||
void Node::ImmediateRemoveAllKeyframesCommand::prepare()
|
||||
{
|
||||
for (const NodeKeyframeTrack& track : immediate_->keyframe_tracks()) {
|
||||
keys_.append(track);
|
||||
}
|
||||
}
|
||||
|
||||
void Node::ImmediateRemoveAllKeyframesCommand::redo()
|
||||
{
|
||||
for (auto it=keys_.cbegin(); it!=keys_.cend(); it++) {
|
||||
(*it)->setParent(&memory_manager_);
|
||||
}
|
||||
}
|
||||
|
||||
void Node::ImmediateRemoveAllKeyframesCommand::undo()
|
||||
{
|
||||
for (auto it=keys_.crbegin(); it!=keys_.crend(); it++) {
|
||||
(*it)->setParent(&memory_manager_);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+30
-5
@@ -834,14 +834,14 @@ public:
|
||||
*
|
||||
* Nodes must be of the same types (i.e. have the same ID)
|
||||
*/
|
||||
static void CopyInputs(const Node *source, Node* destination, bool include_connections = true);
|
||||
static void CopyInputs(const Node *source, Node* destination, bool include_connections = true, MultiUndoCommand *command = nullptr);
|
||||
|
||||
static void CopyInput(const Node *src, Node* dst, const QString& input, bool include_connections, bool traverse_arrays);
|
||||
static void CopyInput(const Node *src, Node* dst, const QString& input, bool include_connections, bool traverse_arrays, MultiUndoCommand *command);
|
||||
|
||||
static void CopyValuesOfElement(const Node* src, Node* dst, const QString& input, int src_element, int dst_element);
|
||||
static void CopyValuesOfElement(const Node* src, Node* dst, const QString& input, int element)
|
||||
static void CopyValuesOfElement(const Node* src, Node* dst, const QString& input, int src_element, int dst_element, MultiUndoCommand *command = nullptr);
|
||||
static void CopyValuesOfElement(const Node* src, Node* dst, const QString& input, int element, MultiUndoCommand *command = nullptr)
|
||||
{
|
||||
return CopyValuesOfElement(src, dst, input, element, element);
|
||||
return CopyValuesOfElement(src, dst, input, element, element, command);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1265,6 +1265,31 @@ private:
|
||||
int array_size;
|
||||
};
|
||||
|
||||
class ImmediateRemoveAllKeyframesCommand : public UndoCommand
|
||||
{
|
||||
public:
|
||||
ImmediateRemoveAllKeyframesCommand(NodeInputImmediate *immediate) :
|
||||
immediate_(immediate)
|
||||
{}
|
||||
|
||||
virtual Project* GetRelevantProject() const override { return nullptr; }
|
||||
|
||||
protected:
|
||||
virtual void prepare() override;
|
||||
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
NodeInputImmediate *immediate_;
|
||||
|
||||
QObject memory_manager_;
|
||||
|
||||
QVector<NodeKeyframe*> keys_;
|
||||
|
||||
};
|
||||
|
||||
NodeInputImmediate* CreateImmediate(const QString& input);
|
||||
|
||||
NodeInputImmediate* GetImmediate(const QString& input, int element) const;
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "nodeparamview.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QMessageBox>
|
||||
#include <QScrollArea>
|
||||
#include <QScrollBar>
|
||||
#include <QSplitter>
|
||||
@@ -594,9 +595,82 @@ bool NodeParamView::Paste()
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Pasting nodes
|
||||
ProjectSerializer::Result res = ProjectSerializer::Paste(QStringLiteral("nodes"));
|
||||
if (res.GetLoadedNodes().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
// Determine if any nodes of this type are already in the editor
|
||||
QMap<Node*, Node*> existing_nodes;
|
||||
for (Node *n : res.GetLoadedNodes()) {
|
||||
if (Node *existing = GetNodeWithID(n->id())) {
|
||||
if (!existing_nodes.contains(existing)) {
|
||||
existing_nodes.insert(existing, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QVector<Node*> nodes_to_paste_as_new = res.GetLoadedNodes();
|
||||
MultiUndoCommand *command = new MultiUndoCommand();
|
||||
|
||||
if (!existing_nodes.empty()) {
|
||||
QMessageBox b(this);
|
||||
b.setWindowTitle(tr("Paste Nodes"));
|
||||
|
||||
QStringList node_names;
|
||||
for (auto it=existing_nodes.cbegin(); it!=existing_nodes.cend(); it++) {
|
||||
node_names.append(it.key()->GetLabelAndName());
|
||||
}
|
||||
|
||||
b.setText(tr("The following node types already exist in this context:\n\n"
|
||||
"%1\n\n"
|
||||
"Do you wish to paste values onto the existing nodes or paste new nodes?").arg(node_names.join('\n')));
|
||||
|
||||
auto as_vals = b.addButton(tr("Paste As Values"), QMessageBox::YesRole);
|
||||
auto as_nodes = b.addButton(tr("Paste As Nodes"), QMessageBox::NoRole);
|
||||
auto cancel_btn = b.addButton(QMessageBox::Cancel);
|
||||
|
||||
Q_UNUSED(as_nodes)
|
||||
|
||||
b.exec();
|
||||
|
||||
if (b.clickedButton() == cancel_btn) {
|
||||
|
||||
// Delete pasted nodes and clear array so no later code runs
|
||||
qDeleteAll(nodes_to_paste_as_new);
|
||||
nodes_to_paste_as_new.clear();
|
||||
|
||||
} else if (b.clickedButton() == as_vals) {
|
||||
|
||||
// Filter out existing nodes
|
||||
for (auto it=existing_nodes.cbegin(); it!=existing_nodes.cend(); it++) {
|
||||
Node::CopyInputs(it.value(), it.key(), false, command);
|
||||
nodes_to_paste_as_new.removeOne(it.value());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (!nodes_to_paste_as_new.isEmpty()) {
|
||||
Node::PositionMap map;
|
||||
|
||||
for (auto it=res.GetLoadData().properties.cbegin(); it!=res.GetLoadData().properties.cend(); it++) {
|
||||
if (nodes_to_paste_as_new.contains(it.key())) {
|
||||
Node::Position pos;
|
||||
|
||||
const QMap<QString, QString> &node_props = it.value();
|
||||
pos.position.setX(node_props.value(QStringLiteral("x")).toDouble());
|
||||
pos.position.setY(node_props.value(QStringLiteral("y")).toDouble());
|
||||
pos.expanded = node_props.value(QStringLiteral("expanded")).toDouble();
|
||||
|
||||
map.insert(it.key(), pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NodeParamView::UpdateItemTime(const rational &time)
|
||||
|
||||
@@ -27,9 +27,8 @@ namespace olive {
|
||||
|
||||
NodeParamSetKeyframingCommand::NodeParamSetKeyframingCommand(const NodeInput &input, bool setting) :
|
||||
input_(input),
|
||||
setting_(setting)
|
||||
new_setting_(setting)
|
||||
{
|
||||
Q_ASSERT(setting != input_.IsKeyframing());
|
||||
}
|
||||
|
||||
Project *NodeParamSetKeyframingCommand::GetRelevantProject() const
|
||||
@@ -39,12 +38,13 @@ Project *NodeParamSetKeyframingCommand::GetRelevantProject() const
|
||||
|
||||
void NodeParamSetKeyframingCommand::redo()
|
||||
{
|
||||
input_.node()->SetInputIsKeyframing(input_, setting_);
|
||||
old_setting_ = input_.IsKeyframing();
|
||||
input_.node()->SetInputIsKeyframing(input_, new_setting_);
|
||||
}
|
||||
|
||||
void NodeParamSetKeyframingCommand::undo()
|
||||
{
|
||||
input_.node()->SetInputIsKeyframing(input_, !setting_);
|
||||
input_.node()->SetInputIsKeyframing(input_, old_setting_);
|
||||
}
|
||||
|
||||
NodeParamSetKeyframeValueCommand::NodeParamSetKeyframeValueCommand(NodeKeyframe* key, const QVariant& value) :
|
||||
|
||||
@@ -41,7 +41,8 @@ protected:
|
||||
|
||||
private:
|
||||
NodeInput input_;
|
||||
bool setting_;
|
||||
bool new_setting_;
|
||||
bool old_setting_;
|
||||
|
||||
};
|
||||
|
||||
@@ -145,6 +146,43 @@ private:
|
||||
|
||||
};
|
||||
|
||||
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:
|
||||
|
||||
@@ -112,18 +112,6 @@ Project *NodeAddCommand::GetRelevantProject() const
|
||||
return dynamic_cast<Project*>(graph_);
|
||||
}
|
||||
|
||||
NodeCopyInputsCommand::NodeCopyInputsCommand(const Node *src, Node *dest, bool include_connections) :
|
||||
src_(src),
|
||||
dest_(dest),
|
||||
include_connections_(include_connections)
|
||||
{
|
||||
}
|
||||
|
||||
void NodeCopyInputsCommand::redo()
|
||||
{
|
||||
Node::CopyInputs(src_, dest_, include_connections_);
|
||||
}
|
||||
|
||||
void NodeRemoveAndDisconnectCommand::prepare()
|
||||
{
|
||||
command_ = new MultiUndoCommand();
|
||||
|
||||
@@ -193,28 +193,6 @@ private:
|
||||
|
||||
};
|
||||
|
||||
class NodeCopyInputsCommand : public UndoCommand {
|
||||
public:
|
||||
NodeCopyInputsCommand(const Node* src,
|
||||
Node* dest,
|
||||
bool include_connections);
|
||||
|
||||
virtual Project* GetRelevantProject() const override {return nullptr;}
|
||||
|
||||
protected:
|
||||
virtual void redo() override;
|
||||
|
||||
virtual void undo() override {}
|
||||
|
||||
private:
|
||||
const Node* src_;
|
||||
|
||||
Node* dest_;
|
||||
|
||||
bool include_connections_;
|
||||
|
||||
};
|
||||
|
||||
class NodeLinkCommand : public UndoCommand {
|
||||
public:
|
||||
NodeLinkCommand(Node* a, Node* b, bool link) :
|
||||
@@ -324,6 +302,10 @@ 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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user