You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdevelop/parts/classview/digraphview.cpp

415 lines
12 KiB

/***************************************************************************
* Copyright (C) 2001 by Bernd Gehrmann *
* bernd@tdevelop.org *
* *
* 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 <tqapplication.h>
#include <tqpainter.h>
#include <tqpaintdevicemetrics.h>
#include <textstream.h>
#include <kglobal.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <kprocess.h>
#include <kstandarddirs.h>
#include <kglobalsettings.h>
#include <ktempfile.h>
#include <tdeversion.h>
#include <kdebug.h>
struct DigraphNode
{
int x;
int y;
int w;
int h;
TQString name;
};
struct DigraphEdge
{
TQPointArray points;
};
DigraphView::DigraphView(TQWidget *parent, const char *name)
: TQScrollView(parent, name, WRepaintNoErase|WStaticContents|WResizeNoErase)
{
viewport()->setBackgroundMode(PaletteBase);
TQPaintDeviceMetrics 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 TQString &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 TQString &/*name1*/, const TQString &/*name2*/,
TQMemArray<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] = TQPoint(toXPixel(coords[2*i]), toYPixel(coords[2*i+1]));
edges.append(edge);
}
void DigraphView::addEdge(const TQString &name1, const TQString &name2)
{
TQString 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 TQString &name)
{
TQPtrListIterator<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 TQString &name)
{
TQPtrListIterator<DigraphNode> it(nodes);
for (; it.current(); ++it) {
if (it.current()->name == name) {
TQScrollView::ensureVisible((*it)->x, (*it)->y, (*it)->w, (*it)->h);
return;
}
}
}
TQStringList DigraphView::splitLine(TQString str)
{
TQStringList 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 TQStringList &list)
{
TQStringList::ConstIterator it;
for (it = list.begin(); it != list.end(); ++it) {
TQStringList 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;
TQMemArray<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 TQString& file, const TQString& ext )
{
TQString 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;
}
TQStringList results;
KTempFile ifile, ofile;
TQTextStream &is = *ifile.textStream();
is << "digraph G {" << endl;
is << "rankdir=LR;" << endl;
is << "node [shape=box,fontname=Helvetica,fontsize=12];" << endl;
TQStringList::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 << TQString("-T")+ext << ifile.name() << "-o" << file;
kdDebug() << "Executing: " << cmd <<" "<<TQString("-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;
}
TQTextStream &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(TQPainter* p, int clipx, int clipy, int clipw, int cliph)
{
TQRect clipRect(clipx, clipy, clipw, cliph);
p->eraseRect(clipRect);
p->setFont(KGlobalSettings::generalFont());
TQPtrListIterator<DigraphNode> it1(nodes);
for (; it1.current(); ++it1) {
TQRect 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, TQBrush(lightGray, SolidPattern));
else
p->drawRect(r);
p->drawText(r, AlignCenter, (*it1)->name);
}
}
p->setBrush(TQBrush(black, SolidPattern));
TQPtrListIterator<DigraphEdge> it2(edges);
for (; it2.current(); ++it2) {
int n = (*it2)->points.count();
for (int i=0; i+3 < n; i+=3)
{
TQPointArray a(4);
TQPointArray &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);
}
TQPoint p1 = (*it2)->points[n-2];
TQPoint p2 = (*it2)->points[n-1];
TQPoint 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();
TQPointArray triangle(3);
triangle[0] = p2 + TQPoint((int)(d11+d21),(int)(d12+d22));
triangle[1] = p2 + TQPoint((int)(d11-d21),(int)(d12-d22));
triangle[2] = p2;
p->drawPolygon(triangle, true);
}
}
void DigraphView::contentsMousePressEvent(TQMouseEvent *e)
{
TQPtrListIterator<DigraphNode> it1(nodes);
for (; it1.current(); ++it1) {
TQRect r((*it1)->x-(*it1)->w/2, (*it1)->y-(*it1)->h/2, (*it1)->w, (*it1)->h);
if (r.contains(e->pos())) {
if (selNode) {
TQRect 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);
}
}
}
TQSize DigraphView::sizeHint() const
{
if (width == -1)
return TQSize(100, 100); // arbitrary
TQSize dsize = KGlobalSettings::desktopGeometry(viewport()).size();
kdDebug(9003) << "sizehint for inheritance diagram" << dsize << " " << width << " " << height << endl;
return TQSize(width, height).boundedTo(TQSize(dsize.width()*2/3, dsize.height()*2/3));
}
#if 0
int main(int argc, char **argv)
{
TQApplication 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"