1 /*
2 Copyright (c) 2008-2020, Benoit AUTHEMAN All rights reserved.
3
4 Redistribution and use in source and binary forms, with or without
5 modification, are permitted provided that the following conditions are met:
6 * Redistributions of source code must retain the above copyright
7 notice, this list of conditions and the following disclaimer.
8 * Redistributions in binary form must reproduce the above copyright
9 notice, this list of conditions and the following disclaimer in the
10 documentation and/or other materials provided with the distribution.
11 * Neither the name of the author or Destrat.io nor the
12 names of its contributors may be used to endorse or promote products
13 derived from this software without specific prior written permission.
14
15 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
19 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 //-----------------------------------------------------------------------------
28 // This file is a part of the QuickQanava software library.
29 //
30 // \file qanGraph.hpp
31 // \author benoit@destrat.io
32 // \date 2017 03 19
33 //-----------------------------------------------------------------------------
34
35
36 namespace qan { // ::qan
37
38 /* Graph Factories *///--------------------------------------------------------
39 template < class Node_t >
insertNode(QQmlComponent * nodeComponent,qan::NodeStyle * nodeStyle)40 qan::Node* Graph::insertNode(QQmlComponent* nodeComponent, qan::NodeStyle* nodeStyle)
41 {
42 if (nodeComponent == nullptr) {
43 const auto engine = qmlEngine(this);
44 nodeComponent = _nodeDelegate.get(); // If no delegate component is specified, try the node type delegate() factory
45 if (nodeComponent == nullptr &&
46 engine != nullptr) // Otherwise, use default node delegate component
47 nodeComponent = Node_t::delegate(*engine);
48 }
49 if (nodeComponent == nullptr) { // Otherwise, generate a warning and create a "non visual node"
50 qWarning() << "qan::Graph::insertNode(): Can't find a valid node delegate component.";
51 return nullptr;
52 }
53 if (nodeComponent->isError()) { // If component exists, it should be instanciable
54 qWarning() << "Component error: " << nodeComponent->errors();
55 return nullptr;
56 }
57 const auto node = std::make_shared<Node_t>();
58 try {
59 QQmlEngine::setObjectOwnership(node.get(), QQmlEngine::CppOwnership);
60 if (nodeStyle == nullptr)
61 nodeStyle = Node_t::style(nullptr);
62 if (nodeStyle == nullptr)
63 throw qan::Error{"style() factory has returned a nullptr style."};
64 _styleManager.setStyleComponent(nodeStyle, nodeComponent); // nullptr nodeComponent is ok
65 qan::NodeItem* nodeItem = static_cast<qan::NodeItem*>(createFromComponent(nodeComponent,
66 *nodeStyle,
67 node.get()));
68 if (nodeItem == nullptr)
69 throw qan::Error{"Node item creation failed."};
70 nodeItem->setNode(node.get());
71 nodeItem->setGraph(this);
72 node->setItem(nodeItem);
73 auto notifyNodeClicked = [this] (qan::NodeItem* nodeItem, QPointF p) {
74 if (nodeItem != nullptr && nodeItem->getNode() != nullptr)
75 emit this->nodeClicked(nodeItem->getNode(), p);
76 };
77 connect(nodeItem, &qan::NodeItem::nodeClicked, notifyNodeClicked);
78
79 auto notifyNodeRightClicked = [this] (qan::NodeItem* nodeItem, QPointF p) {
80 if (nodeItem != nullptr && nodeItem->getNode() != nullptr)
81 emit this->nodeRightClicked(nodeItem->getNode(), p);
82 };
83 connect( nodeItem, &qan::NodeItem::nodeRightClicked, notifyNodeRightClicked );
84
85 auto notifyNodeDoubleClicked = [this] (qan::NodeItem* nodeItem, QPointF p) {
86 if (nodeItem != nullptr && nodeItem->getNode() != nullptr)
87 emit this->nodeDoubleClicked(nodeItem->getNode(), p);
88 };
89 connect(nodeItem, &qan::NodeItem::nodeDoubleClicked, notifyNodeDoubleClicked);
90 { // Send item to front
91 _maxZ += 1;
92 nodeItem->setZ(_maxZ);
93 }
94 gtpo_graph_t::insert_node(node); // Insert visual or non visual node
95 } catch (const gtpo::bad_topology_error& e) {
96 qWarning() << "qan::Graph::insertNode(): Error: Topology error: " << e.what();
97 return nullptr; // node eventually destroyed by shared_ptr
98 }
99 catch (const qan::Error& e) {
100 qWarning() << "qan::Graph::insertNode(): Error: " << e.getMsg();
101 return nullptr; // node eventually destroyed by shared_ptr
102 }
103 catch (...) {
104 qWarning() << "qan::Graph::insertNode(): Error: Topology error.";
105 return nullptr; // node eventually destroyed by shared_ptr
106 }
107 const auto nodePtr = node.get();
108 if (nodePtr != nullptr) { // Notify user.
109 onNodeInserted(*nodePtr);
110 emit nodeInserted(nodePtr);
111 }
112 return node.get();
113 }
114
115 template < class Node_t >
insertNonVisualNode()116 qan::Node* Graph::insertNonVisualNode()
117 {
118 const auto node = std::make_shared<Node_t>();
119 try {
120 QQmlEngine::setObjectOwnership( node.get(), QQmlEngine::CppOwnership );
121 gtpo_graph_t::insert_node( node );
122 } catch (const gtpo::bad_topology_error& e) {
123 qWarning() << "qan::Graph::insertNonVisualNode(): Error: Topology error:" << e.what();
124 return nullptr; // node eventually destroyed by shared_ptr
125 }
126 catch ( ... ) {
127 qWarning() << "qan::Graph::insertNonVisualNode(): Error: Topology error.";
128 return nullptr; // node eventually destroyed by share_ptr
129 }
130 const auto nodePtr = node.get();
131 if (nodePtr != nullptr) { // Notify user.
132 onNodeInserted(*nodePtr);
133 emit nodeInserted(nodePtr);
134 }
135 return node.get();
136 }
137 //-----------------------------------------------------------------------------
138
139 /* Graph Edge Management *///--------------------------------------------------
140 template < class Edge_t >
insertEdge(qan::Node & src,qan::Node * dstNode,QQmlComponent * edgeComponent)141 qan::Edge* Graph::insertEdge(qan::Node& src, qan::Node* dstNode, QQmlComponent* edgeComponent)
142 {
143 if (dstNode == nullptr)
144 return nullptr;
145 if (edgeComponent == nullptr) {
146 const auto engine = qmlEngine(this);
147 if (engine != nullptr)
148 edgeComponent = Edge_t::delegate(*engine, nullptr); // If no delegate component is specified, try the edge type delegate() factory
149 if (edgeComponent == nullptr)
150 edgeComponent = _edgeDelegate.get(); // Otherwise, use default edge delegate component
151 }
152 if (edgeComponent == nullptr) { // Otherwise, throw an error, a visual edge must have a delegate
153 qWarning() << "qan::Graph::insertEdge<>(): Error: Can't find a valid edge delegate component.";
154 return nullptr;
155 }
156 const auto style = qobject_cast<qan::EdgeStyle*>(Edge_t::style(nullptr));
157 if (style == nullptr) {
158 qWarning() << "qan::Graph::insertEdge(): Error: style() factory has returned a nullptr style.";
159 return nullptr;
160 }
161 qan::Edge* configuredEdge = nullptr;
162 try {
163 auto edge = std::make_shared<Edge_t>(nullptr);
164 QQmlEngine::setObjectOwnership(edge.get(), QQmlEngine::CppOwnership);
165 if (configureEdge(*edge, *edgeComponent, *style,
166 src, dstNode)) {
167 gtpo_graph_t::insert_edge(edge);
168 configuredEdge = edge.get();
169 } else {
170 qWarning() << "qan::Graph::insertEdge<>(): Error: Internal error during edge configuration.";
171 // Note: edge is deleted since it is unreferenced...
172 }
173 } catch (const gtpo::bad_topology_error& e) {
174 qWarning() << "qan::Graph::insertEdge<>(): Error: Topology error:" << e.what();
175 // Note: edge is cleaned automatically if it has still not been inserted to graph
176 } catch (...) {
177 qWarning() << "qan::Graph::insertEdge<>(): Error: Topology error.";
178 // Note: edge is cleaned automatically if it has still not been inserted to graph
179 }
180 if (configuredEdge != nullptr)
181 emit edgeInserted(configuredEdge);
182 return configuredEdge;
183 }
184
185 template < class Edge_t >
insertNonVisualEdge(qan::Node & src,qan::Node * dstNode)186 qan::Edge* Graph::insertNonVisualEdge( qan::Node& src, qan::Node* dstNode )
187 {
188 if ( dstNode == nullptr )
189 return nullptr;
190 auto edge = std::make_shared<Edge_t>();
191 try {
192 QQmlEngine::setObjectOwnership( edge.get(), QQmlEngine::CppOwnership );
193 edge->set_src( std::static_pointer_cast<Config::final_node_t>(src.shared_from_this()) );
194 if ( dstNode != nullptr )
195 edge->set_dst( std::static_pointer_cast<Config::final_node_t>(dstNode->shared_from_this()) );
196 gtpo_graph_t::insert_edge( edge );
197 } catch (const gtpo::bad_topology_error& e) {
198 qWarning() << "qan::Graph::insertNonVisualEdge<>(): Error: Topology error:" << e.what();
199 }
200 catch ( ... ) {
201 qWarning() << "qan::Graph::insertNonVisualEdge<>(): Error: Topology error.";
202 }
203 return edge.get();
204 }
205 //-----------------------------------------------------------------------------
206
207 /* Graph Group Management *///-------------------------------------------------
208 template <class Group_t>
insertGroup()209 qan::Group* Graph::insertGroup()
210 {
211 const auto engine = qmlEngine(this);
212 QQmlComponent* groupComponent = nullptr;
213 if (engine != nullptr)
214 groupComponent = Group_t::delegate(*engine, nullptr);
215 if (groupComponent == nullptr)
216 groupComponent = _groupDelegate.get();
217 auto group = std::make_shared<Group_t>();
218 if (!insertGroup(group, groupComponent, Group_t::style(nullptr)))
219 qWarning() << "qan::Graph::insertGroup<>(): Warning: Error at group insertion.";
220 return group.get();
221 }
222 //-----------------------------------------------------------------------------
223
224 } // ::qan
225
226