/*** 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 "nodeviewscene.h" #include "nodeviewedge.h" #include "nodeviewitem.h" OLIVE_NAMESPACE_ENTER NodeViewScene::NodeViewScene(QObject *parent) : QGraphicsScene(parent), graph_(nullptr), direction_(NodeViewCommon::kLeftToRight) { } void NodeViewScene::SetFlowDirection(NodeViewCommon::FlowDirection direction) { direction_ = direction; { // Iterate over node items setting 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()); } } { // Iterate over edge items setting direction QHash::const_iterator i; for (i=edge_map_.constBegin(); i!=edge_map_.constEnd(); i++) { i.value()->SetFlowDirection(direction_); } } } void NodeViewScene::clear() { // Deselect everything (prevents signals that a selection has changed after deleting an object) DeselectAll(); // HACK: QGraphicsScene contains some sort of internal caching of the selected items which doesn't update unless // we call a function like this. That means even though we deselect all items above, QGraphicsScene will // continue to incorrectly signal selectionChanged() when items that were selected (but are now not) get // deleted. Calling this function appears to update the internal cache and prevent this. selectedItems(); { QHash::const_iterator i; for (i=item_map_.begin();i!=item_map_.end();i++) { delete i.value(); } item_map_.clear(); } { QHash::const_iterator i; for (i=edge_map_.begin();i!=edge_map_.end();i++) { delete i.value(); } edge_map_.clear(); } } void NodeViewScene::SelectAll() { QList all_items = this->items(); foreach (QGraphicsItem* i, all_items) { i->setSelected(true); } } void NodeViewScene::DeselectAll() { QList selected_items = this->selectedItems(); foreach (QGraphicsItem* i, selected_items) { i->setSelected(false); } } NodeViewItem *NodeViewScene::NodeToUIObject(Node *n) { return item_map_.value(n); } NodeViewEdge *NodeViewScene::EdgeToUIObject(NodeEdgePtr n) { return edge_map_.value(n.get()); } void NodeViewScene::SetGraph(NodeGraph *graph) { graph_ = graph; } QList NodeViewScene::GetSelectedNodes() const { QHash::const_iterator iterator; QList selected; for (iterator=item_map_.begin();iterator!=item_map_.end();iterator++) { if (iterator.value()->isSelected()) { selected.append(iterator.key()); } } return selected; } QList NodeViewScene::GetSelectedItems() const { QHash::const_iterator iterator; QList selected; for (iterator=item_map_.begin();iterator!=item_map_.end();iterator++) { if (iterator.value()->isSelected()) { selected.append(iterator.value()); } } return selected; } QList NodeViewScene::GetSelectedEdges() const { QList edges; QHash::const_iterator i; for (i=edge_map_.constBegin(); i!=edge_map_.constEnd(); i++) { if (i.value()->isSelected()) { edges.append(i.key()); } } return edges; } const QHash &NodeViewScene::item_map() const { return item_map_; } const QHash &NodeViewScene::edge_map() const { return edge_map_; } void NodeViewScene::AddNode(Node* node) { NodeViewItem* item = new NodeViewItem(); item->SetFlowDirection(direction_); item->SetNode(node); addItem(item); item_map_.insert(node, 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); } } } connect(node, &Node::PositionChanged, this, &NodeViewScene::NodePositionChanged); connect(node, &Node::LabelChanged, this, &NodeViewScene::NodeLabelChanged); } void NodeViewScene::RemoveNode(Node *node) { disconnect(node, &Node::LabelChanged, this, &NodeViewScene::NodeLabelChanged); disconnect(node, &Node::PositionChanged, this, &NodeViewScene::NodePositionChanged); delete item_map_.take(node); } void NodeViewScene::AddEdge(NodeEdgePtr edge) { NodeViewEdge* edge_ui = new NodeViewEdge(); edge_ui->SetEdge(edge); edge_ui->SetFlowDirection(direction_); addItem(edge_ui); edge_map_.insert(edge.get(), edge_ui); } void NodeViewScene::RemoveEdge(NodeEdgePtr edge) { delete edge_map_.take(edge.get()); } Qt::Orientation NodeViewScene::GetFlowOrientation() const { return NodeViewCommon::GetFlowOrientation(direction_); } NodeViewCommon::FlowDirection NodeViewScene::GetFlowDirection() const { return direction_; } void NodeViewScene::ReorganizeFrom(Node* n) { QList immediates = n->GetImmediateDependencies(); if (immediates.isEmpty()) { // Nothing to do return; } QPointF parent_pos = n->GetPosition(); qreal child_x = parent_pos.x() - 1.0; qreal children_height = immediates.size()-1; qreal children_y = parent_pos.y() - children_height * 0.5; for (int i=0;iSetPosition(QPointF(child_x, children_y + i)); ReorganizeFrom(immediates.at(i)); } } void NodeViewScene::NodePositionChanged(const QPointF &pos) { item_map_.value(static_cast(sender()))->SetNodePosition(pos); } void NodeViewScene::NodeLabelChanged() { item_map_.value(static_cast(sender()))->update(); } OLIVE_NAMESPACE_EXIT