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.
tdesvn/src/svnfrontend/graphtree/revgraphview.cpp

951 lines
29 KiB

/***************************************************************************
* 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.h"
#include <kapp.h>
#include <kdebug.h>
#include <tdetempfile.h>
#include <ktempdir.h>
#include <kprocess.h>
#include <tdelocale.h>
#include <tdefiledialog.h>
#include <tdemessagebox.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(TDEProcess*,char * buffer,int buflen)
{
dotOutput+=TQString::fromLocal8Bit(buffer, buflen);
}
void RevGraphView::dotExit(TDEProcess*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 TDEProcess();
renderProcess->setEnvironment("LANG","C");
*renderProcess << "dot";
*renderProcess << dotTmpFile->name() << "-Tplain";
connect(renderProcess,TQT_SIGNAL(processExited(TDEProcess*)),this,TQT_SLOT(dotExit(TDEProcess*)));
connect(renderProcess,TQT_SIGNAL(receivedStdout(TDEProcess*,char*,int)),
this,TQT_SLOT(readDotOutput(TDEProcess*,char*,int)) );
if (!renderProcess->start(TDEProcess::NotifyOnExit,TDEProcess::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(TDEApplication::activeModalWidget()),what,i18n("SVN Error"));
}
#include "revgraphview.moc"