diff --git a/app/config/config.cpp b/app/config/config.cpp index 3c818f084..2b317a51c 100644 --- a/app/config/config.cpp +++ b/app/config/config.cpp @@ -89,6 +89,7 @@ void Config::SetDefaults() SetEntryInternal(QStringLiteral("RectifiedWaveforms"), NodeParam::kBoolean, false); SetEntryInternal(QStringLiteral("DropWithoutSequenceBehavior"), NodeParam::kInt, ImportTool::kDWSAsk); SetEntryInternal(QStringLiteral("Loop"), NodeParam::kBoolean, false); + SetEntryInternal(QStringLiteral("SplitClipsCopyNodes"), NodeParam::kBoolean, true); SetEntryInternal(QStringLiteral("AutoCacheInterval"), NodeParam::kInt, 250); diff --git a/app/dialog/preferences/tabs/preferencesbehaviortab.cpp b/app/dialog/preferences/tabs/preferencesbehaviortab.cpp index 6713fe81b..3feb53035 100644 --- a/app/dialog/preferences/tabs/preferencesbehaviortab.cpp +++ b/app/dialog/preferences/tabs/preferencesbehaviortab.cpp @@ -99,6 +99,11 @@ PreferencesBehaviorTab::PreferencesBehaviorTab() AddItem(tr("Auto-Scale By Default"), QStringLiteral("AutoscaleByDefault"), node_group); + AddItem(tr("Splitting Clips Copies Dependencies"), + QStringLiteral("SplitClipsCopyNodes"), + tr("Multiple clips can share the same nodes. Disable this to automatically share node " + "dependencies among clips when copying or splitting them."), + node_group); } void PreferencesBehaviorTab::Accept() diff --git a/app/node/node.cpp b/app/node/node.cpp index c531f3c14..0abc3e69d 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -29,6 +29,7 @@ #include "project/project.h" #include "project/item/footage/footage.h" #include "project/item/footage/videostream.h" +#include "widget/nodeview/nodeviewundo.h" OLIVE_NAMESPACE_ENTER @@ -239,6 +240,66 @@ TimeRange Node::OutputTimeAdjustment(NodeInput *, const TimeRange &input_time) c return input_time; } +QVector Node::CopyDependencyGraph(const QVector &nodes, QUndoCommand* command) +{ + int nb_nodes = nodes.size(); + + QVector copies(nb_nodes); + + for (int i=0; icopy();; + + // Copy the values, but NOT the connections, since we'll be connecting to our own clones later + Node::CopyInputs(nodes.at(i), c, false); + + // Add to graph + NodeGraph* graph = static_cast(nodes.at(i)->parent()); + if (command) { + new NodeAddCommand(graph, c, command); + } else { + graph->AddNode(c); + } + + // Store in array at the same index as source + copies[i] = c; + } + + CopyDependencyGraph(nodes, copies, command); + + return copies; +} + +void Node::CopyDependencyGraph(const QVector &src, const QVector &dst, QUndoCommand *command) +{ + int nb_nodes = src.size(); + + for (int i=0; i inputs = src.at(i)->GetInputsIncludingArrays(); + + for (int j=0; jget_connected_node() == src.at(j)) { + // Found a connection + NodeOutput* copy_output = dst.at(j)->GetOutputWithID(input->get_connected_output()->id()); + NodeInput* copy_input = dst.at(i)->GetInputWithID(input->id()); + + if (command) { + new NodeEdgeAddCommand(copy_output, copy_input, command); + } else { + NodeParam::ConnectEdge(copy_output, copy_input); + } + } + } + } + } +} + void Node::SendInvalidateCache(const TimeRange &range, NodeInput *source) { // Loop through all parameters (there should be no children that are not NodeParams) diff --git a/app/node/node.h b/app/node/node.h index 003b3911d..b69dde82c 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -346,6 +346,12 @@ public: */ static void CopyInputs(Node* source, Node* destination, bool include_connections = true); + /** + * @brief Clones a set of nodes and connects the new ones the way the old ones were + */ + static QVector CopyDependencyGraph(const QVector& nodes, QUndoCommand *command); + static void CopyDependencyGraph(const QVector& src, const QVector& dst, QUndoCommand *command); + /** * @brief Return whether this Node can be deleted or not */ diff --git a/app/widget/nodeview/nodeview.cpp b/app/widget/nodeview/nodeview.cpp index 2f40b1aa3..bfc2472aa 100644 --- a/app/widget/nodeview/nodeview.cpp +++ b/app/widget/nodeview/nodeview.cpp @@ -288,43 +288,11 @@ void NodeView::Duplicate() QUndoCommand* command = new QUndoCommand(); - QList duplicated_nodes; - - foreach (Node* n, selected) { - Node* copy = n->copy(); - - Node::CopyInputs(n, copy, false); - - duplicated_nodes.append(copy); - - new NodeAddCommand(graph_, copy, command); - } - - for (int i=0;ioutput()->edges()) { - if (edge->input()->parentNode() == dst) { - new NodeEdgeAddCommand(duplicated_nodes.at(i)->output(), - duplicated_nodes.at(j)->GetInputWithID(edge->input()->id()), - command); - } - } - } - } + QVector duplicated_nodes = Node::CopyDependencyGraph(selected, command); Core::instance()->undo_stack()->pushIfHasChildren(command); - if (!duplicated_nodes.isEmpty()) { - AttachNodesToCursor(duplicated_nodes); - } + AttachNodesToCursor(duplicated_nodes); } void NodeView::ItemsChanged() diff --git a/app/widget/timelinewidget/tool/pointer.cpp b/app/widget/timelinewidget/tool/pointer.cpp index 86c501d9b..d1a6a257b 100644 --- a/app/widget/timelinewidget/tool/pointer.cpp +++ b/app/widget/timelinewidget/tool/pointer.cpp @@ -610,13 +610,23 @@ void PointerTool::FinishDrag(TimelineViewMouseEvent *event) if (duplicate_clips) { // Duplicate rather than move - Node* copy = block->copy(); + Node* copy; - new NodeAddCommand(static_cast(block->parent()), - copy, - command); + if (Config::Current()[QStringLiteral("SplitClipsCopyNodes")].toBool()) { + QVector nodes_to_clone; + nodes_to_clone.append(block); + nodes_to_clone.append(block->GetDependencies()); + QVector duplicated = Node::CopyDependencyGraph(nodes_to_clone, command); + copy = duplicated.first(); + } else { + copy = block->copy(); - new NodeCopyInputsCommand(block, copy, true, command); + new NodeAddCommand(static_cast(block->parent()), + copy, + command); + + new NodeCopyInputsCommand(block, copy, true, command); + } // Place the copy instead of the original block block = static_cast(copy); diff --git a/app/widget/timelinewidget/undo/undo.cpp b/app/widget/timelinewidget/undo/undo.cpp index e1e59abfa..36f81e8e1 100644 --- a/app/widget/timelinewidget/undo/undo.cpp +++ b/app/widget/timelinewidget/undo/undo.cpp @@ -20,6 +20,7 @@ #include "undo.h" +#include "config/config.h" #include "core.h" #include "node/block/clip/clip.h" #include "node/block/transition/transition.h" @@ -209,10 +210,21 @@ void TrackRippleRemoveAreaCommand::redo_internal() if (splice_) { // Split the block here - trim_in_ = static_cast(trim_out_->copy()); + splice_split_command_ = new QUndoCommand(); - static_cast(track_->parent())->AddNode(trim_in_); - Node::CopyInputs(trim_out_, trim_in_); + if (Config::Current()[QStringLiteral("SplitClipsCopyNodes")].toBool()) { + QVector nodes_to_clone; + nodes_to_clone.append(trim_out_); + nodes_to_clone.append(trim_out_->GetDependencies()); + QVector duplicated = Node::CopyDependencyGraph(nodes_to_clone, splice_split_command_); + trim_in_ = static_cast(duplicated.first()); + } else { + trim_in_ = static_cast(trim_out_->copy()); + new NodeAddCommand(static_cast(track_->parent()), trim_in_, splice_split_command_); + new NodeCopyInputsCommand(trim_out_, trim_in_, true, splice_split_command_); + } + + splice_split_command_->redo(); trim_out_old_length_ = trim_out_->length(); trim_out_->set_length_and_media_out(in_ - trim_out_->in()); @@ -292,7 +304,8 @@ void TrackRippleRemoveAreaCommand::undo_internal() track_->RippleRemoveBlock(trim_in_); trim_out_->set_length_and_media_out(trim_out_old_length_); - TakeNodeFromParentGraph(trim_in_, &memory_manager_); + splice_split_command_->undo(); + delete splice_split_command_; } else { @@ -428,8 +441,33 @@ void BlockSplitCommand::redo_internal() { track_->BeginOperation(); - static_cast(block_->parent())->AddNode(new_block_); - Node::CopyInputs(block_, new_block_); + NodeGraph* graph = static_cast(block_->parent()); + + add_command_ = new QUndoCommand(); + new NodeAddCommand(graph, new_block_, add_command_); + new NodeCopyInputsCommand(block_, new_block_, true, add_command_); + + if (Config::Current()[QStringLiteral("SplitClipsCopyNodes")].toBool()) { + + QVector src_nodes; + QVector dst_nodes; + + src_nodes.append(block_); + src_nodes.append(block_->GetDependencies()); + + dst_nodes.resize(src_nodes.size()); + dst_nodes[0] = new_block_; + for (int i=1; icopy(); + new NodeAddCommand(graph, dst_nodes[i], add_command_); + Node::CopyInputs(src_nodes[i], dst_nodes[i], false); + } + + Node::CopyDependencyGraph(src_nodes, dst_nodes, add_command_); + + } + + add_command_->redo(); rational new_part_length = block_->length() - (point_ - block_->in()); @@ -451,16 +489,18 @@ void BlockSplitCommand::undo_internal() { track_->BeginOperation(); - block_->set_length_and_media_out(old_length_); - track_->RippleRemoveBlock(new_block_); - - TakeNodeFromParentGraph(new_block_, &memory_manager_); - foreach (NodeInput* transition, transitions_to_move_) { NodeParam::DisconnectEdge(new_block_->output(), transition); NodeParam::ConnectEdge(block_->output(), transition); } + block_->set_length_and_media_out(old_length_); + track_->RippleRemoveBlock(new_block_); + + add_command_->undo(); + new_block_->setParent(&memory_manager_); + delete add_command_; + track_->EndOperation(); } diff --git a/app/widget/timelinewidget/undo/undo.h b/app/widget/timelinewidget/undo/undo.h index e59255f3e..b8be979b2 100644 --- a/app/widget/timelinewidget/undo/undo.h +++ b/app/widget/timelinewidget/undo/undo.h @@ -192,6 +192,7 @@ protected: rational out_; bool splice_; + QUndoCommand* splice_split_command_; Block* trim_out_; Block* trim_in_; @@ -329,17 +330,18 @@ protected: private: TrackOutput* track_; Block* block_; + Block* new_block_; rational new_length_; rational old_length_; rational point_; - Block* new_block_; - QList transitions_to_move_; QObject memory_manager_; + QUndoCommand* add_command_; + }; class TrackSplitAtTimeCommand : public UndoCommand {