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"