1 /*
2  *  SPDX-FileCopyrightText: 2014 Andreas Cord-Landwehr <cordlandwehr@kde.org>
3  *
4  *  SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5  */
6 
7 #include "graphdocument.h"
8 #include "view.h"
9 #include "edge.h"
10 #include "fileformats/fileformatmanager.h"
11 #include "logging_p.h"
12 #include <KLocalizedString>
13 #include <QSurfaceFormat>
14 #include <QString>
15 
16 using namespace GraphTheory;
17 
18 // initialize number of edge objects
19 uint GraphDocument::objectCounter = 0;
20 
21 class GraphTheory::GraphDocumentPrivate {
22 public:
GraphDocumentPrivate()23     GraphDocumentPrivate()
24         : m_valid(false)
25         , m_view(nullptr)
26         , m_documentUrl(QUrl())
27         , m_name(QString())
28         , m_lastGeneratedId(0)
29         , m_modified(false)
30     {
31     }
32 
~GraphDocumentPrivate()33     ~GraphDocumentPrivate()
34     {
35     }
36 
37     GraphDocumentPtr q;
38     bool m_valid;
39     View *m_view;
40     QList<EdgeTypePtr> m_edgeTypes;
41     QList<NodeTypePtr> m_nodeTypes;
42     NodeList m_nodes;
43     EdgeList m_edges;
44 
45     QUrl m_documentUrl;
46     QString m_name;
47     uint m_lastGeneratedId;
48     bool m_modified;
49 };
50 
self() const51 GraphDocumentPtr GraphDocument::self() const
52 {
53     return d->q;
54 }
55 
destroy()56 void GraphDocument::destroy()
57 {
58     d->m_valid = false;
59     const auto edges = d->m_edges;
60     for (const EdgePtr &edge : edges) {
61         edge->destroy();
62     }
63     d->m_edges.clear();
64     const auto nodes = d->m_nodes;
65     for (const NodePtr &node : nodes) {
66         node->destroy();
67     }
68     d->m_nodes.clear();
69     const auto nodeTypes = d->m_nodeTypes;
70     for (const NodeTypePtr &type : nodeTypes) {
71         type->destroy();
72     }
73     d->m_nodeTypes.clear();
74     const auto edgeTypes = d->m_edgeTypes;
75     for (const EdgeTypePtr &type : edgeTypes) {
76         type->destroy();
77     }
78     d->m_edgeTypes.clear();
79 
80     // reset last reference to this object
81     d->q.reset();
82 }
83 
GraphDocument()84 GraphDocument::GraphDocument()
85     : QObject()
86     , d(new GraphDocumentPrivate)
87 {
88     ++GraphDocument::objectCounter;
89 }
90 
~GraphDocument()91 GraphDocument::~GraphDocument()
92 {
93 
94     --GraphDocument::objectCounter;
95 }
96 
createView(QWidget * parent)97 View * GraphDocument::createView(QWidget *parent)
98 {
99     if (d->m_view) {
100         return d->m_view;
101     }
102     d->m_view = new View(parent);
103     d->m_view->setGraphDocument(d->q);
104 
105     // apply antialiasing on the view
106     QSurfaceFormat format = d->m_view->format();
107     format.setSamples(16);
108     d->m_view->setFormat(format);
109 
110     return d->m_view;
111 }
112 
create()113 GraphDocumentPtr GraphDocument::create()
114 {
115     GraphDocumentPtr pi(new GraphDocument);
116     pi->setQpointer(pi);
117 
118     // create default edge unidirectional
119     EdgeTypePtr edgeTypeUnidirectional = EdgeType::create(pi);
120     edgeTypeUnidirectional->setName(i18n("Unidirectional"));
121     edgeTypeUnidirectional->setDirection(EdgeType::Direction::Unidirectional);
122 
123     // create default edge bidirectional
124     EdgeTypePtr edgeTypeBidirectional = EdgeType::create(pi);
125     edgeTypeBidirectional->setName(i18n("Bidirectional"));
126     edgeTypeBidirectional->setDirection(EdgeType::Direction::Bidirectional);
127 
128     // create default node type
129     NodeType::create(pi)->setName(i18n("Default"));
130     pi->d->m_valid = true;
131     pi->d->m_modified = false;
132     return pi;
133 }
134 
nodes(NodeTypePtr type) const135 NodeList GraphDocument::nodes(NodeTypePtr type) const
136 {
137     if (!type) {
138         return d->m_nodes;
139     }
140 
141     NodeList nodes;
142     for (const NodePtr &node : std::as_const(d->m_nodes)) {
143         if (node->type() == type) {
144             nodes.append(node);
145         }
146     }
147     return nodes;
148 }
149 
edges(EdgeTypePtr type) const150 EdgeList GraphDocument::edges(EdgeTypePtr type) const
151 {
152     if (!type) {
153         return d->m_edges;
154     }
155 
156     EdgeList edges;
157     for (const EdgePtr &edge : std::as_const(d->m_edges)) {
158         if (edge->type() == type) {
159             edges.append(edge);
160         }
161     }
162     return edges;
163 }
164 
insert(NodePtr node)165 void GraphDocument::insert(NodePtr node)
166 {
167     Q_ASSERT(node);
168     Q_ASSERT(node->document() == d->q);
169 
170     if (!node || d->m_nodes.contains(node)) {
171         return;
172     }
173     if (0 <= node->id() && (uint)node->id() < d->m_lastGeneratedId) {
174         d->m_lastGeneratedId = node->id();
175     }
176 
177     Q_EMIT nodeAboutToBeAdded(node, d->m_nodes.length());
178     d->m_nodes.append(node);
179     Q_EMIT nodeAdded();
180     setModified(true);
181 }
182 
insert(EdgePtr edge)183 void GraphDocument::insert(EdgePtr edge)
184 {
185     Q_ASSERT(edge);
186     Q_ASSERT(edge->from()->document() == d->q);
187     Q_ASSERT(edge->to()->document() == d->q);
188 
189     if (!edge || d->m_edges.contains(edge)) {
190         return;
191     }
192 
193     Q_EMIT edgeAboutToBeAdded(edge, d->m_edges.length());
194     d->m_edges.append(edge);
195     Q_EMIT edgeAdded();
196     setModified(true);
197 }
198 
insert(NodeTypePtr type)199 void GraphDocument::insert(NodeTypePtr type)
200 {
201     Q_ASSERT(type);
202     if (d->m_nodeTypes.contains(type)) {
203         return;
204     }
205     if (0 <= type->id() && (uint)type->id() < d->m_lastGeneratedId) {
206         d->m_lastGeneratedId = type->id();
207     }
208     Q_EMIT nodeTypeAboutToBeAdded(type, d->m_nodeTypes.length());
209     d->m_nodeTypes.append(type);
210     Q_EMIT nodeTypeAdded();
211     setModified(true);
212 }
213 
insert(EdgeTypePtr type)214 void GraphDocument::insert(EdgeTypePtr type)
215 {
216     Q_ASSERT(type);
217     if (d->m_edgeTypes.contains(type)) {
218         return;
219     }
220     if (0 <= type->id() && (uint)type->id() < d->m_lastGeneratedId) {
221         d->m_lastGeneratedId = type->id();
222     }
223     Q_EMIT edgeTypeAboutToBeAdded(type, d->m_edgeTypes.length());
224     d->m_edgeTypes.append(type);
225     Q_EMIT edgeTypeAdded();
226     setModified(true);
227 }
228 
remove(NodePtr node)229 void GraphDocument::remove(NodePtr node)
230 {
231     if (node->isValid()) {
232         node->destroy();
233     }
234     int index = d->m_nodes.indexOf(node);
235     if (index >= 0) {
236         Q_EMIT nodesAboutToBeRemoved(index,index);
237         d->m_nodes.removeAt(index);
238         Q_EMIT nodesRemoved();
239     }
240     setModified(true);
241 }
242 
remove(EdgePtr edge)243 void GraphDocument::remove(EdgePtr edge)
244 {
245     if (edge->isValid()) {
246         edge->destroy();
247     }
248     int index = d->m_edges.indexOf(edge);
249     if (index >= 0) {
250         Q_EMIT edgesAboutToBeRemoved(index,index);
251         d->m_edges.removeAt(index);
252         Q_EMIT edgesRemoved();
253     }
254     setModified(true);
255 }
256 
remove(NodeTypePtr type)257 void GraphDocument::remove(NodeTypePtr type)
258 {
259     const auto nodes = d->m_nodes;
260     for (const NodePtr &node : nodes) {
261         if (node->type() == type) {
262             node->destroy();
263         }
264     }
265     if (type->isValid()) {
266         type->destroy();
267     }
268     int index = d->m_nodeTypes.indexOf(type);
269     Q_EMIT nodeTypesAboutToBeRemoved(index, index);
270     d->m_nodeTypes.removeOne(type);
271     Q_EMIT nodeTypesRemoved();
272     setModified(true);
273 }
274 
remove(EdgeTypePtr type)275 void GraphDocument::remove(EdgeTypePtr type)
276 {
277     const auto typeEdges = edges(type);
278     for (const EdgePtr &edge : typeEdges) {
279         edge->destroy();
280     }
281     if (type->isValid()) {
282         type->destroy();
283     }
284     int index = d->m_edgeTypes.indexOf(type);
285     Q_EMIT edgeTypesAboutToBeRemoved(index, index);
286     d->m_edgeTypes.removeOne(type);
287     Q_EMIT edgeTypesRemoved();
288     setModified(true);
289 }
290 
edgeTypes() const291 QList< EdgeTypePtr > GraphDocument::edgeTypes() const
292 {
293     return d->m_edgeTypes;
294 }
295 
nodeTypes() const296 QList< NodeTypePtr > GraphDocument::nodeTypes() const
297 {
298     return d->m_nodeTypes;
299 }
300 
generateId()301 uint GraphDocument::generateId()
302 {
303     return ++d->m_lastGeneratedId;
304 }
305 
setQpointer(GraphDocumentPtr q)306 void GraphDocument::setQpointer(GraphDocumentPtr q)
307 {
308     d->q = q;
309 }
310 
311 //BEGIN file stuff
documentName() const312 QString GraphDocument::documentName() const
313 {
314     if (d->m_name.isEmpty()) {
315         return i18nc("@title:tab initial title for graph document", "New Graph");
316     }
317     return d->m_name;
318 }
319 
setDocumentName(const QString & name)320 void GraphDocument::setDocumentName(const QString& name)
321 {
322     if (name == d->m_name) {
323         return;
324     }
325     d->m_name = name;
326     Q_EMIT documentNameChanged(name);
327     setModified(true);
328 }
329 //END file stuff
330 
331 
332 //BEGIN file stuff
documentReload()333 bool GraphDocument::documentReload()
334 {
335     qCCritical(GRAPHTHEORY_GENERAL) << "graph reloading not implemented!";
336     //FIXME reload document
337     setModified(false);
338     return true;
339 }
340 
documentSave()341 bool GraphDocument::documentSave()
342 {
343     return documentSaveAs(d->m_documentUrl);
344 }
345 
documentSaveAs(const QUrl & documentUrl)346 bool GraphDocument::documentSaveAs(const QUrl &documentUrl)
347 {
348     if (!documentUrl.isValid()) {
349         qCCritical(GRAPHTHEORY_GENERAL) << "No valid document url specified, abort saving.";
350         return false;
351     }
352 
353     FileFormatManager fileFormatManager;
354     FileFormatInterface *serializer = fileFormatManager.defaultBackend();
355     serializer->setFile(documentUrl);
356     serializer->writeFile(d->q);
357     if (serializer->hasError()) {
358         qCCritical(GRAPHTHEORY_GENERAL) << "Graph file serializer reported error:" << serializer->errorString();
359         return false;
360     }
361 
362     // update document path if necessary
363     if (d->m_documentUrl != documentUrl) {
364         d->m_documentUrl = documentUrl;
365         Q_EMIT documentUrlChanged();
366     }
367     setModified(false);
368 
369     return true;
370 }
371 
documentUrl() const372 QUrl GraphDocument::documentUrl() const
373 {
374     return d->m_documentUrl;
375 }
376 
setDocumentUrl(const QUrl & documentUrl)377 void GraphDocument::setDocumentUrl(const QUrl &documentUrl)
378 {
379     d->m_documentUrl = documentUrl;
380     Q_EMIT documentUrlChanged();
381 }
382 
isModified() const383 bool GraphDocument::isModified() const
384 {
385     return d->m_modified;
386 }
387 
setModified(bool modified)388 void GraphDocument::setModified(bool modified)
389 {
390     if (modified == d->m_modified) {
391         return;
392     }
393     d->m_modified = modified;
394     Q_EMIT modifiedChanged();
395 }
396 
397 //END
398