KDevelop API Documentation

digraphview.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  *   Copyright (C) 2001 by Bernd Gehrmann                                  *
00003  *   bernd@kdevelop.org                                                    *
00004  *                                                                         *
00005  *   This program is free software; you can redistribute it and/or modify  *
00006  *   it under the terms of the GNU General Public License as published by  *
00007  *   the Free Software Foundation; either version 2 of the License, or     *
00008  *   (at your option) any later version.                                   *
00009  *                                                                         *
00010  ***************************************************************************/
00011 
00012 #include "digraphview.h"
00013 
00014 #include <math.h>
00015 #include <stdlib.h>
00016 #include <qapplication.h>
00017 #include <qpainter.h>
00018 #include <qpaintdevicemetrics.h>
00019 #include <qtextstream.h>
00020 #include <kglobal.h>
00021 #include <klocale.h>
00022 #include <kmessagebox.h>
00023 #include <kprocess.h>
00024 #include <kstandarddirs.h>
00025 #include <kglobalsettings.h>
00026 #include <ktempfile.h>
00027 #include <kdeversion.h>
00028 
00029 struct DigraphNode
00030 {
00031     int x;
00032     int y;
00033     int w;
00034     int h;
00035     QString name;
00036 };
00037 
00038 
00039 struct DigraphEdge
00040 {
00041     QPointArray points;
00042 };
00043 
00044 
00045 DigraphView::DigraphView(QWidget *parent, const char *name)
00046     : QScrollView(parent, name, WRepaintNoErase|WStaticContents|WResizeNoErase)
00047 {
00048     viewport()->setBackgroundMode(PaletteBase);
00049 
00050     QPaintDeviceMetrics m(this);
00051     xscale = m.logicalDpiX();
00052     yscale = m.logicalDpiY();
00053 
00054     width = -1;
00055     height = -1;
00056 
00057     nodes.setAutoDelete(true);
00058     edges.setAutoDelete(true);
00059     selNode = 0;
00060 }
00061 
00062 
00063 DigraphView::~DigraphView()
00064 {
00065 }
00066 
00067 
00068 int DigraphView::toXPixel(double x)
00069 {
00070     return (int) (x*xscale);
00071 }
00072 
00073 
00074 int DigraphView::toYPixel(double y)
00075 {
00076     return height - (int) (y*yscale);
00077 }
00078 
00079 
00080 void DigraphView::setRenderedExtent(double w, double h)
00081 {
00082     width = (int) (w*xscale);
00083     height = (int) (h*yscale);
00084     resizeContents(width+1, height+1);
00085 }
00086 
00087 
00088 void DigraphView::addRenderedNode(const QString &name,
00089                                   double x, double y, double w, double h)
00090 {
00091     DigraphNode *node = new DigraphNode;
00092     node->x = toXPixel(x);
00093     node->y = toYPixel(y);
00094     node->w = (int) (w*xscale);
00095     node->h = (int) (h*yscale);
00096     node->name = name;
00097     nodes.append(node);
00098 }
00099 
00100 
00101 void DigraphView::addRenderedEdge(const QString &/*name1*/, const QString &/*name2*/,
00102                                   QMemArray<double> coords)
00103 {
00104     if (coords.count() < 4)
00105         return;
00106 
00107     DigraphEdge *edge = new DigraphEdge;
00108     edge->points.resize(coords.count()/2);
00109 
00110     for (uint i = 0; i < edge->points.count(); ++i)
00111         edge->points[i] = QPoint(toXPixel(coords[2*i]), toYPixel(coords[2*i+1]));
00112 
00113     edges.append(edge);
00114 }
00115 
00116 
00117 void DigraphView::addEdge(const QString &name1, const QString &name2)
00118 {
00119     QString line;
00120     line = "\"";
00121     line += name1;
00122     line += "\" -> \"";
00123     line += name2;
00124     line += "\";";
00125     inputs.append(line);
00126 }
00127 
00128 
00129 void DigraphView::clear()
00130 {
00131     nodes.clear();
00132     edges.clear();
00133     selNode = 0;
00134     viewport()->update();
00135 }
00136 
00137 
00138 void DigraphView::setSelected(const QString &name)
00139 {
00140     QPtrListIterator<DigraphNode> it(nodes);
00141     for (; it.current(); ++it) {
00142         if (it.current()->name == name) {
00143             updateContents(selNode->x-selNode->w/2, selNode->y-selNode->h/2,
00144                            selNode->w, selNode->h);
00145             selNode = it.current();
00146             updateContents(selNode->x-selNode->w/2, selNode->y-selNode->h/2,
00147                            selNode->w, selNode->h);
00148             return;
00149         }
00150     }
00151 }
00152 
00153 
00154 void DigraphView::ensureVisible(const QString &name)
00155 {
00156     QPtrListIterator<DigraphNode> it(nodes);
00157     for (; it.current(); ++it) {
00158         if (it.current()->name == name) {
00159             QScrollView::ensureVisible((*it)->x, (*it)->y, (*it)->w, (*it)->h);
00160             return;
00161         }
00162     }
00163 }
00164 
00165 
00166 QStringList DigraphView::splitLine(QString str)
00167 {
00168     QStringList result;
00169 
00170     while (!str.isEmpty()) {
00171         if (str[0] == '"') {
00172             int pos = str.find('"', 1);
00173             if (pos == -1)
00174                 pos = str.length();
00175             result << str.mid(1, pos-1);
00176             str.remove(0, pos+1);
00177         } else {
00178             int pos = str.find(' ');
00179             if (pos == -1)
00180                 pos = str.length();
00181             result << str.left(pos);
00182             str.remove(0, pos+1);
00183         }
00184         uint i = 0; while (i<str.length() && str[i] == ' ') ++i;
00185         str.remove(0, i);
00186     }
00187 
00188     return result;
00189 }
00190 
00191 
00192 void DigraphView::parseDotResults(const QStringList &list)
00193 {
00194     QStringList::ConstIterator it;
00195     for (it = list.begin(); it != list.end(); ++it) {
00196         QStringList tokens = splitLine(*it);
00197         if (tokens.count() == 0)
00198             continue;
00199         if (tokens[0] == "graph") {
00200             if (tokens.count() < 4)
00201                 continue;
00202             setRenderedExtent(tokens[2].toDouble(), tokens[3].toDouble());
00203         } else if (tokens[0] == "node") {
00204             if (tokens.count() < 6)
00205                 continue;
00206             addRenderedNode(tokens[1], tokens[2].toDouble(), tokens[3].toDouble(),
00207                             tokens[4].toDouble(), tokens[5].toDouble());
00208         } else if (tokens[0] == "edge") {
00209             if (tokens.count() < 8)
00210                 continue;
00211             QMemArray<double> coords(tokens.count()-6);
00212             for (uint i=0; i != tokens.count()-6; ++i)
00213                 coords[i] = tokens[i+4].toDouble();
00214             addRenderedEdge(tokens[1], tokens[2], coords);
00215         }
00216     }
00217 }
00218 
00219 
00220 void DigraphView::process()
00221 {
00222     QString cmd = KGlobal::dirs()->findExe("dot");
00223     if (cmd.isEmpty()) {
00224         KMessageBox::sorry(0, i18n("You do not have 'dot' installed.\nIt can be downloaded from www.graphviz.org."));
00225         return;
00226     }
00227 
00228     QStringList results;
00229 
00230     KTempFile ifile, ofile;
00231     QTextStream &is = *ifile.textStream();
00232     is << "digraph G {" << endl;
00233     is << "rankdir=LR;" << endl;
00234     is << "node [shape=box,fontname=Helvetica,fontsize=12];" << endl;
00235     QStringList::Iterator it;
00236     for (it = inputs.begin(); it != inputs.end(); ++it)
00237         is << (*it) << endl;
00238     is << "}" << endl;
00239     ifile.close();
00240 
00241     KProcess proc;
00242     proc << cmd << "-Tplain" << ifile.name() << "-o" << ofile.name();
00243     proc.start(KProcess::Block);
00244 
00245     QTextStream &os = *ofile.textStream();
00246     while (!os.atEnd())
00247         results << os.readLine();
00248     ofile.close();
00249 
00250     parseDotResults(results);
00251     inputs.clear();
00252 
00253     if (nodes.first())
00254         selNode = nodes.first();
00255     viewport()->update();
00256 }
00257 
00258 
00259 void DigraphView::drawContents(QPainter* p, int clipx, int clipy, int clipw, int cliph)
00260 {
00261     QRect clipRect(clipx, clipy, clipw, cliph);
00262     p->eraseRect(clipRect);
00263 
00264     p->setFont(KGlobalSettings::generalFont());
00265     QPtrListIterator<DigraphNode> it1(nodes);
00266     for (; it1.current(); ++it1) {
00267         QRect r((*it1)->x-(*it1)->w/2, (*it1)->y-(*it1)->h/2, (*it1)->w, (*it1)->h);
00268         if (r.intersects(clipRect)) {
00269             if (it1.current() == selNode)
00270                 p->fillRect(r, QBrush(lightGray, SolidPattern));
00271             else
00272                 p->drawRect(r);
00273             p->drawText(r, AlignCenter, (*it1)->name);
00274         }
00275     }
00276     p->setBrush(QBrush(black, SolidPattern));
00277     QPtrListIterator<DigraphEdge> it2(edges);
00278     for (; it2.current(); ++it2) {
00279         int n = (*it2)->points.count();
00280         for (int i=0; i+3 < n; i+=3)
00281             {
00282                 QPointArray a(4);
00283                 QPointArray &b = (*it2)->points;
00284                 for (int j=0; j<4; ++j)
00285                     a.setPoint(j, b.point(i+j));
00286                 if (a.boundingRect().intersects(clipRect))
00287                     p->drawCubicBezier((*it2)->points, i);
00288             }
00289         QPoint p1 = (*it2)->points[n-2];
00290         QPoint p2 = (*it2)->points[n-1];
00291         QPoint d = p1-p2;
00292         double l = sqrt(d.x()*d.x()+d.y()*d.y());
00293         double d11 = (10.0)/l*d.x();
00294         double d12 = (10.0)/l*d.y();
00295         double d21 = -(3.0/l)*d.y();
00296         double d22 = (3.0/l)*d.x();
00297         QPointArray triangle(3);
00298         triangle[0] = p2 + QPoint((int)(d11+d21),(int)(d12+d22));
00299         triangle[1] = p2 + QPoint((int)(d11-d21),(int)(d12-d22));
00300         triangle[2] = p2;
00301         p->drawPolygon(triangle, true);
00302     }
00303 }
00304 
00305 
00306 void DigraphView::contentsMousePressEvent(QMouseEvent *e)
00307 {
00308     QPtrListIterator<DigraphNode> it1(nodes);
00309     for (; it1.current(); ++it1) {
00310         QRect r((*it1)->x-(*it1)->w/2, (*it1)->y-(*it1)->h/2, (*it1)->w, (*it1)->h);
00311         if (r.contains(e->pos())) {
00312             if (selNode) {
00313                 QRect oldr(selNode->x-selNode->w/2, selNode->y-selNode->h/2,
00314                            selNode->w, selNode->h);
00315                 updateContents(oldr);
00316             }
00317             selNode = it1.current();
00318             emit selected(selNode->name);
00319             updateContents(r);
00320         }
00321 
00322     }
00323 }
00324 
00325 
00326 QSize DigraphView::sizeHint() const
00327 {
00328     if (width == -1)
00329         return QSize(100, 100); // arbitrary
00330 
00331 #if defined(KDE_IS_VERSION)
00332 #if (KDE_IS_VERSION(3,1,90))
00333     QSize dsize = KGlobalSettings::desktopGeometry(viewport()).size();
00334 #else
00335     QSize dsize = QApplication::desktop()->size();
00336 #endif
00337 #else
00338     QSize dsize = QApplication::desktop()->size();
00339 #endif
00340     return QSize(width, height).boundedTo(QSize(dsize.width()*2/3, dsize.height()*2/3));
00341 }
00342 
00343 
00344 #if 0
00345 int main(int argc, char **argv)
00346 {
00347     QApplication app(argc, argv);
00348 
00349     DigraphView *dw = new DigraphView(0, "dot widget");
00350 dw->addEdge(    "5th Edition", "6th Edition");
00351 dw->addEdge(    "5th Edition", "PWB 1.0");
00352 dw->addEdge(    "6th Edition", "LSX");
00353 dw->addEdge(    "6th Edition", "1 BSD");
00354 dw->addEdge(    "6th Edition", "Mini Unix");
00355 dw->addEdge(    "6th Edition", "Wollongong");
00356 dw->addEdge(    "6th Edition", "Interdata");
00357 dw->addEdge(    "Interdata", "Unix/TS 3.0");
00358 dw->addEdge(    "Interdata", "PWB 2.0");
00359 dw->addEdge(    "Interdata", "7th Edition");
00360 dw->addEdge(    "7th Edition", "8th Edition");
00361 dw->addEdge(    "7th Edition", "32V");
00362 dw->addEdge(    "7th Edition", "V7M");
00363 dw->addEdge(    "7th Edition", "Ultrix-11");
00364 dw->addEdge(    "7th Edition", "Xenix");
00365 dw->addEdge(    "7th Edition", "UniPlus+");
00366 dw->addEdge(    "V7M", "Ultrix-11");
00367 dw->addEdge(    "8th Edition", "9th Edition");
00368 dw->addEdge(    "1 BSD", "2 BSD");
00369 dw->addEdge(    "2 BSD", "2.8 BSD");
00370 dw->addEdge(    "2.8 BSD", "Ultrix-11");
00371 dw->addEdge(    "2.8 BSD", "2.9 BSD");
00372 dw->addEdge(    "32V", "3 BSD");
00373 dw->addEdge(    "3 BSD", "4 BSD");
00374 dw->addEdge(    "4 BSD", "4.1 BSD");
00375 dw->addEdge(    "4.1 BSD", "4.2 BSD");
00376 dw->addEdge(    "4.1 BSD", "2.8 BSD");
00377 dw->addEdge(    "4.1 BSD", "8th Edition");
00378 dw->addEdge(    "4.2 BSD", "4.3 BSD");
00379 dw->addEdge(    "4.2 BSD", "Ultrix-32");
00380 dw->addEdge(    "PWB 1.0", "PWB 1.2");
00381 dw->addEdge(    "PWB 1.0", "USG 1.0");
00382 dw->addEdge(    "PWB 1.2", "PWB 2.0");
00383 dw->addEdge(    "USG 1.0", "CB Unix 1");
00384 dw->addEdge(    "USG 1.0", "USG 2.0");
00385 dw->addEdge(    "CB Unix 1", "CB Unix 2");
00386 dw->addEdge(    "CB Unix 2", "CB Unix 3");
00387 dw->addEdge(    "CB Unix 3", "Unix/TS++");
00388 dw->addEdge(    "CB Unix 3", "PDP-11 Sys V");
00389 dw->addEdge(    "USG 2.0", "USG 3.0");
00390 dw->addEdge(    "USG 3.0", "Unix/TS 3.0");
00391 dw->addEdge(    "PWB 2.0", "Unix/TS 3.0");
00392 dw->addEdge(    "Unix/TS 1.0", "Unix/TS 3.0");
00393 dw->addEdge(    "Unix/TS 3.0", "TS 4.0");
00394 dw->addEdge(    "Unix/TS++", "TS 4.0");
00395 dw->addEdge(    "CB Unix 3", "TS 4.0");
00396 dw->addEdge(    "TS 4.0", "System V.0");
00397 dw->addEdge(    "System V.0", "System V.2");
00398 dw->addEdge(    "System V.2", "System V.3");
00399     dw->process();
00400     dw->show();
00401 
00402     return app.exec();
00403 }
00404 #endif
00405 
00406 #include "digraphview.moc"
KDE Logo
This file is part of the documentation for KDevelop Version 3.1.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Tue Feb 22 09:22:38 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003