|
|
|
/***************************************************************************
|
|
|
|
* Copyright (C) 2006-2007 by Rajko Albrecht *
|
|
|
|
* ral@alwins-world.de *
|
|
|
|
* *
|
|
|
|
* 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. *
|
|
|
|
* *
|
|
|
|
* This program is distributed in the hope that it will be useful, *
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
|
|
* GNU General Public License for more details. *
|
|
|
|
* *
|
|
|
|
* You should have received a copy of the GNU General Public License *
|
|
|
|
* along with this program; if not, write to the *
|
|
|
|
* Free Software Foundation, Inc., *
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
|
|
|
|
***************************************************************************/
|
|
|
|
#include "revgraphview.h"
|
|
|
|
#include "graphtreelabel.h"
|
|
|
|
#include "pannerview.h"
|
|
|
|
#include "graphtree_defines.h"
|
|
|
|
#include "tdesvnsettings.h"
|
|
|
|
#include "stopdlg.h"
|
|
|
|
#include "client.hpp"
|
|
|
|
|
|
|
|
#include <kapp.h>
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include <ktempfile.h>
|
|
|
|
#include <ktempdir.h>
|
|
|
|
#include <kprocess.h>
|
|
|
|
#include <klocale.h>
|
|
|
|
#include <kfiledialog.h>
|
|
|
|
#include <kmessagebox.h>
|
|
|
|
|
|
|
|
#include <tqtooltip.h>
|
|
|
|
#include <tqwmatrix.h>
|
|
|
|
#include <tqpopupmenu.h>
|
|
|
|
#include <tqpainter.h>
|
|
|
|
#include <tqregexp.h>
|
|
|
|
|
|
|
|
#include <math.h>
|
|
|
|
|
|
|
|
#define LABEL_WIDTH 160
|
|
|
|
#define LABEL_HEIGHT 90
|
|
|
|
|
|
|
|
|
|
|
|
class GraphViewTip:public TQToolTip
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
GraphViewTip( TQWidget* p ):TQToolTip(p) {}
|
|
|
|
virtual ~GraphViewTip(){}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
void maybeTip( const TQPoint & );
|
|
|
|
};
|
|
|
|
|
|
|
|
void GraphViewTip::maybeTip( const TQPoint & pos)
|
|
|
|
{
|
|
|
|
if (!parentWidget()->inherits( "RevGraphView" )) return;
|
|
|
|
RevGraphView* cgv = (RevGraphView*)parentWidget();
|
|
|
|
TQPoint cPos = cgv->viewportToContents(pos);
|
|
|
|
TQCanvasItemList l = cgv->canvas()->collisions(cPos);
|
|
|
|
if (l.count() == 0) return;
|
|
|
|
TQCanvasItem* i = l.first();
|
|
|
|
if (i->rtti() == GRAPHTREE_LABEL) {
|
|
|
|
GraphTreeLabel*tl = (GraphTreeLabel*)i;
|
|
|
|
TQString nm = tl->nodename();
|
|
|
|
TQString tipStr = cgv->toolTip(nm);
|
|
|
|
if (tipStr.length()>0) {
|
|
|
|
TQPoint vPosTL = cgv->contentsToViewport(i->boundingRect().topLeft());
|
|
|
|
TQPoint vPosBR = cgv->contentsToViewport(i->boundingRect().bottomRight());
|
|
|
|
tip(TQRect(vPosTL, vPosBR), tipStr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
RevGraphView::RevGraphView(TQObject*aListener,svn::Client*_client,TQWidget * parent, const char * name, WFlags f)
|
|
|
|
: TQCanvasView(parent,name,f)
|
|
|
|
{
|
|
|
|
m_Canvas = 0L;
|
|
|
|
m_Client = _client;
|
|
|
|
m_Listener = aListener;
|
|
|
|
dotTmpFile = 0;
|
|
|
|
m_Selected = 0;
|
|
|
|
renderProcess = 0;
|
|
|
|
m_Marker = 0;
|
|
|
|
m_Tip = new GraphViewTip(this);
|
|
|
|
m_CompleteView = new PannerView(this);
|
|
|
|
m_CompleteView->setVScrollBarMode(TQScrollView::AlwaysOff);
|
|
|
|
m_CompleteView->setHScrollBarMode(TQScrollView::AlwaysOff);
|
|
|
|
m_CompleteView->raise();
|
|
|
|
m_CompleteView->hide();
|
|
|
|
connect(this, TQT_SIGNAL(contentsMoving(int,int)),
|
|
|
|
this, TQT_SLOT(contentsMovingSlot(int,int)));
|
|
|
|
connect(m_CompleteView, TQT_SIGNAL(zoomRectMoved(int,int)),
|
|
|
|
this, TQT_SLOT(zoomRectMoved(int,int)));
|
|
|
|
connect(m_CompleteView, TQT_SIGNAL(zoomRectMoveFinished()),
|
|
|
|
this, TQT_SLOT(zoomRectMoveFinished()));
|
|
|
|
m_LastAutoPosition = TopLeft;
|
|
|
|
_isMoving = false;
|
|
|
|
_noUpdateZoomerPos = false;
|
|
|
|
m_LabelMap[""]="";
|
|
|
|
}
|
|
|
|
|
|
|
|
RevGraphView::~RevGraphView()
|
|
|
|
{
|
|
|
|
setCanvas(0);
|
|
|
|
delete m_Canvas;
|
|
|
|
delete dotTmpFile;
|
|
|
|
delete m_CompleteView;
|
|
|
|
delete m_Tip;
|
|
|
|
delete renderProcess;
|
|
|
|
}
|
|
|
|
|
|
|
|
void RevGraphView::showText(const TQString&s)
|
|
|
|
{
|
|
|
|
clear();
|
|
|
|
m_Canvas = new TQCanvas(TQApplication::desktop()->width(),
|
|
|
|
TQApplication::desktop()->height());
|
|
|
|
|
|
|
|
TQCanvasText* t = new TQCanvasText(s, m_Canvas);
|
|
|
|
t->move(5, 5);
|
|
|
|
t->show();
|
|
|
|
center(0,0);
|
|
|
|
setCanvas(m_Canvas);
|
|
|
|
m_Canvas->update();
|
|
|
|
m_CompleteView->hide();
|
|
|
|
}
|
|
|
|
|
|
|
|
void RevGraphView::clear()
|
|
|
|
{
|
|
|
|
if (m_Selected) {
|
|
|
|
m_Selected->setSelected(false);
|
|
|
|
m_Selected=0;
|
|
|
|
}
|
|
|
|
if (m_Marker) {
|
|
|
|
m_Marker->hide();
|
|
|
|
delete m_Marker;
|
|
|
|
m_Marker=0;
|
|
|
|
}
|
|
|
|
if (!m_Canvas) return;
|
|
|
|
delete m_Canvas;
|
|
|
|
m_Canvas = 0;
|
|
|
|
setCanvas(0);
|
|
|
|
m_CompleteView->setCanvas(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void RevGraphView::beginInsert()
|
|
|
|
{
|
|
|
|
viewport()->setUpdatesEnabled(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void RevGraphView::endInsert()
|
|
|
|
{
|
|
|
|
if (m_Canvas) {
|
|
|
|
_cvZoom = 0;
|
|
|
|
updateSizes();
|
|
|
|
m_Canvas->update();
|
|
|
|
}
|
|
|
|
viewport()->setUpdatesEnabled(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void RevGraphView::readDotOutput(KProcess*,char * buffer,int buflen)
|
|
|
|
{
|
|
|
|
dotOutput+=TQString::fromLocal8Bit(buffer, buflen);
|
|
|
|
}
|
|
|
|
|
|
|
|
void RevGraphView::dotExit(KProcess*p)
|
|
|
|
{
|
|
|
|
if (p!=renderProcess)return;
|
|
|
|
// remove line breaks when lines to long
|
|
|
|
TQRegExp endslash("\\\\\\n");
|
|
|
|
dotOutput.replace(endslash,"");
|
|
|
|
double scale = 1.0, scaleX = 1.0, scaleY = 1.0;
|
|
|
|
double dotWidth, dotHeight;
|
|
|
|
TQTextStream* dotStream;
|
|
|
|
dotStream = new TQTextStream(dotOutput, IO_ReadOnly);
|
|
|
|
TQString line,cmd;
|
|
|
|
int lineno=0;
|
|
|
|
clear();
|
|
|
|
beginInsert();
|
|
|
|
/* mostly taken from tdecachegrind */
|
|
|
|
while (1) {
|
|
|
|
line = dotStream->readLine();
|
|
|
|
if (line.isNull()) break;
|
|
|
|
lineno++;
|
|
|
|
if (line.isEmpty()) continue;
|
|
|
|
TQTextStream lineStream(line, IO_ReadOnly);
|
|
|
|
lineStream >> cmd;
|
|
|
|
if (cmd == "stop") {break; }
|
|
|
|
|
|
|
|
if (cmd == "graph") {
|
|
|
|
lineStream >> scale >> dotWidth >> dotHeight;
|
|
|
|
scaleX = scale * 60; scaleY = scale * 100;
|
|
|
|
int w = (int)(scaleX * dotWidth);
|
|
|
|
int h = (int)(scaleY * dotHeight);
|
|
|
|
|
|
|
|
_xMargin = 50;
|
|
|
|
if (w < TQApplication::desktop()->width())
|
|
|
|
_xMargin += (TQApplication::desktop()->width()-w)/2;
|
|
|
|
_yMargin = 50;
|
|
|
|
if (h < TQApplication::desktop()->height())
|
|
|
|
_yMargin += (TQApplication::desktop()->height()-h)/2;
|
|
|
|
m_Canvas = new TQCanvas(int(w+2*_xMargin), int(h+2*_yMargin));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if ((cmd != "node") && (cmd != "edge")) {
|
|
|
|
kdWarning() << "Ignoring unknown command '" << cmd << "' from dot ("
|
|
|
|
<< dotTmpFile->name() << ":" << lineno << ")" << endl;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (cmd=="node") {
|
|
|
|
TQString nodeName, label;
|
|
|
|
TQString _x,_y,_w,_h;
|
|
|
|
double x, y, width, height;
|
|
|
|
lineStream >> nodeName >> _x >> _y >> _w >> _h;
|
|
|
|
x=_x.toDouble();
|
|
|
|
y=_y.toDouble();
|
|
|
|
width=_w.toDouble();
|
|
|
|
height=_h.toDouble();
|
|
|
|
// better here 'cause dot may scramble utf8 labels so we regenerate it better
|
|
|
|
// and do not read it in.
|
|
|
|
label = getLabelstring(nodeName);
|
|
|
|
int xx = (int)(scaleX * x + _xMargin);
|
|
|
|
int yy = (int)(scaleY * (dotHeight - y) + _yMargin);
|
|
|
|
int w = (int)(scaleX * width);
|
|
|
|
int h = (int)(scaleY * height);
|
|
|
|
TQRect r(xx-w/2, yy-h/2, w, h);
|
|
|
|
GraphTreeLabel*t=new GraphTreeLabel(label,nodeName,r,m_Canvas);
|
|
|
|
if (isStart(nodeName)) {
|
|
|
|
ensureVisible(r.x(),r.y());
|
|
|
|
}
|
|
|
|
t->setBgColor(getBgColor(nodeName));
|
|
|
|
t->setZ(1.0);
|
|
|
|
t->show();
|
|
|
|
m_NodeList[nodeName]=t;
|
|
|
|
} else {
|
|
|
|
TQString node1Name, node2Name, label;
|
|
|
|
TQString _x,_y;
|
|
|
|
double x, y;
|
|
|
|
TQPointArray pa;
|
|
|
|
int points, i;
|
|
|
|
lineStream >> node1Name >> node2Name;
|
|
|
|
lineStream >> points;
|
|
|
|
pa.resize(points);
|
|
|
|
for (i=0;i<points;++i) {
|
|
|
|
if (lineStream.atEnd()) break;
|
|
|
|
lineStream >> _x >> _y;
|
|
|
|
x=_x.toDouble();
|
|
|
|
y=_y.toDouble();
|
|
|
|
int xx = (int)(scaleX * x + _xMargin);
|
|
|
|
int yy = (int)(scaleY * (dotHeight - y) + _yMargin);
|
|
|
|
|
|
|
|
if (0) tqDebug(" P %d: ( %f / %f ) => ( %d / %d)",
|
|
|
|
i, x, y, xx, yy);
|
|
|
|
pa.setPoint(i, xx, yy);
|
|
|
|
}
|
|
|
|
if (i < points) {
|
|
|
|
tqDebug("CallGraphView: Can't read %d spline points (%d)",
|
|
|
|
points, lineno);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
GraphEdge * n = new GraphEdge(m_Canvas);
|
|
|
|
TQColor arrowColor = TQt::black;
|
|
|
|
n->setPen(TQPen(arrowColor,1));
|
|
|
|
n->setControlPoints(pa,false);
|
|
|
|
n->setZ(0.5);
|
|
|
|
n->show();
|
|
|
|
|
|
|
|
/* arrow */
|
|
|
|
TQPoint arrowDir;
|
|
|
|
int indexHead = -1;
|
|
|
|
|
|
|
|
TQMap<TQString,GraphTreeLabel*>::Iterator it;
|
|
|
|
it = m_NodeList.find(node2Name);
|
|
|
|
if (it!=m_NodeList.end()) {
|
|
|
|
it.data()->setSource(node1Name);
|
|
|
|
}
|
|
|
|
it = m_NodeList.find(node1Name);
|
|
|
|
if (it!=m_NodeList.end()) {
|
|
|
|
GraphTreeLabel*tlab = it.data();
|
|
|
|
if (tlab) {
|
|
|
|
TQPoint toCenter = tlab->rect().center();
|
|
|
|
int dx0 = pa.point(0).x() - toCenter.x();
|
|
|
|
int dy0 = pa.point(0).y() - toCenter.y();
|
|
|
|
int dx1 = pa.point(points-1).x() - toCenter.x();
|
|
|
|
int dy1 = pa.point(points-1).y() - toCenter.y();
|
|
|
|
if (dx0*dx0+dy0*dy0 > dx1*dx1+dy1*dy1) {
|
|
|
|
// start of spline is nearer to call target node
|
|
|
|
indexHead=-1;
|
|
|
|
while(arrowDir.isNull() && (indexHead<points-2)) {
|
|
|
|
indexHead++;
|
|
|
|
arrowDir = pa.point(indexHead) - pa.point(indexHead+1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (arrowDir.isNull()) {
|
|
|
|
indexHead = points;
|
|
|
|
// sometimes the last spline points from dot are the same...
|
|
|
|
while(arrowDir.isNull() && (indexHead>1)) {
|
|
|
|
indexHead--;
|
|
|
|
arrowDir = pa.point(indexHead) - pa.point(indexHead-1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!arrowDir.isNull()) {
|
|
|
|
arrowDir *= 10.0/sqrt(double(arrowDir.x()*arrowDir.x() +
|
|
|
|
arrowDir.y()*arrowDir.y()));
|
|
|
|
TQPointArray a(3);
|
|
|
|
a.setPoint(0, pa.point(indexHead) + arrowDir);
|
|
|
|
a.setPoint(1, pa.point(indexHead) + TQPoint(arrowDir.y()/2,
|
|
|
|
-arrowDir.x()/2));
|
|
|
|
a.setPoint(2, pa.point(indexHead) + TQPoint(-arrowDir.y()/2,
|
|
|
|
arrowDir.x()/2));
|
|
|
|
GraphEdgeArrow* aItem = new GraphEdgeArrow(n,m_Canvas);
|
|
|
|
aItem->setPoints(a);
|
|
|
|
aItem->setBrush(arrowColor);
|
|
|
|
aItem->setZ(1.5);
|
|
|
|
aItem->show();
|
|
|
|
// sItem->setArrow(aItem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!m_Canvas) {
|
|
|
|
TQString s = i18n("Error running the graph layouting tool.\n");
|
|
|
|
s += i18n("Please check that 'dot' is installed (package GraphViz).");
|
|
|
|
showText(s);
|
|
|
|
} else {
|
|
|
|
setCanvas(m_Canvas);
|
|
|
|
m_CompleteView->setCanvas(m_Canvas);
|
|
|
|
}
|
|
|
|
endInsert();
|
|
|
|
delete p;
|
|
|
|
renderProcess=0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool RevGraphView::isStart(const TQString&nodeName)const
|
|
|
|
{
|
|
|
|
bool res = false;
|
|
|
|
trevTree::ConstIterator it;
|
|
|
|
it = m_Tree.find(nodeName);
|
|
|
|
if (it==m_Tree.end()) {
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
switch (it.data().Action) {
|
|
|
|
case 'A':
|
|
|
|
res = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
char RevGraphView::getAction(const TQString&nodeName)const
|
|
|
|
{
|
|
|
|
trevTree::ConstIterator it;
|
|
|
|
it = m_Tree.find(nodeName);
|
|
|
|
if (it==m_Tree.end()) {
|
|
|
|
return (char)0;
|
|
|
|
}
|
|
|
|
return it.data().Action;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQColor RevGraphView::getBgColor(const TQString&nodeName)const
|
|
|
|
{
|
|
|
|
trevTree::ConstIterator it;
|
|
|
|
it = m_Tree.find(nodeName);
|
|
|
|
TQColor res = TQt::white;
|
|
|
|
if (it==m_Tree.end()) {
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
switch (it.data().Action) {
|
|
|
|
case 'D':
|
|
|
|
res = Kdesvnsettings::tree_delete_color();
|
|
|
|
break;
|
|
|
|
case 'R':
|
|
|
|
case 'M':
|
|
|
|
res = Kdesvnsettings::tree_modify_color();
|
|
|
|
break;
|
|
|
|
case 'A':
|
|
|
|
res = Kdesvnsettings::tree_add_color();
|
|
|
|
break;
|
|
|
|
case 'C':
|
|
|
|
case 1:
|
|
|
|
res = Kdesvnsettings::tree_copy_color();
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
res = Kdesvnsettings::tree_rename_color();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
res = Kdesvnsettings::tree_modify_color();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
const TQString&RevGraphView::getLabelstring(const TQString&nodeName)
|
|
|
|
{
|
|
|
|
TQMap<TQString,TQString>::ConstIterator nIt;
|
|
|
|
nIt = m_LabelMap.find(nodeName);
|
|
|
|
if (nIt!=m_LabelMap.end()) {
|
|
|
|
return nIt.data();
|
|
|
|
}
|
|
|
|
trevTree::ConstIterator it1;
|
|
|
|
it1 = m_Tree.find(nodeName);
|
|
|
|
if (it1==m_Tree.end()) {
|
|
|
|
return m_LabelMap[""];
|
|
|
|
}
|
|
|
|
TQString res;
|
|
|
|
switch (it1.data().Action) {
|
|
|
|
case 'D':
|
|
|
|
res = i18n("Deleted at revision %1").arg(it1.data().rev);
|
|
|
|
break;
|
|
|
|
case 'A':
|
|
|
|
res = i18n("Added at revision %1 as %2")
|
|
|
|
.arg(it1.data().rev)
|
|
|
|
.arg(it1.data().name);
|
|
|
|
break;
|
|
|
|
case 'C':
|
|
|
|
case 1:
|
|
|
|
res = i18n("Copied to %1 at revision %2").arg(it1.data().name).arg(it1.data().rev);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
res = i18n("Renamed to %1 at revision %2").arg(it1.data().name).arg(it1.data().rev);
|
|
|
|
break;
|
|
|
|
case 'M':
|
|
|
|
res = i18n("Modified at revision %1").arg(it1.data().rev);
|
|
|
|
break;
|
|
|
|
case 'R':
|
|
|
|
res = i18n("Replaced at revision %1").arg(it1.data().rev);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
res=i18n("Revision %1").arg(it1.data().rev);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
m_LabelMap[nodeName]=res;
|
|
|
|
return m_LabelMap[nodeName];
|
|
|
|
}
|
|
|
|
|
|
|
|
void RevGraphView::dumpRevtree()
|
|
|
|
{
|
|
|
|
delete dotTmpFile;
|
|
|
|
clear();
|
|
|
|
dotOutput = "";
|
|
|
|
dotTmpFile = new KTempFile(TQString(),".dot");
|
|
|
|
dotTmpFile->setAutoDelete(true);
|
|
|
|
|
|
|
|
TQTextStream* stream = dotTmpFile->textStream();
|
|
|
|
if (!stream) {
|
|
|
|
showText(i18n("Could not open tempfile %1 for writing.").arg(dotTmpFile->name()));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
*stream << "digraph \"callgraph\" {\n";
|
|
|
|
*stream << " bgcolor=\"transparent\";\n";
|
|
|
|
int dir = Kdesvnsettings::tree_direction();
|
|
|
|
*stream << TQString(" rankdir=\"");
|
|
|
|
switch (dir) {
|
|
|
|
case 3:
|
|
|
|
*stream << "TB";
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
*stream << "RL";
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
*stream << "BT";
|
|
|
|
break;
|
|
|
|
case 0:
|
|
|
|
default:
|
|
|
|
*stream << "LR";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
*stream << "\";\n";
|
|
|
|
|
|
|
|
//*stream << TQString(" overlap=false;\n splines=true;\n");
|
|
|
|
|
|
|
|
RevGraphView::trevTree::ConstIterator it1;
|
|
|
|
for (it1=m_Tree.begin();it1!=m_Tree.end();++it1) {
|
|
|
|
*stream << " " << it1.key()
|
|
|
|
<< "[ "
|
|
|
|
<< "shape=box, "
|
|
|
|
<< "label=\""<<getLabelstring(it1.key())<<"\","
|
|
|
|
<< "];\n";
|
|
|
|
for (unsigned j=0;j<it1.data().targets.count();++j) {
|
|
|
|
*stream<<" "<<it1.key().latin1()<< " "
|
|
|
|
<< "->"<<" "<<it1.data().targets[j].key
|
|
|
|
<< " [fontsize=10,style=\"solid\"];\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*stream << "}\n"<<flush;
|
|
|
|
renderProcess = new KProcess();
|
|
|
|
renderProcess->setEnvironment("LANG","C");
|
|
|
|
*renderProcess << "dot";
|
|
|
|
*renderProcess << dotTmpFile->name() << "-Tplain";
|
|
|
|
connect(renderProcess,TQT_SIGNAL(processExited(KProcess*)),this,TQT_SLOT(dotExit(KProcess*)));
|
|
|
|
connect(renderProcess,TQT_SIGNAL(receivedStdout(KProcess*,char*,int)),
|
|
|
|
this,TQT_SLOT(readDotOutput(KProcess*,char*,int)) );
|
|
|
|
if (!renderProcess->start(KProcess::NotifyOnExit,KProcess::Stdout)) {
|
|
|
|
TQString arguments;
|
|
|
|
for (unsigned c=0;c<renderProcess->args().count();++c) {
|
|
|
|
arguments+=TQString(" %1").arg(renderProcess->args()[c].data());
|
|
|
|
}
|
|
|
|
TQString error = i18n("Could not start process \"%1\".").arg(arguments);
|
|
|
|
showText(error);
|
|
|
|
renderProcess=0;
|
|
|
|
//delete renderProcess;<
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString RevGraphView::toolTip(const TQString&_nodename,bool full)const
|
|
|
|
{
|
|
|
|
TQString res = TQString();
|
|
|
|
trevTree::ConstIterator it;
|
|
|
|
it = m_Tree.find(_nodename);
|
|
|
|
if (it==m_Tree.end()) {
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
TQStringList sp = TQStringList::split("\n",it.data().Message);
|
|
|
|
TQString sm;
|
|
|
|
if (sp.count()==0) {
|
|
|
|
sm = it.data().Message;
|
|
|
|
} else {
|
|
|
|
if (!full) {
|
|
|
|
sm = sp[0]+"...";
|
|
|
|
} else {
|
|
|
|
for (unsigned j = 0; j<sp.count(); ++j) {
|
|
|
|
if (j>0) sm+="<br>";
|
|
|
|
sm+=sp[j];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!full && sm.length()>50) {
|
|
|
|
sm.truncate(47);
|
|
|
|
sm+="...";
|
|
|
|
}
|
|
|
|
static TQString csep = "</td><td>";
|
|
|
|
static TQString rend = "</td></tr>";
|
|
|
|
static TQString rstart = "<tr><td>";
|
|
|
|
res = TQString("<html><body>");
|
|
|
|
|
|
|
|
if (!full) {
|
|
|
|
res+=TQString("<b>%1</b>").arg(it.data().name);
|
|
|
|
res += i18n("<br>Revision: %1<br>Author: %2<br>Date: %3<br>Log: %4</html>")
|
|
|
|
.arg(it.data().rev)
|
|
|
|
.arg(it.data().Author)
|
|
|
|
.arg(it.data().Date)
|
|
|
|
.arg(sm);
|
|
|
|
} else {
|
|
|
|
res+="<table><tr><th colspan=\"2\"><b>"+it.data().name+"</b></th></tr>";
|
|
|
|
res+=rstart;
|
|
|
|
res+=i18n("<b>Revision</b>%1%2%3").arg(csep).arg(it.data().rev).arg(rend);
|
|
|
|
res+=rstart+i18n("<b>Author</b>%1%2%3").arg(csep).arg(it.data().Author).arg(rend);
|
|
|
|
res+=rstart+i18n("<b>Date</b>%1%2%3").arg(csep).arg(it.data().Date).arg(rend);
|
|
|
|
res+=rstart+i18n("<b>Log</b>%1%2%3").arg(csep).arg(sm).arg(rend);
|
|
|
|
res+="</table></body></html>";
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
void RevGraphView::updateSizes(TQSize s)
|
|
|
|
{
|
|
|
|
if (!m_Canvas) return;
|
|
|
|
if (s == TQSize(0,0)) s = size();
|
|
|
|
|
|
|
|
// the part of the canvas that should be visible
|
|
|
|
int cWidth = m_Canvas->width() - 2*_xMargin + 100;
|
|
|
|
int cHeight = m_Canvas->height() - 2*_yMargin + 100;
|
|
|
|
|
|
|
|
// hide birds eye view if no overview needed
|
|
|
|
if (((cWidth < s.width()) && cHeight < s.height())||m_NodeList.count()==0) {
|
|
|
|
m_CompleteView->hide();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_CompleteView->show();
|
|
|
|
|
|
|
|
// first, assume use of 1/3 of width/height (possible larger)
|
|
|
|
double zoom = .33 * s.width() / cWidth;
|
|
|
|
if (zoom * cHeight < .33 * s.height()) zoom = .33 * s.height() / cHeight;
|
|
|
|
|
|
|
|
// fit to widget size
|
|
|
|
if (cWidth * zoom > s.width()) zoom = s.width() / (double)cWidth;
|
|
|
|
if (cHeight * zoom > s.height()) zoom = s.height() / (double)cHeight;
|
|
|
|
|
|
|
|
// scale to never use full height/width
|
|
|
|
zoom = zoom * 3/4;
|
|
|
|
|
|
|
|
// at most a zoom of 1/3
|
|
|
|
if (zoom > .33) zoom = .33;
|
|
|
|
|
|
|
|
if (zoom != _cvZoom) {
|
|
|
|
_cvZoom = zoom;
|
|
|
|
if (0) tqDebug("Canvas Size: %dx%d, Visible: %dx%d, Zoom: %f",
|
|
|
|
m_Canvas->width(), m_Canvas->height(),
|
|
|
|
cWidth, cHeight, zoom);
|
|
|
|
|
|
|
|
TQWMatrix wm;
|
|
|
|
wm.scale( zoom, zoom );
|
|
|
|
m_CompleteView->setWorldMatrix(wm);
|
|
|
|
|
|
|
|
// make it a little bigger to compensate for widget frame
|
|
|
|
m_CompleteView->resize(int(cWidth * zoom) + 4,
|
|
|
|
int(cHeight * zoom) + 4);
|
|
|
|
|
|
|
|
// update ZoomRect in completeView
|
|
|
|
contentsMovingSlot(contentsX(), contentsY());
|
|
|
|
}
|
|
|
|
|
|
|
|
m_CompleteView->setContentsPos(int(zoom*(_xMargin-50)),
|
|
|
|
int(zoom*(_yMargin-50)));
|
|
|
|
updateZoomerPos();
|
|
|
|
}
|
|
|
|
|
|
|
|
void RevGraphView::updateZoomerPos()
|
|
|
|
{
|
|
|
|
int cvW = m_CompleteView->width();
|
|
|
|
int cvH = m_CompleteView->height();
|
|
|
|
int x = width()- cvW - verticalScrollBar()->width() -2;
|
|
|
|
int y = height()-cvH - horizontalScrollBar()->height() -2;
|
|
|
|
|
|
|
|
TQPoint oldZoomPos = m_CompleteView->pos();
|
|
|
|
TQPoint newZoomPos = TQPoint(0,0);
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
ZoomPosition zp = _zoomPosition;
|
|
|
|
if (zp == Auto) {
|
|
|
|
#else
|
|
|
|
ZoomPosition zp = m_LastAutoPosition;
|
|
|
|
#endif
|
|
|
|
TQPoint tl1Pos = viewportToContents(TQPoint(0,0));
|
|
|
|
TQPoint tl2Pos = viewportToContents(TQPoint(cvW,cvH));
|
|
|
|
TQPoint tr1Pos = viewportToContents(TQPoint(x,0));
|
|
|
|
TQPoint tr2Pos = viewportToContents(TQPoint(x+cvW,cvH));
|
|
|
|
TQPoint bl1Pos = viewportToContents(TQPoint(0,y));
|
|
|
|
TQPoint bl2Pos = viewportToContents(TQPoint(cvW,y+cvH));
|
|
|
|
TQPoint br1Pos = viewportToContents(TQPoint(x,y));
|
|
|
|
TQPoint br2Pos = viewportToContents(TQPoint(x+cvW,y+cvH));
|
|
|
|
int tlCols = m_Canvas->collisions(TQRect(tl1Pos,tl2Pos)).count();
|
|
|
|
int trCols = m_Canvas->collisions(TQRect(tr1Pos,tr2Pos)).count();
|
|
|
|
int blCols = m_Canvas->collisions(TQRect(bl1Pos,bl2Pos)).count();
|
|
|
|
int brCols = m_Canvas->collisions(TQRect(br1Pos,br2Pos)).count();
|
|
|
|
int minCols = tlCols;
|
|
|
|
zp = m_LastAutoPosition;
|
|
|
|
switch(zp) {
|
|
|
|
case TopRight: minCols = trCols; break;
|
|
|
|
case BottomLeft: minCols = blCols; break;
|
|
|
|
case BottomRight: minCols = brCols; break;
|
|
|
|
default:
|
|
|
|
case TopLeft: minCols = tlCols; break;
|
|
|
|
}
|
|
|
|
if (minCols > tlCols) { minCols = tlCols; zp = TopLeft; }
|
|
|
|
if (minCols > trCols) { minCols = trCols; zp = TopRight; }
|
|
|
|
if (minCols > blCols) { minCols = blCols; zp = BottomLeft; }
|
|
|
|
if (minCols > brCols) { minCols = brCols; zp = BottomRight; }
|
|
|
|
|
|
|
|
m_LastAutoPosition = zp;
|
|
|
|
#if 0
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
switch(zp) {
|
|
|
|
case TopRight:
|
|
|
|
newZoomPos = TQPoint(x,0);
|
|
|
|
break;
|
|
|
|
case BottomLeft:
|
|
|
|
newZoomPos = TQPoint(0,y);
|
|
|
|
break;
|
|
|
|
case BottomRight:
|
|
|
|
newZoomPos = TQPoint(x,y);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (newZoomPos != oldZoomPos) m_CompleteView->move(newZoomPos);
|
|
|
|
}
|
|
|
|
|
|
|
|
void RevGraphView::contentsMovingSlot(int x,int y)
|
|
|
|
{
|
|
|
|
TQRect z(int(x * _cvZoom), int(y * _cvZoom),
|
|
|
|
int(visibleWidth() * _cvZoom)-1, int(visibleHeight() * _cvZoom)-1);
|
|
|
|
if (0) tqDebug("moving: (%d,%d) => (%d/%d - %dx%d)",
|
|
|
|
x, y, z.x(), z.y(), z.width(), z.height());
|
|
|
|
m_CompleteView->setZoomRect(z);
|
|
|
|
if (!_noUpdateZoomerPos) {
|
|
|
|
updateZoomerPos();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void RevGraphView::zoomRectMoved(int dx,int dy)
|
|
|
|
{
|
|
|
|
if (leftMargin()>0) dx = 0;
|
|
|
|
if (topMargin()>0) dy = 0;
|
|
|
|
_noUpdateZoomerPos = true;
|
|
|
|
scrollBy(int(dx/_cvZoom),int(dy/_cvZoom));
|
|
|
|
_noUpdateZoomerPos = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void RevGraphView::zoomRectMoveFinished()
|
|
|
|
{
|
|
|
|
#if 0
|
|
|
|
if (_zoomPosition == Auto)
|
|
|
|
#endif
|
|
|
|
updateZoomerPos();
|
|
|
|
}
|
|
|
|
|
|
|
|
void RevGraphView::resizeEvent(TQResizeEvent*e)
|
|
|
|
{
|
|
|
|
TQCanvasView::resizeEvent(e);
|
|
|
|
if (m_Canvas) updateSizes(e->size());
|
|
|
|
}
|
|
|
|
|
|
|
|
void RevGraphView::makeSelected(GraphTreeLabel*gtl)
|
|
|
|
{
|
|
|
|
if (m_Selected) {
|
|
|
|
m_Selected->setSelected(false);
|
|
|
|
}
|
|
|
|
m_Selected=gtl;
|
|
|
|
if (m_Marker) {
|
|
|
|
m_Marker->hide();
|
|
|
|
delete m_Marker;
|
|
|
|
m_Marker=0;
|
|
|
|
}
|
|
|
|
if (gtl) {
|
|
|
|
m_Marker = new GraphMark(gtl,m_Canvas);
|
|
|
|
m_Marker->setZ(-1);
|
|
|
|
m_Marker->show();
|
|
|
|
m_Selected->setSelected(true);
|
|
|
|
}
|
|
|
|
m_Canvas->update();
|
|
|
|
m_CompleteView->updateCurrentRect();
|
|
|
|
}
|
|
|
|
|
|
|
|
void RevGraphView::contentsMouseDoubleClickEvent ( TQMouseEvent * e )
|
|
|
|
{
|
|
|
|
setFocus();
|
|
|
|
if (e->button() == Qt::LeftButton) {
|
|
|
|
TQCanvasItemList l = canvas()->collisions(e->pos());
|
|
|
|
if (l.count()>0) {
|
|
|
|
TQCanvasItem* i = l.first();
|
|
|
|
if (i->rtti()==GRAPHTREE_LABEL) {
|
|
|
|
makeSelected( (GraphTreeLabel*)i);
|
|
|
|
emit dispDetails(toolTip(((GraphTreeLabel*)i)->nodename(),true));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void RevGraphView::contentsMousePressEvent ( TQMouseEvent * e )
|
|
|
|
{
|
|
|
|
setFocus();
|
|
|
|
_isMoving = true;
|
|
|
|
_lastPos = e->globalPos();
|
|
|
|
}
|
|
|
|
|
|
|
|
void RevGraphView::contentsMouseReleaseEvent ( TQMouseEvent * )
|
|
|
|
{
|
|
|
|
_isMoving = false;
|
|
|
|
updateZoomerPos();
|
|
|
|
}
|
|
|
|
|
|
|
|
void RevGraphView::contentsMouseMoveEvent ( TQMouseEvent * e )
|
|
|
|
{
|
|
|
|
if (_isMoving) {
|
|
|
|
int dx = e->globalPos().x() - _lastPos.x();
|
|
|
|
int dy = e->globalPos().y() - _lastPos.y();
|
|
|
|
_noUpdateZoomerPos = true;
|
|
|
|
scrollBy(-dx, -dy);
|
|
|
|
_noUpdateZoomerPos = false;
|
|
|
|
_lastPos = e->globalPos();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void RevGraphView::setNewDirection(int dir)
|
|
|
|
{
|
|
|
|
if (dir<0)dir=3;
|
|
|
|
else if (dir>3)dir=0;
|
|
|
|
Kdesvnsettings::setTree_direction(dir);
|
|
|
|
dumpRevtree();
|
|
|
|
}
|
|
|
|
|
|
|
|
void RevGraphView::contentsContextMenuEvent(TQContextMenuEvent* e)
|
|
|
|
{
|
|
|
|
if (!m_Canvas) return;
|
|
|
|
TQCanvasItemList l = canvas()->collisions(e->pos());
|
|
|
|
TQCanvasItem* i = (l.count() == 0) ? 0 : l.first();
|
|
|
|
|
|
|
|
TQPopupMenu popup;
|
|
|
|
if (i && i->rtti()==GRAPHTREE_LABEL) {
|
|
|
|
if (!((GraphTreeLabel*)i)->source().isEmpty() && getAction(((GraphTreeLabel*)i)->nodename())!='D') {
|
|
|
|
popup.insertItem(i18n("Diff to previous"),301);
|
|
|
|
}
|
|
|
|
if (m_Selected && m_Selected != i && getAction(m_Selected->nodename())!='D'
|
|
|
|
&& getAction(((GraphTreeLabel*)i)->nodename())!='D') {
|
|
|
|
popup.insertItem(i18n("Diff to selected item"),302);
|
|
|
|
}
|
|
|
|
if (getAction(((GraphTreeLabel*)i)->nodename())!='D') {
|
|
|
|
popup.insertItem(i18n("Cat this version"),303);
|
|
|
|
}
|
|
|
|
if (m_Selected == i) {
|
|
|
|
popup.insertItem(i18n("Unselect item"),401);
|
|
|
|
} else {
|
|
|
|
popup.insertItem(i18n("Select item"),402);
|
|
|
|
}
|
|
|
|
popup.insertSeparator();
|
|
|
|
popup.insertItem(i18n("Display details"),403);
|
|
|
|
popup.insertSeparator();
|
|
|
|
}
|
|
|
|
popup.insertItem(i18n("Rotate counter-clockwise"),101);
|
|
|
|
popup.insertItem(i18n("Rotate clockwise"),102);
|
|
|
|
popup.insertSeparator();
|
|
|
|
int it = popup.insertItem(i18n("Diff in revisiontree is recursive"),202);
|
|
|
|
popup.setCheckable(true);
|
|
|
|
popup.setItemChecked(it,Kdesvnsettings::tree_diff_rec());
|
|
|
|
popup.insertItem(i18n("Save tree as png"),201);
|
|
|
|
|
|
|
|
int r = popup.exec(e->globalPos());
|
|
|
|
|
|
|
|
switch (r) {
|
|
|
|
case 101:
|
|
|
|
{
|
|
|
|
int dir = Kdesvnsettings::tree_direction();
|
|
|
|
setNewDirection(++dir);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 102:
|
|
|
|
{
|
|
|
|
int dir = Kdesvnsettings::tree_direction();
|
|
|
|
setNewDirection(--dir);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 201:
|
|
|
|
{
|
|
|
|
TQString fn = KFileDialog::getSaveFileName(":","*.png");
|
|
|
|
if (!fn.isEmpty()) {
|
|
|
|
if (m_Marker) {
|
|
|
|
m_Marker->hide();
|
|
|
|
}
|
|
|
|
if (m_Selected) {
|
|
|
|
m_Selected->setSelected(false);
|
|
|
|
}
|
|
|
|
TQPixmap pix(m_Canvas->size());
|
|
|
|
TQPainter p(&pix);
|
|
|
|
m_Canvas->drawArea( m_Canvas->rect(), &p );
|
|
|
|
pix.save(fn,"PNG");
|
|
|
|
if (m_Marker) {
|
|
|
|
m_Marker->show();
|
|
|
|
}
|
|
|
|
if (m_Selected) {
|
|
|
|
m_Selected->setSelected(true);
|
|
|
|
m_Canvas->update();
|
|
|
|
m_CompleteView->updateCurrentRect();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case 202:
|
|
|
|
{
|
|
|
|
Kdesvnsettings::setTree_diff_rec(!Kdesvnsettings::tree_diff_rec());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 301:
|
|
|
|
if (i && i->rtti()==GRAPHTREE_LABEL && !((GraphTreeLabel*)i)->source().isEmpty()) {
|
|
|
|
makeDiffPrev((GraphTreeLabel*)i);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 302:
|
|
|
|
if (i && i->rtti()==GRAPHTREE_LABEL && m_Selected) {
|
|
|
|
makeDiff(((GraphTreeLabel*)i)->nodename(),m_Selected->nodename());
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 303:
|
|
|
|
if (i && i->rtti()==GRAPHTREE_LABEL) {
|
|
|
|
makeCat((GraphTreeLabel*)i);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 401:
|
|
|
|
makeSelected(0);
|
|
|
|
break;
|
|
|
|
case 402:
|
|
|
|
makeSelected((GraphTreeLabel*)i);
|
|
|
|
break;
|
|
|
|
case 403:
|
|
|
|
emit dispDetails(toolTip(((GraphTreeLabel*)i)->nodename(),true));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void RevGraphView::makeCat(GraphTreeLabel*_l)
|
|
|
|
{
|
|
|
|
if (!_l) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
TQString n1 = _l->nodename();
|
|
|
|
trevTree::ConstIterator it = m_Tree.find(n1);
|
|
|
|
if (it==m_Tree.end()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
svn::Revision tr(it.data().rev);
|
|
|
|
TQString tp = _basePath+it.data().name;
|
|
|
|
emit makeCat(tr,tp,it.data().name,tr,TQT_TQWIDGET(kapp->activeModalWidget()));
|
|
|
|
}
|
|
|
|
|
|
|
|
void RevGraphView::makeDiffPrev(GraphTreeLabel*_l)
|
|
|
|
{
|
|
|
|
if (!_l) return;
|
|
|
|
TQString n1,n2;
|
|
|
|
n1 = _l->nodename();
|
|
|
|
n2 = _l->source();
|
|
|
|
makeDiff(n1,n2);
|
|
|
|
}
|
|
|
|
|
|
|
|
void RevGraphView::makeDiff(const TQString&n1,const TQString&n2)
|
|
|
|
{
|
|
|
|
if (n1.isEmpty()||n2.isEmpty()) return;
|
|
|
|
trevTree::ConstIterator it;
|
|
|
|
it = m_Tree.find(n2);
|
|
|
|
if (it==m_Tree.end()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
svn::Revision sr(it.data().rev);
|
|
|
|
TQString sp = _basePath+it.data().name;
|
|
|
|
|
|
|
|
it = m_Tree.find(n1);
|
|
|
|
if (it==m_Tree.end()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
svn::Revision tr(it.data().rev);
|
|
|
|
TQString tp = _basePath+it.data().name;
|
|
|
|
if (Kdesvnsettings::tree_diff_rec()) {
|
|
|
|
emit makeRecDiff(sp,sr,tp,tr,TQT_TQWIDGET(kapp->activeModalWidget()));
|
|
|
|
} else {
|
|
|
|
emit makeNorecDiff(sp,sr,tp,tr,TQT_TQWIDGET(kapp->activeModalWidget()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void RevGraphView::setBasePath(const TQString&_path)
|
|
|
|
{
|
|
|
|
_basePath = _path;
|
|
|
|
}
|
|
|
|
|
|
|
|
void RevGraphView::slotClientException(const TQString&what)
|
|
|
|
{
|
|
|
|
KMessageBox::sorry(TQT_TQWIDGET(KApplication::activeModalWidget()),what,i18n("SVN Error"));
|
|
|
|
}
|
|
|
|
|
|
|
|
#include "revgraphview.moc"
|