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