00001
00002
00003
00004
00005
00006
00007
00008
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 &,
const QString &,
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 don't 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);
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"