summaryrefslogtreecommitdiffstats
path: root/src/svnfrontend/graphtree
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-03-15 17:32:48 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-03-15 17:32:48 +0000
commite2f541c98dfa4081fa3ab3d28f08ea2309281884 (patch)
treecb721a55bc88753ddeb9754dc98ef45e2850ce30 /src/svnfrontend/graphtree
downloadtdesvn-e2f541c98dfa4081fa3ab3d28f08ea2309281884.tar.gz
tdesvn-e2f541c98dfa4081fa3ab3d28f08ea2309281884.zip
Added KDE3 version of kdesvn
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/kdesvn@1103685 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src/svnfrontend/graphtree')
-rw-r--r--src/svnfrontend/graphtree/drawparams.cpp708
-rw-r--r--src/svnfrontend/graphtree/drawparams.h200
-rw-r--r--src/svnfrontend/graphtree/elogentry.cpp76
-rw-r--r--src/svnfrontend/graphtree/elogentry.h37
-rw-r--r--src/svnfrontend/graphtree/graphtree_defines.h31
-rw-r--r--src/svnfrontend/graphtree/graphtreelabel.cpp220
-rw-r--r--src/svnfrontend/graphtree/graphtreelabel.h89
-rw-r--r--src/svnfrontend/graphtree/pannerview.cpp101
-rw-r--r--src/svnfrontend/graphtree/pannerview.h53
-rw-r--r--src/svnfrontend/graphtree/revgraphview.cpp950
-rw-r--r--src/svnfrontend/graphtree/revgraphview.h151
-rw-r--r--src/svnfrontend/graphtree/revisiontree.cpp544
-rw-r--r--src/svnfrontend/graphtree/revisiontree.h75
-rw-r--r--src/svnfrontend/graphtree/revtreewidget.cpp115
-rw-r--r--src/svnfrontend/graphtree/revtreewidget.h74
15 files changed, 3424 insertions, 0 deletions
diff --git a/src/svnfrontend/graphtree/drawparams.cpp b/src/svnfrontend/graphtree/drawparams.cpp
new file mode 100644
index 0000000..ced7bd4
--- /dev/null
+++ b/src/svnfrontend/graphtree/drawparams.cpp
@@ -0,0 +1,708 @@
+/* This file is part of KCachegrind.
+ Copyright (C) 2002, 2003 Josef Weidendorfer <[email protected]>
+ Adapted for the needs of kdesvn by Rajko Albrecht <[email protected]>
+
+ KCachegrind 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, version 2.
+
+ 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; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+/*
+ * A Widget for visualizing hierarchical metrics as areas.
+ * The API is similar to QListView.
+ */
+
+#include <math.h>
+
+#include <qpainter.h>
+#include <qtooltip.h>
+#include <qregexp.h>
+#include <qstyle.h>
+#include <qpopupmenu.h>
+
+#include <klocale.h>
+#include <kconfig.h>
+#include <kdebug.h>
+
+#include "drawparams.h"
+
+
+// set this to 1 to enable debug output
+#define DEBUG_DRAWING 0
+#define MAX_FIELD 12
+
+
+//
+// StoredDrawParams
+//
+StoredDrawParams::StoredDrawParams()
+{
+ _selected = false;
+ _current = false;
+ _shaded = true;
+ _rotated = false;
+
+ _backColor = Qt::white;
+
+ // field array has size 0
+}
+
+StoredDrawParams::StoredDrawParams(QColor c,
+ bool selected, bool current)
+{
+ _backColor = c;
+
+ _selected = selected;
+ _current = current;
+ _shaded = true;
+ _rotated = false;
+ _drawFrame = true;
+
+ // field array has size 0
+}
+
+QString StoredDrawParams::text(int f) const
+{
+ if ((f<0) || (f >= (int)_field.size()))
+ return QString::null;
+
+ return _field[f].text;
+}
+
+QPixmap StoredDrawParams::pixmap(int f) const
+{
+ if ((f<0) || (f >= (int)_field.size()))
+ return QPixmap();
+
+ return _field[f].pix;
+}
+
+DrawParams::Position StoredDrawParams::position(int f) const
+{
+ if ((f<0) || (f >= (int)_field.size()))
+ return Default;
+
+ return _field[f].pos;
+}
+
+int StoredDrawParams::maxLines(int f) const
+{
+ if ((f<0) || (f >= (int)_field.size()))
+ return 0;
+
+ return _field[f].maxLines;
+}
+
+const QFont& StoredDrawParams::font() const
+{
+ static QFont* f = 0;
+ if (!f) f = new QFont(QApplication::font());
+
+ return *f;
+}
+
+void StoredDrawParams::ensureField(int f)
+{
+ static Field* def = 0;
+ if (!def) {
+ def = new Field();
+ def->pos = Default;
+ def->maxLines = 0;
+ }
+
+ if (f<0 || f>=MAX_FIELD) return;
+
+ if ((int)_field.size() < f+1) _field.resize(f+1, *def);
+}
+
+
+void StoredDrawParams::setField(int f, QString t, QPixmap pm,
+ Position p, int maxLines)
+{
+ if (f<0 || f>=MAX_FIELD) return;
+ ensureField(f);
+
+ _field[f].text = t;
+ _field[f].pix = pm;
+ _field[f].pos = p;
+ _field[f].maxLines = maxLines;
+}
+
+void StoredDrawParams::setText(int f, QString t)
+{
+ if (f<0 || f>=MAX_FIELD) return;
+ ensureField(f);
+
+ _field[f].text = t;
+}
+
+void StoredDrawParams::setPixmap(int f, QPixmap pm)
+{
+ if (f<0 || f>=MAX_FIELD) return;
+ ensureField(f);
+
+ _field[f].pix = pm;
+}
+
+void StoredDrawParams::setPosition(int f, Position p)
+{
+ if (f<0 || f>=MAX_FIELD) return;
+ ensureField(f);
+
+ _field[f].pos = p;
+}
+
+void StoredDrawParams::setMaxLines(int f, int m)
+{
+ if (f<0 || f>=MAX_FIELD) return;
+ ensureField(f);
+
+ _field[f].maxLines = m;
+}
+
+
+
+//
+// RectDrawing
+//
+
+RectDrawing::RectDrawing(QRect r)
+{
+ _fm = 0;
+ _dp = 0;
+ setRect(r);
+}
+
+
+RectDrawing::~RectDrawing()
+{
+ delete _fm;
+ delete _dp;
+}
+
+DrawParams* RectDrawing::drawParams()
+{
+ if (!_dp)
+ _dp = new StoredDrawParams();
+
+ return _dp;
+}
+
+
+void RectDrawing::setDrawParams(DrawParams* dp)
+{
+ if (_dp) delete _dp;
+ _dp = dp;
+}
+
+void RectDrawing::setRect(QRect r)
+{
+ _rect = r;
+
+ _usedTopLeft = 0;
+ _usedTopCenter = 0;
+ _usedTopRight = 0;
+ _usedBottomLeft = 0;
+ _usedBottomCenter = 0;
+ _usedBottomRight = 0;
+
+ _fontHeight = 0;
+}
+
+QRect RectDrawing::remainingRect(DrawParams* dp)
+{
+ if (!dp) dp = drawParams();
+
+ if ((_usedTopLeft >0) ||
+ (_usedTopCenter >0) ||
+ (_usedTopRight >0)) {
+ if (dp->rotated())
+ _rect.setLeft(_rect.left() + _fontHeight);
+ else
+ _rect.setTop(_rect.top() + _fontHeight);
+ }
+
+ if ((_usedBottomLeft >0) ||
+ (_usedBottomCenter >0) ||
+ (_usedBottomRight >0)) {
+ if (dp->rotated())
+ _rect.setRight(_rect.right() - _fontHeight);
+ else
+ _rect.setBottom(_rect.bottom() - _fontHeight);
+ }
+ return _rect;
+}
+
+
+void RectDrawing::drawBack(QPainter* p, DrawParams* dp)
+{
+ if (!dp) dp = drawParams();
+ if (_rect.width()<=0 || _rect.height()<=0) return;
+
+ QRect r = _rect;
+ QColor normal = dp->backColor();
+ if (dp->selected()) normal = normal.light();
+ bool isCurrent = dp->current();
+
+ if (dp->drawFrame() || isCurrent) {
+ // 3D raised/sunken frame effect...
+ QColor high = normal.light();
+ QColor low = normal.dark();
+ p->setPen( isCurrent ? low:high);
+ p->drawLine(r.left(), r.top(), r.right(), r.top());
+ p->drawLine(r.left(), r.top(), r.left(), r.bottom());
+ p->setPen( isCurrent ? high:low);
+ p->drawLine(r.right(), r.top(), r.right(), r.bottom());
+ p->drawLine(r.left(), r.bottom(), r.right(), r.bottom());
+ r.setRect(r.x()+1, r.y()+1, r.width()-2, r.height()-2);
+ }
+ if (r.width()<=0 || r.height()<=0) return;
+
+ if (dp->shaded()) {
+ // some shading
+ bool goDark = qGray(normal.rgb())>128;
+ int rBase, gBase, bBase;
+ normal.rgb(&rBase, &gBase, &bBase);
+ p->setBrush(QBrush::NoBrush);
+
+ // shade parameters:
+ int d = 7;
+ float factor = 0.1, forth=0.7, back1 =0.9, toBack2 = .7, back2 = 0.97;
+
+ // coefficient corrections because of rectangle size
+ int s = r.width();
+ if (s > r.height()) s = r.height();
+ if (s<100) {
+ forth -= .3 * (100-s)/100;
+ back1 -= .2 * (100-s)/100;
+ back2 -= .02 * (100-s)/100;
+ }
+
+
+ // maximal color difference
+ int rDiff = goDark ? -rBase/d : (255-rBase)/d;
+ int gDiff = goDark ? -gBase/d : (255-gBase)/d;
+ int bDiff = goDark ? -bBase/d : (255-bBase)/d;
+
+ QColor shadeColor;
+ while (factor<.95) {
+ shadeColor.setRgb((int)(rBase+factor*rDiff+.5),
+ (int)(gBase+factor*gDiff+.5),
+ (int)(bBase+factor*bDiff+.5));
+ p->setPen(shadeColor);
+ p->drawRect(r);
+ r.setRect(r.x()+1, r.y()+1, r.width()-2, r.height()-2);
+ if (r.width()<=0 || r.height()<=0) return;
+ factor = 1.0 - ((1.0 - factor) * forth);
+ }
+
+ // and back (1st half)
+ while (factor>toBack2) {
+ shadeColor.setRgb((int)(rBase+factor*rDiff+.5),
+ (int)(gBase+factor*gDiff+.5),
+ (int)(bBase+factor*bDiff+.5));
+ p->setPen(shadeColor);
+ p->drawRect(r);
+ r.setRect(r.x()+1, r.y()+1, r.width()-2, r.height()-2);
+ if (r.width()<=0 || r.height()<=0) return;
+ factor = 1.0 - ((1.0 - factor) / back1);
+ }
+
+ // and back (2nd half)
+ while ( factor>.01) {
+ shadeColor.setRgb((int)(rBase+factor*rDiff+.5),
+ (int)(gBase+factor*gDiff+.5),
+ (int)(bBase+factor*bDiff+.5));
+ p->setPen(shadeColor);
+ p->drawRect(r);
+ r.setRect(r.x()+1, r.y()+1, r.width()-2, r.height()-2);
+ if (r.width()<=0 || r.height()<=0) return;
+
+ factor = factor * back2;
+ }
+ }
+
+ // fill inside
+ p->setPen(QPen::NoPen);
+ p->setBrush(normal);
+ p->drawRect(r);
+}
+
+
+bool RectDrawing::drawField(QPainter* p, int f, DrawParams* dp)
+{
+ if (!dp) dp = drawParams();
+
+ if (!_fm) {
+ _fm = new QFontMetrics(dp->font());
+ _fontHeight = _fm->height();
+ }
+
+ QRect r = _rect;
+
+ if (0) kdDebug(90100) << "DrawField: Rect " << r.x() << "/" << r.y()
+ << " - " << r.width() << "x" << r.height() << endl;
+
+ int h = _fontHeight;
+ bool rotate = dp->rotated();
+ int width = (rotate ? r.height() : r.width()) -4;
+ int height = (rotate ? r.width() : r.height());
+ int lines = height / h;
+
+ // stop if we have no space available
+ if (lines<1) return false;
+
+ // calculate free space in first line (<unused>)
+ int pos = dp->position(f);
+ if (pos == DrawParams::Default) {
+ switch(f%4) {
+ case 0: pos = DrawParams::TopLeft; break;
+ case 1: pos = DrawParams::TopRight; break;
+ case 2: pos = DrawParams::BottomRight; break;
+ case 3: pos = DrawParams::BottomLeft; break;
+ }
+ }
+
+ int unused = 0;
+ bool isBottom = false;
+ bool isCenter = false;
+ bool isRight = false;
+ int* used = 0;
+ switch(pos) {
+ case DrawParams::TopLeft:
+ used = &_usedTopLeft;
+ if (_usedTopLeft == 0) {
+ if (_usedTopCenter)
+ unused = (width - _usedTopCenter)/2;
+ else
+ unused = width - _usedTopRight;
+ }
+ break;
+
+ case DrawParams::TopCenter:
+ isCenter = true;
+ used = &_usedTopCenter;
+ if (_usedTopCenter == 0) {
+ if (_usedTopLeft > _usedTopRight)
+ unused = width - 2 * _usedTopLeft;
+ else
+ unused = width - 2 * _usedTopRight;
+ }
+ break;
+
+ case DrawParams::TopRight:
+ isRight = true;
+ used = &_usedTopRight;
+ if (_usedTopRight == 0) {
+ if (_usedTopCenter)
+ unused = (width - _usedTopCenter)/2;
+ else
+ unused = width - _usedTopLeft;
+ }
+ break;
+
+ case DrawParams::BottomLeft:
+ isBottom = true;
+ used = &_usedBottomLeft;
+ if (_usedBottomLeft == 0) {
+ if (_usedBottomCenter)
+ unused = (width - _usedBottomCenter)/2;
+ else
+ unused = width - _usedBottomRight;
+ }
+ break;
+
+ case DrawParams::BottomCenter:
+ isCenter = true;
+ isBottom = true;
+ used = &_usedBottomCenter;
+ if (_usedBottomCenter == 0) {
+ if (_usedBottomLeft > _usedBottomRight)
+ unused = width - 2 * _usedBottomLeft;
+ else
+ unused = width - 2 * _usedBottomRight;
+ }
+ break;
+
+ case DrawParams::BottomRight:
+ isRight = true;
+ isBottom = true;
+ used = &_usedBottomRight;
+ if (_usedBottomRight == 0) {
+ if (_usedBottomCenter)
+ unused = (width - _usedBottomCenter)/2;
+ else
+ unused = width - _usedBottomLeft;
+ }
+ break;
+ }
+
+ if (isBottom) {
+ if ((_usedTopLeft >0) ||
+ (_usedTopCenter >0) ||
+ (_usedTopRight >0))
+ lines--;
+ }
+ else if (!isBottom) {
+ if ((_usedBottomLeft >0) ||
+ (_usedBottomCenter >0) ||
+ (_usedBottomRight >0))
+ lines--;
+ }
+ if (lines<1) return false;
+
+
+ int y = isBottom ? height - h : 0;
+
+ if (unused < 0) unused = 0;
+ if (unused == 0) {
+ // no space available in last line at this position
+ y = isBottom ? (y-h) : (y+h);
+ lines--;
+
+ if (lines<1) return false;
+
+ // new line: reset used space
+ if (isBottom)
+ _usedBottomLeft = _usedBottomCenter = _usedBottomRight = 0;
+ else
+ _usedTopLeft = _usedTopCenter = _usedTopRight = 0;
+
+ unused = width;
+ }
+
+ // stop as soon as possible when there's no space for "..."
+ static int dotW = 0;
+ if (!dotW) dotW = _fm->width("...");
+ if (width < dotW) return false;
+
+ // get text and pixmap now, only if we need to, because it is possible
+ // that they are calculated on demand (and this can take some time)
+ QString name = dp->text(f);
+ if (name.isEmpty()) return 0;
+ QPixmap pix = dp->pixmap(f);
+
+ // check if pixmap can be drawn
+ int pixW = pix.width();
+ int pixH = pix.height();
+ int pixY = 0;
+ bool pixDrawn = true;
+ if (pixW>0) {
+ pixW += 2; // X distance from pix
+ if ((width < pixW + dotW) || (height < pixH)) {
+ // don't draw
+ pixW = 0;
+ }
+ else
+ pixDrawn = false;
+ }
+
+ // width of text and pixmap to be drawn
+ int w = pixW + _fm->width(name);
+
+ if (0) kdDebug(90100) << " For '" << name << "': Unused " << unused
+ << ", StrW " << w << ", Width " << width << endl;
+
+ // if we have limited space at 1st line:
+ // use it only if whole name does fit in last line...
+ if ((unused < width) && (w > unused)) {
+ y = isBottom ? (y-h) : (y+h);
+ lines--;
+
+ if (lines<1) return false;
+
+ // new line: reset used space
+ if (isBottom)
+ _usedBottomLeft = _usedBottomCenter = _usedBottomRight = 0;
+ else
+ _usedTopLeft = _usedTopCenter = _usedTopRight = 0;
+ }
+
+ p->save();
+ p->setPen( (qGray(dp->backColor().rgb())>100) ? Qt::black : Qt::white);
+ p->setFont(dp->font());
+ if (rotate) {
+ //p->translate(r.x()+2, r.y()+r.height());
+ p->translate(r.x(), r.y()+r.height()-2);
+ p->rotate(270);
+ }
+ else
+ p->translate(r.x()+2, r.y());
+
+
+ // adjust available lines according to maxLines
+ int max = dp->maxLines(f);
+ if ((max > 0) && (lines>max)) lines = max;
+
+ /* loop over name parts to break up string depending on available width.
+ * every char category change is supposed a possible break,
+ * with the exception Uppercase=>Lowercase.
+ * It's good enough for numbers, Symbols...
+ *
+ * If the text is to be written at the bottom, we start with the
+ * end of the string (so everything is reverted)
+ */
+ QString remaining;
+ int origLines = lines;
+ while (lines>0) {
+
+ if (w>width && lines>1) {
+ int lastBreakPos = name.length(), lastWidth = w;
+ int len = name.length();
+ QChar::Category caOld, ca;
+
+ if (!isBottom) {
+ // start with comparing categories of last 2 chars
+ caOld = name[len-1].category();
+ while (len>2) {
+ len--;
+ ca = name[len-1].category();
+ if (ca != caOld) {
+ // "Aa" has no break between...
+ if (ca == QChar::Letter_Uppercase &&
+ caOld == QChar::Letter_Lowercase) {
+ caOld = ca;
+ continue;
+ }
+ caOld = ca;
+ lastBreakPos = len;
+ w = pixW + _fm->width(name, len);
+ lastWidth = w;
+ if (w <= width) break;
+ }
+ }
+ w = lastWidth;
+ remaining = name.mid(lastBreakPos);
+ // remove space on break point
+ if (name[lastBreakPos-1].category() == QChar::Separator_Space)
+ name = name.left(lastBreakPos-1);
+ else
+ name = name.left(lastBreakPos);
+ }
+ else { // bottom
+ int l = len;
+ caOld = name[l-len].category();
+ while (len>2) {
+ len--;
+ ca = name[l-len].category();
+
+ if (ca != caOld) {
+ // "Aa" has no break between...
+ if (caOld == QChar::Letter_Uppercase &&
+ ca == QChar::Letter_Lowercase) {
+ caOld = ca;
+ continue;
+ }
+ caOld = ca;
+ lastBreakPos = len;
+ w = pixW + _fm->width(name.right(len));
+ lastWidth = w;
+ if (w <= width) break;
+ }
+ }
+ w = lastWidth;
+ remaining = name.left(l-lastBreakPos);
+ // remove space on break point
+ if (name[l-lastBreakPos].category() == QChar::Separator_Space)
+ name = name.right(lastBreakPos-1);
+ else
+ name = name.right(lastBreakPos);
+ }
+ }
+ else
+ remaining = QString::null;
+
+ /* truncate and add ... if needed */
+ if (w>width) {
+ int len = name.length();
+ w += dotW;
+ while (len>2 && (w > width)) {
+ len--;
+ w = pixW + _fm->width(name, len) + dotW;
+ }
+ // stop drawing: we cannot draw 2 chars + "..."
+ if (w>width) break;
+
+ name = name.left(len) + "...";
+ }
+
+ int x = 0;
+ if (isCenter)
+ x = (width - w)/2;
+ else if (isRight)
+ x = width - w;
+
+ if (!pixDrawn) {
+ pixY = y+(h-pixH)/2; // default: center vertically
+ if (pixH > h) pixY = isBottom ? y-(pixH-h) : y;
+
+ p->drawPixmap( x, pixY, pix);
+
+ // for distance to next text
+ pixY = isBottom ? (pixY - h - 2) : (pixY + pixH + 2);
+ pixDrawn = true;
+ }
+
+
+ if (0) kdDebug(90100) << " Drawing '" << name << "' at "
+ << x+pixW << "/" << y << endl;
+
+ p->drawText( x+pixW, y,
+ width - pixW, h,
+ Qt::AlignLeft, name);
+ y = isBottom ? (y-h) : (y+h);
+ lines--;
+
+ if (remaining.isEmpty()) break;
+ name = remaining;
+ w = pixW + _fm->width(name);
+ }
+
+ // make sure the pix stays visible
+ if (pixDrawn && (pixY>0)) {
+ if (isBottom && (pixY<y)) y = pixY;
+ if (!isBottom && (pixY>y)) y = pixY;
+ }
+
+ if (origLines > lines) {
+ // if only 1 line written, don't reset _used* vars
+ if (lines - origLines >1) {
+ if (isBottom)
+ _usedBottomLeft = _usedBottomCenter = _usedBottomRight = 0;
+ else
+ _usedTopLeft = _usedTopCenter = _usedTopRight = 0;
+ }
+
+ // take back one line
+ y = isBottom ? (y+h) : (y-h);
+ if (used) *used = w;
+ }
+
+ // update free space
+ if (!isBottom) {
+ if (rotate)
+ _rect.setRect(r.x()+y, r.y(), r.width()-y, r.height());
+ else
+ _rect.setRect(r.x(), r.y()+y, r.width(), r.height()-y);
+ }
+ else {
+ if (rotate)
+ _rect.setRect(r.x(), r.y(), y+h, r.height());
+ else
+ _rect.setRect(r.x(), r.y(), r.width(), y+h);
+ }
+
+ p->restore();
+
+ return true;
+}
diff --git a/src/svnfrontend/graphtree/drawparams.h b/src/svnfrontend/graphtree/drawparams.h
new file mode 100644
index 0000000..4898111
--- /dev/null
+++ b/src/svnfrontend/graphtree/drawparams.h
@@ -0,0 +1,200 @@
+/* This file is part of KCachegrind.
+ Copyright (C) 2002, 2003 Josef Weidendorfer <[email protected]>
+ Adapted for the needs of kdesvn by Rajko Albrecht <[email protected]>
+
+ KCachegrind 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, version 2.
+
+ 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; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+/**
+ * A Widget for visualizing hierarchical metrics as areas.
+ * The API is similar to QListView.
+ *
+ * This file defines the following classes:
+ * DrawParams, RectDrawing, TreeMapItem, TreeMapWidget
+ *
+ * DrawParams/RectDrawing allows reusing of TreeMap drawing
+ * functions in other widgets.
+ */
+
+#ifndef DRAWPARAMS_H
+#define DRAWPARAMS_H
+
+#include <qstring.h>
+#include <qwidget.h>
+#include <qpixmap.h>
+#include <qptrlist.h>
+#include <qvaluevector.h>
+#include <qcolor.h>
+#include <qapplication.h>
+#include <qstringlist.h>
+
+class QPopupMenu;
+class QString;
+
+class KConfigGroup;
+
+
+/**
+ * Drawing parameters for an object.
+ * A Helper Interface for RectDrawing.
+ */
+class DrawParams
+{
+public:
+ /**
+ * Positions for drawing into a rectangle.
+ *
+ * The specified position assumes no rotation.
+ * If there is more than one text for one position, it is put
+ * nearer to the center of the item.
+ *
+ * Drawing at top positions cuts free space from top,
+ * drawing at bottom positions cuts from bottom.
+ * Default usually gives positions clockwise according to field number.
+ */
+ enum Position { TopLeft, TopCenter, TopRight,
+ BottomLeft, BottomCenter, BottomRight,
+ Default, Unknown};
+
+ // no constructor as this is an abstract class
+ virtual ~DrawParams() {}
+
+ virtual QString text(int) const = 0;
+ virtual QPixmap pixmap(int) const = 0;
+ virtual Position position(int) const = 0;
+ // 0: no limit, negative: leave at least -maxLines() free
+ virtual int maxLines(int) const { return 0; }
+ virtual int fieldCount() const { return 0; }
+
+ virtual QColor backColor() const { return Qt::white; }
+ virtual const QFont& font() const = 0;
+
+ virtual bool selected() const { return false; }
+ virtual bool current() const { return false; }
+ virtual bool shaded() const { return true; }
+ virtual bool rotated() const { return false; }
+ virtual bool drawFrame() const { return true; }
+};
+
+
+/*
+ * DrawParam with attributes stored
+ */
+class StoredDrawParams: public DrawParams
+{
+public:
+ StoredDrawParams();
+ StoredDrawParams(QColor c,
+ bool selected = false, bool current = false);
+
+ // getters
+ QString text(int) const;
+ QPixmap pixmap(int) const;
+ Position position(int) const;
+ int maxLines(int) const;
+ int fieldCount() const { return _field.size(); }
+
+ QColor backColor() const { return _backColor; }
+ bool selected() const { return _selected; }
+ bool current() const { return _current; }
+ bool shaded() const { return _shaded; }
+ bool rotated() const { return _rotated; }
+ bool drawFrame() const { return _drawFrame; }
+
+ const QFont& font() const;
+
+ // attribute setters
+ void setField(int f, QString t, QPixmap pm = QPixmap(),
+ Position p = Default, int maxLines = 0);
+ void setText(int f, QString);
+ void setPixmap(int f, QPixmap);
+ void setPosition(int f, Position);
+ void setMaxLines(int f, int);
+ void setBackColor(QColor c) { _backColor = c; }
+ void setSelected(bool b) { _selected = b; }
+ void setCurrent(bool b) { _current = b; }
+ void setShaded(bool b) { _shaded = b; }
+ void setRotated(bool b) { _rotated = b; }
+ void drawFrame(bool b) { _drawFrame = b; }
+
+protected:
+ QColor _backColor;
+ bool _selected :1;
+ bool _current :1;
+ bool _shaded :1;
+ bool _rotated :1;
+ bool _drawFrame :1;
+
+private:
+ // resize field array if needed to allow to access field <f>
+ void ensureField(int f);
+
+ struct Field {
+ QString text;
+ QPixmap pix;
+ Position pos;
+ int maxLines;
+ };
+
+ QValueVector<Field> _field;
+};
+
+
+/* State for drawing on a rectangle.
+ *
+ * Following drawing functions are provided:
+ * - background drawing with shading and 3D frame
+ * - successive pixmap/text drawing at various positions with wrap-around
+ * optimized for minimal space usage (e.g. if a text is drawn at top right
+ * after text on top left, the same line is used if space allows)
+ *
+ */
+class RectDrawing
+{
+public:
+ RectDrawing(QRect);
+ ~RectDrawing();
+
+ // The default DrawParams object used.
+ DrawParams* drawParams();
+ // we take control over the given object (i.e. delete at destruction)
+ void setDrawParams(DrawParams*);
+
+ // draw on a given QPainter, use this class as info provider per default
+ void drawBack(QPainter*, DrawParams* dp = 0);
+ /* Draw field at position() from pixmap()/text() with maxLines().
+ * Returns true if something was drawn
+ */
+ bool drawField(QPainter*, int f, DrawParams* dp = 0);
+
+ // resets rectangle for free space
+ void setRect(QRect);
+
+ // Returns the rectangle area still free of text/pixmaps after
+ // a number of drawText() calls.
+ QRect remainingRect(DrawParams* dp = 0);
+
+private:
+ int _usedTopLeft, _usedTopCenter, _usedTopRight;
+ int _usedBottomLeft, _usedBottomCenter, _usedBottomRight;
+ QRect _rect;
+
+ // temporary
+ int _fontHeight;
+ QFontMetrics* _fm;
+ DrawParams* _dp;
+};
+
+#endif
diff --git a/src/svnfrontend/graphtree/elogentry.cpp b/src/svnfrontend/graphtree/elogentry.cpp
new file mode 100644
index 0000000..924332c
--- /dev/null
+++ b/src/svnfrontend/graphtree/elogentry.cpp
@@ -0,0 +1,76 @@
+/***************************************************************************
+ * Copyright (C) 2005-2007 by Rajko Albrecht *
+ * *
+ * 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 "elogentry.h"
+
+#include <kdebug.h>
+
+eLog_Entry::eLog_Entry(const svn::LogEntry&old)
+ : svn::LogEntry(old)
+{
+}
+
+eLog_Entry::eLog_Entry()
+ : svn::LogEntry()
+{
+}
+
+eLog_Entry::~eLog_Entry()
+{
+}
+
+void eLog_Entry::addCopyTo(const QString&current,const QString&target,
+ svn_revnum_t target_rev,char _action,svn_revnum_t from_rev)
+{
+ svn::LogChangePathEntry _entry;
+ _entry.copyToPath=target;
+ _entry.path = current;
+ _entry.copyToRevision = target_rev;
+ _entry.action=_action;
+ _entry.copyFromRevision = from_rev;
+ switch (_action) {
+ case 'A':
+ if (!target.isEmpty()) {
+ //kdDebug()<<"Adding a history "<< current << " -> " << target << endl;
+ _entry.action = 'H';
+ }else{
+ }
+ break;
+ case 'D':
+ break;
+ case 'R':
+#if 0
+ if (!target.isEmpty()) {
+ kdDebug()<<"Adding a rename "<< current << " -> " << target << endl;
+ }
+#endif
+ break;
+ case 'M':
+ break;
+ default:
+ break;
+ }
+ /* make sure that ALL writing operations are BEFORE deletion of item,
+ * otherwise search will fail */
+ if (_action=='D') {
+ changedPaths.push_back(_entry);
+ } else {
+ changedPaths.push_front(_entry);
+ }
+}
diff --git a/src/svnfrontend/graphtree/elogentry.h b/src/svnfrontend/graphtree/elogentry.h
new file mode 100644
index 0000000..0fa63ef
--- /dev/null
+++ b/src/svnfrontend/graphtree/elogentry.h
@@ -0,0 +1,37 @@
+/***************************************************************************
+ * Copyright (C) 2005-2007 by Rajko Albrecht *
+ * *
+ * 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. *
+ ***************************************************************************/
+#ifndef ELOGENTRY_H
+#define ELOGENTRY_H
+
+#include <svnqt/log_entry.hpp>
+
+/**
+ @author Rajko Albrecht <[email protected]>
+*/
+struct eLog_Entry : public svn::LogEntry
+{
+ eLog_Entry();
+ eLog_Entry(const svn::LogEntry&);
+ ~eLog_Entry();
+
+ void addCopyTo(const QString&,const QString&,svn_revnum_t,char _action,svn_revnum_t fromRev=-1);
+ void addAction(const QString&,char _action);
+};
+#endif
diff --git a/src/svnfrontend/graphtree/graphtree_defines.h b/src/svnfrontend/graphtree/graphtree_defines.h
new file mode 100644
index 0000000..596cb7d
--- /dev/null
+++ b/src/svnfrontend/graphtree/graphtree_defines.h
@@ -0,0 +1,31 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Rajko Albrecht *
+ * *
+ * 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. *
+ ***************************************************************************/
+
+#ifndef __GRAPHTREE_DEFINES
+#define __GRAPHTREE_DEFINES
+
+enum {
+ GRAPHTREE_LABEL = 1100,
+ GRAPHTREE_LINE,
+ GRAPHTREE_ARROW,
+ GRAPHTREE_MARK
+};
+
+#endif
diff --git a/src/svnfrontend/graphtree/graphtreelabel.cpp b/src/svnfrontend/graphtree/graphtreelabel.cpp
new file mode 100644
index 0000000..9770fef
--- /dev/null
+++ b/src/svnfrontend/graphtree/graphtreelabel.cpp
@@ -0,0 +1,220 @@
+/***************************************************************************
+ * Copyright (C) 2006-2007 by Rajko Albrecht *
+ * *
+ * 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 "graphtreelabel.h"
+#include "graphtree_defines.h"
+#include <qpainter.h>
+
+GraphTreeLabel::GraphTreeLabel(const QString&text, const QString&_nodename,const QRect&r,QCanvas*c)
+ : QCanvasRectangle(r,c),StoredDrawParams()
+{
+ m_Nodename = _nodename;
+ m_SourceNode = QString::null;
+ setText(0,text);
+ setPosition(0, DrawParams::TopCenter);
+ drawFrame(true);
+}
+
+GraphTreeLabel::~GraphTreeLabel()
+{
+}
+
+const QString&GraphTreeLabel::nodename()const
+{
+ return m_Nodename;
+}
+
+int GraphTreeLabel::rtti()const
+{
+ return GRAPHTREE_LABEL;
+}
+
+void GraphTreeLabel::setBgColor(const QColor&c)
+{
+ _backColor=c;
+}
+
+void GraphTreeLabel::drawShape(QPainter& p)
+{
+ QRect r = rect();
+/*
+ p.setPen(blue);
+ p.drawRect(r);
+*/
+ RectDrawing d(r);
+ d.drawBack(&p,this);
+ d.drawField(&p, 0, this);
+ d.drawField(&p, 1, this);
+}
+
+void GraphTreeLabel::setSelected(bool s)
+{
+ QCanvasRectangle::setSelected(s);
+ StoredDrawParams::setSelected(s);
+ update();
+}
+
+const QString&GraphTreeLabel::source()const
+{
+ return m_SourceNode;
+}
+
+void GraphTreeLabel::setSource(const QString&_s)
+{
+ m_SourceNode=_s;
+}
+
+GraphEdge::GraphEdge(QCanvas*c)
+ : QCanvasSpline(c)
+{
+}
+
+GraphEdge::~GraphEdge()
+{
+}
+
+void GraphEdge::drawShape(QPainter& p)
+{
+ p.drawPolyline(poly);
+}
+
+QPointArray GraphEdge::areaPoints() const
+{
+ int minX = poly[0].x(), minY = poly[0].y();
+ int maxX = minX, maxY = minY;
+ int i;
+
+ if (0) qDebug("GraphEdge::areaPoints\n P 0: %d/%d", minX, minY);
+ int len = poly.count();
+ for (i=1;i<len;i++) {
+ if (poly[i].x() < minX) minX = poly[i].x();
+ if (poly[i].y() < minY) minY = poly[i].y();
+ if (poly[i].x() > maxX) maxX = poly[i].x();
+ if (poly[i].y() > maxY) maxY = poly[i].y();
+ if (0) qDebug(" P %d: %d/%d", i, poly[i].x(), poly[i].y());
+ }
+ QPointArray a = poly.copy(), b = poly.copy();
+ if (minX == maxX) {
+ a.translate(-2, 0);
+ b.translate(2, 0);
+ }
+ else {
+ a.translate(0, -2);
+ b.translate(0, 2);
+ }
+ a.resize(2*len);
+ for (i=0;i<len;i++)
+ a[2 * len - 1 -i] = b[i];
+
+ if (0) {
+ qDebug(" Result:");
+ for (i=0;i<2*len;i++)
+ qDebug(" P %d: %d/%d", i, a[i].x(), a[i].y());
+ }
+
+ return a;
+
+}
+
+int GraphEdge::rtti()const
+{
+ return GRAPHTREE_LINE;
+}
+
+GraphEdgeArrow::GraphEdgeArrow(GraphEdge*_parent,QCanvas*c)
+ : QCanvasPolygon(c),_edge(_parent)
+{
+}
+
+void GraphEdgeArrow::drawShape(QPainter&p)
+{
+ QCanvasPolygon::drawShape(p);
+}
+
+int GraphEdgeArrow::rtti()const
+{
+ return GRAPHTREE_ARROW;
+}
+
+GraphEdge*GraphEdgeArrow::edge()
+{
+ return _edge;
+}
+
+/* taken from KCacheGrind project */
+QPixmap*GraphMark::_p=0;
+
+GraphMark::GraphMark(GraphTreeLabel*n,QCanvas*c)
+ : QCanvasRectangle(c)
+{
+ if (!_p) {
+
+ int d = 5;
+ float v1 = 130.0, v2 = 10.0, v = v1, f = 1.03;
+
+ // calculate pix size
+ QRect r(0, 0, 30, 30);
+ while (v>v2) {
+ r.setRect(r.x()-d, r.y()-d, r.width()+2*d, r.height()+2*d);
+ v /= f;
+ }
+
+ _p = new QPixmap(r.size());
+ _p->fill(Qt::white);
+ QPainter p(_p);
+ p.setPen(Qt::NoPen);
+
+ r.moveBy(-r.x(), -r.y());
+
+ while (v<v1) {
+ v *= f;
+ p.setBrush(QColor(265-(int)v, 265-(int)v, 265-(int)v));
+
+ p.drawRect(QRect(r.x(), r.y(), r.width(), d));
+ p.drawRect(QRect(r.x(), r.bottom()-d, r.width(), d));
+ p.drawRect(QRect(r.x(), r.y()+d, d, r.height()-2*d));
+ p.drawRect(QRect(r.right()-d, r.y()+d, d, r.height()-2*d));
+
+ r.setRect(r.x()+d, r.y()+d, r.width()-2*d, r.height()-2*d);
+ }
+ }
+
+ setSize(_p->width(), _p->height());
+ move(n->rect().center().x()-_p->width()/2,
+ n->rect().center().y()-_p->height()/2);
+}
+
+GraphMark::~ GraphMark()
+{
+}
+
+bool GraphMark::hit(const QPoint&)const
+{
+ return false;
+}
+
+int GraphMark::rtti()const
+{
+ return GRAPHTREE_MARK;
+}
+
+void GraphMark::drawShape(QPainter&p)
+{
+ p.drawPixmap( int(x()), int(y()), *_p );
+}
diff --git a/src/svnfrontend/graphtree/graphtreelabel.h b/src/svnfrontend/graphtree/graphtreelabel.h
new file mode 100644
index 0000000..dec788f
--- /dev/null
+++ b/src/svnfrontend/graphtree/graphtreelabel.h
@@ -0,0 +1,89 @@
+/***************************************************************************
+ * Copyright (C) 2006-2007 by Rajko Albrecht *
+ * *
+ * 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. *
+ ***************************************************************************/
+#ifndef GRAPHTREELABEL_H
+#define GRAPHTREELABEL_H
+
+#include "graphtree/drawparams.h"
+#include <qcanvas.h>
+
+/**
+ @author Rajko Albrecht <[email protected]>
+*/
+class GraphTreeLabel : public QCanvasRectangle,StoredDrawParams
+{
+public:
+ GraphTreeLabel(const QString&,const QString&,const QRect&r,QCanvas*c);
+ virtual ~GraphTreeLabel();
+
+ virtual int rtti()const;
+ virtual void drawShape(QPainter& p);
+
+ void setBgColor(const QColor&);
+
+ const QString&nodename()const;
+ const QString&source()const;
+ void setSource(const QString&);
+ virtual void setSelected(bool);
+
+protected:
+ QString m_Nodename;
+ QString m_SourceNode;
+};
+
+class GraphEdge;
+
+class GraphEdgeArrow:public QCanvasPolygon
+{
+public:
+ GraphEdgeArrow(GraphEdge*,QCanvas*);
+ GraphEdge*edge();
+ virtual void drawShape(QPainter&);
+ virtual int rtti()const;
+
+private:
+ GraphEdge*_edge;
+};
+
+/* line */
+class GraphEdge:public QCanvasSpline
+{
+public:
+ GraphEdge(QCanvas*);
+ virtual ~GraphEdge();
+
+ virtual void drawShape(QPainter&);
+ QPointArray areaPoints() const;
+ virtual int rtti()const;
+};
+
+class GraphMark:public QCanvasRectangle
+{
+public:
+ GraphMark(GraphTreeLabel*,QCanvas*);
+ virtual ~GraphMark();
+ virtual int rtti()const;
+ virtual bool hit(const QPoint&)const;
+
+ virtual void drawShape(QPainter&);
+private:
+ static QPixmap*_p;
+};
+
+#endif
diff --git a/src/svnfrontend/graphtree/pannerview.cpp b/src/svnfrontend/graphtree/pannerview.cpp
new file mode 100644
index 0000000..5f14e82
--- /dev/null
+++ b/src/svnfrontend/graphtree/pannerview.cpp
@@ -0,0 +1,101 @@
+/***************************************************************************
+ * Copyright (C) 2006-2007 by Rajko Albrecht *
+ * *
+ * 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 "pannerview.h"
+#include <qpainter.h>
+
+PannerView::PannerView(QWidget* parent, const char* name)
+ : QCanvasView(parent, name,WNoAutoErase | WStaticContents )
+{
+ m_Moving = false;
+ viewport()->setBackgroundMode(Qt::NoBackground);
+ setBackgroundMode(Qt::NoBackground);
+}
+
+PannerView::~PannerView()
+{
+}
+
+void PannerView::drawContents(QPainter* p, int clipx, int clipy, int clipw, int cliph)
+{
+ p->save();
+ QCanvasView::drawContents(p,clipx,clipy,clipw,cliph);
+ p->restore();
+ if (m_ZoomRect.isValid()) {
+ p->setPen(red.dark());
+ p->drawRect(m_ZoomRect);
+ p->setPen( red);
+ p->drawRect(QRect(m_ZoomRect.x()+1, m_ZoomRect.y()+1,
+ m_ZoomRect.width()-2, m_ZoomRect.height()-2));
+ }
+}
+
+void PannerView::setZoomRect(const QRect& theValue)
+{
+ QRect oldRect = m_ZoomRect;
+ m_ZoomRect = theValue;
+ updateContents(oldRect);
+ updateContents(m_ZoomRect);
+}
+
+/*!
+ \fn PannerView::contentsMouseMoveEvent(QMouseEvent* e)
+ */
+void PannerView::contentsMouseMoveEvent(QMouseEvent* e)
+{
+ if (m_Moving) {
+ emit zoomRectMoved(e->pos().x() - m_LastPos.x(), e->pos().y() - m_LastPos.y());
+ m_LastPos = e->pos();
+ }
+}
+
+/*!
+ \fn PannerView::contentsMousePressEvent(QMouseEvent* e)
+ */
+void PannerView::contentsMousePressEvent(QMouseEvent* e)
+{
+ if (m_ZoomRect.isValid()) {
+ if (!m_ZoomRect.contains(e->pos())) {
+ emit zoomRectMoved(e->pos().x() - m_ZoomRect.center().x(),
+ e->pos().y() - m_ZoomRect.center().y());
+ }
+ m_Moving = true;
+ m_LastPos = e->pos();
+ }
+}
+
+/*!
+ \fn PannerView::contentsMouseReleaseEvent(QMouseEvent*)
+ */
+void PannerView::contentsMouseReleaseEvent(QMouseEvent*)
+{
+ m_Moving = false;
+ emit zoomRectMoveFinished();
+}
+
+/*!
+ \fn PannerView::updateCurrentRect()
+ */
+void PannerView::updateCurrentRect()
+{
+ if (m_ZoomRect.isValid()) updateContents(m_ZoomRect);
+}
+
+#include "pannerview.moc"
+
diff --git a/src/svnfrontend/graphtree/pannerview.h b/src/svnfrontend/graphtree/pannerview.h
new file mode 100644
index 0000000..6c1a310
--- /dev/null
+++ b/src/svnfrontend/graphtree/pannerview.h
@@ -0,0 +1,53 @@
+/***************************************************************************
+ * Copyright (C) 2006-2007 by Rajko Albrecht *
+ * *
+ * 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. *
+ ***************************************************************************/
+#ifndef PANNERVIEW_H
+#define PANNERVIEW_H
+
+#include <qcanvas.h>
+
+/**
+ @author Rajko Albrecht <[email protected]>
+*/
+class PannerView : public QCanvasView
+{
+Q_OBJECT
+public:
+ PannerView(QWidget* parent=0, const char* name=0);
+ virtual ~PannerView();
+
+ void setZoomRect(const QRect& theValue);
+ void updateCurrentRect();
+
+signals:
+ void zoomRectMoved(int dx, int dy);
+ void zoomRectMoveFinished();
+
+protected:
+ virtual void drawContents(QPainter* p, int clipx, int clipy, int clipw, int cliph);
+ virtual void contentsMouseMoveEvent(QMouseEvent* e);
+ virtual void contentsMousePressEvent(QMouseEvent* e);
+ virtual void contentsMouseReleaseEvent(QMouseEvent*);
+protected:
+ QRect m_ZoomRect;
+ bool m_Moving;
+ QPoint m_LastPos;
+};
+
+#endif
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 *
+ * *
+ * 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"
diff --git a/src/svnfrontend/graphtree/revgraphview.h b/src/svnfrontend/graphtree/revgraphview.h
new file mode 100644
index 0000000..039bc4b
--- /dev/null
+++ b/src/svnfrontend/graphtree/revgraphview.h
@@ -0,0 +1,151 @@
+/***************************************************************************
+ * Copyright (C) 2006-2007 by Rajko Albrecht *
+ * *
+ * 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. *
+ ***************************************************************************/
+#ifndef REVGRAPHVIEW_H
+#define REVGRAPHVIEW_H
+
+#include <svnqt/revision.hpp>
+
+#include <qcanvas.h>
+
+namespace svn {
+ class LogEntry;
+ class Client;
+}
+
+class KTempFile;
+class KProcess;
+class RevisionTree;
+class GraphTreeLabel;
+class GraphViewTip;
+class GraphMark;
+class PannerView;
+class CContextListener;
+
+/**
+ @author Rajko Albrecht <[email protected]>
+*/
+class RevGraphView : public QCanvasView
+{
+ Q_OBJECT
+public:
+ enum ZoomPosition { TopLeft, TopRight, BottomLeft, BottomRight, Auto };
+ /* avoid large copy operations */
+ friend class RevisionTree;
+
+ RevGraphView(QObject*,svn::Client*,QWidget * parent = 0, const char * name = 0, WFlags f = 0);
+ virtual ~RevGraphView();
+
+ void showText(const QString&s);
+ void clear();
+
+ void beginInsert();
+ void endInsert();
+
+ struct targetData {
+ char Action;
+ QString key;
+ targetData(const QString&n,char _a)
+ {
+ key = n;
+ Action = _a;
+ }
+ targetData(){Action=0;key="";}
+ };
+ typedef QValueList<targetData> tlist;
+
+ struct keyData {
+ QString name,Author,Date,Message;
+ long rev;
+ char Action;
+ tlist targets;
+ };
+
+ typedef QMap<QString,keyData> trevTree;
+
+ QString toolTip(const QString&nodename,bool full=false)const;
+
+ void setBasePath(const QString&);
+ void dumpRevtree();
+
+signals:
+ void dispDetails(const QString&);
+ void makeCat(const svn::Revision&,const QString&,const QString&,const svn::Revision&,QWidget*);
+ void makeNorecDiff(const QString&,const svn::Revision&,const QString&,const svn::Revision&,QWidget*);
+ void makeRecDiff(const QString&,const svn::Revision&,const QString&,const svn::Revision&,QWidget*);
+
+public slots:
+ virtual void contentsMovingSlot(int,int);
+ virtual void zoomRectMoved(int,int);
+ virtual void zoomRectMoveFinished();
+ virtual void slotClientException(const QString&what);
+
+protected slots:
+ virtual void readDotOutput(KProcess * proc,char * buffer,int buflen);
+ virtual void dotExit(KProcess*);
+
+protected:
+ QCanvas*m_Canvas;
+ GraphMark*m_Marker;
+ svn::Client*m_Client;
+ GraphTreeLabel*m_Selected;
+ QObject*m_Listener;
+ KTempFile*dotTmpFile;
+ QString dotOutput;
+ KProcess*renderProcess;
+ trevTree m_Tree;
+ QColor getBgColor(const QString&nodeName)const;
+ bool isStart(const QString&nodeName)const;
+ char getAction(const QString&)const;
+ const QString&getLabelstring(const QString&nodeName);
+
+ QMap<QString,GraphTreeLabel*> m_NodeList;
+ QMap<QString,QString> m_LabelMap;
+
+ int _xMargin,_yMargin;
+ GraphViewTip*m_Tip;
+ PannerView*m_CompleteView;
+ double _cvZoom;
+ ZoomPosition m_LastAutoPosition;
+
+ virtual void resizeEvent(QResizeEvent*);
+ virtual void contentsMousePressEvent ( QMouseEvent * e );
+ virtual void contentsMouseReleaseEvent ( QMouseEvent * e );
+ virtual void contentsMouseMoveEvent ( QMouseEvent*e);
+ virtual void contentsContextMenuEvent(QContextMenuEvent*e);
+ virtual void contentsMouseDoubleClickEvent ( QMouseEvent * e );
+
+ bool _isMoving;
+ QPoint _lastPos;
+
+ bool _noUpdateZoomerPos;
+
+ QString _basePath;
+
+private:
+ void updateSizes(QSize s = QSize(0,0));
+ void updateZoomerPos();
+ void setNewDirection(int dir);
+ void makeDiffPrev(GraphTreeLabel*);
+ void makeDiff(const QString&,const QString&);
+ void makeSelected(GraphTreeLabel*);
+ void makeCat(GraphTreeLabel*_l);
+};
+
+#endif
diff --git a/src/svnfrontend/graphtree/revisiontree.cpp b/src/svnfrontend/graphtree/revisiontree.cpp
new file mode 100644
index 0000000..bb64cf7
--- /dev/null
+++ b/src/svnfrontend/graphtree/revisiontree.cpp
@@ -0,0 +1,544 @@
+/***************************************************************************
+ * Copyright (C) 2005-2007 by Rajko Albrecht *
+ * *
+ * 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 "revisiontree.h"
+#include "../stopdlg.h"
+#include "src/svnqt/log_entry.hpp"
+#include "src/svnqt/cache/LogCache.hpp"
+#include "src/svnqt/cache/ReposLog.hpp"
+#include "src/svnqt/url.hpp"
+#include "helpers/sub2qt.h"
+#include "revtreewidget.h"
+#include "revgraphview.h"
+#include "elogentry.h"
+#include "src/svnfrontend/fronthelpers/cursorstack.h"
+#include "src/settings/kdesvnsettings.h"
+
+#include <kdebug.h>
+#include <kprogress.h>
+#include <klocale.h>
+#include <kapp.h>
+#include <klistview.h>
+#include <kmdcodec.h>
+#include <kmessagebox.h>
+
+#include <qwidget.h>
+#include <qdatetime.h>
+#include <qlabel.h>
+#include <qfile.h>
+#include <qtextstream.h>
+#include <qregexp.h>
+
+#define INTERNALCOPY 1
+#define INTERNALRENAME 2
+
+class RtreeData
+{
+public:
+ RtreeData();
+ virtual ~RtreeData();
+
+ QMap<long,eLog_Entry> m_History;
+
+ svn::LogEntriesMap m_OldHistory;
+
+ long max_rev,min_rev;
+ KProgressDialog*progress;
+ QTime m_stopTick;
+
+ QWidget*dlgParent;
+ RevTreeWidget*m_TreeDisplay;
+
+ svn::Client*m_Client;
+ QObject* m_Listener;
+
+ bool getLogs(const QString&,const svn::Revision&startr,const svn::Revision&endr,const QString&origin);
+};
+
+RtreeData::RtreeData()
+ : max_rev(-1),min_rev(-1)
+{
+ progress=0;
+ m_TreeDisplay = 0;
+ m_Client = 0;
+ dlgParent = 0;
+ m_Listener = 0;
+}
+
+RtreeData::~RtreeData()
+{
+ delete progress;
+}
+
+bool RtreeData::getLogs(const QString&reposRoot,const svn::Revision&startr,const svn::Revision&endr,const QString&origin)
+{
+ if (!m_Listener||!m_Client) {
+ return false;
+ }
+ try {
+ CursorStack a(Qt::BusyCursor);
+ StopDlg sdlg(m_Listener,dlgParent,
+ 0,"Logs",i18n("Getting logs - hit cancel for abort"));
+ if (svn::Url::isLocal(reposRoot) ) {
+ m_Client->log(reposRoot,endr,startr,m_OldHistory,startr,true,false,0);
+ } else {
+ svn::cache::ReposLog rl(m_Client,reposRoot);
+ if (rl.isValid()) {
+ rl.simpleLog(m_OldHistory,startr,endr,!Kdesvnsettings::network_on());
+ } else if (Kdesvnsettings::network_on()) {
+ m_Client->log(reposRoot,endr,startr,m_OldHistory,startr,true,false,0);
+ } else {
+ KMessageBox::error(0,i18n("Could not retrieve logs, reason:\n%1").arg(i18n("No logcache possible due broken database and networking not allowed.")));
+ return false;
+ }
+ }
+ } catch (const svn::Exception&ce) {
+ kdDebug()<<ce.msg() << endl;
+ KMessageBox::error(0,i18n("Could not retrieve logs, reason:\n%1").arg(ce.msg()));
+ return false;
+ }
+ return true;
+}
+
+RevisionTree::RevisionTree(svn::Client*aClient,
+ QObject*aListener,
+ const QString& reposRoot,
+ const svn::Revision&startr,const svn::Revision&endr,
+ const QString&origin,
+ const svn::Revision& baserevision,
+ QWidget*treeParent,QWidget*parent)
+ :m_InitialRevsion(0),m_Path(origin),m_Valid(false)
+{
+ m_Data = new RtreeData;
+ m_Data->m_Client=aClient;
+ m_Data->m_Listener=aListener;
+ m_Data->dlgParent=parent;
+
+ if (!m_Data->getLogs(reposRoot,startr,endr,origin)) {
+ return;
+ }
+
+ long possible_rev=-1;
+ kdDebug()<<"Origin: "<<origin << endl;
+
+ m_Data->progress=new KProgressDialog(
+ parent,"progressdlg",i18n("Scanning logs"),i18n("Scanning the logs for %1").arg(origin),true);
+ m_Data->progress->setMinimumDuration(100);
+ m_Data->progress->show();
+ m_Data->progress->setAllowCancel(true);
+ m_Data->progress->progressBar()->setTotalSteps(m_Data->m_OldHistory.size());
+ m_Data->progress->setAutoClose(false);
+ m_Data->progress->show();
+ bool cancel=false;
+ svn::LogEntriesMap::Iterator it;
+ unsigned count = 0;
+ for (it=m_Data->m_OldHistory.begin();it!=m_Data->m_OldHistory.end();++it) {
+ m_Data->progress->progressBar()->setProgress(count);
+ kapp->processEvents();
+ if (m_Data->progress->wasCancelled()) {
+ cancel=true;
+ break;
+ }
+ if (it.key()>m_Data->max_rev) {
+ m_Data->max_rev=it.key();
+ }
+ if (it.key()<m_Data->min_rev||m_Data->min_rev==-1) {
+ m_Data->min_rev=it.key();
+ }
+ if (baserevision.kind()==svn_opt_revision_date) {
+ if (baserevision.date()<=it.data().date && possible_rev==-1||possible_rev>it.key()) {
+ possible_rev=it.key();
+ }
+ }
+ ++count;
+ }
+ if (baserevision.kind()==svn_opt_revision_head||baserevision.kind()==svn_opt_revision_working) {
+ m_Baserevision=m_Data->max_rev;
+ } else if (baserevision.kind()==svn_opt_revision_number) {
+ m_Baserevision=baserevision.revnum();
+ } else if (baserevision.kind()==svn_opt_revision_date) {
+ m_Baserevision=possible_rev;
+ }
+ if (!cancel) {
+ kdDebug( )<<" max revision " << m_Data->max_rev
+ << " min revision " << m_Data->min_rev << endl;
+ if (topDownScan()) {
+ kdDebug()<<"topdown end"<<endl;
+ m_Data->progress->setAutoReset(true);
+ m_Data->progress->progressBar()->setTotalSteps(100);
+ m_Data->progress->progressBar()->setPercentageVisible(false);
+ m_Data->m_stopTick.restart();
+ m_Data->m_TreeDisplay=new RevTreeWidget(m_Data->m_Listener,m_Data->m_Client,treeParent);
+ if (bottomUpScan(m_InitialRevsion,0,m_Path,0)) {
+ kdDebug()<<"Bottom up end"<<endl;
+ m_Valid=true;
+ m_Data->m_TreeDisplay->setBasePath(reposRoot);
+ m_Data->m_TreeDisplay->dumpRevtree();
+ } else {
+ delete m_Data->m_TreeDisplay;
+ m_Data->m_TreeDisplay = 0;
+ }
+ }
+ } else {
+ kdDebug()<<"Canceld"<<endl;
+ }
+ m_Data->progress->hide();
+}
+
+RevisionTree::~RevisionTree()
+{
+ delete m_Data;
+}
+
+bool RevisionTree::isDeleted(long revision,const QString&path)
+{
+ for (unsigned i = 0;i<m_Data->m_History[revision].changedPaths.count();++i) {
+ if (isParent(m_Data->m_History[revision].changedPaths[i].path,path) &&
+ m_Data->m_History[revision].changedPaths[i].action=='D') {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool RevisionTree::topDownScan()
+{
+ m_Data->progress->progressBar()->setTotalSteps(m_Data->max_rev-m_Data->min_rev);
+ bool cancel=false;
+ QString label;
+ QString olabel = m_Data->progress->labelText();
+ for (long j=m_Data->max_rev;j>=m_Data->min_rev;--j) {
+ m_Data->progress->progressBar()->setProgress(m_Data->max_rev-j);
+ kapp->processEvents();
+ if (m_Data->progress->wasCancelled()) {
+ cancel=true;
+ break;
+ }
+ for (unsigned i = 0; i<m_Data->m_OldHistory[j].changedPaths.count();++i) {
+ if (i>0 && i%100==0) {
+ if (m_Data->progress->wasCancelled()) {
+ cancel=true;
+ break;
+ }
+ label = i18n("%1<br>Check change entry %2 of %3")
+ .arg(olabel).arg(i).arg(m_Data->m_OldHistory[j].changedPaths.count());
+ m_Data->progress->setLabel(label);
+ kapp->processEvents();
+ }
+ /* find min revision of item */
+ if (m_Data->m_OldHistory[j].changedPaths[i].action=='A'&&
+ isParent(m_Data->m_OldHistory[j].changedPaths[i].path,m_Path))
+ {
+ if (!m_Data->m_OldHistory[j].changedPaths[i].copyFromPath.isEmpty()) {
+ if (m_InitialRevsion<m_Data->m_OldHistory[j].revision) {
+ QString tmpPath = m_Path;
+ QString r = m_Path.mid(m_Data->m_OldHistory[j].changedPaths[i].path.length());
+ m_Path=m_Data->m_OldHistory[j].changedPaths[i].copyFromPath;
+ m_Path+=r;
+ }
+ } else if (m_Data->m_OldHistory[j].changedPaths[i].path==m_Path && m_Data->m_OldHistory[j].changedPaths[i].copyToPath.isEmpty()){
+ // here it is added
+ m_InitialRevsion = m_Data->m_OldHistory[j].revision;
+ }
+ }
+ }
+ }
+ kdDebug()<<"Stage one done"<<endl;
+ if (cancel==true) {
+ return false;
+ }
+ m_Data->progress->setLabel(olabel);
+ /* find forward references and filter them out */
+ for (long j=m_Data->max_rev;j>=m_Data->min_rev;--j) {
+ m_Data->progress->progressBar()->setProgress(m_Data->max_rev-j);
+ kapp->processEvents();
+ if (m_Data->progress->wasCancelled()) {
+ cancel=true;
+ break;
+ }
+ for (unsigned i = 0; i<m_Data->m_OldHistory[j].changedPaths.count();++i) {
+ if (i>0 && i%100==0) {
+ if (m_Data->progress->wasCancelled()) {
+ cancel=true;
+ break;
+ }
+ label = i18n("%1<br>Check change entry %2 of %3").arg(olabel).arg(i).arg(m_Data->m_OldHistory[j].changedPaths.count());
+ m_Data->progress->setLabel(label);
+ kapp->processEvents();
+ }
+ if (!m_Data->m_OldHistory[j].changedPaths[i].copyFromPath.isEmpty()) {
+ long r = m_Data->m_OldHistory[j].changedPaths[i].copyFromRevision;
+ QString sourcepath = m_Data->m_OldHistory[j].changedPaths[i].copyFromPath;
+ char a = m_Data->m_OldHistory[j].changedPaths[i].action;
+ if (m_Data->m_OldHistory[j].changedPaths[i].path.isEmpty()) {
+ kdDebug()<<"Empty entry! rev " << j << " source " << sourcepath << endl;
+ continue;
+ }
+ if (a=='R') {
+ m_Data->m_OldHistory[j].changedPaths[i].action=0;
+ } else if (a=='A'){
+ a=INTERNALCOPY;
+ for (unsigned z = 0;z<m_Data->m_OldHistory[j].changedPaths.count();++z) {
+ if (m_Data->m_OldHistory[j].changedPaths[z].action=='D'
+ && isParent(m_Data->m_OldHistory[j].changedPaths[z].path,sourcepath) ) {
+ a=INTERNALRENAME;
+ m_Data->m_OldHistory[j].changedPaths[z].action=0;
+ break;
+ }
+ }
+ m_Data->m_History[r].addCopyTo(sourcepath,m_Data->m_OldHistory[j].changedPaths[i].path,j,a,r);
+ m_Data->m_OldHistory[j].changedPaths[i].action=0;
+ } else {
+ kdDebug()<<"Action with source path but wrong action \""<<a<<"\" found!"<<endl;
+ }
+ }
+ }
+ }
+ kdDebug()<<"Stage two done"<<endl;
+ if (cancel==true) {
+ return false;
+ }
+ m_Data->progress->setLabel(olabel);
+ for (long j=m_Data->max_rev;j>=m_Data->min_rev;--j) {
+ m_Data->progress->progressBar()->setProgress(m_Data->max_rev-j);
+ kapp->processEvents();
+ if (m_Data->progress->wasCancelled()) {
+ cancel=true;
+ break;
+ }
+ for (unsigned i = 0; i<m_Data->m_OldHistory[j].changedPaths.count();++i) {
+ if (m_Data->m_OldHistory[j].changedPaths[i].action==0) {
+ continue;
+ }
+ if (i>0 && i%100==0) {
+ if (m_Data->progress->wasCancelled()) {
+ cancel=true;
+ break;
+ }
+ label = i18n("%1<br>Check change entry %2 of %3").arg(olabel).arg(i).arg(m_Data->m_OldHistory[j].changedPaths.count());
+ m_Data->progress->setLabel(label);
+ kapp->processEvents();
+ }
+ m_Data->m_History[j].addCopyTo(m_Data->m_OldHistory[j].changedPaths[i].path,QString::null,-1,m_Data->m_OldHistory[j].changedPaths[i].action);
+ }
+ m_Data->m_History[j].author=m_Data->m_OldHistory[j].author;
+ m_Data->m_History[j].date=m_Data->m_OldHistory[j].date;
+ m_Data->m_History[j].revision=m_Data->m_OldHistory[j].revision;
+ m_Data->m_History[j].message=m_Data->m_OldHistory[j].message;
+ }
+ kdDebug()<<"Stage three done"<<endl;
+ return !cancel;
+}
+
+bool RevisionTree::isParent(const QString&_par,const QString&tar)
+{
+ if (_par==tar) return true;
+ QString par = _par+(_par.endsWith("/")?"":"/");
+ return tar.startsWith(par);
+}
+
+bool RevisionTree::isValid()const
+{
+ return m_Valid;
+}
+
+static QString uniqueNodeName(long rev,const QString&path)
+{
+ QString res = KCodecs::base64Encode(path.local8Bit(),false);
+ res.replace("\"","_quot_");
+ res.replace(" ","_space_");
+ QString n; n.sprintf("%05ld",rev);
+ res = "\""+n+QString("_%1\"").arg(res);
+ return res;
+}
+
+bool RevisionTree::bottomUpScan(long startrev,unsigned recurse,const QString&_path,long _last)
+{
+#define REVENTRY m_Data->m_History[j]
+#define FORWARDENTRY m_Data->m_History[j].changedPaths[i]
+
+ QString path = _path;
+ long lastrev = _last;
+ /* this is required if an item will modified AND copied at same revision.*/
+ long trev = -1;
+#ifdef DEBUG_PARSE
+ kdDebug()<<"Searching for "<<path<< " at revision " << startrev
+ << " recursion " << recurse << endl;
+#endif
+ bool cancel = false;
+ for (long j=startrev;j<=m_Data->max_rev;++j) {
+ if (m_Data->m_stopTick.elapsed()>500) {
+ m_Data->progress->progressBar()->advance(1);
+ kapp->processEvents();
+ m_Data->m_stopTick.restart();
+ }
+ if (m_Data->progress->wasCancelled()) {
+ cancel=true;
+ break;
+ }
+ for (unsigned i=0;i<REVENTRY.changedPaths.count();++i) {
+ if (!isParent(FORWARDENTRY.path,path)) {
+ continue;
+ }
+ QString n1,n2;
+ if (isParent(FORWARDENTRY.path,path)) {
+ bool get_out = false;
+ if (FORWARDENTRY.path!=path) {
+#ifdef DEBUG_PARSE
+ kdDebug()<<"Parent rename? "<< FORWARDENTRY.path << " -> " << FORWARDENTRY.copyToPath << " -> " << FORWARDENTRY.copyFromPath << endl;
+#endif
+ }
+ if (FORWARDENTRY.action==INTERNALCOPY ||
+ FORWARDENTRY.action==INTERNALRENAME ) {
+ bool ren = FORWARDENTRY.action==INTERNALRENAME;
+ QString tmpPath = path;
+ QString recPath;
+ if (FORWARDENTRY.copyToPath.length()==0) {
+ continue;
+ }
+ QString r = path.mid(FORWARDENTRY.path.length());
+ recPath= FORWARDENTRY.copyToPath;
+ recPath+=r;
+ n1 = uniqueNodeName(lastrev,tmpPath);
+ n2 = uniqueNodeName(FORWARDENTRY.copyToRevision,recPath);
+ if (lastrev>0) {
+ m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n1].targets.append(RevGraphView::targetData(n2,FORWARDENTRY.action));
+ }
+ m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n2].name=recPath;
+ m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n2].rev = FORWARDENTRY.copyToRevision;
+ m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n2].Action=FORWARDENTRY.action;
+ m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n2].Author=m_Data->m_History[FORWARDENTRY.copyToRevision].author;
+ m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n2].Message=m_Data->m_History[FORWARDENTRY.copyToRevision].message;
+ m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n2].Date=helpers::sub2qt::apr_time2qtString(m_Data->m_History[FORWARDENTRY.copyToRevision].date);
+ if (ren) {
+ lastrev = FORWARDENTRY.copyToRevision;
+ /* skip items between */
+ j=lastrev;
+#ifdef DEBUG_PARSE
+ kdDebug()<<"Renamed to "<< recPath << " at revision " << FORWARDENTRY.copyToRevision << endl;
+#endif
+ path=recPath;
+ } else {
+#ifdef DEBUG_PARSE
+ kdDebug()<<"Copy to "<< recPath << endl;
+#endif
+ if (!bottomUpScan(FORWARDENTRY.copyToRevision,recurse+1,recPath,FORWARDENTRY.copyToRevision)) {
+ return false;
+ }
+ }
+ } else if (FORWARDENTRY.path==path) {
+ switch (FORWARDENTRY.action) {
+ case 'A':
+#ifdef DEBUG_PARSE
+ kdDebug()<<"Inserting adding base item"<<endl;
+#endif
+ n1 = uniqueNodeName(j,FORWARDENTRY.path);
+ m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n1].Action=FORWARDENTRY.action;
+ fillItem(j,i,n1,path);
+ lastrev=j;
+ break;
+ case 'M':
+ case 'R':
+#ifdef DEBUG_PARSE
+ kdDebug()<<"Item modified at revision "<< j << " recurse " << recurse << endl;
+#endif
+ n1 = uniqueNodeName(j,FORWARDENTRY.path);
+ n2 = uniqueNodeName(lastrev,FORWARDENTRY.path);
+ if (lastrev>0) m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n2].targets.append(RevGraphView::targetData(n1,FORWARDENTRY.action));
+ fillItem(j,i,n1,path);
+ /* modify of same item (in same recurse) should be only once at a revision
+ * so check if lastrev==j must not be done but will cost cpu ticks so I always
+ * set trev and lastrev.
+ */
+ trev = lastrev;
+ lastrev = j;
+ break;
+ case 'D':
+#ifdef DEBUG_PARSE
+ kdDebug()<<"(Sloppy match) Item deleted at revision "<< j << " recurse " << recurse << endl;
+#endif
+ n1 = uniqueNodeName(j,path);
+ n2 = uniqueNodeName(lastrev,path);
+ if (n1==n2) {
+ /* cvs import - copy and deletion at same revision.
+ * CVS sucks.
+ */
+ n1 = uniqueNodeName(j,"D_"+path);
+ }
+ if (lastrev>0) m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n2].targets.append(RevGraphView::targetData(n1,FORWARDENTRY.action));
+ fillItem(j,i,n1,path);
+ lastrev = j;
+ get_out= true;
+ break;
+ default:
+ break;
+ }
+ } else {
+ switch (FORWARDENTRY.action) {
+ case 'D':
+#ifdef DEBUG_PARSE
+ kdDebug()<<"(Exact match) Item deleted at revision "<< j << " recurse " << recurse << endl;
+#endif
+ n1 = uniqueNodeName(j,path);
+ n2 = uniqueNodeName(lastrev,path);
+ if (n1==n2) {
+ /* cvs import - copy and deletion at same revision.
+ * CVS sucks.
+ */
+ n1 = uniqueNodeName(j,"D_"+path);
+ }
+ if (lastrev>0) m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[n2].targets.append(RevGraphView::targetData(n1,FORWARDENTRY.action));
+ fillItem(j,i,n1,path);
+ lastrev = j;
+ get_out = true;
+ break;
+ default:
+ break;
+ }
+ }
+ if (get_out) {
+ return true;
+ }
+ }
+ }
+ }
+ return !cancel;
+}
+
+QWidget*RevisionTree::getView()
+{
+ return m_Data->m_TreeDisplay;
+}
+
+void RevisionTree::fillItem(long rev,int pathIndex,const QString&nodeName,const QString&path)
+{
+ m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].name=path;
+ m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].rev = rev;
+ if (pathIndex>=0) {
+ m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].Action=m_Data->m_History[rev].changedPaths[pathIndex].action;
+ m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].Author=m_Data->m_History[rev].author;
+ m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].Message=m_Data->m_History[rev].message;
+ m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].Date=helpers::sub2qt::apr_time2qtString(m_Data->m_History[rev].date);
+ } else {
+ m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].Action=0;
+ m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].Author="";
+ m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].Message="";
+ m_Data->m_TreeDisplay->m_RevGraphView->m_Tree[nodeName].Date=helpers::sub2qt::apr_time2qtString(0);
+ }
+}
diff --git a/src/svnfrontend/graphtree/revisiontree.h b/src/svnfrontend/graphtree/revisiontree.h
new file mode 100644
index 0000000..508306f
--- /dev/null
+++ b/src/svnfrontend/graphtree/revisiontree.h
@@ -0,0 +1,75 @@
+/***************************************************************************
+ * Copyright (C) 2005-2007 by Rajko Albrecht *
+ * *
+ * 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. *
+ ***************************************************************************/
+#ifndef REVISIONTREE_H
+#define REVISIONTREE_H
+
+#include "src/svnqt/log_entry.hpp"
+#include "src/svnqt/revision.hpp"
+#include "src/svnqt/client.hpp"
+
+#include <qstring.h>
+#include <qmap.h>
+#include <qpixmap.h>
+
+class RtreeData;
+class QWidget;
+class KListViewItem;
+class KListView;
+class CContextListener;
+
+namespace svn
+{
+ class Client;
+}
+
+/**
+ @author Rajko Albrecht <[email protected]>
+*/
+class RevisionTree{
+public:
+ RevisionTree(svn::Client*,
+ QObject*aListener,
+ const QString& reposRoot,
+ const svn::Revision&startr,const svn::Revision&endr,
+ const QString&,const svn::Revision& baserevision,QWidget*treeParent,
+ QWidget*parent=0);
+ virtual ~RevisionTree();
+
+ bool isValid()const;
+ QWidget*getView();
+
+protected:
+ long m_Baserevision;
+ long m_InitialRevsion;
+ QString m_Path;
+ bool m_Valid;
+
+ RtreeData*m_Data;
+
+ bool topDownScan();
+ bool bottomUpScan(long startrev,unsigned recurse,const QString&path,long sRev = -1);
+ bool isDeleted(long revision,const QString&);
+
+ static bool isParent(const QString&_par,const QString&tar);
+
+ void fillItem(long revIndex,int pathIndex,const QString&nodeName,const QString&path);
+};
+
+#endif
diff --git a/src/svnfrontend/graphtree/revtreewidget.cpp b/src/svnfrontend/graphtree/revtreewidget.cpp
new file mode 100644
index 0000000..35b5258
--- /dev/null
+++ b/src/svnfrontend/graphtree/revtreewidget.cpp
@@ -0,0 +1,115 @@
+/***************************************************************************
+ * Copyright (C) 2006-2007 by Rajko Albrecht *
+ * *
+ * 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 "revtreewidget.h"
+#include "src/settings/kdesvnsettings.h"
+
+#include <qvariant.h>
+#include <qsplitter.h>
+#include <ktextbrowser.h>
+#include <qlayout.h>
+#include <qtooltip.h>
+#include <qwhatsthis.h>
+#include "revgraphview.h"
+#include "ktextbrowser.h"
+
+/*
+ * Constructs a RevTreeWidget as a child of 'parent', with the
+ * name 'name' and widget flags set to 'f'.
+ */
+RevTreeWidget::RevTreeWidget(QObject*lt,svn::Client*cl, QWidget* parent, const char* name, WFlags fl )
+ : QWidget( parent, name, fl )
+{
+ if ( !name )
+ setName( "RevTreeWidget" );
+ RevTreeWidgetLayout = new QVBoxLayout( this, 11, 6, "RevTreeWidgetLayout");
+
+ m_Splitter = new QSplitter( this, "m_Splitter" );
+ m_Splitter->setOrientation( QSplitter::Vertical );
+
+ m_RevGraphView = new RevGraphView(lt,cl, m_Splitter, "m_RevGraphView" );
+ m_RevGraphView->setSizePolicy( QSizePolicy( (QSizePolicy::SizeType)5, (QSizePolicy::SizeType)5, 0, 2, m_RevGraphView->sizePolicy().hasHeightForWidth() ) );
+ connect(m_RevGraphView,SIGNAL(dispDetails(const QString&)),this,SLOT(setDetailText(const QString&)));
+ connect(m_RevGraphView,
+ SIGNAL(makeNorecDiff(const QString&,const svn::Revision&,const QString&,const svn::Revision&,QWidget*)),
+ this,
+ SIGNAL(makeNorecDiff(const QString&,const svn::Revision&,const QString&,const svn::Revision&,QWidget*))
+ );
+ connect(m_RevGraphView,
+ SIGNAL(makeRecDiff(const QString&,const svn::Revision&,const QString&,const svn::Revision&,QWidget*)),
+ this,
+ SIGNAL(makeRecDiff(const QString&,const svn::Revision&,const QString&,const svn::Revision&,QWidget*))
+ );
+ connect(m_RevGraphView,
+ SIGNAL(makeCat(const svn::Revision&,const QString&,const QString&,const svn::Revision&,QWidget*)),
+ this,
+ SIGNAL(makeCat(const svn::Revision&,const QString&,const QString&,const svn::Revision&,QWidget*))
+ );
+
+ m_Detailstext = new KTextBrowser( m_Splitter, "m_Detailstext" );
+ m_Detailstext->setSizePolicy( QSizePolicy( (QSizePolicy::SizeType)7, (QSizePolicy::SizeType)7, 0, 0, m_Detailstext->sizePolicy().hasHeightForWidth() ) );
+ m_Detailstext->setResizePolicy( KTextBrowser::Manual );
+ RevTreeWidgetLayout->addWidget( m_Splitter );
+ resize( QSize(600, 480).expandedTo(minimumSizeHint()) );
+ clearWState( WState_Polished );
+ QValueList<int> list = Kdesvnsettings::tree_detail_height();
+ if (list.count()==2 && (list[0]>0||list[1]>0)) {
+ m_Splitter->setSizes(list);
+ }
+}
+
+/*
+ * Destroys the object and frees any allocated resources
+ */
+RevTreeWidget::~RevTreeWidget()
+{
+ // no need to delete child widgets, Qt does it all for us
+ QValueList<int> list = m_Splitter->sizes();
+ if (list.count()==2) {
+ Kdesvnsettings::setTree_detail_height(list);
+ Kdesvnsettings::writeConfig();
+ }
+}
+
+void RevTreeWidget::setBasePath(const QString&_p)
+{
+ m_RevGraphView->setBasePath(_p);
+}
+
+void RevTreeWidget::dumpRevtree()
+{
+ m_RevGraphView->dumpRevtree();
+}
+
+void RevTreeWidget::setDetailText(const QString&_s)
+{
+ m_Detailstext->setText(_s);
+ QValueList<int> list = m_Splitter->sizes();
+ if (list.count()!=2) return;
+ if (list[1]==0) {
+ int h = height();
+ int th = h/10;
+ list[0]=h-th;
+ list[1]=th;
+ m_Splitter->setSizes(list);
+ }
+}
+
+#include "revtreewidget.moc"
+
diff --git a/src/svnfrontend/graphtree/revtreewidget.h b/src/svnfrontend/graphtree/revtreewidget.h
new file mode 100644
index 0000000..324f0ba
--- /dev/null
+++ b/src/svnfrontend/graphtree/revtreewidget.h
@@ -0,0 +1,74 @@
+/***************************************************************************
+ * Copyright (C) 2006-2007 by Rajko Albrecht *
+ * *
+ * 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. *
+ ***************************************************************************/
+#ifndef REVTREEWIDGET_H
+#define REVTREEWIDGET_H
+
+#include <svnqt/revision.hpp>
+
+#include <qvariant.h>
+#include <qpixmap.h>
+#include <qwidget.h>
+
+class QVBoxLayout;
+class QHBoxLayout;
+class QGridLayout;
+class QSpacerItem;
+class RevGraphView;
+class QSplitter;
+class KTextBrowser;
+class CContextListener;
+
+namespace svn {
+ class LogEntry;
+ class Client;
+}
+
+class RevTreeWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ RevTreeWidget(QObject*,svn::Client*,QWidget* parent = 0, const char* name = 0, WFlags fl = 0 );
+ ~RevTreeWidget();
+
+ QSplitter* m_Splitter;
+ RevGraphView* m_RevGraphView;
+
+ void setBasePath(const QString&);
+ void dumpRevtree();
+
+protected:
+ QVBoxLayout* RevTreeWidgetLayout;
+ KTextBrowser* m_Detailstext;
+
+signals:
+ void makeCat(const svn::Revision&,const QString&,const QString&,const svn::Revision&,QWidget*);
+ void makeNorecDiff(const QString&,const svn::Revision&,const QString&,const svn::Revision&,QWidget*);
+ void makeRecDiff(const QString&,const svn::Revision&,const QString&,const svn::Revision&,QWidget*);
+
+protected slots:
+ virtual void setDetailText(const QString&);
+
+private:
+ QPixmap image0;
+
+};
+
+#endif // REVTREEWIDGET_H