diff --git a/app/node/graph.cpp b/app/node/graph.cpp index 972310906..3710c5241 100644 --- a/app/node/graph.cpp +++ b/app/node/graph.cpp @@ -75,6 +75,18 @@ void NodeGraph::childEvent(QChildEvent *event) emit NodeRemoved(node); emit node->RemovedFromGraph(this); + for (auto it=position_map_.begin(); it!=position_map_.end(); it++) { + PositionMap &map = it.value(); + for (auto jt=map.begin(); jt!=map.end(); ) { + if (jt.key() == node) { + jt = map.erase(jt); + emit NodePositionRemoved(node, it.key()); + } else { + jt++; + } + } + } + } } } diff --git a/app/node/graph.h b/app/node/graph.h index ecb1073a9..74f300622 100644 --- a/app/node/graph.h +++ b/app/node/graph.h @@ -63,6 +63,39 @@ public: return default_nodes_; } + bool NodeMapContainsNode(Node* node, void* relative) const + { + return position_map_.value(relative).contains(node); + } + + QPointF GetNodePosition(Node* node, void* relative) + { + return position_map_.value(relative).value(node); + } + + void SetNodePosition(Node* node, void* relative, const QPointF& pos) + { + position_map_[relative].insert(node, pos); + emit NodePositionAdded(node, relative, pos); + } + + void RemoveNodePosition(Node* node, void* relative) + { + PositionMap& map = position_map_[relative]; + map.remove(node); + if (map.isEmpty()) { + position_map_.remove(relative); + } + emit NodePositionRemoved(node, relative);; + } + + using PositionMap = QMap; + + const PositionMap &GetNodesForRelative(void *relative) + { + return position_map_[relative]; + } + signals: /** * @brief Signal emitted when a Node is added to the graph @@ -80,6 +113,10 @@ signals: void ValueChanged(const NodeInput& input); + void NodePositionAdded(Node *node, void *relative, const QPointF &position); + + void NodePositionRemoved(Node *node, void *relative); + protected: void AddDefaultNode(Node* n) { @@ -93,6 +130,10 @@ private: QVector default_nodes_; + QMap position_map_; + + PositionMap root_position_map_; + }; } diff --git a/app/node/node.cpp b/app/node/node.cpp index a33ae58bd..14988edf2 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -91,20 +91,6 @@ void Node::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, uint versi LoadInput(reader, xml_node_data, cancelled); } else if (reader->name() == QStringLiteral("ptr")) { xml_node_data.node_ptrs.insert(reader->readElementText().toULongLong(), this); - } else if (reader->name() == QStringLiteral("pos")) { - QPointF p; - - while (XMLReadNextStartElement(reader)) { - if (reader->name() == QStringLiteral("x")) { - p.setX(reader->readElementText().toDouble()); - } else if (reader->name() == QStringLiteral("y")) { - p.setY(reader->readElementText().toDouble()); - } else { - reader->skipCurrentElement(); - } - } - - SetPosition(p); } else if (reader->name() == QStringLiteral("label")) { SetLabel(reader->readElementText()); } else if (reader->name() == QStringLiteral("color")) { @@ -166,11 +152,6 @@ void Node::Save(QXmlStreamWriter *writer) const { writer->writeTextElement(QStringLiteral("ptr"), QString::number(reinterpret_cast(this))); - writer->writeStartElement(QStringLiteral("pos")); - writer->writeTextElement(QStringLiteral("x"), QString::number(GetPosition().x())); - writer->writeTextElement(QStringLiteral("y"), QString::number(GetPosition().y())); - writer->writeEndElement(); // pos - writer->writeTextElement(QStringLiteral("label"), GetLabel()); writer->writeTextElement(QStringLiteral("color"), QString::number(override_color_)); @@ -1483,7 +1464,6 @@ void Node::CopyInputs(const Node *source, Node *destination, bool include_connec CopyInput(source, destination, input, include_connections, true); } - destination->SetPosition(source->GetPosition()); destination->SetLabel(source->GetLabel()); destination->SetOverrideColor(source->GetOverrideColor()); } @@ -1832,29 +1812,6 @@ QVariant Node::PtrToValue(void *ptr) return reinterpret_cast(ptr); } -const QPointF &Node::GetPosition() const -{ - return position_; -} - -void Node::SetPosition(const QPointF &pos, bool move_dependencies_relatively_too) -{ - QPointF old_pos = position_; - - position_ = pos; - - emit PositionChanged(position_); - - if (move_dependencies_relatively_too) { - QPointF difference = pos - old_pos; - - for (auto it=input_connections_.cbegin(); it!=input_connections_.cend(); it++) { - Node* c = it->second.node(); - c->SetPosition(c->GetPosition() + difference, true); - } - } -} - void Node::ParameterValueChanged(const QString& input, int element, const TimeRange& range) { UpdateLastChangedTime(); @@ -2303,7 +2260,7 @@ void NodeSetPositionAndShiftSurroundingsCommand::redo() { if (commands_.isEmpty()) { // Move first node - NodeSetPositionCommand* set_pos_command = new NodeSetPositionCommand(node_, position_, move_dependencies_); + NodeSetPositionCommand* set_pos_command = new NodeSetPositionCommand(node_, relative_, position_, move_dependencies_); set_pos_command->redo(); commands_.append(set_pos_command); @@ -2312,18 +2269,19 @@ void NodeSetPositionAndShiftSurroundingsCommand::redo() // Start moving other nodes foreach (Node* surrounding, node_->parent()->nodes()) { - if (bounding_rect.contains(surrounding->GetPosition()) && surrounding != node_) { - QPointF new_pos = surrounding->GetPosition(); + QPointF surrounding_position = node_->parent()->GetNodePosition(surrounding, relative_); + if (bounding_rect.contains(surrounding_position) && surrounding != node_) { + QPointF new_pos = surrounding_position; qreal move_rate = 0.50; - if (surrounding->GetPosition().y() < position_.y()) { + if (surrounding_position.y() < position_.y()) { move_rate = -move_rate; } new_pos.setY(new_pos.y() + move_rate); - auto sur_command = new NodeSetPositionAndShiftSurroundingsCommand(surrounding, new_pos, true); + auto sur_command = new NodeSetPositionAndShiftSurroundingsCommand(surrounding, relative_, new_pos, true); sur_command->redo(); commands_.append(sur_command); } @@ -2335,4 +2293,66 @@ void NodeSetPositionAndShiftSurroundingsCommand::redo() } } +void NodeSetPositionCommand::redo() +{ + NodeGraph* graph = node_->parent(); + if (!(added_ = !graph->NodeMapContainsNode(node_, relevant_))) { + old_pos_ = graph->GetNodePosition(node_, relevant_); + } + graph->SetNodePosition(node_, relevant_, pos_); +} + +void NodeSetPositionCommand::undo() +{ + NodeGraph* graph = node_->parent(); + if (added_) { + graph->RemoveNodePosition(node_, relevant_); + } else { + graph->SetNodePosition(node_, relevant_, old_pos_); + } +} + +void NodeSetPositionAsChildCommand::redo() +{ + if (!sub_command_) { + // Calculate position of node + NodeGraph *graph = parent_->parent(); + QPointF pos = graph->GetNodePosition(parent_, relative_); + + // This is a dependency, so we'll place it one X before + pos.setX(pos.x() - 1); + + // The Y will be calculated using the index and child count + pos.setY(pos.y() - (double(child_count_)*0.5) + this_index_ + 0.5); + + sub_command_ = new MultiUndoCommand(); + if (shift_surroundings_) { + if (relative_) { + sub_command_->add_child(new NodeSetPositionAndShiftSurroundingsCommand(node_, relative_, pos, true)); + } + sub_command_->add_child(new NodeSetPositionAndShiftSurroundingsCommand(node_, nullptr, pos, true)); + } else { + if (relative_) { + sub_command_->add_child(new NodeSetPositionCommand(node_, relative_, pos, true)); + } + sub_command_->add_child(new NodeSetPositionCommand(node_, nullptr, pos, true)); + } + } + + sub_command_->redo(); +} + +void NodeSetPositionToOffsetOfAnotherNodeCommand::redo() +{ + NodeGraph *graph = node_->parent(); + old_pos_ = graph->GetNodePosition(node_, relative_); + graph->SetNodePosition(node_, relative_, graph->GetNodePosition(other_node_, relative_) + offset_); +} + +void NodeSetPositionToOffsetOfAnotherNodeCommand::undo() +{ + NodeGraph *graph = node_->parent(); + graph->SetNodePosition(node_, relative_, old_pos_); +} + } diff --git a/app/node/node.h b/app/node/node.h index 71e6e78cd..9778c688e 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -718,10 +718,6 @@ public: */ virtual NodeValueTable Value(const QString &output, NodeValueDatabase& value) const; - const QPointF& GetPosition() const; - - void SetPosition(const QPointF& pos, bool move_dependencies_relatively_too = false); - virtual bool HasGizmos() const; virtual void DrawGizmos(NodeValueDatabase& db, QPainter* p); @@ -1180,11 +1176,6 @@ private: */ bool can_be_deleted_; - /** - * @brief UI position for NodeViews - */ - QPointF position_; - /** * @brief Custom user label for node */ @@ -1312,35 +1303,29 @@ using NodePtr = std::shared_ptr; class NodeSetPositionCommand : public UndoCommand { public: - NodeSetPositionCommand(Node* node, const QPointF& position, bool move_dependencies_relatively) : - node_(node), - new_pos_(position), - move_deps_(move_dependencies_relatively) + NodeSetPositionCommand(Node* node, void* relevant, const QPointF& pos, bool move_dependencies_relatively) { + node_ = node; + relevant_ = relevant; + pos_ = pos; + move_deps_ = move_dependencies_relatively; } - virtual Project * GetRelevantProject() const override + virtual Project* GetRelevantProject() const override { return node_->project(); } - virtual void redo() override - { - old_pos_ = node_->GetPosition(); - node_->SetPosition(new_pos_, move_deps_); - } + virtual void redo() override; - virtual void undo() override - { - node_->SetPosition(old_pos_, move_deps_); - } + virtual void undo() override; private: Node* node_; - - QPointF new_pos_; + void* relevant_; + QPointF pos_; QPointF old_pos_; - + bool added_; bool move_deps_; }; @@ -1348,8 +1333,9 @@ private: class NodeSetPositionAndShiftSurroundingsCommand : public UndoCommand { public: - NodeSetPositionAndShiftSurroundingsCommand(Node* node, const QPointF& pos, bool move_dependencies_relatively) : + NodeSetPositionAndShiftSurroundingsCommand(Node* node, void *relative, const QPointF& pos, bool move_dependencies_relatively) : node_(node), + relative_(relative), position_(pos), move_dependencies_(move_dependencies_relatively) {} @@ -1376,6 +1362,8 @@ public: private: Node* node_; + void *relative_; + QPointF position_; bool move_dependencies_; @@ -1387,9 +1375,10 @@ private: class NodeSetPositionAsChildCommand : public UndoCommand { public: - NodeSetPositionAsChildCommand(Node* node, Node* parent, int this_index, int child_count, bool shift_surroundings) : + NodeSetPositionAsChildCommand(Node* node, Node* parent, void *relative, int this_index, int child_count, bool shift_surroundings) : node_(node), parent_(parent), + relative_(relative), this_index_(this_index), child_count_(child_count), shift_surroundings_(shift_surroundings), @@ -1407,27 +1396,7 @@ public: return node_->project(); } - virtual void redo() override - { - if (!sub_command_) { - // Calculate position of node - QPointF pos = parent_->GetPosition(); - - // This is a dependency, so we'll place it one X before - pos.setX(pos.x() - 1); - - // The Y will be calculated using the index and child count - pos.setY(pos.y() - (double(child_count_)*0.5) + this_index_ + 0.5); - - if (shift_surroundings_) { - sub_command_ = new NodeSetPositionAndShiftSurroundingsCommand(node_, pos, true); - } else { - sub_command_ = new NodeSetPositionCommand(node_, pos, true); - } - } - - sub_command_->redo(); - } + virtual void redo() override; virtual void undo() override { @@ -1437,22 +1406,24 @@ public: private: Node* node_; Node* parent_; + void *relative_; int this_index_; int child_count_; bool shift_surroundings_; - UndoCommand* sub_command_; + MultiUndoCommand* sub_command_; }; class NodeSetPositionToOffsetOfAnotherNodeCommand : public UndoCommand { public: - NodeSetPositionToOffsetOfAnotherNodeCommand(Node* node, Node* other_node, const QPointF& offset) : + NodeSetPositionToOffsetOfAnotherNodeCommand(Node* node, Node* other_node, void *relative, const QPointF& offset) : node_(node), other_node_(other_node), + relative_(relative), offset_(offset) {} @@ -1461,20 +1432,16 @@ public: return node_->project(); } - virtual void redo() override - { - node_->SetPosition(other_node_->GetPosition() + offset_); - } + virtual void redo() override; - virtual void undo() override - { - node_->SetPosition(other_node_->GetPosition() - offset_); - } + virtual void undo() override; private: Node* node_; Node* other_node_; + void *relative_; QPointF offset_; + QPointF old_pos_; }; diff --git a/app/node/project/folder/folder.cpp b/app/node/project/folder/folder.cpp index dc07f574e..784d4e376 100644 --- a/app/node/project/folder/folder.cpp +++ b/app/node/project/folder/folder.cpp @@ -137,9 +137,8 @@ void FolderAddChild::redo() Node::ConnectEdge(child_, NodeInput(folder_, Folder::kChildInput, array_index)); if (autoposition_) { - old_position_ = child_->GetPosition(); if (!position_command_) { - position_command_ = new NodeSetPositionAsChildCommand(child_, folder_, array_index, array_index+1, true); + position_command_ = new NodeSetPositionAsChildCommand(child_, folder_, folder_->project(), array_index, array_index+1, true); } position_command_->redo(); } diff --git a/app/node/project/folder/folder.h b/app/node/project/folder/folder.h index 3e96cb293..fa21bc4c7 100644 --- a/app/node/project/folder/folder.h +++ b/app/node/project/folder/folder.h @@ -180,8 +180,6 @@ private: bool autoposition_; - QPointF old_position_; - NodeSetPositionAsChildCommand* position_command_; }; diff --git a/app/node/project/project.cpp b/app/node/project/project.cpp index ab4717e7c..35a8b485b 100644 --- a/app/node/project/project.cpp +++ b/app/node/project/project.cpp @@ -42,14 +42,14 @@ Project::Project() : // Adds a color manager "node" to this project so that it synchronizes color_manager_ = new ColorManager(); color_manager_->setParent(this); - color_manager_->SetPosition(QPointF(1, 0)); + SetNodePosition(color_manager_, this, QPointF(1, 0)); color_manager_->SetCanBeDeleted(false); AddDefaultNode(color_manager_); // Same with project settings settings_ = new ProjectSettingsNode(); settings_->setParent(this); - settings_->SetPosition(QPointF(2, 0)); + SetNodePosition(settings_, this, QPointF(2, 0)); settings_->SetCanBeDeleted(false); AddDefaultNode(settings_); @@ -58,6 +58,7 @@ Project::Project() : root_->setParent(this); root_->SetLabel(tr("Root")); root_->SetCanBeDeleted(false); + SetNodePosition(root_, this, QPointF(0, 0)); connect(color_manager(), &ColorManager::ValueChanged, this, &Project::ColorManagerValueChanged); diff --git a/app/panel/node/node.h b/app/panel/node/node.h index 999626b7c..02600ec65 100644 --- a/app/panel/node/node.h +++ b/app/panel/node/node.h @@ -40,9 +40,14 @@ public: return node_view_->GetGraph(); } - void SetGraph(NodeGraph *graph) + void SetGraph(NodeGraph *graph, const QVector &nodes) { - node_view_->SetGraph(graph); + node_view_->SetGraph(graph, nodes); + } + + void ClearGraph() + { + node_view_->ClearGraph(); } virtual void SelectAll() override @@ -106,20 +111,6 @@ public slots: node_view_->SelectWithDependencies(nodes); } - void SelectBlocks(const QVector& blocks) - { - QVector nodes(blocks.size()); - memcpy(nodes.data(), blocks.constData(), blocks.size() * sizeof(Block*)); - node_view_->SelectWithDependencies(nodes); - } - - void DeselectBlocks(const QVector& nodes) - { - Q_UNUSED(nodes) - qDebug() << "Stub"; - //node_view_->DeselectBlocks(nodes); - } - signals: void NodesSelected(const QVector& nodes); diff --git a/app/panel/timeline/timeline.h b/app/panel/timeline/timeline.h index 5a195b818..1b33ac9fb 100644 --- a/app/panel/timeline/timeline.h +++ b/app/panel/timeline/timeline.h @@ -89,6 +89,11 @@ public: void OverwriteFootageAtPlayhead(const QVector &footage); + const QVector& GetSelectedBlocks() const + { + return static_cast(GetTimeBasedWidget())->GetSelectedBlocks(); + } + protected: virtual void Retranslate() override; diff --git a/app/undo/undocommand.cpp b/app/undo/undocommand.cpp index d00a327ec..668617b65 100644 --- a/app/undo/undocommand.cpp +++ b/app/undo/undocommand.cpp @@ -24,17 +24,28 @@ namespace olive { +MultiUndoCommand::MultiUndoCommand() : + done_(false) +{ +} + void MultiUndoCommand::redo() { - for (auto it=children_.cbegin(); it!=children_.cend(); it++) { - (*it)->redo_and_set_modified(); + if (!done_) { + for (auto it=children_.cbegin(); it!=children_.cend(); it++) { + (*it)->redo_and_set_modified(); + } + done_ = true; } } void MultiUndoCommand::undo() { - for (auto it=children_.crbegin(); it!=children_.crend(); it++) { - (*it)->undo_and_set_modified(); + if (done_) { + for (auto it=children_.crbegin(); it!=children_.crend(); it++) { + (*it)->undo_and_set_modified(); + } + done_ = false; } } diff --git a/app/undo/undocommand.h b/app/undo/undocommand.h index 54f663d64..c310bf131 100644 --- a/app/undo/undocommand.h +++ b/app/undo/undocommand.h @@ -71,7 +71,7 @@ private: class MultiUndoCommand : public UndoCommand { public: - MultiUndoCommand() = default; + MultiUndoCommand(); virtual void redo() override; virtual void undo() override; @@ -99,6 +99,8 @@ public: private: std::vector children_; + bool done_; + }; } diff --git a/app/widget/nodeview/nodeview.cpp b/app/widget/nodeview/nodeview.cpp index 118583f0f..fcfe6736e 100644 --- a/app/widget/nodeview/nodeview.cpp +++ b/app/widget/nodeview/nodeview.cpp @@ -44,7 +44,8 @@ NodeView::NodeView(QWidget *parent) : create_edge_(nullptr), create_edge_dst_(nullptr), create_edge_dst_temp_expanded_(false), - filter_mode_(kFilterShowSelectedBlocks), + paste_command_(nullptr), + filter_mode_(kFilterShowSelective), scale_(1.0) { setScene(&scene_); @@ -59,57 +60,78 @@ NodeView::NodeView(QWidget *parent) : ConnectSelectionChangedSignal(); SetFlowDirection(NodeViewCommon::kTopToBottom); - - // Set massive scene rect and hide the scrollbars to create an "infinite space" effect - scene_.setSceneRect(-1000000, -1000000, 2000000, 2000000); - setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); } NodeView::~NodeView() { // Unset the current graph - SetGraph(nullptr); + ClearGraph(); } -void NodeView::SetGraph(NodeGraph *graph) +void NodeView::SetGraph(NodeGraph *graph, const QVector &nodes) { - if (graph_ == graph) { - return; - } + // Handle potentially changing graph + if (graph_ != graph) { + if (graph_) { + disconnect(graph_, &NodeGraph::NodeAdded, this, &NodeView::AddNode); + disconnect(graph_, &NodeGraph::NodeRemoved, this, &NodeView::RemoveNode); + disconnect(graph_, &NodeGraph::InputConnected, this, &NodeView::AddEdge); + disconnect(graph_, &NodeGraph::InputDisconnected, this, &NodeView::RemoveEdge); + disconnect(graph_, &NodeGraph::NodePositionAdded, this, &NodeView::AddNodePosition); + disconnect(graph_, &NodeGraph::NodePositionRemoved, this, &NodeView::RemoveNodePosition); - if (graph_) { - disconnect(graph_, &NodeGraph::NodeAdded, &scene_, &NodeViewScene::AddNode); - disconnect(graph_, &NodeGraph::NodeRemoved, &scene_, &NodeViewScene::RemoveNode); - disconnect(graph_, &NodeGraph::InputConnected, &scene_, &NodeViewScene::AddEdge); - disconnect(graph_, &NodeGraph::InputDisconnected, &scene_, &NodeViewScene::RemoveEdge); - - DeselectAll(); - - // Clear the scene of all UI objects - scene_.clear(); - } - - // Set reference to the graph - graph_ = graph; - - // If the graph is valid, add UI objects for each of its Nodes - if (graph_) { - connect(graph_, &NodeGraph::NodeAdded, &scene_, &NodeViewScene::AddNode); - connect(graph_, &NodeGraph::NodeRemoved, &scene_, &NodeViewScene::RemoveNode); - connect(graph_, &NodeGraph::InputConnected, &scene_, &NodeViewScene::AddEdge); - connect(graph_, &NodeGraph::InputDisconnected, &scene_, &NodeViewScene::RemoveEdge); - - foreach (Node* n, graph_->nodes()) { - scene_.AddNode(n); + if (filter_mode_ == kFilterShowAll) { + // Switching graphs, close all nodes + DeselectAll(); + scene_.clear(); + } } - foreach (Node* n, graph_->nodes()) { - for (auto it=n->input_connections().cbegin(); it!=n->input_connections().cend(); it++) { - scene_.AddEdge(it->second, it->first); + graph_ = graph; + + if (graph_) { + connect(graph_, &NodeGraph::NodeAdded, this, &NodeView::AddNode); + connect(graph_, &NodeGraph::NodeRemoved, this, &NodeView::RemoveNode); + connect(graph_, &NodeGraph::InputConnected, this, &NodeView::AddEdge); + connect(graph_, &NodeGraph::InputDisconnected, this, &NodeView::RemoveEdge); + connect(graph_, &NodeGraph::NodePositionAdded, this, &NodeView::AddNodePosition); + connect(graph_, &NodeGraph::NodePositionRemoved, this, &NodeView::RemoveNodePosition); + + if (filter_mode_ == kFilterShowAll) { + foreach (Node* n, graph_->nodes()) { + scene_.AddNode(n); + } + + foreach (Node* n, graph_->nodes()) { + for (auto it=n->input_connections().cbegin(); it!=n->input_connections().cend(); it++) { + scene_.AddEdge(it->second, it->first); + } + } } } } + + // Handle changing nodes + if (filter_nodes_ != nodes) { + DeselectAll(); + scene_.clear(); + + filter_nodes_ = nodes; + + foreach (void *n, filter_nodes_) { + const NodeGraph::PositionMap &map = graph_->GetNodesForRelative(n); + + for (auto it=map.cbegin(); it!=map.cend(); it++) { + NodeViewItem *item = scene_.AddNode(it.key()); + item->SetNodePosition(it.value()); + } + } + } +} + +void NodeView::ClearGraph() +{ + SetGraph(nullptr, QVector()); } void NodeView::DeleteSelected() @@ -295,15 +317,15 @@ void NodeView::Paste() return; } - MultiUndoCommand* command = new MultiUndoCommand(); + paste_command_ = new MultiUndoCommand(); - QVector pasted_nodes = PasteNodesFromClipboard(graph_, command); + QVector pasted_nodes = PasteNodesFromClipboard(graph_, paste_command_); if (!pasted_nodes.isEmpty()) { - command->add_child(new NodeViewAttachNodesToCursor(this, pasted_nodes)); + paste_command_->add_child(new NodeViewAttachNodesToCursor(this, pasted_nodes)); } - Core::instance()->undo_stack()->pushIfHasChildren(command); + paste_command_->redo(); } void NodeView::Duplicate() @@ -318,15 +340,15 @@ void NodeView::Duplicate() return; } - MultiUndoCommand* command = new MultiUndoCommand(); + paste_command_ = new MultiUndoCommand(); - QVector duplicated_nodes = Node::CopyDependencyGraph(selected, command); + QVector duplicated_nodes = Node::CopyDependencyGraph(selected, paste_command_); if (!duplicated_nodes.isEmpty()) { - command->add_child(new NodeViewAttachNodesToCursor(this, duplicated_nodes)); + paste_command_->add_child(new NodeViewAttachNodesToCursor(this, duplicated_nodes)); } - Core::instance()->undo_stack()->pushIfHasChildren(command); + paste_command_->redo(); } void NodeView::SetColorLabel(int index) @@ -346,6 +368,59 @@ void NodeView::ZoomOut() ZoomFromKeyboard(0.8); } +/*void NodeView::AddNodesToFilter(const QVector &nodes) +{ + // Determine new nodes + QVector multiple_sources; + + foreach (Node* node, nodes) { + // Node is new and being added + scene_.AddNode(node); + + QList visible = graph_->GetNodesForRelative(node); + foreach (Node* v, visible) { + if (scene_.NodeToUIObject(v)) { + multiple_sources.append(v); + } else { + NodeViewItem* item = scene_.AddNode(v); + item->SetNodePosition(graph_->GetNodePosition(v, node)); + } + } + } + + filter_nodes_.append(nodes); +} + +void NodeView::RemoveNodesFromFilter(const QVector &nodes) +{ + // Determine old nodes + foreach (Node* node, nodes) { + // Node is old and being removed + QList visible = graph_->GetNodesForRelative(node); + foreach (Node* v, visible) { + bool found = false; + + foreach (Node* n, filter_nodes_) { + if (node != n) { + QList other_deps = graph_->GetNodesForRelative(n); + + if (other_deps.contains(v)) { + found = true; + break; + } + } + } + + if (!found) { + scene_.RemoveNode(v); + } + } + + scene_.RemoveNode(node); + filter_nodes_.removeOne(node); + } +}*/ + void NodeView::keyPressEvent(QKeyEvent *event) { super::keyPressEvent(event); @@ -354,8 +429,11 @@ void NodeView::keyPressEvent(QKeyEvent *event) DetachItemsFromCursor(); // We undo the last action which SHOULD be adding the node - // FIXME: Possible danger of this not being the case? - Core::instance()->undo_stack()->undo(); + if (paste_command_) { + paste_command_->undo(); + delete paste_command_; + paste_command_ = nullptr; + } } } @@ -596,27 +674,34 @@ void NodeView::mouseReleaseEvent(QMouseEvent *event) } if (!attached_items_.isEmpty()) { + MultiUndoCommand* command = new MultiUndoCommand(); + + if (paste_command_) { + // We've already "done" this command, but MultiUndoCommand prevents "redoing" twice, so we + // add it to this command (which may have extra commands added too) so that it all gets undone + // in the same action + command->add_child(paste_command_); + paste_command_ = nullptr; + } + if (attached_items_.size() == 1) { Node* dropping_node = attached_items_.first().item->GetNode(); if (drop_edge_) { - // We have everything we need to place the node in between - MultiUndoCommand* command = new MultiUndoCommand(); - // Remove old edge command->add_child(new NodeEdgeRemoveCommand(drop_edge_->output(), drop_edge_->input())); // Place new edges command->add_child(new NodeEdgeAddCommand(drop_edge_->output(), drop_input_)); command->add_child(new NodeEdgeAddCommand(dropping_node, drop_edge_->input())); - - Core::instance()->undo_stack()->push(command); } drop_edge_ = nullptr; } DetachItemsFromCursor(); + + Core::instance()->undo_stack()->push(command); } super::mouseReleaseEvent(event); @@ -718,12 +803,12 @@ void NodeView::ShowContextMenu(const QPoint &pos) Menu* filter_menu = new Menu(tr("Filter"), &m); m.addMenu(filter_menu); - filter_menu->AddActionWithData(tr("Show All"), + filter_menu->AddActionWithData(tr("Show All Nodes"), kFilterShowAll, filter_mode_); - filter_menu->AddActionWithData(tr("Show Selected Blocks Only"), - kFilterShowSelectedBlocks, + filter_menu->AddActionWithData(tr("Show Selected"), + kFilterShowSelective, filter_mode_); connect(filter_menu, &Menu::triggered, this, &NodeView::ContextMenuFilterChanged); @@ -791,7 +876,22 @@ void NodeView::AutoPositionDescendents() void NodeView::ContextMenuFilterChanged(QAction *action) { - Q_UNUSED(action) + FilterMode mode = static_cast(action->data().toInt()); + + if (filter_mode_ != mode) { + // Store temporary graph variables + NodeGraph *graph = graph_; + QVector nodes = filter_nodes_; + + // Unset graph with current filter mode + ClearGraph(); + + // Change filter mode + filter_mode_ = mode; + + // Re-set graph with new filter mode + SetGraph(graph, nodes); + } } void NodeView::OpenSelectedNodeInViewer() @@ -804,6 +904,59 @@ void NodeView::OpenSelectedNodeInViewer() } } +void NodeView::AddNode(Node *node) +{ + if (filter_mode_ == kFilterShowAll) { + scene_.AddNode(node); + } +} + +void NodeView::RemoveNode(Node *node) +{ + if (filter_mode_ == kFilterShowAll) { + scene_.RemoveNode(node); + } +} + +void NodeView::AddEdge(const NodeOutput &output, const NodeInput &input) +{ + if (filter_mode_ == kFilterShowAll) { + scene_.AddEdge(output, input); + } +} + +void NodeView::RemoveEdge(const NodeOutput &output, const NodeInput &input) +{ + if (filter_mode_ == kFilterShowAll) { + scene_.RemoveEdge(output, input); + } +} + +void NodeView::AddNodePosition(Node *node, void *relative, const QPointF &pos) +{ + if (filter_mode_ == kFilterShowSelective) { + if (filter_nodes_.contains(relative)) { + NodeViewItem *item = scene_.item_map().value(node); + + if (!item) { + item = scene_.AddNode(node); + } + + item->SetNodePosition(pos); + } + } +} + +void NodeView::RemoveNodePosition(Node *node, void *relative) +{ + if (filter_mode_ == kFilterShowSelective) { + if (filter_nodes_.contains(relative)) { + NodeViewItem *item = scene_.item_map().value(node); + delete item; + } + } +} + void NodeView::AttachNodesToCursor(const QVector &nodes) { QVector items(nodes.size()); diff --git a/app/widget/nodeview/nodeview.h b/app/widget/nodeview/nodeview.h index 28fb3c2d9..05b0ca8a4 100644 --- a/app/widget/nodeview/nodeview.h +++ b/app/widget/nodeview/nodeview.h @@ -51,10 +51,9 @@ public: return graph_; } - /** - * @brief Sets the graph to view - */ - void SetGraph(NodeGraph* graph); + void SetGraph(NodeGraph *graph, const QVector &nodes); + + void ClearGraph(); /** * @brief Delete selected nodes from graph (user-friendly/undoable) @@ -147,17 +146,19 @@ private: NodeViewScene scene_; - QVector selected_nodes_; + MultiUndoCommand* paste_command_; - QVector selected_blocks_; + QVector selected_nodes_; enum FilterMode { kFilterShowAll, - kFilterShowSelectedBlocks + kFilterShowSelective }; FilterMode filter_mode_; + QVector filter_nodes_; + double scale_; bool create_edge_already_exists_; @@ -200,6 +201,14 @@ private slots: */ void OpenSelectedNodeInViewer(); + void AddNode(Node *node); + void RemoveNode(Node *node); + void AddEdge(const NodeOutput& output, const NodeInput& input); + void RemoveEdge(const NodeOutput& output, const NodeInput& input); + + void AddNodePosition(Node *node, void *relative, const QPointF &pos); + void RemoveNodePosition(Node *node, void *relative); + }; } diff --git a/app/widget/nodeview/nodeviewitem.cpp b/app/widget/nodeview/nodeviewitem.cpp index ef600bb61..d07fbaa9d 100644 --- a/app/widget/nodeview/nodeviewitem.cpp +++ b/app/widget/nodeview/nodeviewitem.cpp @@ -192,8 +192,6 @@ void NodeViewItem::SetNode(Node *n) node_inputs_.append(input); } } - - SetNodePosition(node_->GetPosition()); } update(); @@ -352,10 +350,6 @@ void NodeViewItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) QVariant NodeViewItem::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) { if (change == ItemPositionHasChanged && node_) { - node_->blockSignals(true); - node_->SetPosition(GetNodePosition()); - node_->blockSignals(false); - ReadjustAllEdges(); } diff --git a/app/widget/nodeview/nodeviewscene.cpp b/app/widget/nodeview/nodeviewscene.cpp index d1447239a..e389b0ce4 100644 --- a/app/widget/nodeview/nodeviewscene.cpp +++ b/app/widget/nodeview/nodeviewscene.cpp @@ -43,9 +43,6 @@ void NodeViewScene::SetFlowDirection(NodeViewCommon::FlowDirection direction) QHash::const_iterator i; for (i=item_map_.constBegin(); i!=item_map_.constEnd(); i++) { i.value()->SetFlowDirection(direction_); - - // Update position too - i.value()->SetNodePosition(i.key()->GetPosition()); } } @@ -150,7 +147,7 @@ QVector NodeViewScene::GetSelectedEdges() const return edges; } -void NodeViewScene::AddNode(Node* node) +NodeViewItem* NodeViewScene::AddNode(Node* node) { NodeViewItem* item = new NodeViewItem(); @@ -160,16 +157,16 @@ void NodeViewScene::AddNode(Node* node) addItem(item); item_map_.insert(node, item); - connect(node, &Node::PositionChanged, this, &NodeViewScene::NodePositionChanged); connect(node, &Node::LabelChanged, this, &NodeViewScene::NodeAppearanceChanged); connect(node, &Node::ColorChanged, this, &NodeViewScene::NodeAppearanceChanged); + + return item; } void NodeViewScene::RemoveNode(Node *node) { disconnect(node, &Node::ColorChanged, this, &NodeViewScene::NodeAppearanceChanged); disconnect(node, &Node::LabelChanged, this, &NodeViewScene::NodeAppearanceChanged); - disconnect(node, &Node::PositionChanged, this, &NodeViewScene::NodePositionChanged); delete item_map_.take(node); } @@ -229,6 +226,7 @@ NodeViewCommon::FlowDirection NodeViewScene::GetFlowDirection() const void NodeViewScene::ReorganizeFrom(Node* n) { + /* QVector immediates = n->GetImmediateDependencies(); if (immediates.isEmpty()) { @@ -258,6 +256,7 @@ void NodeViewScene::ReorganizeFrom(Node* n) ReorganizeFrom(i); } } + */ } void NodeViewScene::SetEdgesAreCurved(bool curved) @@ -271,12 +270,6 @@ void NodeViewScene::SetEdgesAreCurved(bool curved) } } -void NodeViewScene::NodePositionChanged(const QPointF &pos) -{ - // Update node's internal position - item_map_.value(static_cast(sender()))->SetNodePosition(pos); -} - void NodeViewScene::NodeAppearanceChanged() { // Force item to update diff --git a/app/widget/nodeview/nodeviewscene.h b/app/widget/nodeview/nodeviewscene.h index 501e691f4..272c592ce 100644 --- a/app/widget/nodeview/nodeviewscene.h +++ b/app/widget/nodeview/nodeviewscene.h @@ -88,7 +88,7 @@ public slots: * This should NEVER be called directly, only connected to a NodeGraph. To add a Node to the NodeGraph * use NodeGraph::AddNode(). */ - void AddNode(Node* node); + NodeViewItem *AddNode(Node* node); /** * @brief Slot when a Node is removed from a graph (SetGraph() connects this) @@ -122,11 +122,6 @@ private: bool curved_edges_; private slots: - /** - * @brief Receiver for whenever a node position changes - */ - void NodePositionChanged(const QPointF& pos); - /** * @brief Receiver for when a node's label has changed */ diff --git a/app/widget/timelinewidget/timelineundo.cpp b/app/widget/timelinewidget/timelineundo.cpp index b48cdd452..51625ead2 100644 --- a/app/widget/timelinewidget/timelineundo.cpp +++ b/app/widget/timelinewidget/timelineundo.cpp @@ -260,7 +260,7 @@ void TrackReplaceBlockWithGapCommand::redo() track_->ReplaceBlock(block_, our_gap_); if (!position_command_) { - position_command_ = new NodeSetPositionAsChildCommand(our_gap_, track_, our_gap_->index(), track_->Blocks().size(), true); + position_command_ = new NodeSetPositionAsChildCommand(our_gap_, track_, track_, our_gap_->index(), track_->Blocks().size(), true); } position_command_->redo(); } diff --git a/app/widget/timelinewidget/timelineundo.h b/app/widget/timelinewidget/timelineundo.h index de868f2d6..3a1a25041 100644 --- a/app/widget/timelinewidget/timelineundo.h +++ b/app/widget/timelinewidget/timelineundo.h @@ -372,7 +372,7 @@ public: // Position the block if (!position_command_) { - position_command_ = new NodeSetPositionAsChildCommand(new_block(), track, new_block()->index(), track->Blocks().size(), true); + position_command_ = new NodeSetPositionAsChildCommand(new_block(), track, nullptr, new_block()->index(), track->Blocks().size(), true); } position_command_->redo(); @@ -1198,7 +1198,7 @@ public: timeline_->ArrayAppend(); int track_total_index = timeline_->parent()->GetTracks().size(); if (!position_command_) { - position_command_ = new NodeSetPositionAsChildCommand(track_, timeline_->parent(), track_total_index, track_total_index + 1, true); + position_command_ = new NodeSetPositionAsChildCommand(track_, timeline_->parent(), timeline_->parent(), track_total_index, track_total_index + 1, true); } position_command_->redo(); Node::ConnectEdge(track_, timeline_->track_input(timeline_->ArraySize() - 1)); @@ -1385,9 +1385,9 @@ public: if (position_commands_.isEmpty()) { // Create position commands for insert and gap if necessary if (gap_) { - position_commands_.append(new NodeSetPositionAsChildCommand(gap_, track, gap_->index(), track->Blocks().size(), true)); + position_commands_.append(new NodeSetPositionAsChildCommand(gap_, track, track, gap_->index(), track->Blocks().size(), true)); } - position_commands_.append(new NodeSetPositionAsChildCommand(insert_, track, insert_->index(), track->Blocks().size(), true)); + position_commands_.append(new NodeSetPositionAsChildCommand(insert_, track, track, insert_->index(), track->Blocks().size(), true)); } } else { // Place the Block at this point @@ -1401,7 +1401,7 @@ public: track->InsertBlockAfter(insert_, ripple_remove_command_->GetInsertionIndex()); if (position_commands_.isEmpty()) { - position_commands_.append(new NodeSetPositionAsChildCommand(insert_, track, insert_->index(), track->Blocks().size(), true)); + position_commands_.append(new NodeSetPositionAsChildCommand(insert_, track, track, insert_->index(), track->Blocks().size(), true)); } } diff --git a/app/widget/timelinewidget/tool/add.cpp b/app/widget/timelinewidget/tool/add.cpp index 06412b6c8..930eec4e3 100644 --- a/app/widget/timelinewidget/tool/add.cpp +++ b/app/widget/timelinewidget/tool/add.cpp @@ -121,7 +121,7 @@ void AddTool::MouseRelease(TimelineViewMouseEvent *event) solid)); command->add_child(new NodeEdgeAddCommand(solid, NodeInput(clip, ClipBlock::kBufferIn))); - command->add_child(new NodeSetPositionToOffsetOfAnotherNodeCommand(solid, clip, extra_node_offset)); + command->add_child(new NodeSetPositionToOffsetOfAnotherNodeCommand(solid, clip, clip, extra_node_offset)); break; } case olive::Tool::kAddableTitle: @@ -132,7 +132,7 @@ void AddTool::MouseRelease(TimelineViewMouseEvent *event) text)); command->add_child(new NodeEdgeAddCommand(text, NodeInput(clip, ClipBlock::kBufferIn))); - command->add_child(new NodeSetPositionToOffsetOfAnotherNodeCommand(text, clip, extra_node_offset)); + command->add_child(new NodeSetPositionToOffsetOfAnotherNodeCommand(text, clip, clip, extra_node_offset)); break; } case olive::Tool::kAddableBars: diff --git a/app/widget/timelinewidget/tool/import.cpp b/app/widget/timelinewidget/tool/import.cpp index 205d0f00c..251e21e14 100644 --- a/app/widget/timelinewidget/tool/import.cpp +++ b/app/widget/timelinewidget/tool/import.cpp @@ -387,7 +387,9 @@ void ImportTool::DropGhosts(bool insert) clip->set_length_and_media_out(ghost->GetLength()); clip->SetLabel(footage_stream.footage->GetLabel()); command->add_child(new NodeAddCommand(dst_graph, clip)); - command->add_child(new NodeSetPositionToOffsetOfAnotherNodeCommand(clip, footage_stream.footage, QPointF(2, 0))); + command->add_child(new NodeSetPositionToOffsetOfAnotherNodeCommand(clip, footage_stream.footage, clip, QPointF(2, 0))); + + command->add_child(new NodeSetPositionCommand(footage_stream.footage, clip, QPointF(-2, 0), false)); switch (Track::Reference::TypeFromString(footage_stream.output)) { case Track::kVideo: @@ -397,7 +399,7 @@ void ImportTool::DropGhosts(bool insert) command->add_child(new NodeEdgeAddCommand(corresponding_output, NodeInput(transform, TransformDistortNode::kTextureInput))); command->add_child(new NodeEdgeAddCommand(transform, NodeInput(clip, ClipBlock::kBufferIn))); - command->add_child(new NodeSetPositionToOffsetOfAnotherNodeCommand(transform, clip, QPointF(-1, 0))); + command->add_child(new NodeSetPositionCommand(transform, clip, QPointF(-1, 0), false)); break; } case Track::kAudio: @@ -407,7 +409,7 @@ void ImportTool::DropGhosts(bool insert) command->add_child(new NodeEdgeAddCommand(corresponding_output, NodeInput(volume_node, VolumeNode::kSamplesInput))); command->add_child(new NodeEdgeAddCommand(volume_node, NodeInput(clip, ClipBlock::kBufferIn))); - command->add_child(new NodeSetPositionToOffsetOfAnotherNodeCommand(volume_node, clip, QPointF(-1, 0))); + command->add_child(new NodeSetPositionCommand(volume_node, clip, QPointF(-1, 0), false)); break; } default: diff --git a/app/window/mainwindow/mainwindow.cpp b/app/window/mainwindow/mainwindow.cpp index 8fb4a2bc7..0329bd741 100644 --- a/app/window/mainwindow/mainwindow.cpp +++ b/app/window/mainwindow/mainwindow.cpp @@ -385,7 +385,7 @@ void MainWindow::ProjectClose(Project *p) // Close project from NodeView if (node_panel_->GetGraph() == p) { - node_panel_->SetGraph(nullptr); + node_panel_->ClearGraph(); } } @@ -539,8 +539,6 @@ TimelinePanel* MainWindow::AppendTimelinePanel() connect(panel, &TimelinePanel::TimeChanged, param_panel_, &ParamPanel::SetTimestamp); connect(panel, &TimelinePanel::TimeChanged, table_panel_, &NodeTablePanel::SetTimestamp); connect(panel, &TimelinePanel::TimeChanged, sequence_viewer_panel_, &SequenceViewerPanel::SetTimestamp); - connect(panel, &TimelinePanel::BlocksSelected, node_panel_, &NodePanel::SelectBlocks); - connect(panel, &TimelinePanel::BlocksDeselected, node_panel_, &NodePanel::DeselectBlocks); connect(param_panel_, &ParamPanel::TimeChanged, panel, &TimelinePanel::SetTimestamp); connect(curve_panel_, &ParamPanel::TimeChanged, panel, &TimelinePanel::SetTimestamp); connect(sequence_viewer_panel_, &SequenceViewerPanel::TimeChanged, panel, &TimelinePanel::SetTimestamp); @@ -698,24 +696,24 @@ void MainWindow::UpdateAudioMonitorParams(ViewerOutput *viewer) void MainWindow::FocusedPanelChanged(PanelWidget *panel) { // Update audio monitor panel - TimeBasedPanel* tbp = dynamic_cast(panel); - if (tbp) { + if (TimeBasedPanel* tbp = dynamic_cast(panel)) { UpdateAudioMonitorParams(tbp->GetConnectedViewer()); } - // Signal timeline focus - TimelinePanel* timeline = dynamic_cast(panel); - if (timeline) { + if (TimelinePanel* timeline = dynamic_cast(panel)) { + // Signal timeline focus TimelineFocused(timeline->GetConnectedViewer()); - return; - } - // Signal project panel focus - ProjectPanel* project = dynamic_cast(panel); - if (project) { + NodeGraph *graph = timeline->GetConnectedViewer() ? timeline->GetConnectedViewer()->parent() : nullptr; + QVector n(timeline->GetSelectedBlocks().size()); + for (int j=0; jGetSelectedBlocks().at(j); + } + node_panel_->SetGraph(graph, n); + } else if (ProjectPanel* project = dynamic_cast(panel)) { + // Signal project panel focus UpdateTitle(); - node_panel_->SetGraph(project->project()); - return; + node_panel_->SetGraph(project->project(), {project->project()}); } }