/*** Olive - Non-Linear Video Editor Copyright (C) 2019 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 . ***/ #include "nodeview.h" #include "nodeviewundo.h" #include "node/factory.h" #include "undo/undostack.h" NodeView::NodeView(QWidget *parent) : QGraphicsView(parent), graph_(nullptr) { setScene(&scene_); setDragMode(RubberBandDrag); setContextMenuPolicy(Qt::CustomContextMenu); connect(&scene_, SIGNAL(changed(const QList&)), this, SLOT(ItemsChanged())); connect(&scene_, SIGNAL(selectionChanged()), this, SLOT(SceneSelectionChangedSlot())); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(ShowContextMenu(const QPoint &))); } NodeView::~NodeView() { // Unset the current graph SetGraph(nullptr); } void NodeView::SetGraph(NodeGraph *graph) { if (graph_ == graph) { return; } if (graph_ != nullptr) { disconnect(graph_, SIGNAL(NodeAdded(Node*)), this, SLOT(AddNode(Node*))); disconnect(graph_, SIGNAL(NodeRemoved(Node*)), this, SLOT(RemoveNode(Node*))); disconnect(graph_, SIGNAL(EdgeAdded(NodeEdgePtr)), this, SLOT(AddEdge(NodeEdgePtr))); disconnect(graph_, SIGNAL(EdgeRemoved(NodeEdgePtr)), this, SLOT(RemoveEdge(NodeEdgePtr))); } // 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_ != nullptr) { connect(graph_, SIGNAL(NodeAdded(Node*)), this, SLOT(AddNode(Node*))); connect(graph_, SIGNAL(NodeRemoved(Node*)), this, SLOT(RemoveNode(Node*))); connect(graph_, SIGNAL(EdgeAdded(NodeEdgePtr)), this, SLOT(AddEdge(NodeEdgePtr))); connect(graph_, SIGNAL(EdgeRemoved(NodeEdgePtr)), this, SLOT(RemoveEdge(NodeEdgePtr))); QList graph_nodes = graph_->nodes(); foreach (Node* node, graph_nodes) { AddNode(node); } } } NodeViewItem *NodeView::NodeToUIObject(QGraphicsScene *scene, Node *n) { QList graphics_items = scene->items(); for (int i=0;i(graphics_items.at(i)); if (item != nullptr) { if (item->node() == n) { return item; } } } return nullptr; } NodeViewEdge *NodeView::EdgeToUIObject(QGraphicsScene *scene, NodeEdgePtr n) { QList graphics_items = scene->items(); for (int i=0;i(graphics_items.at(i)); if (edge != nullptr) { if (edge->edge() == n) { return edge; } } } return nullptr; } NodeViewItem *NodeView::NodeToUIObject(Node *n) { return NodeToUIObject(&scene_, n); } NodeViewEdge *NodeView::EdgeToUIObject(NodeEdgePtr n) { return EdgeToUIObject(&scene_, n); } void NodeView::DeleteSelected() { if (!graph_) { return; } QList selected = scene_.selectedItems(); QList selected_nodes; foreach (QGraphicsItem* item, selected) { NodeViewItem* node_item = dynamic_cast(item); if (node_item) { selected_nodes.append(node_item->node()); } } if (selected_nodes.isEmpty()) { return; } olive::undo_stack.push(new NodeRemoveCommand(graph_, selected_nodes)); } void NodeView::AddNode(Node* node) { NodeViewItem* item = new NodeViewItem(); item->SetNode(node); scene_.addItem(item); // Add a NodeViewEdge for each connection foreach (NodeParam* param, node->parameters()) { // We only bother working with outputs since eventually this will cover all inputs too // (covering both would lead to duplicates since every edge connects to one input and one output) if (param->type() == NodeParam::kOutput) { const QVector& edges = param->edges(); foreach(NodeEdgePtr edge, edges) { AddEdge(edge); } } } } void NodeView::RemoveNode(Node *node) { NodeViewItem* item = NodeToUIObject(scene(), node); delete item; } void NodeView::AddEdge(NodeEdgePtr edge) { NodeViewEdge* edge_ui = new NodeViewEdge(); edge_ui->SetEdge(edge); scene_.addItem(edge_ui); } void NodeView::RemoveEdge(NodeEdgePtr edge) { NodeViewEdge* edge_ui = EdgeToUIObject(scene(), edge); delete edge_ui; } void NodeView::ItemsChanged() { QList items = scene_.items(); foreach (QGraphicsItem* item, items) { NodeViewEdge* edge = dynamic_cast(item); if (edge != nullptr) { edge->Adjust(); } } } void NodeView::SceneSelectionChangedSlot() { // Get the scene's selected items and convert it into a list of selected nodes QList selected_items = scene_.selectedItems(); QList selected_nodes; for (int i=0;i(selected_items.at(i)); if (item != nullptr) { selected_nodes.append(item->node()); } } emit SelectionChanged(selected_nodes); } void NodeView::ShowContextMenu(const QPoint &pos) { if (!graph_) { return; } Menu* m = new Menu(); Menu* add_menu = NodeFactory::CreateMenu(); add_menu->setTitle(tr("Add")); connect(add_menu, SIGNAL(triggered(QAction*)), this, SLOT(CreateNodeSlot(QAction*))); m->addMenu(add_menu); m->exec(mapToGlobal(pos)); } void NodeView::CreateNodeSlot(QAction *action) { Node* new_node = NodeFactory::CreateFromMenuAction(action); if (new_node) { olive::undo_stack.push(new NodeAddCommand(graph_, new_node)); } }