diff options
Diffstat (limited to 'src/svnfrontend/graphtree/revgraphview.cpp')
-rw-r--r-- | src/svnfrontend/graphtree/revgraphview.cpp | 950 |
1 files changed, 950 insertions, 0 deletions
diff --git a/src/svnfrontend/graphtree/revgraphview.cpp b/src/svnfrontend/graphtree/revgraphview.cpp new file mode 100644 index 0000000..cc85c0e --- /dev/null +++ b/src/svnfrontend/graphtree/revgraphview.cpp @@ -0,0 +1,950 @@ +/*************************************************************************** + * Copyright (C) 2006-2007 by Rajko Albrecht * + * [email protected] * + * * + * 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 "src/settings/kdesvnsettings.h" +#include "../stopdlg.h" +#include "src/svnqt/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 <qtooltip.h> +#include <qwmatrix.h> +#include <qpopupmenu.h> +#include <qpainter.h> +#include <qregexp.h> + +#include <math.h> + +#define LABEL_WIDTH 160 +#define LABEL_HEIGHT 90 + + +class GraphViewTip:public QToolTip +{ +public: + GraphViewTip( QWidget* p ):QToolTip(p) {} + virtual ~GraphViewTip(){} + +protected: + void maybeTip( const QPoint & ); +}; + +void GraphViewTip::maybeTip( const QPoint & pos) +{ + if (!parentWidget()->inherits( "RevGraphView" )) return; + RevGraphView* cgv = (RevGraphView*)parentWidget(); + QPoint cPos = cgv->viewportToContents(pos); + QCanvasItemList l = cgv->canvas()->collisions(cPos); + if (l.count() == 0) return; + QCanvasItem* i = l.first(); + if (i->rtti() == GRAPHTREE_LABEL) { + GraphTreeLabel*tl = (GraphTreeLabel*)i; + QString nm = tl->nodename(); + QString tipStr = cgv->toolTip(nm); + if (tipStr.length()>0) { + QPoint vPosTL = cgv->contentsToViewport(i->boundingRect().topLeft()); + QPoint vPosBR = cgv->contentsToViewport(i->boundingRect().bottomRight()); + tip(QRect(vPosTL, vPosBR), tipStr); + } + } +} + +RevGraphView::RevGraphView(QObject*aListener,svn::Client*_client,QWidget * parent, const char * name, WFlags f) + : QCanvasView(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(QScrollView::AlwaysOff); + m_CompleteView->setHScrollBarMode(QScrollView::AlwaysOff); + m_CompleteView->raise(); + m_CompleteView->hide(); + connect(this, SIGNAL(contentsMoving(int,int)), + this, SLOT(contentsMovingSlot(int,int))); + connect(m_CompleteView, SIGNAL(zoomRectMoved(int,int)), + this, SLOT(zoomRectMoved(int,int))); + connect(m_CompleteView, SIGNAL(zoomRectMoveFinished()), + this, 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 QString&s) +{ + clear(); + m_Canvas = new QCanvas(QApplication::desktop()->width(), + QApplication::desktop()->height()); + + QCanvasText* t = new QCanvasText(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+=QString::fromLocal8Bit(buffer, buflen); +} + +void RevGraphView::dotExit(KProcess*p) +{ + if (p!=renderProcess)return; + // remove line breaks when lines to long + QRegExp endslash("\\\\\\n"); + dotOutput.replace(endslash,""); + double scale = 1.0, scaleX = 1.0, scaleY = 1.0; + double dotWidth, dotHeight; + QTextStream* dotStream; + dotStream = new QTextStream(dotOutput, IO_ReadOnly); + QString line,cmd; + int lineno=0; + clear(); + beginInsert(); + /* mostly taken from kcachegrind */ + while (1) { + line = dotStream->readLine(); + if (line.isNull()) break; + lineno++; + if (line.isEmpty()) continue; + QTextStream 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 < QApplication::desktop()->width()) + _xMargin += (QApplication::desktop()->width()-w)/2; + _yMargin = 50; + if (h < QApplication::desktop()->height()) + _yMargin += (QApplication::desktop()->height()-h)/2; + m_Canvas = new QCanvas(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") { + QString nodeName, label; + QString _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); + QRect 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 { + QString node1Name, node2Name, label; + QString _x,_y; + double x, y; + QPointArray 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) qDebug(" P %d: ( %f / %f ) => ( %d / %d)", + i, x, y, xx, yy); + pa.setPoint(i, xx, yy); + } + if (i < points) { + qDebug("CallGraphView: Can't read %d spline points (%d)", + points, lineno); + continue; + } + + GraphEdge * n = new GraphEdge(m_Canvas); + QColor arrowColor = Qt::black; + n->setPen(QPen(arrowColor,1)); + n->setControlPoints(pa,false); + n->setZ(0.5); + n->show(); + + /* arrow */ + QPoint arrowDir; + int indexHead = -1; + + QMap<QString,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) { + QPoint 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())); + QPointArray a(3); + a.setPoint(0, pa.point(indexHead) + arrowDir); + a.setPoint(1, pa.point(indexHead) + QPoint(arrowDir.y()/2, + -arrowDir.x()/2)); + a.setPoint(2, pa.point(indexHead) + QPoint(-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) { + QString 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 QString&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 QString&nodeName)const +{ + trevTree::ConstIterator it; + it = m_Tree.find(nodeName); + if (it==m_Tree.end()) { + return (char)0; + } + return it.data().Action; +} + +QColor RevGraphView::getBgColor(const QString&nodeName)const +{ + trevTree::ConstIterator it; + it = m_Tree.find(nodeName); + QColor res = Qt::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 QString&RevGraphView::getLabelstring(const QString&nodeName) +{ + QMap<QString,QString>::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[""]; + } + QString 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(QString::null,".dot"); + dotTmpFile->setAutoDelete(true); + + QTextStream* 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 << QString(" 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 << QString(" 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,SIGNAL(processExited(KProcess*)),this,SLOT(dotExit(KProcess*))); + connect(renderProcess,SIGNAL(receivedStdout(KProcess*,char*,int)), + this,SLOT(readDotOutput(KProcess*,char*,int)) ); + if (!renderProcess->start(KProcess::NotifyOnExit,KProcess::Stdout)) { + QString arguments; + for (unsigned c=0;c<renderProcess->args().count();++c) { + arguments+=QString(" %1").arg(renderProcess->args()[c]); + } + QString error = i18n("Could not start process \"%1\".").arg(arguments); + showText(error); + renderProcess=0; + //delete renderProcess;< + } +} + +QString RevGraphView::toolTip(const QString&_nodename,bool full)const +{ + QString res = QString::null; + trevTree::ConstIterator it; + it = m_Tree.find(_nodename); + if (it==m_Tree.end()) { + return res; + } + QStringList sp = QStringList::split("\n",it.data().Message); + QString 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 QString csep = "</td><td>"; + static QString rend = "</td></tr>"; + static QString rstart = "<tr><td>"; + res = QString("<html><body>"); + + if (!full) { + res+=QString("<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(QSize s) +{ + if (!m_Canvas) return; + if (s == QSize(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) qDebug("Canvas Size: %dx%d, Visible: %dx%d, Zoom: %f", + m_Canvas->width(), m_Canvas->height(), + cWidth, cHeight, zoom); + + QWMatrix 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; + + QPoint oldZoomPos = m_CompleteView->pos(); + QPoint newZoomPos = QPoint(0,0); + +#if 0 + ZoomPosition zp = _zoomPosition; + if (zp == Auto) { +#else + ZoomPosition zp = m_LastAutoPosition; +#endif + QPoint tl1Pos = viewportToContents(QPoint(0,0)); + QPoint tl2Pos = viewportToContents(QPoint(cvW,cvH)); + QPoint tr1Pos = viewportToContents(QPoint(x,0)); + QPoint tr2Pos = viewportToContents(QPoint(x+cvW,cvH)); + QPoint bl1Pos = viewportToContents(QPoint(0,y)); + QPoint bl2Pos = viewportToContents(QPoint(cvW,y+cvH)); + QPoint br1Pos = viewportToContents(QPoint(x,y)); + QPoint br2Pos = viewportToContents(QPoint(x+cvW,y+cvH)); + int tlCols = m_Canvas->collisions(QRect(tl1Pos,tl2Pos)).count(); + int trCols = m_Canvas->collisions(QRect(tr1Pos,tr2Pos)).count(); + int blCols = m_Canvas->collisions(QRect(bl1Pos,bl2Pos)).count(); + int brCols = m_Canvas->collisions(QRect(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 = QPoint(x,0); + break; + case BottomLeft: + newZoomPos = QPoint(0,y); + break; + case BottomRight: + newZoomPos = QPoint(x,y); + break; + default: + break; + } + if (newZoomPos != oldZoomPos) m_CompleteView->move(newZoomPos); +} + +void RevGraphView::contentsMovingSlot(int x,int y) +{ + QRect z(int(x * _cvZoom), int(y * _cvZoom), + int(visibleWidth() * _cvZoom)-1, int(visibleHeight() * _cvZoom)-1); + if (0) qDebug("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(QResizeEvent*e) +{ + QCanvasView::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 ( QMouseEvent * e ) +{ + setFocus(); + if (e->button() == Qt::LeftButton) { + QCanvasItemList l = canvas()->collisions(e->pos()); + if (l.count()>0) { + QCanvasItem* i = l.first(); + if (i->rtti()==GRAPHTREE_LABEL) { + makeSelected( (GraphTreeLabel*)i); + emit dispDetails(toolTip(((GraphTreeLabel*)i)->nodename(),true)); + } + } + } +} + +void RevGraphView::contentsMousePressEvent ( QMouseEvent * e ) +{ + setFocus(); + _isMoving = true; + _lastPos = e->globalPos(); +} + +void RevGraphView::contentsMouseReleaseEvent ( QMouseEvent * ) +{ + _isMoving = false; + updateZoomerPos(); +} + +void RevGraphView::contentsMouseMoveEvent ( QMouseEvent * 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(QContextMenuEvent* e) +{ + if (!m_Canvas) return; + QCanvasItemList l = canvas()->collisions(e->pos()); + QCanvasItem* i = (l.count() == 0) ? 0 : l.first(); + + QPopupMenu 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: + { + QString fn = KFileDialog::getSaveFileName(":","*.png"); + if (!fn.isEmpty()) { + if (m_Marker) { + m_Marker->hide(); + } + if (m_Selected) { + m_Selected->setSelected(false); + } + QPixmap pix(m_Canvas->size()); + QPainter 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; + } + QString n1 = _l->nodename(); + trevTree::ConstIterator it = m_Tree.find(n1); + if (it==m_Tree.end()) { + return; + } + svn::Revision tr(it.data().rev); + QString tp = _basePath+it.data().name; + emit makeCat(tr,tp,it.data().name,tr,kapp->activeModalWidget()); +} + +void RevGraphView::makeDiffPrev(GraphTreeLabel*_l) +{ + if (!_l) return; + QString n1,n2; + n1 = _l->nodename(); + n2 = _l->source(); + makeDiff(n1,n2); +} + +void RevGraphView::makeDiff(const QString&n1,const QString&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); + QString sp = _basePath+it.data().name; + + it = m_Tree.find(n1); + if (it==m_Tree.end()) { + return; + } + svn::Revision tr(it.data().rev); + QString tp = _basePath+it.data().name; + if (Kdesvnsettings::tree_diff_rec()) { + emit makeRecDiff(sp,sr,tp,tr,kapp->activeModalWidget()); + } else { + emit makeNorecDiff(sp,sr,tp,tr,kapp->activeModalWidget()); + } +} + +void RevGraphView::setBasePath(const QString&_path) +{ + _basePath = _path; +} + +void RevGraphView::slotClientException(const QString&what) +{ + KMessageBox::sorry(KApplication::activeModalWidget(),what,i18n("SVN Error")); +} + +#include "revgraphview.moc" |