summaryrefslogtreecommitdiffstats
path: root/parts/classview/digraphview.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'parts/classview/digraphview.cpp')
-rw-r--r--parts/classview/digraphview.cpp414
1 files changed, 414 insertions, 0 deletions
diff --git a/parts/classview/digraphview.cpp b/parts/classview/digraphview.cpp
new file mode 100644
index 00000000..b566757f
--- /dev/null
+++ b/parts/classview/digraphview.cpp
@@ -0,0 +1,414 @@
+/***************************************************************************
+ * Copyright (C) 2001 by Bernd Gehrmann *
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#include "digraphview.h"
+
+#include <math.h>
+#include <stdlib.h>
+#include <qapplication.h>
+#include <qpainter.h>
+#include <qpaintdevicemetrics.h>
+#include <qtextstream.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kprocess.h>
+#include <kstandarddirs.h>
+#include <kglobalsettings.h>
+#include <ktempfile.h>
+#include <kdeversion.h>
+#include <kdebug.h>
+
+struct DigraphNode
+{
+ int x;
+ int y;
+ int w;
+ int h;
+ QString name;
+};
+
+
+struct DigraphEdge
+{
+ QPointArray points;
+};
+
+
+DigraphView::DigraphView(QWidget *parent, const char *name)
+ : QScrollView(parent, name, WRepaintNoErase|WStaticContents|WResizeNoErase)
+{
+ viewport()->setBackgroundMode(PaletteBase);
+
+ QPaintDeviceMetrics m(this);
+ xscale = m.logicalDpiX();
+ yscale = m.logicalDpiY();
+
+ width = -1;
+ height = -1;
+
+ nodes.setAutoDelete(true);
+ edges.setAutoDelete(true);
+ selNode = 0;
+}
+
+
+DigraphView::~DigraphView()
+{
+}
+
+
+int DigraphView::toXPixel(double x)
+{
+ return (int) (x*xscale);
+}
+
+
+int DigraphView::toYPixel(double y)
+{
+ return height - (int) (y*yscale);
+}
+
+
+void DigraphView::setRenderedExtent(double w, double h)
+{
+ width = (int) (w*xscale);
+ height = (int) (h*yscale);
+ resizeContents(width+1, height+1);
+}
+
+
+void DigraphView::addRenderedNode(const QString &name,
+ double x, double y, double w, double h)
+{
+ DigraphNode *node = new DigraphNode;
+ node->x = toXPixel(x);
+ node->y = toYPixel(y);
+ node->w = (int) (w*xscale);
+ node->h = (int) (h*yscale);
+ node->name = name;
+ nodes.append(node);
+}
+
+
+void DigraphView::addRenderedEdge(const QString &/*name1*/, const QString &/*name2*/,
+ QMemArray<double> coords)
+{
+ if (coords.count() < 4)
+ return;
+
+ DigraphEdge *edge = new DigraphEdge;
+ edge->points.resize(coords.count()/2);
+
+ for (uint i = 0; i < edge->points.count(); ++i)
+ edge->points[i] = QPoint(toXPixel(coords[2*i]), toYPixel(coords[2*i+1]));
+
+ edges.append(edge);
+}
+
+
+void DigraphView::addEdge(const QString &name1, const QString &name2)
+{
+ QString line;
+ line = "\"";
+ line += name1;
+ line += "\" -> \"";
+ line += name2;
+ line += "\";";
+ inputs.append(line);
+}
+
+
+void DigraphView::clear()
+{
+ nodes.clear();
+ edges.clear();
+ selNode = 0;
+ width = -1;
+ height = -1;
+ inputs.clear();
+ viewport()->update();
+}
+
+
+void DigraphView::setSelected(const QString &name)
+{
+ QPtrListIterator<DigraphNode> it(nodes);
+ for (; it.current(); ++it) {
+ if (it.current()->name == name) {
+ updateContents(selNode->x-selNode->w/2, selNode->y-selNode->h/2,
+ selNode->w, selNode->h);
+ selNode = it.current();
+ updateContents(selNode->x-selNode->w/2, selNode->y-selNode->h/2,
+ selNode->w, selNode->h);
+ return;
+ }
+ }
+}
+
+
+void DigraphView::ensureVisible(const QString &name)
+{
+ QPtrListIterator<DigraphNode> it(nodes);
+ for (; it.current(); ++it) {
+ if (it.current()->name == name) {
+ QScrollView::ensureVisible((*it)->x, (*it)->y, (*it)->w, (*it)->h);
+ return;
+ }
+ }
+}
+
+
+QStringList DigraphView::splitLine(QString str)
+{
+ QStringList result;
+
+ while (!str.isEmpty()) {
+ if (str[0] == '"') {
+ int pos = str.find('"', 1);
+ if (pos == -1)
+ pos = str.length();
+ result << str.mid(1, pos-1);
+ str.remove(0, pos+1);
+ } else {
+ int pos = str.find(' ');
+ if (pos == -1)
+ pos = str.length();
+ result << str.left(pos);
+ str.remove(0, pos+1);
+ }
+ uint i = 0; while (i<str.length() && str[i] == ' ') ++i;
+ str.remove(0, i);
+ }
+
+ return result;
+}
+
+
+void DigraphView::parseDotResults(const QStringList &list)
+{
+ QStringList::ConstIterator it;
+ for (it = list.begin(); it != list.end(); ++it) {
+ QStringList tokens = splitLine(*it);
+ if (tokens.count() == 0)
+ continue;
+ if (tokens[0] == "graph") {
+ if (tokens.count() < 4)
+ continue;
+ setRenderedExtent(tokens[2].toDouble(), tokens[3].toDouble());
+ } else if (tokens[0] == "node") {
+ if (tokens.count() < 6)
+ continue;
+ addRenderedNode(tokens[1], tokens[2].toDouble(), tokens[3].toDouble(),
+ tokens[4].toDouble(), tokens[5].toDouble());
+ } else if (tokens[0] == "edge") {
+ if (tokens.count() < 8)
+ continue;
+ QMemArray<double> coords(tokens.count()-6);
+ for (uint i=0; i != tokens.count()-6; ++i)
+ coords[i] = tokens[i+4].toDouble();
+ addRenderedEdge(tokens[1], tokens[2], coords);
+ }
+ }
+}
+
+
+void DigraphView::process( const QString& file, const QString& ext )
+{
+ QString cmd = KGlobal::dirs()->findExe("dot");
+ if (cmd.isEmpty()) {
+ KMessageBox::sorry(0, i18n("You do not have 'dot' installed.\nIt can be downloaded from www.graphviz.org."));
+ return;
+ }
+
+ QStringList results;
+
+ KTempFile ifile, ofile;
+ QTextStream &is = *ifile.textStream();
+ is << "digraph G {" << endl;
+ is << "rankdir=LR;" << endl;
+ is << "node [shape=box,fontname=Helvetica,fontsize=12];" << endl;
+ QStringList::Iterator it;
+ for (it = inputs.begin(); it != inputs.end(); ++it)
+ is << (*it) << endl;
+ is << "}" << endl;
+ ifile.close();
+
+ KProcess proc;
+ if( !file.isEmpty() && !ext.isEmpty() )
+ {
+ proc << cmd << QString("-T")+ext << ifile.name() << "-o" << file;
+ kdDebug() << "Executing: " << cmd <<" "<<QString("-T")+ext <<" "<< ifile.name() << "-o"<<file << endl;
+ }else
+ {
+ proc << cmd << "-Tplain" << ifile.name() << "-o" << ofile.name();
+ }
+ proc.start(KProcess::Block);
+
+ if( !file.isEmpty() && !ext.isEmpty() )
+ {
+ return;
+ }
+
+ QTextStream &os = *ofile.textStream();
+ while (!os.atEnd())
+ results << os.readLine();
+ ofile.close();
+
+ parseDotResults(results);
+ inputs.clear();
+
+ if (nodes.first())
+ selNode = nodes.first();
+ viewport()->update();
+}
+
+void DigraphView::drawContents(QPainter* p, int clipx, int clipy, int clipw, int cliph)
+{
+ QRect clipRect(clipx, clipy, clipw, cliph);
+ p->eraseRect(clipRect);
+
+ p->setFont(KGlobalSettings::generalFont());
+ QPtrListIterator<DigraphNode> it1(nodes);
+ for (; it1.current(); ++it1) {
+ QRect r((*it1)->x-(*it1)->w/2, (*it1)->y-(*it1)->h/2, (*it1)->w, (*it1)->h);
+ if (r.intersects(clipRect)) {
+ if (it1.current() == selNode)
+ p->fillRect(r, QBrush(lightGray, SolidPattern));
+ else
+ p->drawRect(r);
+ p->drawText(r, AlignCenter, (*it1)->name);
+ }
+ }
+ p->setBrush(QBrush(black, SolidPattern));
+ QPtrListIterator<DigraphEdge> it2(edges);
+ for (; it2.current(); ++it2) {
+ int n = (*it2)->points.count();
+ for (int i=0; i+3 < n; i+=3)
+ {
+ QPointArray a(4);
+ QPointArray &b = (*it2)->points;
+ for (int j=0; j<4; ++j)
+ a.setPoint(j, b.point(i+j));
+ if (a.boundingRect().intersects(clipRect))
+ p->drawCubicBezier((*it2)->points, i);
+ }
+ QPoint p1 = (*it2)->points[n-2];
+ QPoint p2 = (*it2)->points[n-1];
+ QPoint d = p1-p2;
+ double l = sqrt(d.x()*d.x()+d.y()*d.y());
+ double d11 = (10.0)/l*d.x();
+ double d12 = (10.0)/l*d.y();
+ double d21 = -(3.0/l)*d.y();
+ double d22 = (3.0/l)*d.x();
+ QPointArray triangle(3);
+ triangle[0] = p2 + QPoint((int)(d11+d21),(int)(d12+d22));
+ triangle[1] = p2 + QPoint((int)(d11-d21),(int)(d12-d22));
+ triangle[2] = p2;
+ p->drawPolygon(triangle, true);
+ }
+}
+
+
+void DigraphView::contentsMousePressEvent(QMouseEvent *e)
+{
+ QPtrListIterator<DigraphNode> it1(nodes);
+ for (; it1.current(); ++it1) {
+ QRect r((*it1)->x-(*it1)->w/2, (*it1)->y-(*it1)->h/2, (*it1)->w, (*it1)->h);
+ if (r.contains(e->pos())) {
+ if (selNode) {
+ QRect oldr(selNode->x-selNode->w/2, selNode->y-selNode->h/2,
+ selNode->w, selNode->h);
+ updateContents(oldr);
+ }
+ selNode = it1.current();
+ emit selected(selNode->name);
+ updateContents(r);
+ }
+
+ }
+}
+
+
+QSize DigraphView::sizeHint() const
+{
+ if (width == -1)
+ return QSize(100, 100); // arbitrary
+
+ QSize dsize = KGlobalSettings::desktopGeometry(viewport()).size();
+ kdDebug(9003) << "sizehint for inheritance diagram" << dsize << " " << width << " " << height << endl;
+ return QSize(width, height).boundedTo(QSize(dsize.width()*2/3, dsize.height()*2/3));
+}
+
+
+#if 0
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+
+ DigraphView *dw = new DigraphView(0, "dot widget");
+dw->addEdge( "5th Edition", "6th Edition");
+dw->addEdge( "5th Edition", "PWB 1.0");
+dw->addEdge( "6th Edition", "LSX");
+dw->addEdge( "6th Edition", "1 BSD");
+dw->addEdge( "6th Edition", "Mini Unix");
+dw->addEdge( "6th Edition", "Wollongong");
+dw->addEdge( "6th Edition", "Interdata");
+dw->addEdge( "Interdata", "Unix/TS 3.0");
+dw->addEdge( "Interdata", "PWB 2.0");
+dw->addEdge( "Interdata", "7th Edition");
+dw->addEdge( "7th Edition", "8th Edition");
+dw->addEdge( "7th Edition", "32V");
+dw->addEdge( "7th Edition", "V7M");
+dw->addEdge( "7th Edition", "Ultrix-11");
+dw->addEdge( "7th Edition", "Xenix");
+dw->addEdge( "7th Edition", "UniPlus+");
+dw->addEdge( "V7M", "Ultrix-11");
+dw->addEdge( "8th Edition", "9th Edition");
+dw->addEdge( "1 BSD", "2 BSD");
+dw->addEdge( "2 BSD", "2.8 BSD");
+dw->addEdge( "2.8 BSD", "Ultrix-11");
+dw->addEdge( "2.8 BSD", "2.9 BSD");
+dw->addEdge( "32V", "3 BSD");
+dw->addEdge( "3 BSD", "4 BSD");
+dw->addEdge( "4 BSD", "4.1 BSD");
+dw->addEdge( "4.1 BSD", "4.2 BSD");
+dw->addEdge( "4.1 BSD", "2.8 BSD");
+dw->addEdge( "4.1 BSD", "8th Edition");
+dw->addEdge( "4.2 BSD", "4.3 BSD");
+dw->addEdge( "4.2 BSD", "Ultrix-32");
+dw->addEdge( "PWB 1.0", "PWB 1.2");
+dw->addEdge( "PWB 1.0", "USG 1.0");
+dw->addEdge( "PWB 1.2", "PWB 2.0");
+dw->addEdge( "USG 1.0", "CB Unix 1");
+dw->addEdge( "USG 1.0", "USG 2.0");
+dw->addEdge( "CB Unix 1", "CB Unix 2");
+dw->addEdge( "CB Unix 2", "CB Unix 3");
+dw->addEdge( "CB Unix 3", "Unix/TS++");
+dw->addEdge( "CB Unix 3", "PDP-11 Sys V");
+dw->addEdge( "USG 2.0", "USG 3.0");
+dw->addEdge( "USG 3.0", "Unix/TS 3.0");
+dw->addEdge( "PWB 2.0", "Unix/TS 3.0");
+dw->addEdge( "Unix/TS 1.0", "Unix/TS 3.0");
+dw->addEdge( "Unix/TS 3.0", "TS 4.0");
+dw->addEdge( "Unix/TS++", "TS 4.0");
+dw->addEdge( "CB Unix 3", "TS 4.0");
+dw->addEdge( "TS 4.0", "System V.0");
+dw->addEdge( "System V.0", "System V.2");
+dw->addEdge( "System V.2", "System V.3");
+ dw->process();
+ dw->show();
+
+ return app.exec();
+}
+#endif
+
+#include "digraphview.moc"