diff --git a/libgraphtheory/fileformats/rocs1/autotests/testrocs1fileformat.cpp b/libgraphtheory/fileformats/rocs1/autotests/testrocs1fileformat.cpp index b8fda98e..70a2accc 100644 --- a/libgraphtheory/fileformats/rocs1/autotests/testrocs1fileformat.cpp +++ b/libgraphtheory/fileformats/rocs1/autotests/testrocs1fileformat.cpp @@ -1,142 +1,154 @@ /* * Copyright 2012-2014 Andreas Cord-Landwehr * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "testrocs1fileformat.h" #include "../rocs1fileformat.h" #include "typenames.h" #include "fileformats/fileformatinterface.h" #include "graphdocument.h" #include "node.h" #include "edge.h" #include using namespace GraphTheory; TestRocs1FileFormat::TestRocs1FileFormat() { } void TestRocs1FileFormat::serializeUnserializeTest() { GraphDocumentPtr document = GraphDocument::create(); QMap nodes; // Creates a simple graph with 5 data elements and connect them with pointers. nodes.insert("a", Node::create(document)); nodes["a"]->setDynamicProperty("label", "first node"); nodes.insert("b", Node::create(document)); nodes["b"]->setDynamicProperty("label", "b"); nodes.insert("c", Node::create(document)); nodes["c"]->setDynamicProperty("label", "c"); nodes.insert("d", Node::create(document)); nodes["d"]->setDynamicProperty("label", "d"); nodes.insert("e", Node::create(document)); nodes["e"]->setDynamicProperty("label", "e"); Edge::create(nodes["a"], nodes["b"])->setDynamicProperty("label", "test value"); Edge::create(nodes["b"], nodes["c"]); Edge::create(nodes["c"], nodes["d"]); Edge::create(nodes["d"], nodes["e"]); Edge::create(nodes["e"], nodes["a"]); // serialize to file Rocs1FileFormat format(this, QList()); format.setFile(QUrl::fromLocalFile("test.graph")); format.writeFile(document); QVERIFY(format.hasError() == false); // unserialize and test properties // load document and test format.readFile(); QVERIFY(!format.hasError()); document = format.graphDocument(); QCOMPARE(document->nodes().count(), 5); QCOMPARE(document->edges().count(), 5); foreach(NodePtr node, document->nodes()) { QCOMPARE(node->edges().count(), 2); } } +static void logEdgeTypes(const QList& types) +{ + qDebug() << "Edge types:"; + for (const auto& t : types) { + qDebug() << ".." << t->name() << t->direction(); + } +} + void TestRocs1FileFormat::serializeUnserializeTypesTest() { GraphDocumentPtr document = GraphDocument::create(); QMap nodes; NodeTypePtr nodeType2 = NodeType::create(document); EdgeTypePtr edgeType2 = EdgeType::create(document); + logEdgeTypes(document->edgeTypes()); + // add test data nodes.insert("a", Node::create(document)); nodes["a"]->setDynamicProperty("label", "first node"); nodes.insert("b", Node::create(document)); nodes["b"]->setDynamicProperty("label", "b"); Edge::create(nodes["a"], nodes["b"]); // serialize to file Rocs1FileFormat format(this, QList()); format.setFile(QUrl::fromLocalFile("testtypes.graph")); format.writeFile(document); QVERIFY(format.hasError() == false); // unserialize and test properties // load document and test format.readFile(); QVERIFY(!format.hasError()); document = format.graphDocument(); + logEdgeTypes(document->edgeTypes()); + QCOMPARE(document->nodeTypes().count(), 2); - QCOMPARE(document->edgeTypes().count(), 2); + QCOMPARE(document->edgeTypes().count(), 3); } //TODO move to Rocs project file test // void TestRocs1FileFormat::projectLoadSaveTest() // { // QTemporaryFile temp; // temp.setAutoRemove(false); // temp.open(); // // // prepare project and save // Project* testProject = new Project(QUrl::fromLocalFile(temp.fileName())); // testProject->setName("new test name"); // testProject->addCodeFile(QUrl::fromLocalFile("/path/to/code.js")); // GraphTheory::GraphDocumentPtr graph = testProject->createGraphDocument(); // graph->setDocumentUrl(QUrl::fromLocalFile("/path/to/graph.graph")); // testProject->writeProjectFile(); // // // load project // Project* testProject2 = new Project(QUrl::fromLocalFile(temp.fileName())); // QVERIFY2(testProject2->name().compare("new test name") == 0, "ERROR: project name changed"); // QVERIFY(testProject2->codeFiles().count() == 1); // QVERIFY2( // testProject2->codeFiles().at(0).toLocalFile() == "/path/to/code.js", // "ERROR: path of code file changed" // ); // QVERIFY2( // false, // // testProject2->graphFiles().at(0).toLocalFile() == "/path/to/graph.graph", // "ERROR: path of graph file changed" // ); // // delete testProject; // delete testProject2; // } QTEST_MAIN(TestRocs1FileFormat) diff --git a/libgraphtheory/fileformats/rocs1/rocs1fileformat.cpp b/libgraphtheory/fileformats/rocs1/rocs1fileformat.cpp index 8c8df5de..75cc86cc 100644 --- a/libgraphtheory/fileformats/rocs1/rocs1fileformat.cpp +++ b/libgraphtheory/fileformats/rocs1/rocs1fileformat.cpp @@ -1,347 +1,357 @@ /* This file is part of Rocs. Copyright 2010-2011 Tomaz Canabrava Copyright 2010 Wagner Reck Copyright 2012-2014 Andreas Cord-Landwehr 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 2 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 "rocs1fileformat.h" #include "fileformats/fileformatinterface.h" #include "graphdocument.h" #include "node.h" #include "edge.h" #include "logging_p.h" #include #include #include #include #include #include #include using namespace GraphTheory; K_PLUGIN_FACTORY_WITH_JSON( FilePluginFactory, "rocs1fileformat.json", registerPlugin();) class GraphTheory::Rocs1FileFormatPrivate { public: Rocs1FileFormatPrivate() : _buffer() {} QString _buffer; }; Rocs1FileFormat::Rocs1FileFormat(QObject *parent, const QList< QVariant >&) : FileFormatInterface("rocs_rocs1fileformat", parent) { d = new Rocs1FileFormatPrivate; } Rocs1FileFormat::~Rocs1FileFormat() { } const QStringList Rocs1FileFormat::extensions() const { return QStringList() << i18n("Rocs 1 Graph File Format (%1)", QString("*.graph")); } +template +void addTypes(QMap& map, const QList& list) +{ + int id = 0; + for (const auto& t : list) { + map.insert(id++, t); + } +} + void Rocs1FileFormat::readFile() { GraphDocumentPtr document = GraphDocument::create(); QFile fileHandle(file().toLocalFile()); document->setDocumentUrl(file()); if (!fileHandle.open(QIODevice::ReadOnly | QIODevice::Text)) { setError(CouldNotOpenFile, i18n("Could not open file \"%1\" in read mode: %2", file().toLocalFile(), fileHandle.errorString())); document->destroy(); return; } QMap nodeMap; QMap nodeTypeMap; QMap edgeTypeMap; - nodeTypeMap.insert(0, document->nodeTypes().first()); - edgeTypeMap.insert(0, document->edgeTypes().first()); + + addTypes(nodeTypeMap, document->nodeTypes()); + addTypes(edgeTypeMap, document->edgeTypes()); QTextStream in(&fileHandle); in.setCodec("UTF-8"); while (!in.atEnd()) { QString str = in.readLine().simplified(); if (str.startsWith('#')) { //! Ignore it, commented line. continue; } else if (str.startsWith(QLatin1String("[Document Properties]"))) { QString dataLine = in.readLine().simplified(); while (!in.atEnd() && !dataLine.isEmpty()) { if (dataLine.startsWith(QLatin1String("DataStructurePlugin :"))) { // set plugin by unique plugin identifier // DO NOTHING: data plugins not used anymore } else if (!dataLine.isEmpty()) { break; // go to the last if and finish populating. } dataLine = in.readLine().simplified(); } } else if (str.startsWith(QLatin1String("[DataStructure"))) { QString gName = str.section(' ', 1, 1); gName.remove(']'); // DO NOTHING: data structures not used anymore } else if (str.startsWith(QLatin1String("[DataType"))) { QString identifier = str.section(' ', 1); identifier.remove(']'); int tmpDataTypeId = identifier.toInt(); if (!nodeTypeMap.contains(tmpDataTypeId)) { nodeTypeMap.insert(tmpDataTypeId, NodeType::create(document)); } QString dataLine = in.readLine().simplified(); while (!in.atEnd() && !dataLine.isEmpty()) { /**/ if (dataLine.startsWith(QLatin1String("Name :"))) { nodeTypeMap[tmpDataTypeId]->setName(dataLine.section(' ', 2)); } else if (dataLine.startsWith(QLatin1String("IconName :"))) { QString iconString = dataLine.section(' ', 2); } else if (dataLine.startsWith(QLatin1String("Properties :"))) { QStringList properties = dataLine.section(' ', 2).split(','); foreach(const QString& property, properties) { if (!property.isEmpty()) { nodeTypeMap[tmpDataTypeId]->addDynamicProperty(property.section('=',0,0)); } } } else if (!dataLine.isEmpty()) break; // go to the last if and finish populating. dataLine = in.readLine().simplified(); } } else if (str.startsWith(QLatin1String("[PointerType"))) { QString identifier = str.section(' ', 1); identifier.remove(']'); int tmpEdgeTypeId = identifier.toInt(); if (!edgeTypeMap.contains(tmpEdgeTypeId)) { edgeTypeMap.insert(tmpEdgeTypeId, EdgeType::create(document)); } EdgeTypePtr tmpEdgeType = edgeTypeMap[tmpEdgeTypeId]; QString dataLine = in.readLine().simplified(); while (!in.atEnd() && !dataLine.isEmpty()) { /**/ if (dataLine.startsWith(QLatin1String("Name :"))) { tmpEdgeType->setName(dataLine.section(' ', 2)); } else if (dataLine.startsWith(QLatin1String("Direction :"))) { if (dataLine.section(' ', 2) == "bidirectional") { tmpEdgeType->setDirection(EdgeType::Bidirectional); } else if (dataLine.section(' ', 2) == "unidirectional") { tmpEdgeType->setDirection(EdgeType::Unidirectional); } else { qCCritical(GRAPHTHEORY_FILEFORMAT) << "Direction unset, use default direction of this data type backend."; } } else if (dataLine.startsWith(QLatin1String("Properties :"))) { QStringList properties = dataLine.section(' ', 2).split(','); foreach(const QString& property, properties) { if (!property.isEmpty()) { tmpEdgeType->addDynamicProperty(property.section('=',0,0)); } } } else if (!dataLine.isEmpty()) { break; // go to the last if and finish populating. } dataLine = in.readLine().simplified(); } } else if (str.startsWith(QLatin1String("[Data "))) { NodePtr tmpNode; QString dataLine = in.readLine().simplified(); int type = 0; qreal posX = 0; qreal posY = 0; QString value = ""; QString color = ""; QString name = ""; while (!in.atEnd() && !dataLine.isEmpty()) { /**/ if (dataLine.startsWith(QLatin1String("x :"))) posX = dataLine.section(' ', 2).toFloat(); else if (dataLine.startsWith(QLatin1String("y :"))) posY = dataLine.section(' ', 2).toFloat(); else if (dataLine.startsWith(QLatin1String("type :"))) type = dataLine.section(' ', 2).toInt(); else if (dataLine.startsWith(QLatin1String("value :"))) value = dataLine.section(' ', 2); else if (dataLine.startsWith(QLatin1String("color :"))) color = dataLine.section(' ', 2); else if (dataLine.startsWith(QLatin1String("name :"))) name = dataLine.section(' ', 2); else if (!dataLine.isEmpty()) break; // go to the last if and finish populating. dataLine = in.readLine().simplified(); } if (nodeTypeMap.contains(type)) { tmpNode = Node::create(document); tmpNode->setType(nodeTypeMap[type]); } else { qCDebug(GRAPHTHEORY_FILEFORMAT) << "Create data element of type 0, since type " << type << " was not registered."; tmpNode = Node::create(document); } if (tmpNode) { tmpNode->setColor(color); tmpNode->setX(posX); tmpNode->setY(posY); // add to data element map QString identifier = str.section(' ', 1); identifier.remove(']'); nodeMap.insert(identifier.toInt(), tmpNode); } } else if (str.startsWith(QLatin1String("[Pointer "))) { EdgePtr tmpEdge; QString eName = str.section(' ', 1, 1); eName.remove(']'); QString nameFrom = eName.section("->", 0, 0); QString nameTo = eName.section("->", 1, 1); QString dataLine = in.readLine().simplified(); QString value = ""; int type = 0; QString color = ""; while (!in.atEnd() && !dataLine.isEmpty()) { if (dataLine.startsWith(QLatin1String("value :"))) value = dataLine.section(' ', 2); else if (dataLine.startsWith(QLatin1String("type :"))) type = dataLine.section(' ', 2).toInt(); else if (dataLine.startsWith(QLatin1String("color :"))) color = dataLine.section(' ', 2); else if (!dataLine.isEmpty()) break; // go to the last if and finish populating. dataLine = in.readLine().simplified(); } if (edgeTypeMap.contains(type)) { if (!nodeMap.contains(nameFrom.toInt())) { qCCritical(GRAPHTHEORY_FILEFORMAT) << "Node creating edge, node ID" << nameFrom.toInt() << "not registered"; break; } if (!nodeMap.contains(nameTo.toInt())) { qCCritical(GRAPHTHEORY_FILEFORMAT) << "Node creating edge, node ID" << nameTo.toInt() << "not registered"; break; } tmpEdge = Edge::create(nodeMap[nameFrom.toInt()], nodeMap[nameTo.toInt()]); tmpEdge->setType(edgeTypeMap[type]); } else { qCDebug(GRAPHTHEORY_FILEFORMAT) << "Create pointer of type 0, since type " << type << " was not registered."; tmpEdge = Edge::create(nodeMap[nameFrom.toInt()], nodeMap[nameTo.toInt()]); } } // FIXME dynamic properties are not read // else if (str.contains(':')) { // QString propertyName = str.section(':', 0, 0).trimmed(); // QString propertyValue = str.section(':', 1, 1).trimmed(); // if (!propertyName.isEmpty()) { // tmpObject->setProperty(propertyName.toUtf8() , propertyValue); // } // } } setGraphDocument(document); setError(None); } void Rocs1FileFormat::writeFile(GraphDocumentPtr graph) { QSaveFile saveFile(!file().toLocalFile().endsWith(".graph") ? QString("%1.graph").arg(file().toLocalFile()) : file().toLocalFile()); if (!saveFile.open(QIODevice::WriteOnly)) { setError(FileIsReadOnly, i18n("Could not open file \"%1\" in write mode: %2", file().fileName(), saveFile.errorString())); return; } QTextStream stream(&saveFile); stream.setCodec("UTF-8"); // TODO test for successful serialization serialize(graph); stream << d->_buffer; if (!saveFile.commit()) { setError(FileIsReadOnly, i18n("Could not write data, aborting. Error: %1.", saveFile.errorString())); return; } setError(None); } QString Rocs1FileFormat::serialize(GraphDocumentPtr document) { d->_buffer.clear(); d->_buffer = QString("[Document Properties] \n") + QString("DataStructurePlugin : Graph") + QChar('\n') + QChar('\n'); for(int typeID = 0; typeID < document->nodeTypes().length(); ++typeID) { QStringList properties; foreach (const QString &property, document->nodeTypes().at(typeID)->dynamicProperties()) { properties.append(property + QString('=')); } d->_buffer += QString("[DataType %1]").arg(QString::number(typeID)) + QChar('\n') + QString("Name : ") + document->nodeTypes().at(typeID)->name() + QChar('\n') + QString("Properties : ") + properties.join(QChar(',')) + QChar('\n') + QChar('\n'); } for(int typeID = 0; typeID < document->edgeTypes().length(); ++typeID) { QStringList properties; foreach (const QString &property, document->edgeTypes().at(typeID)->dynamicProperties()) { properties.append(property + QString('=')); } // set direction identifier QString direction = (document->edgeTypes().at(typeID)->direction() == EdgeType::Bidirectional) ? "bidirectional" : "unidirectional"; d->_buffer += QString("[PointerType %1]").arg(QString::number(typeID)) + QChar('\n') + QString("Name : ") + document->edgeTypes().at(typeID)->name() + QChar('\n') + QString("Direction : ") + direction + QChar('\n') + QString("Properties : ") + properties.join(QChar(',')) + QChar('\n') + QChar('\n'); } d->_buffer += QString("[DataStructure 0] \n"); // all in one datastructure now foreach(NodePtr n, document->nodes()) { d->_buffer += QString("[Data %1]\n").arg(QString::number(n->id())); d->_buffer += QString("type : ") + QString::number(document->nodeTypes().indexOf(n->type())) + QChar('\n'); foreach(const QString &property, n->dynamicProperties()) { d->_buffer += QString("%1 : %2 \n").arg(property).arg(n->dynamicProperty(property).toString()); } d->_buffer += QChar('\n'); } foreach(EdgePtr e, document->edges()) { d->_buffer += QString("[Pointer %1->%2]\n"). arg(QString::number(e->from()->id())). arg(QString::number(e->to()->id())).toUtf8(); d->_buffer += QString("type : ") + QString::number(document->edgeTypes().indexOf(e->type())) + QChar('\n'); foreach(const QString &property, e->dynamicProperties()) { d->_buffer += QString("%1 : %2 \n").arg(property).arg(e->dynamicProperty(property).toString()); } d->_buffer += QChar('\n'); } qCDebug(GRAPHTHEORY_FILEFORMAT) << "------- /// BEGIN internal file format /// -------"; qCDebug(GRAPHTHEORY_FILEFORMAT) << d->_buffer; qCDebug(GRAPHTHEORY_FILEFORMAT) << "------- /// internal file format END /// -------"; return d->_buffer; } #include "rocs1fileformat.moc" diff --git a/libgraphtheory/fileformats/tgf/autotests/testtgffileformat.cpp b/libgraphtheory/fileformats/tgf/autotests/testtgffileformat.cpp index e72e8fad..2ba6bd88 100644 --- a/libgraphtheory/fileformats/tgf/autotests/testtgffileformat.cpp +++ b/libgraphtheory/fileformats/tgf/autotests/testtgffileformat.cpp @@ -1,86 +1,95 @@ /* * Copyright 2012-2014 Andreas Cord-Landwehr * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "testtgffileformat.h" #include "../tgffileformat.h" #include "fileformats/fileformatinterface.h" #include "graphdocument.h" #include "node.h" #include "edge.h" #include using namespace GraphTheory; TestTgfFileFormat::TestTgfFileFormat() { } void TestTgfFileFormat::serializeUnserializeTest() { GraphDocumentPtr document = GraphDocument::create(); + QCOMPARE(document->edgeTypes().first()->direction(), EdgeType::Direction::Unidirectional); + document->nodeTypes().first()->addDynamicProperty("label"); document->edgeTypes().first()->addDynamicProperty("label"); QMap dataList; // Creates a simple Graph with 5 data elements and connect them with pointers. dataList.insert("a", Node::create(document)); dataList["a"]->setDynamicProperty("label", "first node"); dataList.insert("b", Node::create(document)); dataList["b"]->setDynamicProperty("label", "b"); dataList.insert("c", Node::create(document)); dataList["c"]->setDynamicProperty("label", "c"); dataList.insert("d", Node::create(document)); dataList["d"]->setDynamicProperty("label", "d"); dataList.insert("e", Node::create(document)); dataList["e"]->setDynamicProperty("label", "e"); Edge::create(dataList["a"], dataList["b"])->setDynamicProperty("label", "test value"); Edge::create(dataList["b"], dataList["c"]); Edge::create(dataList["c"], dataList["d"]); Edge::create(dataList["d"], dataList["e"]); Edge::create(dataList["e"], dataList["a"]); // create exporter plugin TgfFileFormat serializer(this, QList()); serializer.setFile(QUrl::fromLocalFile("test.tgf")); serializer.writeFile(document); QVERIFY(serializer.hasError() == false); // create importer TgfFileFormat importer(this, QList()); importer.setFile(QUrl::fromLocalFile("test.tgf")); importer.readFile(); QVERIFY(importer.hasError() == false); QVERIFY(importer.isGraphDocument()); document = importer.graphDocument(); // test imported values QVERIFY2(document->nodes().size() == 5, "ERROR: Number of data is not 5 "); QVERIFY2(document->edges().size() == 5, "ERROR: Number of pointers is not 5 "); foreach(NodePtr node, document->nodes()) { + qDebug() << "Node" << node->dynamicProperty("label"); + for(const auto& e : node->outEdges()) { + qDebug() << ".. out" << e->from()->dynamicProperty("label") << e->to()->dynamicProperty("label"); + } + for(const auto& e : node->inEdges()) { + qDebug() << ".. in " << e->from()->dynamicProperty("label") << e->to()->dynamicProperty("label"); + } QCOMPARE(node->outEdges().size(), 1); QCOMPARE(node->inEdges().size(), 1); QCOMPARE(node->edges().count(), 2); } QCOMPARE(document->nodes().first()->dynamicProperty("label").toString(), QString("first node")); QCOMPARE(document->edges().first()->dynamicProperty("label").toString(), QString("test value")); } QTEST_MAIN(TestTgfFileFormat); diff --git a/libgraphtheory/graphdocument.cpp b/libgraphtheory/graphdocument.cpp index 189163a6..ba2ff29c 100644 --- a/libgraphtheory/graphdocument.cpp +++ b/libgraphtheory/graphdocument.cpp @@ -1,407 +1,407 @@ /* * Copyright 2014 Andreas Cord-Landwehr * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "graphdocument.h" #include "view.h" #include "edgetype.h" #include "nodetype.h" #include "edge.h" #include "fileformats/fileformatmanager.h" #include "logging_p.h" #include #include #include using namespace GraphTheory; // initialize number of edge objects uint GraphDocument::objectCounter = 0; class GraphTheory::GraphDocumentPrivate { public: GraphDocumentPrivate() : m_valid(false) , m_view(0) , m_documentUrl(QUrl()) , m_name(QString()) , m_lastGeneratedId(0) , m_modified(false) { } ~GraphDocumentPrivate() { } GraphDocumentPtr q; bool m_valid; View *m_view; QList m_edgeTypes; QList m_nodeTypes; NodeList m_nodes; EdgeList m_edges; QUrl m_documentUrl; QString m_name; uint m_lastGeneratedId; bool m_modified; }; GraphDocumentPtr GraphDocument::self() const { return d->q; } void GraphDocument::destroy() { d->m_valid = false; foreach (EdgePtr edge, d->m_edges) { edge->destroy(); } d->m_edges.clear(); foreach (NodePtr node, d->m_nodes) { node->destroy(); } d->m_nodes.clear(); foreach (NodeTypePtr type, d->m_nodeTypes) { type->destroy(); } d->m_nodeTypes.clear(); foreach (EdgeTypePtr type, d->m_edgeTypes) { type->destroy(); } d->m_edgeTypes.clear(); // reset last reference to this object d->q.reset(); } GraphDocument::GraphDocument() : QObject() , d(new GraphDocumentPrivate) { ++GraphDocument::objectCounter; } GraphDocument::~GraphDocument() { --GraphDocument::objectCounter; } View * GraphDocument::createView(QWidget *parent) { if (d->m_view) { return d->m_view; } d->m_view = new View(parent); d->m_view->setGraphDocument(d->q); // apply antialiasing on the view QSurfaceFormat format = d->m_view->format(); format.setSamples(16); d->m_view->setFormat(format); return d->m_view; } GraphDocumentPtr GraphDocument::create() { GraphDocumentPtr pi(new GraphDocument); pi->setQpointer(pi); - // create default edge bidirectional - EdgeTypePtr edgeTypeBidirectional = EdgeType::create(pi); - edgeTypeBidirectional->setName(i18n("bidirectional")); - edgeTypeBidirectional->setDirection(EdgeType::Direction::Bidirectional); - // create default edge unidirectional EdgeTypePtr edgeTypeUnidirectional = EdgeType::create(pi); edgeTypeUnidirectional->setName(i18n("unidirectional")); edgeTypeUnidirectional->setDirection(EdgeType::Direction::Unidirectional); + // create default edge bidirectional + EdgeTypePtr edgeTypeBidirectional = EdgeType::create(pi); + edgeTypeBidirectional->setName(i18n("bidirectional")); + edgeTypeBidirectional->setDirection(EdgeType::Direction::Bidirectional); + // create default node type NodeType::create(pi)->setName(i18n("default")); pi->d->m_valid = true; pi->d->m_modified = false; return pi; } NodeList GraphDocument::nodes(NodeTypePtr type) const { if (!type) { return d->m_nodes; } NodeList nodes; foreach (NodePtr node, d->m_nodes) { if (node->type() == type) { nodes.append(node); } } return nodes; } EdgeList GraphDocument::edges(EdgeTypePtr type) const { if (!type) { return d->m_edges; } EdgeList edges; foreach (EdgePtr edge, d->m_edges) { if (edge->type() == type) { edges.append(edge); } } return edges; } void GraphDocument::insert(NodePtr node) { Q_ASSERT(node); Q_ASSERT(node->document() == d->q); if (!node || d->m_nodes.contains(node)) { return; } if (0 <= node->id() && (uint)node->id() < d->m_lastGeneratedId) { d->m_lastGeneratedId = node->id(); } emit nodeAboutToBeAdded(node, d->m_nodes.length()); d->m_nodes.append(node); emit nodeAdded(); setModified(true); } void GraphDocument::insert(EdgePtr edge) { Q_ASSERT(edge); Q_ASSERT(edge->from()->document() == d->q); Q_ASSERT(edge->to()->document() == d->q); if (!edge || d->m_edges.contains(edge)) { return; } emit edgeAboutToBeAdded(edge, d->m_edges.length()); d->m_edges.append(edge); emit edgeAdded(); setModified(true); } void GraphDocument::insert(NodeTypePtr type) { Q_ASSERT(type); if (d->m_nodeTypes.contains(type)) { return; } if (0 <= type->id() && (uint)type->id() < d->m_lastGeneratedId) { d->m_lastGeneratedId = type->id(); } emit nodeTypeAboutToBeAdded(type, d->m_nodeTypes.length()); d->m_nodeTypes.append(type); emit nodeTypeAdded(); setModified(true); } void GraphDocument::insert(EdgeTypePtr type) { Q_ASSERT(type); if (d->m_edgeTypes.contains(type)) { return; } if (0 <= type->id() && (uint)type->id() < d->m_lastGeneratedId) { d->m_lastGeneratedId = type->id(); } emit edgeTypeAboutToBeAdded(type, d->m_edgeTypes.length()); d->m_edgeTypes.append(type); emit edgeTypeAdded(); setModified(true); } void GraphDocument::remove(NodePtr node) { if (node->isValid()) { node->destroy(); } int index = d->m_nodes.indexOf(node); if (index >= 0) { emit nodesAboutToBeRemoved(index,index); d->m_nodes.removeAt(index); emit nodesRemoved(); } setModified(true); } void GraphDocument::remove(EdgePtr edge) { if (edge->isValid()) { edge->destroy(); } int index = d->m_edges.indexOf(edge); if (index >= 0) { emit edgesAboutToBeRemoved(index,index); d->m_edges.removeAt(index); emit edgesRemoved(); } setModified(true); } void GraphDocument::remove(NodeTypePtr type) { foreach (NodePtr node, d->m_nodes) { if (node->type() == type) { node->destroy(); } } if (type->isValid()) { type->destroy(); } int index = d->m_nodeTypes.indexOf(type); emit nodeTypesAboutToBeRemoved(index, index); d->m_nodeTypes.removeOne(type); emit nodeTypesRemoved(); setModified(true); } void GraphDocument::remove(EdgeTypePtr type) { foreach (EdgePtr edge, edges(type)) { edge->destroy(); } if (type->isValid()) { type->destroy(); } int index = d->m_edgeTypes.indexOf(type); emit edgeTypesAboutToBeRemoved(index, index); d->m_edgeTypes.removeOne(type); emit edgeTypesRemoved(); setModified(true); } QList< EdgeTypePtr > GraphDocument::edgeTypes() const { return d->m_edgeTypes; } QList< NodeTypePtr > GraphDocument::nodeTypes() const { return d->m_nodeTypes; } uint GraphDocument::generateId() { return ++d->m_lastGeneratedId; } void GraphDocument::setQpointer(GraphDocumentPtr q) { d->q = q; } //BEGIN file stuff QString GraphDocument::documentName() const { if (d->m_name.isEmpty()) { return i18nc("@title:tab initial title for graph document", "New Graph"); } return d->m_name; } void GraphDocument::setDocumentName(const QString& name) { if (name == d->m_name) { return; } d->m_name = name; emit documentNameChanged(name); setModified(true); } //END file stuff //BEGIN file stuff bool GraphDocument::documentReload() { qCCritical(GRAPHTHEORY_GENERAL) << "graph reloading not implemented!"; //FIXME reload document setModified(false); return true; } bool GraphDocument::documentSave() { return documentSaveAs(d->m_documentUrl); } bool GraphDocument::documentSaveAs(const QUrl &documentUrl) { if (!documentUrl.isValid()) { qCCritical(GRAPHTHEORY_GENERAL) << "No valid document url specified, abort saving."; return false; } FileFormatManager fileFormatManager; FileFormatInterface *serializer = fileFormatManager.defaultBackend(); serializer->setFile(documentUrl); serializer->writeFile(d->q); if (serializer->hasError()) { qCCritical(GRAPHTHEORY_GENERAL) << "Graph file serializer reported error:" << serializer->errorString(); return false; } // update document path if necessary if (d->m_documentUrl != documentUrl) { d->m_documentUrl = documentUrl; emit documentUrlChanged(); } setModified(false); return true; } QUrl GraphDocument::documentUrl() const { return d->m_documentUrl; } void GraphDocument::setDocumentUrl(const QUrl &documentUrl) { d->m_documentUrl = documentUrl; emit documentUrlChanged(); } bool GraphDocument::isModified() const { return d->m_modified; } void GraphDocument::setModified(bool modified) { if (modified == d->m_modified) { return; } d->m_modified = modified; emit modifiedChanged(); } //END