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