File indexing completed on 2024-05-19 15:27:47
0001 /* This file is part of KGraphViewer. 0002 Copyright (C) 2005-2007 Gael de Chalendar <kleag@free.fr> 0003 0004 KGraphViewer is free software; you can redistribute it and/or 0005 modify it under the terms of the GNU General Public 0006 License as published by the Free Software Foundation, version 2. 0007 0008 This program is distributed in the hope that it will be useful, 0009 but WITHOUT ANY WARRANTY; without even the implied warranty of 0010 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0011 General Public License for more details. 0012 0013 You should have received a copy of the GNU General Public License 0014 along with this program; if not, write to the Free Software 0015 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 0016 02110-1301, USA 0017 */ 0018 0019 #include "dotgraph.h" 0020 #include "DotGraphParsingHelper.h" 0021 #include "canvasedge.h" 0022 #include "canvassubgraph.h" 0023 #include "dotgrammar.h" 0024 #include "graphexporter.h" 0025 #include "kgraphviewerlib_debug.h" 0026 #include "layoutagraphthread.h" 0027 0028 #include "fdstream.hpp" 0029 #include <boost/spirit/include/classic_confix.hpp> 0030 #include <graphviz/gvc.h> 0031 #include <iostream> 0032 #include <stdio.h> 0033 #include <stdlib.h> 0034 0035 #include <QMessageBox> 0036 0037 #include <QByteArray> 0038 #include <QFile> 0039 #include <QMutexLocker> 0040 #include <QPair> 0041 #include <QProcess> 0042 #include <QUuid> 0043 #include <klocalizedstring.h> 0044 0045 using namespace boost; 0046 using namespace boost::spirit::classic; 0047 0048 extern KGraphViewer::DotGraphParsingHelper *phelper; 0049 0050 namespace KGraphViewer 0051 { 0052 const distinct_parser<> keyword_p("0-9a-zA-Z_"); 0053 0054 DotGraph::DotGraph() 0055 : GraphElement() 0056 , m_dotFileName("") 0057 , m_width(0.0) 0058 , m_height(0.0) 0059 , m_scale(1.0) 0060 , m_directed(true) 0061 , m_strict(false) 0062 , m_layoutCommand("") 0063 , m_horizCellFactor(0) 0064 , m_vertCellFactor(0) 0065 , m_wdhcf(0) 0066 , m_hdvcf(0) 0067 , m_readWrite(false) 0068 , m_dot(nullptr) 0069 , m_phase(Initial) 0070 , m_useLibrary(false) 0071 { 0072 setId("unnamed"); 0073 } 0074 0075 DotGraph::DotGraph(const QString &command, const QString &fileName) 0076 : GraphElement() 0077 , m_dotFileName(fileName) 0078 , m_width(0.0) 0079 , m_height(0.0) 0080 , m_scale(1.0) 0081 , m_directed(true) 0082 , m_strict(false) 0083 , m_layoutCommand(command) 0084 , m_horizCellFactor(0) 0085 , m_vertCellFactor(0) 0086 , m_wdhcf(0) 0087 , m_hdvcf(0) 0088 , m_readWrite(false) 0089 , m_dot(nullptr) 0090 , m_phase(Initial) 0091 , m_useLibrary(false) 0092 { 0093 setId("unnamed"); 0094 } 0095 0096 DotGraph::~DotGraph() 0097 { 0098 qDeleteAll(m_subgraphsMap); 0099 m_subgraphsMap.clear(); 0100 qDeleteAll(m_nodesMap); 0101 m_nodesMap.clear(); 0102 qDeleteAll(m_edgesMap); 0103 m_edgesMap.clear(); 0104 } 0105 0106 QString DotGraph::chooseLayoutProgramForFile(const QString &str) 0107 { 0108 QFile iFILE(str); 0109 0110 if (!iFILE.open(QIODevice::ReadOnly)) { 0111 qCWarning(KGRAPHVIEWERLIB_LOG) << "Can't test dot file. Will try to use the dot command on the file: '" << str << "'"; 0112 return "dot"; // -Txdot"; 0113 } 0114 0115 QByteArray fileContent = iFILE.readAll(); 0116 if (fileContent.isEmpty()) 0117 return ""; 0118 std::string s = fileContent.data(); 0119 std::string cmd = "dot"; 0120 parse(s.c_str(), (!(keyword_p("strict")) >> (keyword_p("graph")[assign_a(cmd, "neato")])), (space_p | comment_p("/*", "*/"))); 0121 0122 return QString::fromStdString(cmd); // + " -Txdot" ; 0123 } 0124 0125 bool DotGraph::parseDot(const QString &str) 0126 { 0127 qCDebug(KGRAPHVIEWERLIB_LOG) << str; 0128 m_useLibrary = false; 0129 if (m_layoutCommand.isEmpty()) { 0130 m_layoutCommand = chooseLayoutProgramForFile(str); 0131 if (m_layoutCommand.isEmpty()) { 0132 m_layoutCommand = chooseLayoutProgramForFile(str); 0133 return false; 0134 } 0135 } 0136 0137 qCDebug(KGRAPHVIEWERLIB_LOG) << "Running " << m_layoutCommand << str; 0138 QStringList options; 0139 /// @TODO handle the non-dot commands that could don't know the -T option 0140 // if (m_readWrite && m_phase == Initial) 0141 // { 0142 // options << "-Tdot"; 0143 // } 0144 // else 0145 // { 0146 options << "-Txdot"; 0147 // } 0148 options << str; 0149 0150 qCDebug(KGRAPHVIEWERLIB_LOG) << "m_dot is " << m_dot << ". Acquiring mutex"; 0151 QMutexLocker locker(&m_dotProcessMutex); 0152 qCDebug(KGRAPHVIEWERLIB_LOG) << "mutex acquired "; 0153 if (m_dot) { 0154 disconnect(m_dot, nullptr, this, nullptr); 0155 m_dot->kill(); 0156 delete m_dot; 0157 } 0158 m_dot = new QProcess(); 0159 connect(m_dot, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this, &DotGraph::slotDotRunningDone); 0160 connect(m_dot, &QProcess::errorOccurred, this, &DotGraph::slotDotRunningError); 0161 m_dot->start(m_layoutCommand, options); 0162 qCDebug(KGRAPHVIEWERLIB_LOG) << "process started"; 0163 return true; 0164 } 0165 0166 bool DotGraph::update() 0167 { 0168 GraphExporter exporter; 0169 if (!m_useLibrary) { 0170 qCDebug(KGRAPHVIEWERLIB_LOG) << "command"; 0171 QString str = exporter.writeDot(this); 0172 return parseDot(str); 0173 } else { 0174 qCDebug(KGRAPHVIEWERLIB_LOG) << "library"; 0175 graph_t *graph = exporter.exportToGraphviz(this); 0176 0177 GVC_t *gvc = gvContext(); 0178 threadsafe_wrap_gvLayout(gvc, graph, m_layoutCommand.toUtf8().data()); 0179 threadsafe_wrap_gvRender(gvc, graph, "xdot", nullptr); 0180 0181 updateWithGraph(graph); 0182 0183 gvFreeLayout(gvc, graph); 0184 agclose(graph); 0185 bool result = true; //(gvFreeContext(gvc) == 0); 0186 return result; 0187 } 0188 } 0189 0190 QByteArray DotGraph::getDotResult(int, QProcess::ExitStatus) 0191 { 0192 qCDebug(KGRAPHVIEWERLIB_LOG); 0193 0194 QMutexLocker locker(&m_dotProcessMutex); 0195 if (m_dot == nullptr) { 0196 return QByteArray(); 0197 } 0198 QByteArray result = m_dot->readAll(); 0199 delete m_dot; 0200 m_dot = nullptr; 0201 return result; 0202 } 0203 0204 void DotGraph::slotDotRunningDone(int exitCode, QProcess::ExitStatus exitStatus) 0205 { 0206 qCDebug(KGRAPHVIEWERLIB_LOG); 0207 0208 QByteArray result = getDotResult(exitCode, exitStatus); 0209 result.replace("\\\n", ""); 0210 0211 qCDebug(KGRAPHVIEWERLIB_LOG) << "string content is:" << Qt::endl << result << Qt::endl << "=====================" << result.size(); 0212 std::string s = result.data(); 0213 // std::cerr << "stdstring content is:" << std::endl << s << std::endl << "===================== " << s.size() << std::endl; 0214 if (phelper) { 0215 phelper->graph = nullptr; 0216 delete phelper; 0217 } 0218 // if (parsingResult) 0219 // { 0220 // if (m_readWrite) 0221 // { 0222 // storeOriginalAttributes(); 0223 // update(); 0224 // } 0225 // computeCells(); 0226 // } 0227 0228 DotGraph newGraph(m_layoutCommand, m_dotFileName); 0229 phelper = new DotGraphParsingHelper; 0230 phelper->graph = &newGraph; 0231 phelper->z = 1; 0232 phelper->maxZ = 1; 0233 phelper->uniq = 0; 0234 0235 qCDebug(KGRAPHVIEWERLIB_LOG) << "parsing new dot"; 0236 bool parsingResult = parse(s); 0237 delete phelper; 0238 phelper = nullptr; 0239 qCDebug(KGRAPHVIEWERLIB_LOG) << "phelper deleted"; 0240 0241 if (parsingResult) { 0242 qCDebug(KGRAPHVIEWERLIB_LOG) << "calling updateWithGraph"; 0243 updateWithGraph(newGraph); 0244 } else { 0245 qCWarning(KGRAPHVIEWERLIB_LOG) << "parsing failed"; 0246 } 0247 // return parsingResult; 0248 // if (m_readWrite && m_phase == Initial) 0249 // { 0250 // m_phase = Final; 0251 // update(); 0252 // } 0253 // else 0254 // { 0255 qCDebug(KGRAPHVIEWERLIB_LOG) << "emiting readyToDisplay"; 0256 emit(readyToDisplay()); 0257 // } 0258 } 0259 0260 void DotGraph::slotDotRunningError(QProcess::ProcessError error) 0261 { 0262 qCWarning(KGRAPHVIEWERLIB_LOG) << "DotGraph::slotDotRunningError" << error; 0263 switch (error) { 0264 case QProcess::FailedToStart: 0265 QMessageBox::critical(nullptr, i18n("Layout process failed"), i18n("Unable to start %1.", m_layoutCommand)); 0266 break; 0267 case QProcess::Crashed: 0268 QMessageBox::critical(nullptr, i18n("Layout process failed"), i18n("%1 crashed.", m_layoutCommand)); 0269 break; 0270 case QProcess::Timedout: 0271 QMessageBox::critical(nullptr, i18n("Layout process failed"), i18n("%1 timed out.", m_layoutCommand)); 0272 break; 0273 case QProcess::WriteError: 0274 QMessageBox::critical(nullptr, i18n("Layout process failed"), i18n("Was not able to write data to the %1 process.", m_layoutCommand)); 0275 break; 0276 case QProcess::ReadError: 0277 QMessageBox::critical(nullptr, i18n("Layout process failed"), i18n("Was not able to read data from the %1 process.", m_layoutCommand)); 0278 break; 0279 default: 0280 QMessageBox::critical(nullptr, i18n("Layout process failed"), i18n("Unknown error running %1.", m_layoutCommand)); 0281 } 0282 } 0283 0284 unsigned int DotGraph::cellNumber(int x, int y) 0285 { 0286 /* qCDebug(KGRAPHVIEWERLIB_LOG) << "x= " << x << ", y= " << y << ", m_width= " << m_width << ", m_height= " << m_height << ", m_horizCellFactor= " << m_horizCellFactor << ", m_vertCellFactor= " << m_vertCellFactor << ", m_wdhcf= " << 0287 * m_wdhcf << ", m_hdvcf= " << m_hdvcf;*/ 0288 0289 unsigned int nx = (unsigned int)((x - (x % int(m_wdhcf))) / m_wdhcf); 0290 unsigned int ny = (unsigned int)((y - (y % int(m_hdvcf))) / m_hdvcf); 0291 /* qCDebug(KGRAPHVIEWERLIB_LOG) << "nx = " << (unsigned int)(( x - ( x % int(m_wdhcf) ) ) / m_wdhcf); 0292 qCDebug(KGRAPHVIEWERLIB_LOG) << "ny = " << (unsigned int)(( y - ( y % int(m_hdvcf) ) ) / m_hdvcf); 0293 qCDebug(KGRAPHVIEWERLIB_LOG) << "res = " << ny * m_horizCellFactor + nx;*/ 0294 0295 unsigned int res = ny * m_horizCellFactor + nx; 0296 return res; 0297 } 0298 0299 #define MAXCELLWEIGHT 800 0300 0301 void DotGraph::computeCells() 0302 { 0303 return; 0304 qCDebug(KGRAPHVIEWERLIB_LOG) << m_width << m_height; 0305 m_horizCellFactor = m_vertCellFactor = 1; 0306 m_wdhcf = (int)ceil(((double)m_width) / m_horizCellFactor) + 1; 0307 m_hdvcf = (int)ceil(((double)m_height) / m_vertCellFactor) + 1; 0308 bool stop = true; 0309 do { 0310 stop = true; 0311 m_cells.clear(); 0312 // m_cells.resize(m_horizCellFactor * m_vertCellFactor); 0313 0314 GraphNodeMap::iterator it, it_end; 0315 it = m_nodesMap.begin(); 0316 it_end = m_nodesMap.end(); 0317 for (; it != it_end; it++) { 0318 GraphNode *gn = *it; 0319 // int cellNum = cellNumber(int(gn->x()), int(gn->y())); 0320 int cellNum = cellNumber(0, 0); 0321 qCDebug(KGRAPHVIEWERLIB_LOG) << "Found cell number " << cellNum; 0322 0323 if (m_cells.size() <= cellNum) { 0324 m_cells.resize(cellNum + 1); 0325 } 0326 m_cells[cellNum].insert(gn); 0327 0328 qCDebug(KGRAPHVIEWERLIB_LOG) << "after insert"; 0329 if (m_cells[cellNum].size() > MAXCELLWEIGHT) { 0330 qCDebug(KGRAPHVIEWERLIB_LOG) << "cell number " << cellNum << " contains " << m_cells[cellNum].size() << " nodes"; 0331 if ((m_width / m_horizCellFactor) > (m_height / m_vertCellFactor)) { 0332 m_horizCellFactor++; 0333 m_wdhcf = m_width / m_horizCellFactor; 0334 } else { 0335 m_vertCellFactor++; 0336 m_hdvcf = m_height / m_vertCellFactor; 0337 } 0338 qCDebug(KGRAPHVIEWERLIB_LOG) << "cell factor is now " << m_horizCellFactor << " / " << m_vertCellFactor; 0339 stop = false; 0340 break; 0341 } 0342 } 0343 } while (!stop); 0344 qCDebug(KGRAPHVIEWERLIB_LOG) << "m_wdhcf=" << m_wdhcf << "; m_hdvcf=" << m_hdvcf; 0345 qCDebug(KGRAPHVIEWERLIB_LOG) << "finished"; 0346 } 0347 0348 QSet<GraphNode *> &DotGraph::nodesOfCell(unsigned int id) 0349 { 0350 return m_cells[id]; 0351 } 0352 0353 void DotGraph::storeOriginalAttributes() 0354 { 0355 for (GraphNode *node : nodes()) { 0356 node->storeOriginalAttributes(); 0357 } 0358 for (GraphEdge *edge : edges()) { 0359 edge->storeOriginalAttributes(); 0360 } 0361 for (GraphSubgraph *subgraph : subgraphs()) { 0362 subgraph->storeOriginalAttributes(); 0363 } 0364 GraphElement::storeOriginalAttributes(); 0365 } 0366 0367 void DotGraph::saveTo(const QString &fileName) 0368 { 0369 qCDebug(KGRAPHVIEWERLIB_LOG) << fileName; 0370 m_dotFileName = fileName; 0371 GraphExporter exporter; 0372 exporter.writeDot(this, fileName); 0373 } 0374 0375 void DotGraph::updateWithGraph(graph_t *newGraph) 0376 { 0377 qCDebug(KGRAPHVIEWERLIB_LOG); 0378 0379 // copy global graph render operations and attributes 0380 DotRenderOpVec ops; 0381 // decrease mem peak 0382 setRenderOperations(ops); 0383 0384 if (agget(newGraph, (char *)"_draw_")) { 0385 parse_renderop(agget(newGraph, (char *)"_draw_"), ops); 0386 qCDebug(KGRAPHVIEWERLIB_LOG) << "_draw_: element renderOperations size is now " << ops.size(); 0387 } 0388 if (agget(newGraph, (char *)"_ldraw_")) { 0389 parse_renderop(agget(newGraph, (char *)"_ldraw_"), ops); 0390 qCDebug(KGRAPHVIEWERLIB_LOG) << "_ldraw_: element renderOperations size is now " << ops.size(); 0391 } 0392 0393 setRenderOperations(ops); 0394 0395 Agsym_t *attr = agnxtattr(newGraph, AGRAPH, nullptr); 0396 while (attr) { 0397 qCDebug(KGRAPHVIEWERLIB_LOG) << agnameof(newGraph) << ":" << attr->name << agxget(newGraph, attr); 0398 m_attributes[attr->name] = agxget(newGraph, attr); 0399 attr = agnxtattr(newGraph, AGRAPH, attr); 0400 } 0401 0402 // copy subgraphs 0403 for (graph_t *sg = agfstsubg(newGraph); sg; sg = agnxtsubg(sg)) { 0404 qCDebug(KGRAPHVIEWERLIB_LOG) << "subgraph:" << agnameof(sg); 0405 if (subgraphs().contains(agnameof(sg))) { 0406 qCDebug(KGRAPHVIEWERLIB_LOG) << "known"; 0407 // ??? 0408 // nodes()[ngn->name]->setZ(ngn->z()); 0409 subgraphs()[agnameof(sg)]->updateWithSubgraph(sg); 0410 if (subgraphs()[agnameof(sg)]->canvasElement()) { 0411 // nodes()[ngn->id()]->canvasElement()->setGh(m_height); 0412 } 0413 } else { 0414 qCDebug(KGRAPHVIEWERLIB_LOG) << "new"; 0415 GraphSubgraph *newsg = new GraphSubgraph(sg); 0416 // qCDebug(KGRAPHVIEWERLIB_LOG) << "new created"; 0417 subgraphs().insert(agnameof(sg), newsg); 0418 // qCDebug(KGRAPHVIEWERLIB_LOG) << "new inserted"; 0419 } 0420 } 0421 0422 // copy nodes 0423 node_t *ngn = agfstnode(newGraph); 0424 qCDebug(KGRAPHVIEWERLIB_LOG) << "first node:" << (void *)ngn; 0425 0426 while (ngn) 0427 // foreach (GraphNode* ngn, newGraph.nodes()) 0428 { 0429 qCDebug(KGRAPHVIEWERLIB_LOG) << "node " << agnameof(ngn); 0430 if (nodes().contains(agnameof(ngn))) { 0431 qCDebug(KGRAPHVIEWERLIB_LOG) << "known"; 0432 // ??? 0433 // nodes()[ngn->name]->setZ(ngn->z()); 0434 nodes()[agnameof(ngn)]->updateWithNode(ngn); 0435 if (nodes()[agnameof(ngn)]->canvasElement()) { 0436 // nodes()[ngn->id()]->canvasElement()->setGh(m_height); 0437 } 0438 } else { 0439 qCDebug(KGRAPHVIEWERLIB_LOG) << "new"; 0440 GraphNode *newgn = new GraphNode(ngn); 0441 // qCDebug(KGRAPHVIEWERLIB_LOG) << "new created"; 0442 nodes().insert(agnameof(ngn), newgn); 0443 // qCDebug(KGRAPHVIEWERLIB_LOG) << "new inserted"; 0444 } 0445 0446 // copy node edges 0447 edge_t *nge = agfstout(newGraph, ngn); 0448 while (nge) { 0449 // qCDebug(KGRAPHVIEWERLIB_LOG) << "edge " << nge->id; 0450 const QString edgeName = QString::fromUtf8(agnameof(aghead(nge))) + QString::fromUtf8(agnameof(agtail(nge))); 0451 if (edges().contains(edgeName)) { 0452 // () << "edge known" << nge->id; 0453 // edges()[nge->name]->setZ(nge->z()); 0454 edges()[edgeName]->updateWithEdge(nge); 0455 if (edges()[edgeName]->canvasEdge()) { 0456 // edges()[nge->id()]->canvasEdge()->setGh(m_height); 0457 } 0458 } else { 0459 qCDebug(KGRAPHVIEWERLIB_LOG) << "new edge" << edgeName; 0460 { 0461 GraphEdge *newEdge = new GraphEdge(); 0462 newEdge->setId(edgeName); 0463 newEdge->updateWithEdge(nge); 0464 if (elementNamed(agnameof(agtail(nge))) == nullptr) { 0465 GraphNode *newgn = new GraphNode(); 0466 // qCDebug(KGRAPHVIEWERLIB_LOG) << "new created"; 0467 nodes().insert(agnameof(agtail(nge)), newgn); 0468 } 0469 newEdge->setFromNode(elementNamed(agnameof(agtail(nge)))); 0470 if (elementNamed(agnameof(aghead(nge))) == nullptr) { 0471 GraphNode *newgn = new GraphNode(); 0472 // qCDebug(KGRAPHVIEWERLIB_LOG) << "new created"; 0473 nodes().insert(agnameof(aghead(nge)), newgn); 0474 } 0475 newEdge->setToNode(elementNamed(agnameof(aghead(nge)))); 0476 edges().insert(edgeName, newEdge); 0477 } 0478 } 0479 nge = agnxtedge(newGraph, nge, ngn); 0480 } 0481 ngn = agnxtnode(newGraph, ngn); 0482 } 0483 qCDebug(KGRAPHVIEWERLIB_LOG) << "Done"; 0484 emit readyToDisplay(); 0485 computeCells(); 0486 } 0487 0488 void DotGraph::updateWithGraph(const DotGraph &newGraph) 0489 { 0490 GraphElement::updateWithElement(newGraph); 0491 m_width = newGraph.width(); 0492 m_height = newGraph.height(); 0493 m_scale = newGraph.scale(); 0494 m_directed = newGraph.directed(); 0495 m_strict = newGraph.strict(); 0496 computeCells(); 0497 for (GraphSubgraph *nsg : newGraph.subgraphs()) { 0498 qCDebug(KGRAPHVIEWERLIB_LOG) << "subgraph" << nsg->id(); 0499 if (subgraphs().contains(nsg->id())) { 0500 qCDebug(KGRAPHVIEWERLIB_LOG) << "subgraph known" << nsg->id(); 0501 subgraphs().value(nsg->id())->updateWithSubgraph(*nsg); 0502 if (subgraphs().value(nsg->id())->canvasElement()) { 0503 // subgraphs().value(nsg->id())->canvasElement()->setGh(m_height); 0504 } 0505 } else { 0506 qCDebug(KGRAPHVIEWERLIB_LOG) << "new subgraph" << nsg->id(); 0507 GraphSubgraph *newSubgraph = new GraphSubgraph(); 0508 newSubgraph->updateWithSubgraph(*nsg); 0509 newSubgraph->setZ(0); 0510 subgraphs().insert(nsg->id(), newSubgraph); 0511 } 0512 } 0513 for (GraphNode *ngn : newGraph.nodes()) { 0514 qCDebug(KGRAPHVIEWERLIB_LOG) << "node " << ngn->id(); 0515 if (nodes().contains(ngn->id())) { 0516 qCDebug(KGRAPHVIEWERLIB_LOG) << "known"; 0517 nodes()[ngn->id()]->setZ(ngn->z()); 0518 nodes()[ngn->id()]->updateWithNode(*ngn); 0519 if (nodes()[ngn->id()]->canvasElement()) { 0520 // nodes()[ngn->id()]->canvasElement()->setGh(m_height); 0521 } 0522 } else { 0523 qCDebug(KGRAPHVIEWERLIB_LOG) << "new"; 0524 GraphNode *newgn = new GraphNode(*ngn); 0525 // qCDebug(KGRAPHVIEWERLIB_LOG) << "new created"; 0526 nodes().insert(ngn->id(), newgn); 0527 // qCDebug(KGRAPHVIEWERLIB_LOG) << "new inserted"; 0528 } 0529 } 0530 for (GraphEdge *nge : newGraph.edges()) { 0531 qCDebug(KGRAPHVIEWERLIB_LOG) << "edge " << nge->id(); 0532 if (edges().contains(nge->id())) { 0533 qCDebug(KGRAPHVIEWERLIB_LOG) << "edge known" << nge->id(); 0534 edges()[nge->id()]->setZ(nge->z()); 0535 edges()[nge->id()]->updateWithEdge(*nge); 0536 if (edges()[nge->id()]->canvasEdge()) { 0537 // edges()[nge->id()]->canvasEdge()->setGh(m_height); 0538 } 0539 } else { 0540 qCDebug(KGRAPHVIEWERLIB_LOG) << "new edge" << nge->id(); 0541 { 0542 GraphEdge *newEdge = new GraphEdge(); 0543 newEdge->setId(nge->id()); 0544 newEdge->updateWithEdge(*nge); 0545 newEdge->setFromNode(elementNamed(nge->fromNode()->id())); 0546 newEdge->setToNode(elementNamed(nge->toNode()->id())); 0547 edges().insert(nge->id(), newEdge); 0548 } 0549 } 0550 } 0551 qCDebug(KGRAPHVIEWERLIB_LOG) << "Done"; 0552 computeCells(); 0553 } 0554 0555 void DotGraph::removeNodeNamed(const QString &nodeName) 0556 { 0557 qCDebug(KGRAPHVIEWERLIB_LOG) << nodeName; 0558 GraphNode *node = dynamic_cast<GraphNode *>(elementNamed(nodeName)); 0559 if (node == nullptr) { 0560 qCWarning(KGRAPHVIEWERLIB_LOG) << "No such node " << nodeName; 0561 return; 0562 } 0563 0564 GraphEdgeMap::iterator it, it_end; 0565 it = m_edgesMap.begin(); 0566 it_end = m_edgesMap.end(); 0567 while (it != it_end) { 0568 if (it.value()->fromNode() == node || it.value()->toNode() == node) { 0569 GraphEdge *edge = it.value(); 0570 if (edge->canvasEdge()) { 0571 edge->canvasEdge()->hide(); 0572 delete edge->canvasEdge(); 0573 delete edge; 0574 } 0575 it = edges().erase(it); 0576 } else { 0577 ++it; 0578 } 0579 } 0580 0581 if (node->canvasNode()) { 0582 node->canvasNode()->hide(); 0583 delete node->canvasNode(); 0584 node->setCanvasNode(nullptr); 0585 } 0586 nodes().remove(nodeName); 0587 delete node; 0588 } 0589 0590 void DotGraph::removeNodeFromSubgraph(const QString &nodeName, const QString &subgraphName) 0591 { 0592 qCDebug(KGRAPHVIEWERLIB_LOG) << nodeName << subgraphName; 0593 GraphNode *node = dynamic_cast<GraphNode *>(elementNamed(nodeName)); 0594 if (node == nullptr) { 0595 qCWarning(KGRAPHVIEWERLIB_LOG) << "No such node " << nodeName; 0596 return; 0597 } 0598 0599 GraphSubgraph *subgraph = subgraphs()[subgraphName]; 0600 if (subgraph == nullptr) { 0601 qCWarning(KGRAPHVIEWERLIB_LOG) << "No such subgraph " << subgraphName; 0602 return; 0603 } 0604 0605 subgraph->removeElement(node); 0606 if (subgraph->content().isEmpty()) { 0607 removeSubgraphNamed(subgraphName); 0608 } 0609 } 0610 0611 void DotGraph::removeSubgraphNamed(const QString &subgraphName) 0612 { 0613 qCDebug(KGRAPHVIEWERLIB_LOG) << subgraphName << " from " << subgraphs().keys(); 0614 GraphSubgraph *subgraph = subgraphs()[subgraphName]; 0615 0616 if (subgraph == nullptr) { 0617 qCWarning(KGRAPHVIEWERLIB_LOG) << "Subgraph" << subgraphName << "not found"; 0618 return; 0619 } 0620 GraphEdgeMap::iterator it, it_end; 0621 it = m_edgesMap.begin(); 0622 it_end = m_edgesMap.end(); 0623 while (it != it_end) { 0624 if (it.value()->fromNode() == subgraph || it.value()->toNode() == subgraph) { 0625 GraphEdge *edge = it.value(); 0626 if (edge->canvasEdge()) { 0627 edge->canvasEdge()->hide(); 0628 delete edge->canvasEdge(); 0629 delete edge; 0630 } 0631 it = edges().erase(it); 0632 } else { 0633 ++it; 0634 } 0635 } 0636 0637 if (subgraph->canvasSubgraph()) { 0638 subgraph->canvasSubgraph()->hide(); 0639 delete subgraph->canvasSubgraph(); 0640 subgraph->setCanvasSubgraph(nullptr); 0641 } 0642 for (GraphElement *element : subgraph->content()) { 0643 if (dynamic_cast<GraphNode *>(element)) { 0644 qCDebug(KGRAPHVIEWERLIB_LOG) << "Adding" << element->id() << "to main graph"; 0645 nodes()[element->id()] = dynamic_cast<GraphNode *>(element); 0646 } else if (dynamic_cast<GraphSubgraph *>(element)) { 0647 subgraphs()[element->id()] = dynamic_cast<GraphSubgraph *>(element); 0648 } else { 0649 qCWarning(KGRAPHVIEWERLIB_LOG) << "Don't know how to handle" << element->id(); 0650 } 0651 } 0652 subgraph->content().clear(); 0653 subgraphs().remove(subgraphName); 0654 delete subgraph; 0655 } 0656 0657 void DotGraph::removeEdge(const QString &id) 0658 { 0659 qCDebug(KGRAPHVIEWERLIB_LOG) << id; 0660 GraphEdgeMap::iterator it = edges().begin(); 0661 for (; it != edges().end(); it++) { 0662 GraphEdge *edge = it.value(); 0663 if (edge->id() == id) { 0664 if (edge->canvasEdge()) { 0665 edge->canvasEdge()->hide(); 0666 delete edge->canvasEdge(); 0667 delete edge; 0668 } 0669 edges().remove(id); 0670 break; 0671 } 0672 } 0673 } 0674 0675 void DotGraph::removeElement(const QString &id) 0676 { 0677 qCDebug(KGRAPHVIEWERLIB_LOG) << id; 0678 GraphNodeMap::const_iterator itN = nodes().constBegin(); 0679 for (; itN != nodes().constEnd(); itN++) { 0680 const GraphNode *node = itN.value(); 0681 if (node->id() == id) { 0682 removeNodeNamed(id); 0683 return; 0684 } 0685 } 0686 GraphEdgeMap::const_iterator itE = edges().constBegin(); 0687 for (; itE != edges().constEnd(); itE++) { 0688 const GraphEdge *edge = itE.value(); 0689 if (edge->id() == id) { 0690 removeEdge(id); 0691 return; 0692 } 0693 } 0694 GraphSubgraphMap::const_iterator itS = subgraphs().constBegin(); 0695 for (; itS != subgraphs().constEnd(); itS++) { 0696 const GraphSubgraph *subgraph = itS.value(); 0697 if (subgraph->id() == id) { 0698 removeSubgraphNamed(id); 0699 return; 0700 } 0701 } 0702 } 0703 0704 void DotGraph::setAttribute(const QString &elementId, const QString &attributeName, const QString &attributeValue) 0705 { 0706 if (nodes().contains(elementId)) { 0707 nodes()[elementId]->attributes()[attributeName] = attributeValue; 0708 } else if (edges().contains(elementId)) { 0709 edges()[elementId]->attributes()[attributeName] = attributeValue; 0710 } else if (subgraphs().contains(elementId)) { 0711 subgraphs()[elementId]->attributes()[attributeName] = attributeValue; 0712 } 0713 } 0714 0715 GraphElement *DotGraph::elementNamed(const QString &id) 0716 { 0717 GraphElement *ret = nullptr; 0718 if ((ret = m_nodesMap.value(id, nullptr))) { 0719 return ret; 0720 } 0721 if ((ret = m_edgesMap.value(id, nullptr))) { 0722 return ret; 0723 } 0724 for (GraphSubgraph *subGraph : m_subgraphsMap) { 0725 if ((ret = subGraph->elementNamed(id))) { 0726 return ret; 0727 } 0728 } 0729 return nullptr; 0730 } 0731 0732 void DotGraph::setGraphAttributes(QMap<QString, QString> attribs) 0733 { 0734 qCDebug(KGRAPHVIEWERLIB_LOG) << attribs; 0735 attributes() = attribs; 0736 } 0737 0738 void DotGraph::addNewNode(QMap<QString, QString> attribs) 0739 { 0740 qCDebug(KGRAPHVIEWERLIB_LOG) << attribs; 0741 GraphNode *newNode = new GraphNode(); 0742 newNode->attributes() = attribs; 0743 nodes().insert(newNode->id(), newNode); 0744 qCDebug(KGRAPHVIEWERLIB_LOG) << "node added as" << newNode->id(); 0745 } 0746 0747 void DotGraph::addNewSubgraph(QMap<QString, QString> attribs) 0748 { 0749 qCDebug(KGRAPHVIEWERLIB_LOG) << attribs; 0750 GraphSubgraph *newSG = new GraphSubgraph(); 0751 newSG->attributes() = attribs; 0752 subgraphs()[newSG->id()] = newSG; 0753 qCDebug(KGRAPHVIEWERLIB_LOG) << "subgraph added as" << newSG->id(); 0754 } 0755 0756 void DotGraph::addNewNodeToSubgraph(QMap<QString, QString> attribs, QString subgraph) 0757 { 0758 qCDebug(KGRAPHVIEWERLIB_LOG) << attribs << "to" << subgraph; 0759 GraphNode *newNode = new GraphNode(); 0760 newNode->attributes() = attribs; 0761 subgraphs()[subgraph]->content().push_back(newNode); 0762 0763 qCDebug(KGRAPHVIEWERLIB_LOG) << "node added as" << newNode->id() << "in" << subgraph; 0764 } 0765 0766 void DotGraph::addExistingNodeToSubgraph(QMap<QString, QString> attribs, QString subgraph) 0767 { 0768 qCDebug(KGRAPHVIEWERLIB_LOG) << attribs << "to" << subgraph; 0769 GraphNode *node = dynamic_cast<GraphNode *>(elementNamed(attribs["id"])); 0770 if (node == nullptr) { 0771 qCWarning(KGRAPHVIEWERLIB_LOG) << "No such node" << attribs["id"]; 0772 return; 0773 } 0774 if (nodes().contains(attribs["id"])) { 0775 nodes().remove(attribs["id"]); 0776 node->attributes() = attribs; 0777 subgraphs()[subgraph]->content().push_back(node); 0778 qCDebug(KGRAPHVIEWERLIB_LOG) << "node " << node->id() << " added in " << subgraph; 0779 } else { 0780 for (GraphSubgraph *gs : subgraphs()) { 0781 GraphElement *elt = nullptr; 0782 for (GraphElement *element : gs->content()) { 0783 if (element == node) { 0784 elt = element; 0785 break; 0786 } 0787 } 0788 if (elt) { 0789 qCDebug(KGRAPHVIEWERLIB_LOG) << "removing node " << elt->id() << " from " << gs->id(); 0790 gs->removeElement(elt); 0791 subgraphs()[subgraph]->content().push_back(elt); 0792 qCDebug(KGRAPHVIEWERLIB_LOG) << "node " << elt->id() << " added in " << subgraph; 0793 break; 0794 } 0795 } 0796 } 0797 } 0798 0799 void DotGraph::moveExistingNodeToMainGraph(QMap<QString, QString> attribs) 0800 { 0801 qCDebug(KGRAPHVIEWERLIB_LOG) << attribs; 0802 GraphNode *node = dynamic_cast<GraphNode *>(elementNamed(attribs["id"])); 0803 if (node == nullptr) { 0804 qCWarning(KGRAPHVIEWERLIB_LOG) << "No such node" << attribs["id"]; 0805 return; 0806 } else if (nodes().contains(attribs["id"])) { 0807 qCWarning(KGRAPHVIEWERLIB_LOG) << "Node" << attribs["id"] << "already in main graph"; 0808 return; 0809 } else { 0810 for (GraphSubgraph *gs : subgraphs()) { 0811 bool found = false; 0812 for (GraphElement *element : gs->content()) { 0813 if (element == node) { 0814 found = true; 0815 break; 0816 } 0817 } 0818 if (found) { 0819 qCDebug(KGRAPHVIEWERLIB_LOG) << "removing node " << node->id() << " from " << gs->id(); 0820 gs->removeElement(node); 0821 nodes()[node->id()] = node; 0822 qCDebug(KGRAPHVIEWERLIB_LOG) << "node " << node->id() << " moved to main graph"; 0823 break; 0824 } 0825 } 0826 } 0827 } 0828 0829 void DotGraph::addNewEdge(QString src, QString tgt, QMap<QString, QString> attribs) 0830 { 0831 qCDebug(KGRAPHVIEWERLIB_LOG) << src << tgt << attribs; 0832 GraphEdge *newEdge = new GraphEdge(); 0833 newEdge->attributes() = attribs; 0834 GraphElement *srcElement = elementNamed(src); 0835 if (srcElement == nullptr) { 0836 srcElement = elementNamed(QString("cluster_") + src); 0837 } 0838 GraphElement *tgtElement = elementNamed(tgt); 0839 if (tgtElement == nullptr) { 0840 tgtElement = elementNamed(QString("cluster_") + tgt); 0841 } 0842 0843 if (srcElement == nullptr || tgtElement == nullptr) { 0844 qCWarning(KGRAPHVIEWERLIB_LOG) << src << "or" << tgt << "missing"; 0845 return; 0846 } 0847 if (attribs.contains("id")) { 0848 newEdge->setId(attribs["id"]); 0849 } else { 0850 newEdge->setId(src + tgt + QUuid::createUuid().toString().remove('{').remove('}').remove('-')); 0851 } 0852 newEdge->setFromNode(srcElement); 0853 newEdge->setToNode(tgtElement); 0854 edges().insert(newEdge->id(), newEdge); 0855 } 0856 0857 void DotGraph::removeAttribute(const QString &nodeName, const QString &attribName) 0858 { 0859 GraphElement *element = elementNamed(nodeName); 0860 if (element) { 0861 element->removeAttribute(attribName); 0862 } 0863 } 0864 0865 void DotGraph::renameNode(const QString &oldNodeName, const QString &newNodeName) 0866 { 0867 if (oldNodeName != newNodeName) { 0868 qCDebug(KGRAPHVIEWERLIB_LOG) << "Renaming " << oldNodeName << " into " << newNodeName; 0869 GraphNode *node = nodes()[oldNodeName]; 0870 nodes().remove(oldNodeName); 0871 node->setId(newNodeName); 0872 nodes()[newNodeName] = node; 0873 } 0874 } 0875 0876 QString DotGraph::backColor() const 0877 { 0878 if (m_attributes.find("bgcolor") != m_attributes.end()) { 0879 return m_attributes["bgcolor"]; 0880 } else { 0881 return QString(); 0882 } 0883 } 0884 0885 } 0886 0887 #include "moc_dotgraph.cpp"