diff --git a/src/svnfrontend/graphtree/drawparams.cpp b/src/svnfrontend/graphtree/drawparams.cpp index 20668e85..09423ce0 100644 --- a/src/svnfrontend/graphtree/drawparams.cpp +++ b/src/svnfrontend/graphtree/drawparams.cpp @@ -1,827 +1,827 @@ /*************************************************************************** * Copyright (C) 2005-2009 by Rajko Albrecht ral@alwins-world.de * * http://kdesvn.alwins-world.de/ * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ /* This file was part of KCachegrind. Copyright (C) 2002, 2003 Josef Weidendorfer Adapted for the needs of kdesvn by Rajko Albrecht */ /* * A Widget for visualizing hierarchical metrics as areas. * The API is similar to QListView. */ #include "drawparams.h" #include #include #include // set this to 1 to enable debug output #define DEBUG_DRAWING 0 #define MAX_FIELD 12 // // StoredDrawParams // StoredDrawParams::StoredDrawParams() : _backColor(Qt::white) , _selected(false) , _current(false) , _shaded(true) , _rotated(false) , _drawFrame(false) { // field array has size 0 } StoredDrawParams::StoredDrawParams(const 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 >= _field.size())) { return QString(); } return _field[f].text; } QPixmap StoredDrawParams::pixmap(int f) const { if ((f < 0) || (f >= _field.size())) { return QPixmap(); } return _field[f].pix; } DrawParams::Position StoredDrawParams::position(int f) const { if ((f < 0) || (f >= _field.size())) { return Default; } return _field[f].pos; } int StoredDrawParams::maxLines(int f) const { if ((f < 0) || (f >= _field.size())) { return 0; } return _field[f].maxLines; } QFont StoredDrawParams::font() const { return QFontDatabase::systemFont(QFontDatabase::FixedFont); } void StoredDrawParams::ensureField(int f) { static Field *def = nullptr; if (!def) { def = new Field(); def->pos = Default; def->maxLines = 0; } if (f < 0 || f >= MAX_FIELD) { return; } while (_field.size() < f + 1) { _field.append(*def); } } void StoredDrawParams::setField(int f, const QString &t, const 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, const 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(const QRect &r) : _fm(nullptr) , _dp(nullptr) { 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(); + normal = normal.lighter(); } bool isCurrent = dp->current(); if (dp->drawFrame() || isCurrent) { // 3D raised/sunken frame effect... - QColor high = normal.light(); - QColor low = normal.dark(); + QColor high = normal.lighter(); + QColor low = normal.darker(); 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() && (r.width() > 0 && r.height() > 0)) { // adjustment for drawRect semantic in Qt4: decrement height/width r.setRect(r.x(), r.y(), r.width() - 1, r.height() - 1); // some shading bool goDark = qGray(normal.rgb()) > 128; int rBase, gBase, bBase; normal.getRgb(&rBase, &gBase, &bBase); p->setBrush(Qt::NoBrush); // shade parameters: int d = 7; double 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 && (r.width() >= 0 && r.height() >= 0)) { shadeColor.setRgb(qRound(rBase + factor * rDiff), qRound(gBase + factor * gDiff), qRound(bBase + factor * bDiff)); p->setPen(shadeColor); p->drawRect(r); r.setRect(r.x() + 1, r.y() + 1, r.width() - 2, r.height() - 2); factor = 1.0 - ((1.0 - factor) * forth); } // and back (1st half) while (factor > toBack2 && (r.width() >= 0 && r.height() >= 0)) { shadeColor.setRgb(qRound(rBase + factor * rDiff), qRound(gBase + factor * gDiff), qRound(bBase + factor * bDiff)); p->setPen(shadeColor); p->drawRect(r); r.setRect(r.x() + 1, r.y() + 1, r.width() - 2, r.height() - 2); factor = 1.0 - ((1.0 - factor) / back1); } // and back (2nd half) while (factor > .01 && (r.width() >= 0 && r.height() >= 0)) { shadeColor.setRgb(qRound(rBase + factor * rDiff), qRound(gBase + factor * gDiff), qRound(bBase + factor * bDiff)); p->setPen(shadeColor); p->drawRect(r); r.setRect(r.x() + 1, r.y() + 1, r.width() - 2, r.height() - 2); factor = factor * back2; } normal = shadeColor; // for filling, width and height has to be incremented again r.setRect(r.x(), r.y(), r.width() + 1, r.height() + 1); } // fill inside p->fillRect(r, normal); } /* Helper for drawField * Find a line break position in a string, given a font and maximum width * * Returns the actually used width, and sets */ static int findBreak(int &breakPos, QString text, QFontMetrics *fm, int maxWidth) { int usedWidth; // does full text fit? breakPos = text.length(); - usedWidth = fm->width(text); + usedWidth = fm->horizontalAdvance(text); if (usedWidth < maxWidth) { return usedWidth; } // now lower breakPos until best position is found. // first by binary search, resulting in a position a little bit too large int bottomPos = 0; while (qAbs(maxWidth - usedWidth) > 3 * fm->maxWidth()) { int halfPos = (bottomPos + breakPos) / 2; - int halfWidth = fm->width(text, halfPos); + int halfWidth = fm->horizontalAdvance(text, halfPos); if (halfWidth < maxWidth) { bottomPos = halfPos; } else { breakPos = halfPos; usedWidth = halfWidth; } } // final position by taking break boundaries into account. // possible break boundaries are changing char categories but not middle of "Aa" QChar::Category lastCat, cat; int pos = breakPos; lastCat = text[pos - 1].category(); // at minimum 2 chars before break while (pos > 2) { pos--; cat = text[pos - 1].category(); if (cat == lastCat) { continue; } // "Aa" has not a possible break inbetween if ((cat == QChar::Letter_Uppercase) && (lastCat == QChar::Letter_Lowercase)) { lastCat = cat; continue; } lastCat = cat; breakPos = pos; - usedWidth = fm->width(text, breakPos); + usedWidth = fm->horizontalAdvance(text, breakPos); if (usedWidth < maxWidth) { break; } } return usedWidth; } /* Helper for drawField * Find last line break position in a string from backwards, * given a font and maximum width * * Returns the actually used width, and sets */ static int findBreakBackwards(int &breakPos, QString text, QFontMetrics *fm, int maxWidth) { int usedWidth; // does full text fit? breakPos = 0; - usedWidth = fm->width(text); + usedWidth = fm->horizontalAdvance(text); if (usedWidth < maxWidth) { return usedWidth; } // now raise breakPos until best position is found. // first by binary search, resulting in a position a little bit too small int topPos = text.length(); while (qAbs(maxWidth - usedWidth) > 3 * fm->maxWidth()) { int halfPos = (breakPos + topPos) / 2; - int halfWidth = fm->width(text.mid(halfPos)); + int halfWidth = fm->horizontalAdvance(text.mid(halfPos)); if (halfWidth < maxWidth) { breakPos = halfPos; usedWidth = halfWidth; } else { topPos = halfPos; } } // final position by taking break boundaries into account. // possible break boundaries are changing char categories but not middle of "Aa" QChar::Category lastCat, cat; int pos = breakPos; lastCat = text[pos].category(); // at minimum 2 chars before break while (pos < text.length() - 2) { pos++; cat = text[pos].category(); if (cat == lastCat) { continue; } // "Aa" has not a possible break inbetween if ((lastCat == QChar::Letter_Uppercase) && (cat == QChar::Letter_Lowercase)) { lastCat = cat; continue; } lastCat = cat; breakPos = pos; - usedWidth = fm->width(text.mid(breakPos)); + usedWidth = fm->horizontalAdvance(text.mid(breakPos)); if (usedWidth < maxWidth) { break; } } return usedWidth; } 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; 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 there is no space available if (lines < 1) { return false; } // calculate free space in first line () 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 = nullptr; 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 is no space for "..." static int dotW = 0; if (!dotW) { - dotW = _fm->width("..."); + dotW = _fm->horizontalAdvance(QLatin1String("...")); } 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 false; } 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)) { // do not draw pixW = 0; } else { pixDrawn = false; } } // width of text and pixmap to be drawn - int w = pixW + _fm->width(name); + int w = pixW + _fm->horizontalAdvance(name); // 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 is 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) { // more than one line: search for line break if (w > width && lines > 1) { int breakPos; if (!isBottom) { w = pixW + findBreak(breakPos, name, _fm, width - pixW); remaining = name.mid(breakPos); // remove space on break point if (name[breakPos - 1].category() == QChar::Separator_Space) { name = name.left(breakPos - 1); } else { name = name.left(breakPos); } } else { // bottom w = pixW + findBreakBackwards(breakPos, name, _fm, width - pixW); remaining = name.left(breakPos); // remove space on break point if (name[breakPos].category() == QChar::Separator_Space) { name = name.mid(breakPos + 1); } else { name = name.mid(breakPos); } } } else { remaining.clear(); } /* truncate and add ... if needed */ if (w > width) { name = _fm->elidedText(name, Qt::ElideRight, width - pixW); - w = _fm->width(name) + pixW; + w = _fm->horizontalAdvance(name) + pixW; } 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; } 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); + w = pixW + _fm->horizontalAdvance(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, do not 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/revgraphview.cpp b/src/svnfrontend/graphtree/revgraphview.cpp index 7f209f6f..0c0d59b3 100644 --- a/src/svnfrontend/graphtree/revgraphview.cpp +++ b/src/svnfrontend/graphtree/revgraphview.cpp @@ -1,1006 +1,1005 @@ /*************************************************************************** * Copyright (C) 2006-2009 by Rajko Albrecht * * ral@alwins-world.de * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "revgraphview.h" #include "graphtreelabel.h" #include "pannerview.h" #include "graphtree_defines.h" #include "settings/kdesvnsettings.h" #include "../stopdlg.h" #include "svnqt/client.h" #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #define LABEL_WIDTH 160 #define LABEL_HEIGHT 90 RevGraphView::RevGraphView(const svn::ClientP &_client, QWidget *parent) : QGraphicsView(parent) , m_Scene(nullptr) , m_Marker(nullptr) , m_Client(_client) , m_Selected(nullptr) , m_dotTmpFile(nullptr) , m_renderProcess(nullptr) , m_xMargin(0) , m_yMargin(0) , m_CompleteView(new PannerView(this)) , m_cvZoom(0) , m_LastAutoPosition(TopLeft) , m_isMoving(false) , m_noUpdateZoomerPos(false) { m_CompleteView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_CompleteView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_CompleteView->raise(); m_CompleteView->hide(); connect(m_CompleteView, &PannerView::zoomRectMoved, this, &RevGraphView::zoomRectMoved); connect(m_CompleteView, &PannerView::zoomRectMoveFinished, this, &RevGraphView::zoomRectMoveFinished); } RevGraphView::~RevGraphView() { setScene(nullptr); delete m_Scene; delete m_dotTmpFile; delete m_CompleteView; delete m_renderProcess; } void RevGraphView::showText(const QString &s) { clear(); m_Scene = new QGraphicsScene; m_Scene->addSimpleText(s); setScene(m_Scene); m_Scene->update(); m_CompleteView->hide(); } void RevGraphView::clear() { if (m_Selected) { m_Selected->setSelected(false); m_Selected = nullptr; } if (m_Marker) { m_Marker->hide(); delete m_Marker; m_Marker = nullptr; } setScene(nullptr); m_CompleteView->setScene(nullptr); delete m_Scene; m_Scene = nullptr; } void RevGraphView::beginInsert() { viewport()->setUpdatesEnabled(false); } void RevGraphView::endInsert() { if (m_Scene) { /* _cvZoom = 0; updateSizes(); */ m_Scene->update(); } viewport()->setUpdatesEnabled(true); } void RevGraphView::readDotOutput() { if (!m_renderProcess) { return; } m_dotOutput += QString::fromLocal8Bit(m_renderProcess->readAllStandardOutput()); } void RevGraphView::dotExit(int exitcode, QProcess::ExitStatus exitStatus) { if (!m_renderProcess) { return; } if (exitStatus != QProcess::NormalExit || exitcode != 0) { QString error = i18n("Could not run process \"%1\".\n\nProcess stopped with message:\n%2", m_renderProcess->program().join(" "), QString::fromLocal8Bit(m_renderProcess->readAllStandardError())); showText(error); delete m_renderProcess; m_renderProcess = nullptr; return; } // remove line breaks when lines to long QRegExp endslash("\\\\\\n"); m_dotOutput.remove(endslash); double scale = 1.0; double dotWidth = 1.0, dotHeight = 1.0; QTextStream dotStream(&m_dotOutput, QIODevice::ReadOnly); QString cmd; int lineno = 0; beginInsert(); clear(); /* mostly taken from kcachegrind */ double scaleX = scale * 60; double scaleY = scale * 70; QRectF startRect; while (!dotStream.atEnd()) { QString line = dotStream.readLine(); if (line.isNull()) { break; } lineno++; if (line.isEmpty()) { continue; } QTextStream lineStream(&line, QIODevice::ReadOnly); lineStream >> cmd; if (cmd == QLatin1String("stop")) { break; } if (cmd == QLatin1String("graph")) { lineStream >> scale >> dotWidth >> dotHeight; int w = qRound(scaleX * dotWidth); int h = qRound(scaleY * dotHeight); m_xMargin = 50; const QDesktopWidget *dw = QApplication::desktop(); if (w < dw->width()) { m_xMargin += (dw->width() - w) / 2; } m_yMargin = 50; if (h < dw->height()) { m_yMargin += (dw->height() - h) / 2; } m_Scene = new QGraphicsScene(0.0, 0.0, qreal(w + 2 * m_xMargin), qreal(h + 2 * m_yMargin)); m_Scene->setBackgroundBrush(Qt::white); continue; } if (m_dotTmpFile && (cmd != "node") && (cmd != "edge")) { qWarning() << "Ignoring unknown command '" << cmd << "' from dot (" << m_dotTmpFile->fileName() << ":" << lineno << ")" << endl; continue; } if (!m_Scene) { continue; } if (cmd == QLatin1String("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); double xx = (scaleX * x + m_xMargin); double yy = (scaleY * (dotHeight - y) + m_yMargin); double w = (scaleX * width); double h = (scaleY * height); QRectF r(xx - w / 2, yy - h / 2, w, h); GraphTreeLabel *t = new GraphTreeLabel(label, nodeName, r); m_Scene->addItem(t); if (isStart(nodeName)) { startRect = r; ensureVisible(startRect); } t->setBgColor(getBgColor(nodeName)); t->setZValue(1.0); t->show(); m_NodeList[nodeName] = t; t->setToolTip(toolTip(nodeName)); } else { QString node1Name, node2Name; QString _x, _y; double x, y; QPolygonF 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(); double xx = (scaleX * x + m_xMargin); double yy = (scaleY * (dotHeight - y) + m_yMargin); #if 0 if (0) qDebug(" P %d: ( %f / %f ) => ( %d / %d)", i, x, y, xx, yy); #endif pa[i] = QPointF(xx, yy); } if (i < points) { qDebug("CallGraphView: Can't read %d spline points (%d)", points, lineno); continue; } GraphEdge *n = new GraphEdge(); QColor arrowColor = Qt::black; m_Scene->addItem(n); n->setPen(QPen(arrowColor, 1)); n->setControlPoints(pa); n->setZValue(0.5); n->show(); /* arrow dir * eg. it is vx and vy for computing, NO absolute points! */ QPointF arrowDir; int indexHead = -1; QMap::Iterator it; it = m_NodeList.find(node2Name); if (it != m_NodeList.end()) { it.value()->setSource(node1Name); } it = m_NodeList.find(node1Name); if (it != m_NodeList.end()) { GraphTreeLabel *tlab = it.value(); if (tlab) { QPointF toCenter = tlab->rect().center(); qreal dx0 = pa[0].x() - toCenter.x(); qreal dy0 = pa[0].y() - toCenter.y(); qreal dx1 = pa[points - 1].x() - toCenter.x(); qreal dy1 = pa[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[indexHead] - pa[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[indexHead] - pa[indexHead - 1]; } } if (!arrowDir.isNull()) { QPointF baseDir = arrowDir; arrowDir *= 10.0 / sqrt(double(arrowDir.x() * arrowDir.x() + arrowDir.y() * arrowDir.y())); baseDir /= (sqrt(baseDir.x() * baseDir.x() + baseDir.y() * baseDir.y())); QPointF t1(-baseDir.y() - baseDir.x(), baseDir.x() - baseDir.y()); QPointF t2(baseDir.y() - baseDir.x(), -baseDir.x() - baseDir.y()); QPolygonF a; t1 *= 3; t2 *= 3; a << pa[indexHead] + t1 << pa[indexHead] + arrowDir << pa[indexHead] + t2; GraphEdgeArrow *aItem = new GraphEdgeArrow(n, nullptr); m_Scene->addItem(aItem); aItem->setPolygon(a); aItem->setBrush(arrowColor); aItem->setZValue(1.5); aItem->show(); } } } if (!m_Scene) { QString s = i18n("Error running the graph layouting tool.\n"); s += i18n("Please check that 'dot' is installed (package GraphViz)."); showText(s); } else { setScene(m_Scene); m_CompleteView->setScene(m_Scene); if (startRect.isValid()) { ensureVisible(startRect); } } endInsert(); delete m_renderProcess; m_renderProcess = nullptr; } 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.value().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.value().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.value().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; } QString RevGraphView::getLabelstring(const QString &nodeName) { QMap::ConstIterator nIt; nIt = m_LabelMap.constFind(nodeName); if (nIt != m_LabelMap.constEnd()) { return nIt.value(); } trevTree::ConstIterator it1; it1 = m_Tree.constFind(nodeName); if (it1 == m_Tree.constEnd()) { return QString(); } QString res; QString revstring = svn::Revision(it1.value().rev).toString(); switch (it1.value().Action) { case 'D': res = i18n("Deleted at revision %1", revstring); break; case 'A': res = i18n("Added at revision %1 as %2", revstring, it1.value().name); break; case 'C': case 1: res = i18n("Copied to %1 at revision %2", it1.value().name, revstring); break; case 2: res = i18n("Renamed to %1 at revision %2", it1.value().name, revstring); break; case 'M': res = i18n("Modified at revision %1", revstring); break; case 'R': res = i18n("Replaced at revision %1", revstring); break; default: res = i18n("Revision %1", revstring); break; } m_LabelMap[nodeName] = res; return m_LabelMap[nodeName]; } void RevGraphView::dumpRevtree() { if (m_dotTmpFile) { m_dotTmpFile->close(); delete m_dotTmpFile; } clear(); m_dotOutput.clear(); m_dotTmpFile = new QTemporaryFile(QLatin1String("XXXXXX.dot")); m_dotTmpFile->setAutoRemove(true); m_dotTmpFile->open(); if (!m_dotTmpFile->open()) { showText(i18n("Could not open temporary file %1 for writing.", m_dotTmpFile->fileName())); return; } QTextStream stream(m_dotTmpFile); QFont f = QFontDatabase::systemFont(QFontDatabase::FixedFont); QFontMetrics _fm(f); int _fontsize = _fm.height(); if (_fontsize < 0) { _fontsize = 10; } 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.constBegin(); it1 != m_Tree.constEnd(); ++it1) { stream << " " << it1.key() << "[ " << "shape=box, " << "label=\"" << "Zeile 1 geht ab Zeile 2 geht ab"/*getLabelstring(it1.key())*/ << "\"," << "fontsize=" << _fontsize << ",fontname=\"" << f.family() << "\"," << "];\n"; for (int j = 0; j < it1.value().targets.count(); ++j) { stream << " " << it1.key().toLatin1() << " " << "->" << " " << it1.value().targets[j].key << " [fontsize=" << _fontsize << ",fontname=\"" << f.family() << "\",style=\"solid\"];\n"; } } stream << "}\n" << flush; m_renderProcess = new KProcess; m_renderProcess->setEnv("LANG", "C"); *m_renderProcess << "dot"; *m_renderProcess << m_dotTmpFile->fileName() << "-Tplain"; connect(m_renderProcess, QOverload::of(&QProcess::finished), this, &RevGraphView::dotExit); connect(m_renderProcess, &QProcess::readyReadStandardOutput, this, &RevGraphView::readDotOutput); m_renderProcess->setOutputChannelMode(KProcess::SeparateChannels); m_renderProcess->start(); } QString RevGraphView::toolTip(const QString &_nodename, bool full)const { QString res; trevTree::ConstIterator it = m_Tree.constFind(_nodename); if (it == m_Tree.constEnd()) { return res; } const QVector sp = it.value().Message.splitRef(QLatin1Char('\n')); QString sm; if (sp.isEmpty()) { sm = it.value().Message; } else { if (!full) { sm = sp[0].toString() + QLatin1String("..."); } else { for (int j = 0; j < sp.count(); ++j) { if (j > 0) { sm += QLatin1String("
"); } sm += sp[j].toString(); } } } if (!full && sm.length() > 50) { sm.truncate(47); sm += "..."; } static QLatin1String csep(""); static QLatin1String rend(""); static QLatin1String rstart(""); res = QLatin1String(""); if (!full) { res += QString(QLatin1String("%1")).arg(it.value().name); res += i18n("
Revision: %1
Author: %2
Date: %3
Log: %4", it.value().rev, it.value().Author, it.value().Date, sm); } else { res += QLatin1String("") + rstart + i18n("Revision%1%2%3", csep, it.value().rev, rend) + rstart + i18n("Author%1%2%3", csep, it.value().Author, rend) + rstart + i18n("Date%1%2%3", csep, it.value().Date, rend) + rstart + i18n("Log%1%2%3", csep, sm, rend) + QLatin1String("
") + it.value().name + QLatin1String("
"); } return res; } void RevGraphView::updateSizes(QSize s) { if (!m_Scene) { return; } if (s == QSize(0, 0)) { s = size(); } // the part of the canvas that should be visible qreal cWidth = m_Scene->width() - 2 * m_xMargin + 100; qreal cHeight = m_Scene->height() - 2 * m_yMargin + 100; // hide birds eye view if no overview needed if (((cWidth < s.width()) && cHeight < s.height()) || m_NodeList.isEmpty()) { 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 != m_cvZoom) { m_cvZoom = zoom; - QMatrix wm; - m_CompleteView->setMatrix(wm.scale(zoom, zoom)); + m_CompleteView->setTransform(QTransform::fromScale(zoom, zoom)); // 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 scrollContentsBy(0, 0); } m_CompleteView->centerOn(m_Scene->width() / 2, m_Scene->height() / 2); 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); int tlCols = items(QRect(0, 0, cvW, cvH)).count(); int trCols = items(QRect(x, 0, cvW, cvH)).count(); int blCols = items(QRect(0, y, cvW, cvH)).count(); int brCols = items(QRect(x, y, cvW, cvH)).count(); int minCols = tlCols; ZoomPosition 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; 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::zoomRectMoved(qreal dx, qreal dy) { //if (leftMargin()>0) dx = 0; //if (topMargin()>0) dy = 0; m_noUpdateZoomerPos = true; QScrollBar *hBar = horizontalScrollBar(); QScrollBar *vBar = verticalScrollBar(); hBar->setValue(hBar->value() + int(dx)); vBar->setValue(vBar->value() + int(dy)); m_noUpdateZoomerPos = false; } void RevGraphView::zoomRectMoveFinished() { #if 0 if (_zoomPosition == Auto) #endif updateZoomerPos(); } void RevGraphView::resizeEvent(QResizeEvent *e) { QGraphicsView::resizeEvent(e); if (m_Scene) { 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 = nullptr; } if (gtl) { m_Marker = new GraphMark(gtl); m_Scene->addItem(m_Marker); m_Marker->setPos(gtl->pos()); m_Marker->setZValue(-1); } m_Scene->update(); m_CompleteView->update(); } GraphTreeLabel *RevGraphView::firstLabelAt(const QPoint &pos) const { QList its = items(pos); for (auto &it : its) { if (it->type() == GRAPHTREE_LABEL) { return static_cast(it); } } return nullptr; } void RevGraphView::mouseDoubleClickEvent(QMouseEvent *e) { setFocus(); if (e->button() == Qt::LeftButton) { GraphTreeLabel *i = firstLabelAt(e->pos()); if (i == nullptr) { return; } makeSelected(i); emit dispDetails(toolTip((i)->nodename(), true)); } } void RevGraphView::mousePressEvent(QMouseEvent *e) { setFocus(); if (e->button() == Qt::LeftButton) { m_isMoving = true; m_lastPos = e->pos(); } } void RevGraphView::mouseReleaseEvent(QMouseEvent *e) { if (e->button() == Qt::LeftButton && m_isMoving) { QPointF topLeft = mapToScene(QPoint(0, 0)); QPointF bottomRight = mapToScene(QPoint(width(), height())); QRectF z(topLeft, bottomRight); m_CompleteView->setZoomRect(z); m_isMoving = false; updateZoomerPos(); } } void RevGraphView::scrollContentsBy(int dx, int dy) { // call QGraphicsView implementation QGraphicsView::scrollContentsBy(dx, dy); QPointF topLeft = mapToScene(QPoint(0, 0)); QPointF bottomRight = mapToScene(QPoint(width(), height())); m_CompleteView->setZoomRect(QRectF(topLeft, bottomRight)); if (!m_noUpdateZoomerPos && !m_isMoving) { updateZoomerPos(); } } void RevGraphView::mouseMoveEvent(QMouseEvent *e) { if (m_isMoving) { QPoint delta = e->pos() - m_lastPos; QScrollBar *hBar = horizontalScrollBar(); QScrollBar *vBar = verticalScrollBar(); hBar->setValue(hBar->value() - delta.x()); vBar->setValue(vBar->value() - delta.y()); m_lastPos = e->pos(); } } void RevGraphView::setNewDirection(int dir) { if (dir < 0) { dir = 3; } else if (dir > 3) { dir = 0; } Kdesvnsettings::setTree_direction(dir); dumpRevtree(); } void RevGraphView::contextMenuEvent(QContextMenuEvent *e) { if (!m_Scene) { return; } GraphTreeLabel *i = firstLabelAt(e->pos()); QAction *ac; QMenu popup; if (i) { if (!i->source().isEmpty() && getAction(i->nodename()) != 'D') { popup.addAction(i18n("Diff to previous"))->setData(301); } if (m_Selected && m_Selected != i && getAction(m_Selected->nodename()) != 'D' && getAction(i->nodename()) != 'D') { popup.addAction(i18n("Diff to selected item"))->setData(302); } if (getAction(i->nodename()) != 'D') { popup.addAction(i18n("Cat this version"))->setData(303); } if (m_Selected == i) { popup.addAction(i18n("Unselect item"))->setData(401); } else { popup.addAction(i18n("Select item"))->setData(402); } popup.addSeparator(); popup.addAction(i18n("Display details"))->setData(403); popup.addSeparator(); } popup.addAction(i18n("Rotate counter-clockwise"))->setData(101); popup.addAction(i18n("Rotate clockwise"))->setData(102); popup.addSeparator(); ac = popup.addAction(i18n("Diff in revision tree is recursive")); ac->setData(202); ac->setCheckable(true); ac->setChecked(Kdesvnsettings::tree_diff_rec()); popup.addAction(i18n("Save tree as PNG"))->setData(201); ac = popup.exec(e->globalPos()); int r = 0; if (ac) { r = ac->data().toInt(); } 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 = QFileDialog::getSaveFileName(this, i18n("Save tree as PNG"), QString(), i18n("Image (*.png)")); if (!fn.isEmpty()) { if (m_Marker) { m_Marker->hide(); } if (m_Selected) { m_Selected->setSelected(false); } QRect r = m_Scene->sceneRect().toRect(); QPixmap pix(r.width(), r.height()); pix.fill(); QPainter p(&pix); m_Scene->render(&p); pix.save(fn, "PNG"); if (m_Marker) { m_Marker->show(); } if (m_Selected) { m_Selected->setSelected(true); m_Scene->update(); m_CompleteView->updateCurrentRect(); } } } break; case 202: Kdesvnsettings::setTree_diff_rec(!Kdesvnsettings::tree_diff_rec()); break; case 301: if (i && i->type() == GRAPHTREE_LABEL && !i->source().isEmpty()) { makeDiffPrev(i); } break; case 302: if (i && m_Selected) { makeDiff(i->nodename(), m_Selected->nodename()); } break; case 303: if (i) { makeCat(i); } break; case 401: makeSelected(nullptr); break; case 402: makeSelected(i); break; case 403: if (i) { emit dispDetails(toolTip(i->nodename(), true)); } break; default: break; } } void RevGraphView::makeCat(GraphTreeLabel *_l) { if (!_l) { return; } QString n1 = _l->nodename(); trevTree::ConstIterator it = m_Tree.constFind(n1); if (it == m_Tree.constEnd()) { return; } svn::Revision tr(it.value().rev); QString tp = m_basePath + it.value().name; emit makeCat(tr, tp, it.value().name, tr, QApplication::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.constFind(n2); if (it == m_Tree.constEnd()) { return; } svn::Revision sr(it.value().rev); QString sp = m_basePath + it.value().name; it = m_Tree.constFind(n1); if (it == m_Tree.constEnd()) { return; } svn::Revision tr(it.value().rev); QString tp = m_basePath + it.value().name; if (Kdesvnsettings::tree_diff_rec()) { emit makeRecDiff(sp, sr, tp, tr, QApplication::activeModalWidget()); } else { emit makeNorecDiff(sp, sr, tp, tr, QApplication::activeModalWidget()); } } void RevGraphView::setBasePath(const QString &_path) { m_basePath = _path; } void RevGraphView::slotClientException(const QString &what) { KMessageBox::sorry(QApplication::activeModalWidget(), what, i18n("SVN Error")); } diff --git a/src/svnfrontend/maintreewidget.cpp b/src/svnfrontend/maintreewidget.cpp index 5ec69c1f..a2def953 100644 --- a/src/svnfrontend/maintreewidget.cpp +++ b/src/svnfrontend/maintreewidget.cpp @@ -1,2379 +1,2379 @@ /*************************************************************************** * Copyright (C) 2008 by Rajko Albrecht ral@alwins-world.de * * http://kdesvn.alwins-world.de/ * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "maintreewidget.h" #include "models/svnitemmodel.h" #include "models/svnitemnode.h" #include "models/svnsortfilter.h" #include "models/svndirsortfilter.h" #include "database/dbsettings.h" #include "cursorstack.h" #include "svnactions.h" #include "copymoveview_impl.h" #include "mergedlg_impl.h" #include "checkoutinfo_impl.h" #include "importdir_logmsg.h" #include "settings/kdesvnsettings.h" #include "helpers/sshagent.h" #include "svnqt/targets.h" #include "svnqt/url.h" #include "fronthelpers/rangeinput_impl.h" #include "fronthelpers/widgetblockstack.h" #include "fronthelpers/fronthelpers.h" #include "ksvnwidgets/commitmsg_impl.h" #include "ksvnwidgets/deleteform.h" #include "helpers/kdesvn_debug.h" #include "opencontextmenu.h" #include "EditIgnorePattern.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include class MainTreeWidgetData { public: MainTreeWidgetData() { m_Collection = nullptr; m_Model = nullptr; m_SortModel = nullptr; m_DirSortModel = nullptr; m_remoteRevision = svn::Revision::UNDEFINED; } ~MainTreeWidgetData() { delete m_Model; delete m_SortModel; delete m_DirSortModel; } QModelIndex srcInd(const QModelIndex &ind) { return m_SortModel->mapToSource(ind); } QModelIndex srcDirInd(const QModelIndex &ind) { return m_DirSortModel->mapToSource(ind); } SvnItemModelNode *sourceNode(const QModelIndex &index, bool left) { if (!index.isValid()) { return nullptr; } QModelIndex ind = left ? m_DirSortModel->mapToSource(index) : m_SortModel->mapToSource(index); if (ind.isValid()) { return static_cast(ind.internalPointer()); } return nullptr; } KActionCollection *m_Collection; SvnItemModel *m_Model; SvnSortFilterProxy *m_SortModel; SvnDirSortFilterProxy *m_DirSortModel; svn::Revision m_remoteRevision; QString merge_Target, merge_Src2, merge_Src1; QTimer m_TimeModified, m_TimeUpdates, m_resizeColumnsTimer; }; MainTreeWidget::MainTreeWidget(KActionCollection *aCollection, QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f), m_Data(new MainTreeWidgetData) { setupUi(this); setFocusPolicy(Qt::StrongFocus); m_TreeView->setFocusPolicy(Qt::StrongFocus); m_Data->m_Collection = aCollection; m_Data->m_SortModel = new SvnSortFilterProxy(); m_Data->m_SortModel->setDynamicSortFilter(true); m_Data->m_SortModel->setSortRole(SORT_ROLE); m_Data->m_SortModel->setSortCaseSensitivity(Kdesvnsettings::case_sensitive_sort() ? Qt::CaseSensitive : Qt::CaseInsensitive); m_Data->m_SortModel->sort(0); m_TreeView->setModel(m_Data->m_SortModel); m_TreeView->sortByColumn(0, Qt::AscendingOrder); m_Data->m_Model = new SvnItemModel(this); m_Data->m_SortModel->setSourceModel(m_Data->m_Model); m_Data->m_DirSortModel = new SvnDirSortFilterProxy(); m_Data->m_DirSortModel->setDynamicSortFilter(true); m_Data->m_DirSortModel->setSortRole(SORT_ROLE); m_Data->m_DirSortModel->setSortCaseSensitivity(Kdesvnsettings::case_sensitive_sort() ? Qt::CaseSensitive : Qt::CaseInsensitive); m_DirTreeView->setModel(m_Data->m_DirSortModel); m_Data->m_DirSortModel->setSourceModel(m_Data->m_Model); connect(m_TreeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MainTreeWidget::slotSelectionChanged); connect(m_DirTreeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MainTreeWidget::slotDirSelectionChanged); connect(m_Data->m_Model->svnWrapper(), &SvnActions::clientException, this, &MainTreeWidget::slotClientException); connect(m_Data->m_Model, &SvnItemModel::clientException, this, &MainTreeWidget::slotClientException); connect(m_Data->m_Model->svnWrapper(), &SvnActions::sendNotify, this, &MainTreeWidget::slotNotifyMessage); connect(m_Data->m_Model->svnWrapper(), &SvnActions::reinitItem, this, &MainTreeWidget::slotReinitItem); connect(m_Data->m_Model->svnWrapper(), &SvnActions::sigRefreshAll, this, &MainTreeWidget::refreshCurrentTree); connect(m_Data->m_Model->svnWrapper(), &SvnActions::sigRefreshCurrent, this, &MainTreeWidget::refreshCurrent); connect(m_Data->m_Model->svnWrapper(), &SvnActions::sigRefreshItem, this, &MainTreeWidget::slotRefreshItem); connect(m_Data->m_Model->svnWrapper(), &SvnActions::sigGotourl, this, &MainTreeWidget::_openUrl); connect(m_Data->m_Model->svnWrapper(), &SvnActions::sigCacheStatus, this, &MainTreeWidget::sigCacheStatus); connect(m_Data->m_Model->svnWrapper(), &SvnActions::sigThreadsChanged, this, &MainTreeWidget::enableActions); connect(m_Data->m_Model->svnWrapper(), &SvnActions::sigCacheDataChanged, this, &MainTreeWidget::slotCacheDataChanged); connect(m_Data->m_Model->svnWrapper(), &SvnActions::sigExtraStatusMessage, this, &MainTreeWidget::sigExtraStatusMessage); connect(m_Data->m_Model, &SvnItemModel::urlDropped, this, &MainTreeWidget::slotUrlDropped); connect(m_Data->m_Model, &SvnItemModel::itemsFetched, this, &MainTreeWidget::slotItemsInserted); m_TreeView->sortByColumn(0, Qt::AscendingOrder); m_DirTreeView->sortByColumn(0, Qt::AscendingOrder); checkUseNavigation(true); setupActions(); m_Data->m_TimeModified.setParent(this); connect(&(m_Data->m_TimeModified), &QTimer::timeout, this, &MainTreeWidget::slotCheckModified); m_Data->m_TimeUpdates.setParent(this); connect(&(m_Data->m_TimeUpdates), &QTimer::timeout, this, &MainTreeWidget::slotCheckUpdates); m_Data->m_resizeColumnsTimer.setSingleShot(true); m_Data->m_resizeColumnsTimer.setParent(this); connect(&(m_Data->m_resizeColumnsTimer), &QTimer::timeout, this, &MainTreeWidget::resizeAllColumns); } MainTreeWidget::~MainTreeWidget() { // make sure to not get signals which affect the mmi m_Data->m_Model->disconnect(this); m_Data->m_Model->svnWrapper()->disconnect(this); delete m_Data; } void MainTreeWidget::_openUrl(const QUrl &url) { openUrl(url, true); } void MainTreeWidget::resizeAllColumns() { m_TreeView->resizeColumnToContents(SvnItemModel::Name); m_TreeView->resizeColumnToContents(SvnItemModel::Status); m_TreeView->resizeColumnToContents(SvnItemModel::LastRevision); m_TreeView->resizeColumnToContents(SvnItemModel::LastAuthor); m_TreeView->resizeColumnToContents(SvnItemModel::LastDate); m_DirTreeView->resizeColumnToContents(SvnItemModel::Name); } bool MainTreeWidget::openUrl(const QUrl &url, bool noReinit) { #ifdef DEBUG_TIMER QTime _counttime; _counttime.start(); #endif CursorStack a; m_Data->m_Model->svnWrapper()->killallThreads(); clear(); emit sigProplist(svn::PathPropertiesMapListPtr(new svn::PathPropertiesMapList()), false, false, QString()); if (!noReinit) { m_Data->m_Model->svnWrapper()->reInitClient(); } QUrl _url(url); const QString proto = svn::Url::transformProtokoll(url.scheme()); _url = _url.adjusted(QUrl::StripTrailingSlash|QUrl::NormalizePathSegments); _url.setScheme(proto); const QString baseUriString = _url.url(QUrl::StripTrailingSlash); const QVector s = baseUriString.splitRef(QLatin1Char('?')); if (s.size() > 1) { setBaseUri(s.first().toString()); } else { setBaseUri(baseUriString); } setWorkingCopy(false); setNetworked(false); m_Data->m_remoteRevision = svn::Revision::HEAD; if (QLatin1String("svn+file") == url.scheme()) { setBaseUri(url.path()); } else { if (url.isLocalFile()) { QFileInfo fi(url.path()); if (fi.exists() && fi.isSymLink()) { - const QString sl = fi.readLink(); + const QString sl = fi.symLinkTarget(); if (sl.startsWith(QLatin1Char('/'))) { setBaseUri(sl); } else { fi.setFile(fi.path() + QLatin1Char('/') + sl); setBaseUri(fi.absoluteFilePath()); } } else { setBaseUri(url.path()); } QUrl _dummy; qCDebug(KDESVN_LOG) << "check if " << baseUri() << " is a local wc ..."; if (m_Data->m_Model->svnWrapper()->isLocalWorkingCopy(baseUri(), _dummy)) { setWorkingCopy(true); // make sure a valid path is stored as baseuri setBaseUri(url.toLocalFile()); qCDebug(KDESVN_LOG) << "... yes -> " << baseUri(); } else { setWorkingCopy(false); // make sure a valid url is stored as baseuri setBaseUri(url.toString()); qCDebug(KDESVN_LOG) << "... no -> " << baseUri(); } } else { setNetworked(true); if (!Kdesvnsettings::network_on()) { setBaseUri(QString()); setNetworked(false); clear(); KMessageBox::error(this, i18n("Networked URL to open but networking is disabled.")); emit changeCaption(QString()); emit sigUrlOpened(false); return false; } } } const QList> q = QUrlQuery(url).queryItems(); typedef QPair queryPair; for (const queryPair &p : q) { if (p.first == QLatin1String("rev")) { const QString v = p.second; svn::Revision tmp; m_Data->m_Model->svnWrapper()->svnclient()->url2Revision(v, m_Data->m_remoteRevision, tmp); if (m_Data->m_remoteRevision == svn::Revision::UNDEFINED) { m_Data->m_remoteRevision = svn::Revision::HEAD; } } } if (url.scheme() == QLatin1String("svn+ssh") || url.scheme() == QLatin1String("ksvn+ssh")) { SshAgent ssh; ssh.addSshIdentities(); } m_Data->m_Model->svnWrapper()->clearUpdateCache(); if (isWorkingCopy()) { m_Data->m_Model->initDirWatch(); } bool result = m_Data->m_Model->checkDirs(baseUri(), nullptr) > -1; if (result && isWorkingCopy()) { m_Data->m_Model->svnWrapper()->createModifiedCache(baseUri()); m_DirTreeView->expandToDepth(0); m_DirTreeView->selectionModel()->select(m_Data->m_DirSortModel->mapFromSource(m_Data->m_Model->firstRootIndex()), QItemSelectionModel::Select); } resizeAllColumns(); if (!result) { setBaseUri(QString()); setNetworked(false); clear(); } if (result && isWorkingCopy()) { m_Data->m_Model->svnWrapper()->createModifiedCache(baseUri()); if (Kdesvnsettings::start_updates_check_on_open()) { slotCheckUpdates(); } } #ifdef DEBUG_TIMER _counttime.restart(); #endif if (result && Kdesvnsettings::log_cache_on_open()) { m_Data->m_Model->svnWrapper()->startFillCache(baseUri(), true); } #ifdef DEBUG_TIMER qCDebug(KDESVN_LOG) << "Starting cache " << _counttime.elapsed(); _counttime.restart(); #endif emit changeCaption(baseUri()); emit sigUrlOpened(result); emit sigUrlChanged(baseUriAsUrl()); #ifdef DEBUG_TIMER qCDebug(KDESVN_LOG) << "Fired signals " << _counttime.elapsed(); _counttime.restart(); #endif QTimer::singleShot(1, this, &MainTreeWidget::readSupportData); enableActions(); #ifdef DEBUG_TIMER qCDebug(KDESVN_LOG) << "Enabled actions " << _counttime.elapsed(); #endif /* KNotification * notification=new KNotification("kdesvn-open"); notification->setText("Opened url"); notification->sendEvent(); */ return result; } void MainTreeWidget::clear() { m_Data->m_Model->clear(); } svn::Revision MainTreeWidget::baseRevision()const { return m_Data->m_remoteRevision; } QWidget *MainTreeWidget::realWidget() { return this; } int MainTreeWidget::selectionCount()const { int count = m_TreeView->selectionModel()->selectedRows(0).count(); if (count == 0) { if (m_TreeView->rootIndex().isValid()) { return 1; } } return count; } int MainTreeWidget::DirselectionCount()const { return m_DirTreeView->selectionModel()->selectedRows(0).count(); } SvnItemList MainTreeWidget::SelectionList()const { SvnItemList ret; const QModelIndexList _mi = m_TreeView->selectionModel()->selectedRows(0); ret.reserve(_mi.size()); if (_mi.isEmpty()) { QModelIndex ind = m_TreeView->rootIndex(); if (ind.isValid()) { // really! it will remapped to this before setRootIndex! (see below) ret.push_back(m_Data->sourceNode(ind, false)); } return ret; } for (int i = 0; i < _mi.count(); ++i) { ret.push_back(m_Data->sourceNode(_mi[i], false)); } return ret; } SvnItemList MainTreeWidget::DirSelectionList()const { SvnItemList ret; const QModelIndexList _mi = m_DirTreeView->selectionModel()->selectedRows(0); ret.reserve(_mi.size()); for (int i = 0; i < _mi.count(); ++i) { ret.push_back(m_Data->sourceNode(_mi[i], true)); } return ret; } QModelIndex MainTreeWidget::SelectedIndex()const { const QModelIndexList _mi = m_TreeView->selectionModel()->selectedRows(0); if (_mi.count() != 1) { if (_mi.isEmpty()) { const QModelIndex ind = m_TreeView->rootIndex(); if (ind.isValid()) { return m_Data->m_SortModel->mapToSource(ind); } } return QModelIndex(); } return m_Data->m_SortModel->mapToSource(_mi[0]); } QModelIndex MainTreeWidget::DirSelectedIndex()const { const QModelIndexList _mi = m_DirTreeView->selectionModel()->selectedRows(0); if (_mi.count() != 1) { return QModelIndex(); } return m_Data->m_DirSortModel->mapToSource(_mi[0]); } SvnItemModelNode *MainTreeWidget::SelectedNode()const { const QModelIndex index = SelectedIndex(); if (index.isValid()) { SvnItemModelNode *item = static_cast(index.internalPointer()); return item; } return nullptr; } SvnItemModelNode *MainTreeWidget::DirSelectedNode()const { const QModelIndex index = DirSelectedIndex(); if (index.isValid()) { SvnItemModelNode *item = static_cast(index.internalPointer()); return item; } return nullptr; } void MainTreeWidget::slotSelectionChanged(const QItemSelection &, const QItemSelection &) { enableActions(); QTimer::singleShot(100, this, &MainTreeWidget::_propListTimeout); } SvnItem *MainTreeWidget::Selected()const { return SelectedNode(); } SvnItem *MainTreeWidget::DirSelected()const { return DirSelectedNode(); } SvnItem *MainTreeWidget::DirSelectedOrMain()const { SvnItem *_item = DirSelected(); if (_item == nullptr && isWorkingCopy()) { _item = m_Data->m_Model->firstRootChild(); } return _item; } SvnItem *MainTreeWidget::SelectedOrMain()const { SvnItem *_item = Selected(); if (_item == nullptr && isWorkingCopy()) { _item = m_Data->m_Model->firstRootChild(); } return _item; } void MainTreeWidget::setupActions() { if (!m_Data->m_Collection) { return; } QAction *tmp_action; /* local and remote actions */ /* 1. actions on dirs AND files */ tmp_action = add_action(QStringLiteral("make_svn_log_full"), i18n("History of item"), QKeySequence(Qt::CTRL | Qt::Key_L), QIcon::fromTheme(QStringLiteral("kdesvnlog")), this, SLOT(slotMakeLog())); tmp_action->setIconText(i18n("History")); tmp_action->setStatusTip(i18n("Displays the history log of selected item")); tmp_action = add_action(QStringLiteral("make_svn_log_nofollow"), i18n("History of item ignoring copies"), QKeySequence(Qt::SHIFT | Qt::CTRL | Qt::Key_L), QIcon::fromTheme(QStringLiteral("kdesvnlog")), this, SLOT(slotMakeLogNoFollow())); tmp_action->setIconText(i18n("History")); tmp_action->setStatusTip(i18n("Displays the history log of selected item without following copies")); tmp_action = add_action(QStringLiteral("make_svn_dir_log_nofollow"), i18n("History of item ignoring copies"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnlog")), this, SLOT(slotDirMakeLogNoFollow())); tmp_action->setIconText(i18n("History")); tmp_action->setStatusTip(i18n("Displays the history log of selected item without following copies")); tmp_action = add_action(QStringLiteral("make_svn_tree"), i18n("Full revision tree"), QKeySequence(Qt::CTRL | Qt::Key_T), QIcon::fromTheme(QStringLiteral("kdesvntree")), this, SLOT(slotMakeTree())); tmp_action->setStatusTip(i18n("Shows history of item as linked tree")); tmp_action = add_action(QStringLiteral("make_svn_partialtree"), i18n("Partial revision tree"), QKeySequence(Qt::SHIFT | Qt::CTRL | Qt::Key_T), QIcon::fromTheme(QStringLiteral("kdesvntree")), this, SLOT(slotMakePartTree())); tmp_action->setStatusTip(i18n("Shows history of item as linked tree for a revision range")); tmp_action = add_action(QStringLiteral("make_svn_property"), i18n("Properties"), QKeySequence(Qt::CTRL | Qt::Key_P), QIcon(), this, SLOT(slotRightProperties())); tmp_action = add_action(QStringLiteral("make_left_svn_property"), i18n("Properties"), QKeySequence(), QIcon(), this, SLOT(slotLeftProperties())); add_action(QStringLiteral("get_svn_property"), i18n("Display Properties"), QKeySequence(Qt::SHIFT | Qt::CTRL | Qt::Key_P), QIcon(), this, SLOT(slotDisplayProperties())); tmp_action = add_action(QStringLiteral("make_last_change"), i18n("Display last changes"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvndiff")), this, SLOT(slotDisplayLastDiff())); tmp_action->setToolTip(i18n("Display last changes as difference to previous commit.")); tmp_action = add_action(QStringLiteral("make_svn_info"), i18n("Details"), QKeySequence(Qt::CTRL | Qt::Key_I), QIcon::fromTheme(QStringLiteral("kdesvninfo")), this, SLOT(slotInfo())); tmp_action->setStatusTip(i18n("Show details about selected item")); tmp_action = add_action(QStringLiteral("make_svn_rename"), i18n("Move"), QKeySequence(Qt::Key_F2), QIcon::fromTheme(QStringLiteral("kdesvnmove")), this, SLOT(slotRename())); tmp_action->setStatusTip(i18n("Moves or renames current item")); tmp_action = add_action(QStringLiteral("make_svn_copy"), i18n("Copy"), QKeySequence(Qt::CTRL | Qt::Key_C), QIcon::fromTheme(QStringLiteral("kdesvncopy")), this, SLOT(slotCopy())); tmp_action->setStatusTip(i18n("Create a copy of current item")); tmp_action = add_action(QStringLiteral("make_check_updates"), i18n("Check for updates"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvncheckupdates")), this, SLOT(slotCheckUpdates())); tmp_action->setToolTip(i18n("Check if current working copy has items with newer version in repository")); tmp_action->setStatusTip(tmp_action->toolTip()); tmp_action->setIconText(i18n("Check updates")); /* 2. actions only on files */ tmp_action = add_action(QStringLiteral("make_svn_blame"), i18n("Blame"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnblame")), this, SLOT(slotBlame())); tmp_action->setToolTip(i18n("Output the content of specified files or URLs with revision and author information in-line.")); tmp_action->setStatusTip(tmp_action->toolTip()); tmp_action = add_action(QStringLiteral("make_svn_range_blame"), i18n("Blame range"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnblame")), this, SLOT(slotRangeBlame())); tmp_action->setToolTip(i18n("Output the content of specified files or URLs with revision and author information in-line.")); tmp_action->setStatusTip(tmp_action->toolTip()); tmp_action = add_action(QStringLiteral("make_svn_cat"), i18n("Cat head"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvncat")), this, SLOT(slotCat())); tmp_action->setToolTip(i18n("Output the content of specified files or URLs.")); tmp_action->setStatusTip(tmp_action->toolTip()); tmp_action = add_action(QStringLiteral("make_revisions_cat"), i18n("Cat revision..."), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvncat")), this, SLOT(slotRevisionCat())); tmp_action->setToolTip(i18n("Output the content of specified files or URLs at specific revision.")); tmp_action->setStatusTip(tmp_action->toolTip()); tmp_action = add_action(QStringLiteral("make_svn_lock"), i18n("Lock current items"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnlock")), this, SLOT(slotLock())); tmp_action->setToolTip(i18n("Try lock current item against changes from other users")); tmp_action->setStatusTip(tmp_action->toolTip()); tmp_action = add_action(QStringLiteral("make_svn_unlock"), i18n("Unlock current items"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnunlock")), this, SLOT(slotUnlock())); tmp_action->setToolTip(i18n("Free existing lock on current item")); tmp_action->setStatusTip(tmp_action->toolTip()); /* 3. actions only on dirs */ tmp_action = add_action(QStringLiteral("make_svn_mkdir"), i18n("New folder"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnnewfolder")), this, SLOT(slotMkdir())); tmp_action->setStatusTip(i18n("Create a new folder")); tmp_action = add_action(QStringLiteral("make_svn_switch"), i18n("Switch repository"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnswitch")), m_Data->m_Model->svnWrapper(), SLOT(slotSwitch())); tmp_action->setToolTip(i18n("Switch repository path of current working copy path (\"svn switch\")")); tmp_action->setStatusTip(tmp_action->toolTip()); tmp_action = add_action(QStringLiteral("make_svn_relocate"), i18n("Relocate current working copy URL"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnrelocate")), this, SLOT(slotRelocate())); tmp_action->setToolTip(i18n("Relocate URL of current working copy path to other URL")); tmp_action->setStatusTip(tmp_action->toolTip()); tmp_action = add_action(QStringLiteral("make_check_unversioned"), i18n("Check for unversioned items"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnaddrecursive")), this, SLOT(slotCheckNewItems())); tmp_action->setIconText(i18n("Unversioned")); tmp_action->setToolTip(i18n("Browse folder for unversioned items and add them if wanted.")); tmp_action->setStatusTip(tmp_action->toolTip()); tmp_action = add_action(QStringLiteral("make_switch_to_repo"), i18n("Open repository of working copy"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnrepository")), this, SLOT(slotChangeToRepository())); tmp_action->setToolTip(i18n("Opens the repository the current working copy was checked out from")); tmp_action = add_action(QStringLiteral("make_cleanup"), i18n("Cleanup"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvncleanup")), this, SLOT(slotCleanupAction())); tmp_action->setToolTip(i18n("Recursively clean up the working copy, removing locks, resuming unfinished operations, etc.")); tmp_action = add_action(QStringLiteral("make_import_dirs_into_current"), i18n("Import folders into current"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnimportfolder")), this, SLOT(slotImportDirsIntoCurrent())); tmp_action->setToolTip(i18n("Import folder content into current URL")); /* local only actions */ /* 1. actions on files AND dirs*/ tmp_action = add_action(QStringLiteral("make_svn_add"), i18n("Add selected files/dirs"), QKeySequence(Qt::Key_Insert), QIcon::fromTheme(QStringLiteral("kdesvnadd")), m_Data->m_Model->svnWrapper(), SLOT(slotAdd())); tmp_action->setToolTip(i18n("Adding selected files and/or directories to repository")); tmp_action->setIconText(i18n("Add")); tmp_action = add_action(QStringLiteral("make_svn_addrec"), i18n("Add selected files/dirs recursive"), QKeySequence(Qt::CTRL | Qt::Key_Insert), QIcon::fromTheme(QStringLiteral("kdesvnaddrecursive")), m_Data->m_Model->svnWrapper(), SLOT(slotAddRec())); tmp_action->setToolTip(i18n("Adding selected files and/or directories to repository and all subitems of folders")); tmp_action = add_action(QStringLiteral("make_svn_remove"), i18n("Delete selected files/dirs"), QKeySequence(Qt::Key_Delete), QIcon::fromTheme(QStringLiteral("kdesvndelete")), this, SLOT(slotDelete())); tmp_action->setIconText(i18n("Delete")); tmp_action->setToolTip(i18n("Deleting selected files and/or directories from repository")); tmp_action = add_action(QStringLiteral("make_svn_remove_left"), i18n("Delete folder"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvndelete")), this, SLOT(slotLeftDelete())); tmp_action->setToolTip(i18n("Deleting selected directories from repository")); tmp_action->setIconText(i18n("Delete")); tmp_action = add_action(QStringLiteral("make_svn_revert"), i18n("Revert current changes"), QKeySequence(Qt::CTRL | Qt::Key_R), QIcon::fromTheme(QStringLiteral("kdesvnreverse")), m_Data->m_Model->svnWrapper(), SLOT(slotRevert())); tmp_action = add_action(QStringLiteral("make_resolved"), i18n("Mark resolved"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnresolved")), this, SLOT(slotResolved())); tmp_action->setToolTip(i18n("Marking files or dirs resolved")); tmp_action = add_action(QStringLiteral("make_try_resolve"), i18n("Resolve conflicts"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnresolved")), this, SLOT(slotTryResolve())); tmp_action = add_action(QStringLiteral("make_svn_ignore"), i18n("Ignore/Unignore current item"), QKeySequence(), QIcon(), this, SLOT(slotIgnore())); tmp_action = add_action(QStringLiteral("make_left_add_ignore_pattern"), i18n("Add or Remove ignore pattern"), QKeySequence(), QIcon(), this, SLOT(slotLeftRecAddIgnore())); tmp_action = add_action(QStringLiteral("make_right_add_ignore_pattern"), i18n("Add or Remove ignore pattern"), QKeySequence(), QIcon(), this, SLOT(slotRightRecAddIgnore())); tmp_action = add_action(QStringLiteral("make_svn_headupdate"), i18n("Update to head"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnupdate")), m_Data->m_Model->svnWrapper(), SLOT(slotUpdateHeadRec())); tmp_action->setIconText(i18nc("Menu item", "Update")); tmp_action = add_action(QStringLiteral("make_svn_revupdate"), i18n("Update to revision..."), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnupdate")), m_Data->m_Model->svnWrapper(), SLOT(slotUpdateTo())); tmp_action = add_action(QStringLiteral("make_svn_commit"), i18n("Commit"), QKeySequence(QStringLiteral("CTRL+#")), QIcon::fromTheme(QStringLiteral("kdesvncommit")), this, SLOT(slotCommit())); tmp_action->setIconText(i18n("Commit")); tmp_action = add_action(QStringLiteral("make_svn_basediff"), i18n("Diff local changes"), QKeySequence(Qt::CTRL | Qt::Key_D), QIcon::fromTheme(QStringLiteral("kdesvndiff")), this, SLOT(slotSimpleBaseDiff())); tmp_action->setToolTip(i18n("Diff working copy against BASE (last checked out version) - does not require access to repository")); tmp_action = add_action(QStringLiteral("make_svn_dirbasediff"), i18n("Diff local changes"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvndiff")), this, SLOT(slotDirSimpleBaseDiff())); tmp_action->setToolTip(i18n("Diff working copy against BASE (last checked out version) - does not require access to repository")); tmp_action = add_action(QStringLiteral("make_svn_headdiff"), i18n("Diff against HEAD"), QKeySequence(Qt::CTRL | Qt::Key_H), QIcon::fromTheme(QStringLiteral("kdesvndiff")), this, SLOT(slotSimpleHeadDiff())); tmp_action->setToolTip(i18n("Diff working copy against HEAD (last checked in version)- requires access to repository")); tmp_action = add_action(QStringLiteral("make_svn_itemsdiff"), i18n("Diff items"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvndiff")), this, SLOT(slotDiffPathes())); tmp_action->setToolTip(i18n("Diff two items")); tmp_action = add_action(QStringLiteral("make_svn_diritemsdiff"), i18n("Diff items"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvndiff")), this, SLOT(slotDiffPathes())); tmp_action->setToolTip(i18n("Diff two items")); tmp_action = add_action(QStringLiteral("make_svn_merge_revisions"), i18n("Merge two revisions"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnmerge")), this, SLOT(slotMergeRevisions())); tmp_action->setIconText(i18n("Merge")); tmp_action->setToolTip(i18n("Merge two revisions of this entry into itself")); tmp_action = add_action(QStringLiteral("make_svn_merge"), i18n("Merge..."), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnmerge")), this, SLOT(slotMerge())); tmp_action->setToolTip(i18n("Merge repository path into current working copy path or current repository path into a target")); tmp_action = add_action(QStringLiteral("openwith"), i18n("Open With..."), QKeySequence(), QIcon(), this, SLOT(slotOpenWith())); /* remote actions only */ tmp_action = add_action(QStringLiteral("make_svn_checkout_current"), i18n("Checkout current repository path"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvncheckout")), m_Data->m_Model->svnWrapper(), SLOT(slotCheckoutCurrent())); tmp_action->setIconText(i18n("Checkout")); tmp_action = add_action(QStringLiteral("make_svn_export_current"), i18n("Export current repository path"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnexport")), m_Data->m_Model->svnWrapper(), SLOT(slotExportCurrent())); add_action(QStringLiteral("switch_browse_revision"), i18n("Select browse revision"), QKeySequence(), QIcon(), this, SLOT(slotSelectBrowsingRevision())); /* independe actions */ tmp_action = add_action(QStringLiteral("make_svn_checkout"), i18n("Checkout a repository"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvncheckout")), m_Data->m_Model->svnWrapper(), SLOT(slotCheckout())); tmp_action->setIconText(i18n("Checkout")); tmp_action = add_action(QStringLiteral("make_svn_export"), i18n("Export a repository"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnexport")), m_Data->m_Model->svnWrapper(), SLOT(slotExport())); tmp_action->setIconText(i18n("Export")); tmp_action = add_action(QStringLiteral("make_view_refresh"), i18n("Refresh view"), QKeySequence(Qt::Key_F5), QIcon::fromTheme(QStringLiteral("kdesvnrightreload")), this, SLOT(refreshCurrentTree())); tmp_action->setIconText(i18n("Refresh")); add_action(QStringLiteral("make_revisions_diff"), i18n("Diff revisions"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvndiff")), this, SLOT(slotDiffRevisions())); /* folding options */ tmp_action = add_action(QStringLiteral("view_unfold_tree"), i18n("Unfold File Tree"), QKeySequence(), QIcon(), this, SLOT(slotUnfoldTree())); tmp_action->setToolTip(i18n("Opens all branches of the file tree")); tmp_action = add_action(QStringLiteral("view_fold_tree"), i18n("Fold File Tree"), QKeySequence(), QIcon(), this , SLOT(slotFoldTree())); tmp_action->setToolTip(i18n("Closes all branches of the file tree")); /* caching */ tmp_action = add_action(QStringLiteral("update_log_cache"), i18n("Update log cache"), QKeySequence(), QIcon(), this, SLOT(slotUpdateLogCache())); tmp_action->setToolTip(i18n("Update the log cache for current repository")); tmp_action = add_action(QStringLiteral("make_dir_commit"), i18n("Commit"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvncommit")), this, SLOT(slotDirCommit())); tmp_action = add_action(QStringLiteral("make_dir_update"), i18n("Update to head"), QKeySequence(), QIcon::fromTheme(QStringLiteral("kdesvnupdate")), this, SLOT(slotDirUpdate())); tmp_action = add_action(QStringLiteral("set_rec_property_dir"), i18n("Set property recursive"), QKeySequence(), QIcon(), this, SLOT(slotDirRecProperty())); tmp_action = add_action(QStringLiteral("show_repository_settings"), i18n("Settings for current repository"), QKeySequence(), QIcon(), this, SLOT(slotRepositorySettings())); enableActions(); } bool MainTreeWidget::uniqueTypeSelected() { QModelIndexList _mi = m_TreeView->selectionModel()->selectedRows(0); if (_mi.count() < 1) { return false; } bool dir = static_cast(m_Data->srcInd(_mi[0]).internalPointer())->isDir(); for (int i = 1; i < _mi.count(); ++i) { if (static_cast(m_Data->srcInd(_mi[i]).internalPointer())->isDir() != dir) { return false; } } return true; } void MainTreeWidget::enableAction(const QString &name, bool how) { QAction *temp = filesActions()->action(name); if (temp) { temp->setEnabled(how); temp->setVisible(how); } } void MainTreeWidget::enableActions() { const bool isopen = !baseUri().isEmpty(); const SvnItemList fileList = SelectionList(); const SvnItemList dirList = DirSelectionList(); const SvnItemModelNode *si = SelectedNode(); const bool single = isopen && fileList.size() == 1; const bool multi = isopen && fileList.size() > 1; const bool none = isopen && fileList.isEmpty(); const bool single_dir = single && si && si->isDir(); const bool unique = uniqueTypeSelected(); const bool remote_enabled =/*isopen&&*/m_Data->m_Model->svnWrapper()->doNetworking(); const bool conflicted = single && si && si->isConflicted(); bool at_least_one_changed = false; bool at_least_one_conflicted = false; bool at_least_one_local_added = false; bool all_unversioned = true; bool all_versioned = true; bool at_least_one_directory = false; for (auto item : fileList) { if (!item) { // root item continue; } if (item->isChanged()) { at_least_one_changed = true; } if (item->isConflicted()) { at_least_one_conflicted = true; } if (item->isLocalAdded()) { at_least_one_local_added = true; } if (item->isRealVersioned()) { all_unversioned = false; } else { all_versioned = false; } if (item->isDir()) { at_least_one_directory = true; if (item->isChildModified()) at_least_one_changed = true; } } //qDebug("single: %d, multi: %d, none: %d, single_dir: %d, unique: %d, remove_enabled: %d, conflicted: %d, changed: %d, added: %d", // single, multi, none, single_dir, unique, remote_enabled, conflicted, si && si->isChanged(), si && si->isLocalAdded()); //qDebug("at_least_one_changed: %d, at_least_one_conflicted: %d, at_least_one_local_added: %d, all_unversioned: %d, all_versioned: %d, at_least_one_directory: %d", // at_least_one_changed, at_least_one_conflicted, at_least_one_local_added, all_unversioned, all_versioned, at_least_one_directory); /* local and remote actions */ /* 1. actions on dirs AND files */ enableAction(QStringLiteral("make_svn_log_nofollow"), single || none); enableAction(QStringLiteral("make_svn_dir_log_nofollow"), dirList.size() == 1 && isopen); enableAction(QStringLiteral("make_last_change"), isopen); enableAction(QStringLiteral("make_svn_log_full"), single || none); enableAction(QStringLiteral("make_svn_tree"), single || none); enableAction(QStringLiteral("make_svn_partialtree"), single || none); enableAction(QStringLiteral("make_svn_property"), single); enableAction(QStringLiteral("make_left_svn_property"), dirList.size() == 1); enableAction(QStringLiteral("set_rec_property_dir"), dirList.size() == 1); enableAction(QStringLiteral("get_svn_property"), single); enableAction(QStringLiteral("make_svn_remove"), (multi || single)); enableAction(QStringLiteral("make_svn_remove_left"), dirList.size() > 0); enableAction(QStringLiteral("make_svn_lock"), (multi || single)); enableAction(QStringLiteral("make_svn_unlock"), (multi || single)); enableAction(QStringLiteral("make_svn_ignore"), (single) && si && si->parent() != nullptr && !si->isRealVersioned()); enableAction(QStringLiteral("make_left_add_ignore_pattern"), (dirList.size() == 1) && isWorkingCopy()); enableAction(QStringLiteral("make_right_add_ignore_pattern"), single_dir && isWorkingCopy()); enableAction(QStringLiteral("make_svn_rename"), single && (!isWorkingCopy() || si != m_Data->m_Model->firstRootChild())); enableAction(QStringLiteral("make_svn_copy"), single && (!isWorkingCopy() || si != m_Data->m_Model->firstRootChild())); /* 2. only on files */ enableAction(QStringLiteral("make_svn_blame"), single && !single_dir && remote_enabled); enableAction(QStringLiteral("make_svn_range_blame"), single && !single_dir && remote_enabled); enableAction(QStringLiteral("make_svn_cat"), single && !single_dir); /* 3. actions only on dirs */ enableAction(QStringLiteral("make_svn_mkdir"), single_dir || (none && isopen)); enableAction(QStringLiteral("make_svn_switch"), isWorkingCopy() && (single || none)); enableAction(QStringLiteral("make_switch_to_repo"), isWorkingCopy()); enableAction(QStringLiteral("make_import_dirs_into_current"), single_dir || dirList.size() == 1); enableAction(QStringLiteral("make_svn_relocate"), isWorkingCopy() && (single || none)); enableAction(QStringLiteral("make_svn_export_current"), ((single && single_dir) || none)); /* local only actions */ /* 1. actions on files AND dirs*/ enableAction(QStringLiteral("make_svn_add"), (multi || single) && isWorkingCopy() && all_unversioned); enableAction(QStringLiteral("make_svn_revert"), (multi || single) && isWorkingCopy() && (at_least_one_changed || at_least_one_conflicted || at_least_one_local_added)); enableAction(QStringLiteral("make_resolved"), (multi || single) && isWorkingCopy()); enableAction(QStringLiteral("make_try_resolve"), conflicted && !single_dir); enableAction(QStringLiteral("make_svn_info"), isopen); enableAction(QStringLiteral("make_svn_merge_revisions"), (single || dirList.size() == 1) && isWorkingCopy()); enableAction(QStringLiteral("make_svn_merge"), single || dirList.size() == 1 || none); enableAction(QStringLiteral("make_svn_addrec"), (multi || single) && at_least_one_directory && isWorkingCopy() && all_unversioned); enableAction(QStringLiteral("make_svn_headupdate"), isWorkingCopy() && isopen && remote_enabled); enableAction(QStringLiteral("make_dir_update"), isWorkingCopy() && isopen && remote_enabled); enableAction(QStringLiteral("make_svn_revupdate"), isWorkingCopy() && isopen && remote_enabled); enableAction(QStringLiteral("make_svn_commit"), isWorkingCopy() && isopen && remote_enabled); enableAction(QStringLiteral("make_dir_commit"), isWorkingCopy() && isopen && remote_enabled); enableAction(QStringLiteral("make_svn_basediff"), isWorkingCopy() && (single || none)); enableAction(QStringLiteral("make_svn_dirbasediff"), isWorkingCopy() && (dirList.size() < 2)); enableAction(QStringLiteral("make_svn_headdiff"), isWorkingCopy() && (single || none) && remote_enabled); /// @todo check if all items have same type enableAction(QStringLiteral("make_svn_itemsdiff"), multi && fileList.size() == 2 && unique && remote_enabled && all_versioned); enableAction(QStringLiteral("make_svn_diritemsdiff"), dirList.size() == 2 && isopen && remote_enabled && all_versioned); /* 2. on dirs only */ enableAction(QStringLiteral("make_cleanup"), isWorkingCopy() && (single_dir || none)); enableAction(QStringLiteral("make_check_unversioned"), isWorkingCopy() && ((single_dir && single) || none)); /* remote actions only */ enableAction(QStringLiteral("make_svn_checkout_current"), ((single && single_dir) || none) && !isWorkingCopy() && remote_enabled); /* independ actions */ enableAction(QStringLiteral("make_svn_checkout"), remote_enabled); enableAction(QStringLiteral("make_svn_export"), true); enableAction(QStringLiteral("make_view_refresh"), isopen); enableAction(QStringLiteral("make_revisions_diff"), isopen); enableAction(QStringLiteral("make_revisions_cat"), isopen && !single_dir && single); enableAction(QStringLiteral("switch_browse_revision"), !isWorkingCopy() && isopen); enableAction(QStringLiteral("make_check_updates"), isWorkingCopy() && isopen && remote_enabled); enableAction(QStringLiteral("openwith"), KAuthorized::authorizeAction("openwith") && single && !single_dir); enableAction(QStringLiteral("show_repository_settings"), isopen); enableAction(QStringLiteral("repo_statistic"), isopen); QAction *temp = filesActions()->action(QStringLiteral("update_log_cache")); if (temp) { temp->setEnabled(remote_enabled); if (!m_Data->m_Model->svnWrapper()->threadRunning(SvnActions::fillcachethread)) { temp->setText(i18n("Update log cache")); } else { temp->setText(i18n("Stop updating the log cache")); } } } QAction *MainTreeWidget::add_action(const QString &actionname, const QString &text, const QKeySequence &sequ, const QIcon &icon, QObject *target, const char *slot) { QAction *tmp_action = nullptr; tmp_action = m_Data->m_Collection->addAction(actionname, target, slot); tmp_action->setText(text); m_Data->m_Collection->setDefaultShortcut(tmp_action, sequ); tmp_action->setIcon(icon); return tmp_action; } KActionCollection *MainTreeWidget::filesActions() { return m_Data->m_Collection; } void MainTreeWidget::closeMe() { m_Data->m_Model->svnWrapper()->killallThreads(); clear(); setWorkingCopy(true); setNetworked(false); setWorkingCopy(false); setBaseUri(QString()); emit changeCaption(QString()); emit sigUrlOpened(false); emit sigUrlChanged(QUrl()); enableActions(); m_Data->m_Model->svnWrapper()->reInitClient(); } void MainTreeWidget::refreshCurrentTree() { m_Data->m_Model->refreshCurrentTree(); if (isWorkingCopy()) { m_Data->m_Model->svnWrapper()->createModifiedCache(baseUri()); } m_Data->m_SortModel->invalidate(); setUpdatesEnabled(true); //viewport()->repaint(); QTimer::singleShot(1, this, &MainTreeWidget::readSupportData); } void MainTreeWidget::slotSettingsChanged() { m_Data->m_SortModel->setSortCaseSensitivity(Kdesvnsettings::case_sensitive_sort() ? Qt::CaseSensitive : Qt::CaseInsensitive); m_Data->m_SortModel->invalidate(); m_Data->m_DirSortModel->invalidate(); enableActions(); if (m_Data->m_Model->svnWrapper() && !m_Data->m_Model->svnWrapper()->doNetworking()) { m_Data->m_Model->svnWrapper()->stopFillCache(); } checkUseNavigation(); } KService::List MainTreeWidget::offersList(SvnItem *item, bool execOnly) const { KService::List offers; if (!item) { return offers; } if (!item->mimeType().isValid()) { return offers; } QString constraint(QLatin1String("(DesktopEntryName != 'kdesvn') and (Type == 'Application')")); if (execOnly) { constraint += QLatin1String(" and (exist Exec)"); } offers = KMimeTypeTrader::self()->query(item->mimeType().name(), QString::fromLatin1("Application"), constraint); return offers; } void MainTreeWidget::slotItemActivated(const QModelIndex &_index) { QModelIndex index = m_Data->m_SortModel->mapToSource(_index); itemActivated(index); } void MainTreeWidget::itemActivated(const QModelIndex &index, bool keypress) { Q_UNUSED(keypress); SvnItemModelNode *item; if (index.isValid() && (item = static_cast(index.internalPointer()))) { if (!item->isDir()) { svn::Revision rev; QList lst; lst.append(item->kdeName(rev)); KService::List li = offersList(item, true); if (li.isEmpty() || li.first()->exec().isEmpty()) { li = offersList(item); } if (!li.isEmpty() && !li.first()->exec().isEmpty()) { KService::Ptr ptr = li.first(); KRun::runService(*ptr, lst, QApplication::activeWindow()); } else { KRun::displayOpenWithDialog(lst, QApplication::activeWindow()); } } else if (Kdesvnsettings::show_navigation_panel()) { m_DirTreeView->selectionModel()->select(m_Data->m_DirSortModel->mapFromSource(index), QItemSelectionModel::ClearAndSelect); QModelIndex _ind = m_Data->m_Model->parent(index); if (_ind.isValid()) { m_DirTreeView->expand(m_Data->m_DirSortModel->mapFromSource(_ind)); } } else { } } } void MainTreeWidget::slotCheckUpdates() { if (isWorkingCopy() && m_Data->m_Model->svnWrapper()->doNetworking()) { m_Data->m_TimeUpdates.stop(); m_Data->m_Model->svnWrapper()->createUpdateCache(baseUri()); } } void MainTreeWidget::slotCheckModified() { if (isWorkingCopy()) { m_Data->m_TimeModified.stop(); m_Data->m_Model->svnWrapper()->createModifiedCache(baseUri()); } } void MainTreeWidget::slotNotifyMessage(const QString &what) { emit sigLogMessage(what); QCoreApplication::processEvents(); } void MainTreeWidget::readSupportData() { /// this moment empty cause no usagedata explicit used by MainTreeWidget } void MainTreeWidget::slotClientException(const QString &what) { emit sigLogMessage(what); KMessageBox::sorry(QApplication::activeModalWidget(), what, i18n("SVN Error")); } void MainTreeWidget::slotCacheDataChanged() { m_Data->m_SortModel->invalidate(); if (isWorkingCopy()) { if (!m_Data->m_TimeModified.isActive() && Kdesvnsettings::poll_modified()) { m_Data->m_TimeModified.setInterval(MinutesToMsec(Kdesvnsettings::poll_modified_minutes())); m_Data->m_TimeModified.start(); } if (!m_Data->m_TimeUpdates.isActive() && Kdesvnsettings::poll_updates()) { m_Data->m_TimeUpdates.setInterval(MinutesToMsec(Kdesvnsettings::poll_updates_minutes())); m_Data->m_TimeUpdates.start(); } } } void MainTreeWidget::slotIgnore() { m_Data->m_Model->makeIgnore(SelectedIndex()); m_Data->m_SortModel->invalidate(); } void MainTreeWidget::slotLeftRecAddIgnore() { SvnItem *item = DirSelected(); if (!item || !item->isDir()) { return; } recAddIgnore(item); } void MainTreeWidget::slotRightRecAddIgnore() { SvnItem *item = Selected(); if (!item || !item->isDir()) { return; } recAddIgnore(item); } void MainTreeWidget::recAddIgnore(SvnItem *item) { QPointer dlg(new KSvnSimpleOkDialog(QStringLiteral("ignore_pattern_dlg"))); dlg->setWindowTitle(i18nc("@title:window", "Edit Pattern to Ignore for \"%1\"", item->shortName())); dlg->setWithCancelButton(); EditIgnorePattern *ptr(new EditIgnorePattern(dlg)); dlg->addWidget(ptr); if (dlg->exec() != QDialog::Accepted) { delete dlg; return; } svn::Depth _d = ptr->depth(); QStringList _pattern = ptr->items(); bool unignore = ptr->unignore(); svn::Revision start(svn::Revision::WORKING); if (!isWorkingCopy()) { start = baseRevision(); } svn::StatusEntries res; if (!m_Data->m_Model->svnWrapper()->makeStatus(item->fullName(), res, start, _d, true /* all entries */, false, false)) { return; } for (int i = 0; i < res.count(); ++i) { if (!res[i]->isRealVersioned() || res[i]->entry().kind() != svn_node_dir) { continue; } m_Data->m_Model->svnWrapper()->makeIgnoreEntry(res[i]->path(), _pattern, unignore); } refreshCurrentTree(); delete dlg; } void MainTreeWidget::slotMakeLogNoFollow()const { doLog(false, false); } void MainTreeWidget::slotMakeLog()const { doLog(true, false); } void MainTreeWidget::slotDirMakeLogNoFollow()const { doLog(false, true); } void MainTreeWidget::doLog(bool use_follow_settings, bool left)const { SvnItem *k = left ? DirSelectedOrMain() : SelectedOrMain(); QString what; if (k) { what = k->fullName(); } else if (!isWorkingCopy() && selectionCount() == 0) { what = baseUri(); } else { return; } svn::Revision start(svn::Revision::HEAD); if (!isWorkingCopy()) { start = baseRevision(); } svn::Revision end(svn::Revision::START); bool list = Kdesvnsettings::self()->log_always_list_changed_files(); bool follow = use_follow_settings ? Kdesvnsettings::log_follows_nodes() : false; Kdesvnsettings::setLast_node_follow(follow); int l = 50; m_Data->m_Model->svnWrapper()->makeLog(start, end, (isWorkingCopy() ? svn::Revision::UNDEFINED : baseRevision()), what, follow, list, l); } void MainTreeWidget::slotContextMenu(const QPoint &) { execContextMenu(SelectionList()); } void MainTreeWidget::slotDirContextMenu(const QPoint &vp) { QMenu popup; QAction *temp = nullptr; int count = 0; if ((temp = filesActions()->action(QStringLiteral("make_dir_commit"))) && temp->isEnabled() && ++count) { popup.addAction(temp); } if ((temp = filesActions()->action(QStringLiteral("make_dir_update"))) && temp->isEnabled() && ++count) { popup.addAction(temp); } if ((temp = filesActions()->action(QStringLiteral("make_svn_dirbasediff"))) && temp->isEnabled() && ++count) { popup.addAction(temp); } if ((temp = filesActions()->action(QStringLiteral("make_svn_diritemsdiff"))) && temp->isEnabled() && ++count) { popup.addAction(temp); } if ((temp = filesActions()->action(QStringLiteral("make_svn_dir_log_nofollow"))) && temp->isEnabled() && ++count) { popup.addAction(temp); } if ((temp = filesActions()->action(QStringLiteral("make_left_svn_property"))) && temp->isEnabled() && ++count) { popup.addAction(temp); } if ((temp = filesActions()->action(QStringLiteral("make_svn_remove_left"))) && temp->isEnabled() && ++count) { popup.addAction(temp); } if ((temp = filesActions()->action(QStringLiteral("make_left_add_ignore_pattern"))) && temp->isEnabled() && ++count) { popup.addAction(temp); } if ((temp = filesActions()->action(QStringLiteral("set_rec_property_dir"))) && temp->isEnabled() && ++count) { popup.addAction(temp); } OpenContextmenu *me = nullptr; QAction *menuAction = nullptr; const SvnItemList l = DirSelectionList(); if (l.count() == 1 && l.at(0)) { const KService::List offers = offersList(l.at(0), l.at(0)->isDir()); if (!offers.isEmpty()) { svn::Revision rev(isWorkingCopy() ? svn::Revision::UNDEFINED : baseRevision()); me = new OpenContextmenu(l.at(0)->kdeName(rev), offers, nullptr); me->setTitle(i18n("Open With...")); menuAction = popup.addMenu(me); ++count; } } if (count) { popup.exec(m_DirTreeView->viewport()->mapToGlobal(vp)); } if (menuAction) { popup.removeAction(menuAction); delete menuAction; } delete me; } void MainTreeWidget::execContextMenu(const SvnItemList &l) { bool isopen = baseUri().length() > 0; QString menuname; if (!isopen) { menuname = "empty"; } else if (isWorkingCopy()) { menuname = "local"; } else { menuname = "remote"; } if (l.isEmpty()) { menuname += "_general"; } else if (l.count() > 1) { menuname += "_context_multi"; } else { menuname += "_context_single"; if (isWorkingCopy()) { if (l.at(0)->isRealVersioned()) { if (l.at(0)->isConflicted()) { menuname += "_conflicted"; } else { menuname += "_versioned"; if (l.at(0)->isDir()) { menuname += "_dir"; } } } else { menuname += "_unversioned"; } } else if (l.at(0)->isDir()) { menuname += "_dir"; } } //qDebug("menuname: %s", qPrintable(menuname)); QWidget *target; emit sigShowPopup(menuname, &target); QMenu *popup = static_cast(target); if (!popup) { return; } OpenContextmenu *me = nullptr; QAction *temp = nullptr; QAction *menuAction = nullptr; if (l.count() == 1/*&&!l.at(0)->isDir()*/) { KService::List offers = offersList(l.at(0), l.at(0)->isDir()); if (!offers.isEmpty()) { svn::Revision rev(isWorkingCopy() ? svn::Revision::UNDEFINED : baseRevision()); me = new OpenContextmenu(l.at(0)->kdeName(rev), offers, nullptr); me->setTitle(i18n("Open With...")); menuAction = popup->addMenu(me); } else { temp = filesActions()->action(QStringLiteral("openwith")); if (temp) { popup->addAction(temp); } } } popup->exec(QCursor::pos()); if (menuAction) { popup->removeAction(menuAction); } delete me; if (temp) { popup->removeAction(temp); delete temp; } } void MainTreeWidget::slotUnfoldTree() { m_TreeView->expandAll(); } void MainTreeWidget::slotFoldTree() { m_TreeView->collapseAll(); } void MainTreeWidget::slotOpenWith() { SvnItem *which = Selected(); if (!which || which->isDir()) { return; } svn::Revision rev(isWorkingCopy() ? svn::Revision::UNDEFINED : baseRevision()); QList lst; lst.append(which->kdeName(rev)); KRun::displayOpenWithDialog(lst, QApplication::activeWindow()); } void MainTreeWidget::slotSelectBrowsingRevision() { if (isWorkingCopy()) { return; } Rangeinput_impl::revision_range range; if (Rangeinput_impl::getRevisionRange(range, false)) { m_Data->m_remoteRevision = range.first; clear(); m_Data->m_Model->checkDirs(baseUri(), nullptr); emit changeCaption(baseUri() + QLatin1Char('@') + range.first.toString()); } } void MainTreeWidget::slotMakeTree() { QString what; SvnItem *k = SelectedOrMain(); if (k) { what = k->fullName(); } else if (!isWorkingCopy() && selectionCount() == 0) { what = baseUri(); } else { return; } svn::Revision rev(isWorkingCopy() ? svn::Revision::WORKING : baseRevision()); m_Data->m_Model->svnWrapper()->makeTree(what, rev); } void MainTreeWidget::slotMakePartTree() { QString what; SvnItem *k = SelectedOrMain(); if (k) { what = k->fullName(); } else if (!isWorkingCopy() && selectionCount() == 0) { what = baseUri(); } else { return; } Rangeinput_impl::revision_range range; if (Rangeinput_impl::getRevisionRange(range)) { svn::Revision rev(isWorkingCopy() ? svn::Revision::UNDEFINED : baseRevision()); m_Data->m_Model->svnWrapper()->makeTree(what, rev, range.first, range.second); } } void MainTreeWidget::slotLock() { const SvnItemList lst = SelectionList(); if (lst.isEmpty()) { KMessageBox::error(this, i18n("Nothing selected for unlock")); return; } QPointer dlg(new KSvnSimpleOkDialog(QStringLiteral("locking_log_msg"))); dlg->setWindowTitle(i18nc("@title:window", "Lock Message")); dlg->setWithCancelButton(); Commitmsg_impl *ptr(new Commitmsg_impl(dlg)); ptr->initHistory(); ptr->hideDepth(true); ptr->keepsLocks(false); QCheckBox *_stealLock = new QCheckBox(i18n("Steal lock?")); ptr->addItemWidget(_stealLock); dlg->addWidget(ptr); if (dlg->exec() != QDialog::Accepted) { if (dlg) ptr->saveHistory(true); delete dlg; return; } QString logMessage = ptr->getMessage(); bool steal = _stealLock->isChecked(); ptr->saveHistory(false); QStringList displist; for (int i = 0; i < lst.count(); ++i) { displist.append(lst[i]->fullName()); } m_Data->m_Model->svnWrapper()->makeLock(displist, logMessage, steal); refreshCurrentTree(); delete dlg; } /*! \fn MainTreeWidget::slotUnlock() */ void MainTreeWidget::slotUnlock() { const SvnItemList lst = SelectionList(); if (lst.isEmpty()) { KMessageBox::error(this, i18n("Nothing selected for unlock")); return; } KMessageBox::ButtonCode res = KMessageBox::questionYesNoCancel(this, i18n("Break lock or ignore missing locks?"), i18n("Unlocking items")); if (res == KMessageBox::Cancel) { return; } bool breakit = res == KMessageBox::Yes; QStringList displist; for (int i = 0; i < lst.count(); ++i) { displist.append(lst[i]->fullName()); } m_Data->m_Model->svnWrapper()->makeUnlock(displist, breakit); refreshCurrentTree(); } void MainTreeWidget::slotDisplayLastDiff() { SvnItem *kitem = Selected(); QString what; if (isWorkingCopy()) { QDir::setCurrent(baseUri()); } svn::Revision end = svn::Revision::PREV; if (!kitem) { if (isWorkingCopy()) { kitem = m_Data->m_Model->firstRootChild(); if (!kitem) { return; } what = relativePath(kitem); } else { what = baseUri(); } } else { what = relativePath(kitem); } svn::Revision start; svn::InfoEntry inf; if (!kitem) { // it has to have an item when in working copy, so we know we are in repository view. if (!m_Data->m_Model->svnWrapper()->singleInfo(what, baseRevision(), inf)) { return; } start = inf.cmtRev(); } else { start = kitem->cmtRev(); } if (!isWorkingCopy()) { if (!m_Data->m_Model->svnWrapper()->singleInfo(what, start.revnum() - 1, inf)) { return; } end = inf.cmtRev(); } m_Data->m_Model->svnWrapper()->makeDiff(what, end, what, start, realWidget()); } void MainTreeWidget::slotSimpleBaseDiff() { simpleWcDiff(Selected(), svn::Revision::BASE, svn::Revision::WORKING); } void MainTreeWidget::slotDirSimpleBaseDiff() { simpleWcDiff(DirSelected(), svn::Revision::BASE, svn::Revision::WORKING); } void MainTreeWidget::slotSimpleHeadDiff() { simpleWcDiff(Selected(), svn::Revision::WORKING, svn::Revision::HEAD); } void MainTreeWidget::simpleWcDiff(SvnItem *kitem, const svn::Revision &first, const svn::Revision &second) { QString what; if (isWorkingCopy()) { QDir::setCurrent(baseUri()); } if (!kitem) { what = QLatin1Char('.'); } else { what = relativePath(kitem); } // only possible on working copies - so we may say this values m_Data->m_Model->svnWrapper()->makeDiff(what, first, second, svn::Revision::UNDEFINED, kitem ? kitem->isDir() : true); } void MainTreeWidget::slotDiffRevisions() { SvnItem *k = Selected(); QString what; if (isWorkingCopy()) { QDir::setCurrent(baseUri()); } if (!k) { what = (isWorkingCopy() ? "." : baseUri()); } else { what = relativePath(k); } Rangeinput_impl::revision_range range; if (Rangeinput_impl::getRevisionRange(range)) { svn::Revision _peg = (isWorkingCopy() ? svn::Revision::WORKING : baseRevision()); m_Data->m_Model->svnWrapper()->makeDiff(what, range.first, range.second, _peg, k ? k->isDir() : true); } } void MainTreeWidget::slotDiffPathes() { SvnItemList lst; QObject *tr = sender(); bool unique = false; if (tr == filesActions()->action(QStringLiteral("make_svn_diritemsdiff"))) { unique = true; lst = DirSelectionList(); } else { lst = SelectionList(); } if (lst.count() != 2 || (!unique && !uniqueTypeSelected())) { return; } SvnItem *k1 = lst.at(0); SvnItem *k2 = lst.at(1); QString w1, w2; svn::Revision r1; if (isWorkingCopy()) { QDir::setCurrent(baseUri()); w1 = relativePath(k1); w2 = relativePath(k2); r1 = svn::Revision::WORKING; } else { w1 = k1->fullName(); w2 = k2->fullName(); r1 = baseRevision(); } m_Data->m_Model->svnWrapper()->makeDiff(w1, r1, w2, r1); } void MainTreeWidget::slotInfo() { svn::Revision rev(isWorkingCopy() ? svn::Revision::UNDEFINED : baseRevision()); if (!isWorkingCopy()) { rev = baseRevision(); } SvnItemList lst = SelectionList(); if (lst.isEmpty()) { if (!isWorkingCopy()) { QStringList _sl(baseUri()); m_Data->m_Model->svnWrapper()->makeInfo(_sl, rev, svn::Revision::UNDEFINED, Kdesvnsettings::info_recursive()); } else { lst.append(SelectedOrMain()); } } if (!lst.isEmpty()) { m_Data->m_Model->svnWrapper()->makeInfo(lst, rev, rev, Kdesvnsettings::info_recursive()); } } void MainTreeWidget::slotBlame() { SvnItem *k = Selected(); if (!k) { return; } svn::Revision start(svn::Revision::START); svn::Revision end(svn::Revision::HEAD); m_Data->m_Model->svnWrapper()->makeBlame(start, end, k); } void MainTreeWidget::slotRangeBlame() { SvnItem *k = Selected(); if (!k) { return; } Rangeinput_impl::revision_range range; if (Rangeinput_impl::getRevisionRange(range)) { m_Data->m_Model->svnWrapper()->makeBlame(range.first, range.second, k); } } void MainTreeWidget::_propListTimeout() { dispProperties(false); } void MainTreeWidget::slotDisplayProperties() { dispProperties(true); } void MainTreeWidget::refreshItem(SvnItemModelNode *node) { if (node) { m_Data->m_Model->refreshItem(node); } } void MainTreeWidget::slotChangeProperties(const svn::PropertiesMap &pm, const QStringList &dellist, const QString &path) { m_Data->m_Model->svnWrapper()->changeProperties(pm, dellist, path); SvnItemModelNode *which = SelectedNode(); if (which && which->fullName() == path) { m_Data->m_Model->refreshItem(which); dispProperties(true); } } void MainTreeWidget::dispProperties(bool force) { CursorStack a(Qt::BusyCursor); bool cache_Only = (!force && isNetworked() && !Kdesvnsettings::properties_on_remote_items()); svn::PathPropertiesMapListPtr pm; SvnItem *k = Selected(); if (!k || !k->isRealVersioned()) { emit sigProplist(svn::PathPropertiesMapListPtr(), false, false, QString("")); return; } svn::Revision rev(isWorkingCopy() ? svn::Revision::WORKING : baseRevision()); pm = m_Data->m_Model->svnWrapper()->propList(k->fullName(), rev, cache_Only); emit sigProplist(pm, isWorkingCopy(), k->isDir(), k->fullName()); } void MainTreeWidget::slotCat() { SvnItem *k = Selected(); if (!k) { return; } m_Data->m_Model->svnWrapper()->slotMakeCat(isWorkingCopy() ? svn::Revision::HEAD : baseRevision(), k->fullName(), k->shortName(), isWorkingCopy() ? svn::Revision::HEAD : baseRevision(), nullptr); } void MainTreeWidget::slotRevisionCat() { SvnItem *k = Selected(); if (!k) { return; } Rangeinput_impl::revision_range range; if (Rangeinput_impl::getRevisionRange(range, true, true)) { m_Data->m_Model->svnWrapper()->slotMakeCat(range.first, k->fullName(), k->shortName(), isWorkingCopy() ? svn::Revision::WORKING : baseRevision(), nullptr); } } void MainTreeWidget::slotResolved() { if (!isWorkingCopy()) { return; } SvnItem *which = SelectedOrMain(); if (!which) { return; } m_Data->m_Model->svnWrapper()->slotResolved(which->fullName()); which->refreshStatus(true); } void MainTreeWidget::slotTryResolve() { if (!isWorkingCopy()) { return; } SvnItem *which = Selected(); if (!which || which->isDir()) { return; } m_Data->m_Model->svnWrapper()->slotResolve(which->fullName()); } void MainTreeWidget::slotLeftDelete() { makeDelete(DirSelectionList()); } void MainTreeWidget::slotDelete() { makeDelete(SelectionList()); } void MainTreeWidget::makeDelete(const SvnItemList &lst) { if (lst.isEmpty()) { KMessageBox::error(this, i18n("Nothing selected for delete")); return; } svn::Paths items; QStringList displist; QList kioList; for (const SvnItem *item : lst) { if (!item->isRealVersioned()) { QUrl _uri(QUrl::fromLocalFile(item->fullName())); kioList.append(_uri); } else { items.push_back(item->fullName()); } displist.append(item->fullName()); } QPointer dlg(new DeleteForm(displist, QApplication::activeModalWidget())); dlg->showExtraButtons(isWorkingCopy() && !items.isEmpty()); if (dlg->exec() == QDialog::Accepted) { bool force = dlg->force_delete(); bool keep = dlg->keep_local(); WidgetBlockStack st(this); if (!kioList.isEmpty()) { KIO::Job *aJob = KIO::del(kioList); if (!aJob->exec()) { KJobWidgets::setWindow(aJob, this); aJob->uiDelegate()->showErrorMessage(); delete dlg; return; } } if (!items.isEmpty()) { m_Data->m_Model->svnWrapper()->makeDelete(svn::Targets(items), keep, force); } refreshCurrentTree(); } delete dlg; } void MainTreeWidget::internalDrop(const QList &_lst, Qt::DropAction action, const QModelIndex &index) { if (_lst.isEmpty()) { return; } QList lst = _lst; QString target; QString nProto; if (!isWorkingCopy()) { nProto = svn::Url::transformProtokoll(lst[0].scheme()); } QList::iterator it = lst.begin(); for (; it != lst.end(); ++it) { (*it).setQuery(QUrlQuery()); if (!nProto.isEmpty()) (*it).setScheme(nProto); } if (index.isValid()) { SvnItemModelNode *node = static_cast(index.internalPointer()); target = node->fullName(); } else { target = baseUri(); } if (action == Qt::MoveAction) { m_Data->m_Model->svnWrapper()->makeMove(lst, target); } else if (action == Qt::CopyAction) { m_Data->m_Model->svnWrapper()->makeCopy(lst, target, (isWorkingCopy() ? svn::Revision::UNDEFINED : baseRevision())); } refreshCurrentTree(); } void MainTreeWidget::slotUrlDropped(const QList &_lst, Qt::DropAction action, const QModelIndex &index, bool intern) { if (_lst.isEmpty()) { return; } if (intern) { internalDrop(_lst, action, index); return; } QUrl target; if (index.isValid()) { SvnItemModelNode *node = static_cast(index.internalPointer()); target = node->Url(); } else { target = baseUriAsUrl(); } if (baseUri().isEmpty()) { openUrl(_lst[0]); return; } QString path = _lst[0].path(); QFileInfo fi(path); if (!isWorkingCopy()) { if (!fi.isDir()) { target.setPath(target.path() + QLatin1Char('/') + _lst[0].fileName()); } slotImportIntoDir(_lst[0].toLocalFile(), target, fi.isDir()); } else { WidgetBlockStack w(this); KIO::Job *job = KIO::copy(_lst, target); connect(job, &KJob::result, this, &MainTreeWidget::slotCopyFinished); job->exec(); } } void MainTreeWidget::slotCopyFinished(KJob *_job) { KIO::CopyJob *job = dynamic_cast(_job); if (!job) { return; } bool ok = true; if (job->error()) { KJobWidgets::setWindow(job, this); job->uiDelegate()->showErrorMessage(); ok = false; } if (ok) { const QList lst = job->srcUrls(); const QString base = job->destUrl().toLocalFile() + QLatin1Char('/'); svn::Paths tmp; tmp.reserve(lst.size()); for (const QUrl &url : lst) tmp.push_back(svn::Path(base + url.fileName())); m_Data->m_Model->svnWrapper()->addItems(tmp, svn::DepthInfinity); } refreshCurrentTree(); } void MainTreeWidget::stopLogCache() { QAction *temp = filesActions()->action(QStringLiteral("update_log_cache")); m_Data->m_Model->svnWrapper()->stopFillCache(); if (temp) { temp->setText(i18n("Update log cache")); } } void MainTreeWidget::slotUpdateLogCache() { if (baseUri().length() > 0 && m_Data->m_Model->svnWrapper()->doNetworking()) { QAction *temp = filesActions()->action(QStringLiteral("update_log_cache")); if (!m_Data->m_Model->svnWrapper()->threadRunning(SvnActions::fillcachethread)) { m_Data->m_Model->svnWrapper()->startFillCache(baseUri()); if (temp) { temp->setText(i18n("Stop updating the log cache")); } } else { m_Data->m_Model->svnWrapper()->stopFillCache(); if (temp) { temp->setText(i18n("Update log cache")); } } } } void MainTreeWidget::slotMkBaseDirs() { bool isopen = !baseUri().isEmpty(); if (!isopen) { return; } QString parentDir = baseUri(); svn::Paths targets; targets.append(svn::Path(parentDir + QLatin1String("/trunk"))); targets.append(svn::Path(parentDir + QLatin1String("/branches"))); targets.append(svn::Path(parentDir + QLatin1String("/tags"))); QString msg = i18n("Automatic generated base layout by kdesvn"); isopen = m_Data->m_Model->svnWrapper()->makeMkdir(svn::Targets(targets), msg); if (isopen) { refreshCurrentTree(); } } void MainTreeWidget::slotMkdir() { SvnItemModelNode *k = SelectedNode(); QString parentDir; if (k) { if (!k->isDir()) { KMessageBox::sorry(nullptr, i18n("May not make subdirectories of a file")); return; } parentDir = k->fullName(); } else { parentDir = baseUri(); } QString ex = m_Data->m_Model->svnWrapper()->makeMkdir(parentDir); if (!ex.isEmpty()) { m_Data->m_Model->refreshDirnode(static_cast(k), true, true); } } void MainTreeWidget::slotRename() { copy_move(true); } void MainTreeWidget::slotCopy() { copy_move(false); } void MainTreeWidget::copy_move(bool move) { if (isWorkingCopy() && SelectedNode() == m_Data->m_Model->firstRootChild()) { return; } bool ok; SvnItemModelNode *which = SelectedNode(); if (!which) { return; } QString nName = CopyMoveView_impl::getMoveCopyTo(&ok, move, which->fullName(), baseUri(), this); if (!ok) { return; } if (move) { m_Data->m_Model->svnWrapper()->makeMove(which->fullName(), nName); } else { m_Data->m_Model->svnWrapper()->makeCopy(which->fullName(), nName, isWorkingCopy() ? svn::Revision::HEAD : baseRevision()); } } void MainTreeWidget::slotCleanupAction() { if (!isWorkingCopy()) { return; } SvnItemModelNode *which = SelectedNode(); if (!which) { which = m_Data->m_Model->firstRootChild(); } if (!which || !which->isDir()) { return; } if (m_Data->m_Model->svnWrapper()->makeCleanup(which->fullName())) { which->refreshStatus(true); } } void MainTreeWidget::slotMergeRevisions() { if (!isWorkingCopy()) { return; } SvnItemModelNode *which = SelectedNode(); if (!which) { return; } bool force, dry, rec, irelated, useExternal, allowmixedrevs; Rangeinput_impl::revision_range range; if (!MergeDlg_impl::getMergeRange(range, &force, &rec, &irelated, &dry, &useExternal, &allowmixedrevs, this)) { return; } if (!useExternal) { m_Data->m_Model->svnWrapper()->slotMergeWcRevisions(which->fullName(), range.first, range.second, rec, !irelated, force, dry, allowmixedrevs); } else { m_Data->m_Model->svnWrapper()->slotMergeExternal(which->fullName(), which->fullName(), which->fullName(), range.first, range.second, isWorkingCopy() ? svn::Revision::UNDEFINED : m_Data->m_remoteRevision, rec); } refreshItem(which); if (which->isDir()) { m_Data->m_Model->refreshDirnode(static_cast(which), true, false); } } void MainTreeWidget::slotMerge() { SvnItemModelNode *which = SelectedNode(); QString src1, src2, target; if (isWorkingCopy()) { if (m_Data->merge_Target.isEmpty()) { target = which ? which->fullName() : baseUri(); } else { target = m_Data->merge_Target; } src1 = m_Data->merge_Src1; } else { if (m_Data->merge_Src1.isEmpty()) { src1 = which ? which->fullName() : baseUri(); } else { src1 = m_Data->merge_Src1; } target = m_Data->merge_Target; } src2 = m_Data->merge_Src2; QPointer dlg(new KSvnSimpleOkDialog(QStringLiteral("merge_dialog"))); dlg->setWindowTitle(i18nc("@title:window", "Merge")); dlg->setWithCancelButton(); dlg->setHelp(QLatin1String("merging-items")); MergeDlg_impl *ptr(new MergeDlg_impl(dlg)); ptr->setDest(target); ptr->setSrc1(src1); ptr->setSrc2(src1); dlg->addWidget(ptr); if (dlg->exec() == QDialog::Accepted) { src1 = ptr->Src1(); src2 = ptr->Src2(); if (src2.isEmpty()) { src2 = src1; } target = ptr->Dest(); m_Data->merge_Src2 = src2; m_Data->merge_Src1 = src1; m_Data->merge_Target = target; bool force = ptr->force(); bool dry = ptr->dryrun(); bool rec = ptr->recursive(); bool irelated = ptr->ignorerelated(); bool useExternal = ptr->useExtern(); bool allowmixedrevs = ptr->allowmixedrevs(); bool recordOnly = ptr->recordOnly(); Rangeinput_impl::revision_range range = ptr->getRange(); bool reintegrate = ptr->reintegrate(); if (!useExternal) { m_Data->m_Model->svnWrapper()->slotMerge(src1, src2, target, range.first, range.second, isWorkingCopy() ? svn::Revision::UNDEFINED : m_Data->m_remoteRevision, rec, !irelated, force, dry, recordOnly, reintegrate, allowmixedrevs); } else { m_Data->m_Model->svnWrapper()->slotMergeExternal(src1, src2, target, range.first, range.second, isWorkingCopy() ? svn::Revision::UNDEFINED : m_Data->m_remoteRevision, rec); } if (isWorkingCopy()) { // refreshItem(which); // refreshRecursive(which); refreshCurrentTree(); } } delete dlg; enableActions(); } void MainTreeWidget::slotRelocate() { if (!isWorkingCopy()) { return; } SvnItem *k = SelectedOrMain(); if (!k) { KMessageBox::error(nullptr, i18n("Error getting entry to relocate")); return; } const QString path = k->fullName(); const QUrl fromUrl = k->Url(); QPointer dlg(new KSvnSimpleOkDialog(QStringLiteral("relocate_dlg"))); dlg->setWindowTitle(i18nc("@title:window", "Relocate Path %1", path)); dlg->setWithCancelButton(); CheckoutInfo_impl *ptr(new CheckoutInfo_impl(dlg)); ptr->setStartUrl(fromUrl); ptr->disableAppend(true); ptr->disableTargetDir(true); ptr->disableRange(true); ptr->disableOpen(true); ptr->hideDepth(true); ptr->hideOverwrite(true); dlg->addWidget(ptr); bool done = false; if (dlg->exec() == QDialog::Accepted) { if (!ptr->reposURL().isValid()) { KMessageBox::error(QApplication::activeModalWidget(), i18n("Invalid url given!"), i18n("Relocate path %1", path)); delete dlg; return; } done = m_Data->m_Model->svnWrapper()->makeRelocate(fromUrl, ptr->reposURL(), path, ptr->overwrite(), ptr->ignoreExternals()); } delete dlg; if (done) { refreshItem(k->sItem()); } } void MainTreeWidget::slotImportDirsIntoCurrent() { slotImportIntoCurrent(true); } /*! \fn MainTreeWidget::slotImportIntoCurrent() */ void MainTreeWidget::slotImportIntoCurrent(bool dirs) { if (selectionCount() > 1) { KMessageBox::error(this, i18n("Cannot import into multiple targets")); return; } QUrl targetDir; if (selectionCount() == 0) { if (isNetworked()) targetDir = QUrl(baseUri()); else targetDir = QUrl::fromLocalFile(baseUri()); } else { targetDir = SelectedNode()->Url(); } QString source; if (dirs) { source = QFileDialog::getExistingDirectory(this, i18n("Import files from folder")); } else { source = QFileDialog::getOpenFileName(this, i18n("Import file"), QString()); } slotImportIntoDir(source, targetDir, dirs); } void MainTreeWidget::slotImportIntoDir(const QString &source, const QUrl &_targetUri, bool dirs) { QString sourceUri = source; while (sourceUri.endsWith(QLatin1Char('/'))) { sourceUri.chop(1); } if (sourceUri.isEmpty()) { return; } if (_targetUri.isEmpty()) { return; } QUrl targetUri(_targetUri); QPointer dlg(new KSvnSimpleOkDialog(QStringLiteral("import_log_msg"))); dlg->setWindowTitle(i18nc("@title:window", "Import Log")); dlg->setWithCancelButton(); Commitmsg_impl *ptr = nullptr; Importdir_logmsg *ptr2 = nullptr; if (dirs) { ptr2 = new Importdir_logmsg(dlg); ptr2->createDirboxDir(QLatin1Char('"') + QFileInfo(sourceUri).fileName() + QLatin1Char('"')); ptr = ptr2; } else { ptr = new Commitmsg_impl(dlg); } ptr->initHistory(); dlg->addWidget(ptr); if (dlg->exec() != QDialog::Accepted) { if (dlg) { ptr->saveHistory(true); delete dlg; } return; } QString logMessage = ptr->getMessage(); svn::Depth rec = ptr->getDepth(); ptr->saveHistory(false); if (dirs && ptr2 && ptr2->createDir()) { targetUri.setPath(targetUri.path() + QLatin1Char('/') + QFileInfo(sourceUri).fileName()); } if (ptr2) { m_Data->m_Model->svnWrapper()->slotImport(sourceUri, targetUri, logMessage, rec, ptr2->noIgnore(), ptr2->ignoreUnknownNodes()); } else { m_Data->m_Model->svnWrapper()->slotImport(sourceUri, targetUri, logMessage, rec, false, false); } if (!isWorkingCopy()) { if (selectionCount() == 0) { refreshCurrentTree(); } else { m_Data->m_Model->refreshItem(SelectedNode()); } } delete dlg; } void MainTreeWidget::slotChangeToRepository() { if (!isWorkingCopy()) { return; } SvnItemModelNode *k = m_Data->m_Model->firstRootChild(); /* huh... */ if (!k) { return; } svn::InfoEntry i; if (!m_Data->m_Model->svnWrapper()->singleInfo(k->Url().toString(), svn::Revision::UNDEFINED, i)) { return; } if (i.reposRoot().isEmpty()) { KMessageBox::sorry(QApplication::activeModalWidget(), i18n("Could not retrieve repository of working copy."), i18n("SVN Error")); } else { emit sigSwitchUrl(i.reposRoot()); } } void MainTreeWidget::slotCheckNewItems() { if (!isWorkingCopy()) { KMessageBox::sorry(nullptr, i18n("Only in working copy possible."), i18n("Error")); return; } if (selectionCount() > 1) { KMessageBox::sorry(nullptr, i18n("Only on single folder possible"), i18n("Error")); return; } SvnItem *w = SelectedOrMain(); if (!w) { KMessageBox::sorry(nullptr, i18n("Sorry - internal error"), i18n("Error")); return; } m_Data->m_Model->svnWrapper()->checkAddItems(w->fullName(), true); } void MainTreeWidget::refreshCurrent(SvnItem *cur) { if (!cur || !cur->sItem()) { refreshCurrentTree(); return; } QCoreApplication::processEvents(); setUpdatesEnabled(false); if (cur->isDir()) { m_Data->m_Model->refreshDirnode(static_cast(cur->sItem())); } else { m_Data->m_Model->refreshItem(cur->sItem()); } setUpdatesEnabled(true); m_TreeView->viewport()->repaint(); } void MainTreeWidget::slotReinitItem(SvnItem *item) { if (!item) { return; } SvnItemModelNode *k = item->sItem(); if (!k) { return; } m_Data->m_Model->refreshItem(k); if (k->isDir()) { m_Data->m_Model->clearNodeDir(static_cast(k)); } } void MainTreeWidget::keyPressEvent(QKeyEvent *event) { if ((event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) && !event->isAutoRepeat()) { QModelIndex index = SelectedIndex(); if (index.isValid()) { itemActivated(index, true); return; } } QWidget::keyPressEvent(event); } void MainTreeWidget::slotItemExpanded(const QModelIndex &) { } void MainTreeWidget::slotItemsInserted(const QModelIndex &) { m_Data->m_resizeColumnsTimer.start(50); } void MainTreeWidget::slotDirSelectionChanged(const QItemSelection &_item, const QItemSelection &) { const QModelIndexList _indexes = _item.indexes(); switch (DirselectionCount()) { case 1: m_DirTreeView->setStatusTip(i18n("Hold Ctrl key while click on selected item for unselect")); break; case 2: m_DirTreeView->setStatusTip(i18n("See context menu for more actions")); break; case 0: m_DirTreeView->setStatusTip(i18n("Click for navigate")); break; default: m_DirTreeView->setStatusTip(i18n("Navigation")); break; } if (_indexes.size() >= 1) { const QModelIndex _t = m_Data->srcDirInd(_indexes.at(0)); if (m_Data->m_Model->canFetchMore(_t)) { WidgetBlockStack st(m_TreeView); WidgetBlockStack st2(m_DirTreeView); m_Data->m_Model->fetchMore(_t); } if (Kdesvnsettings::show_navigation_panel()) { m_TreeView->setRootIndex(m_Data->m_SortModel->mapFromSource(_t)); } // Display relative path (including name of the checkout) in the titlebar auto item = m_Data->m_Model->nodeForIndex(_t); if (item) { const QString repoBasePath = baseUri(); const QString relativePath = item->fullName().mid(repoBasePath.lastIndexOf('/') + 1); emit changeCaption(relativePath); } } else { checkSyncTreeModel(); } if (m_TreeView->selectionModel()->hasSelection()) { m_TreeView->selectionModel()->clearSelection(); } else { enableActions(); } resizeAllColumns(); } void MainTreeWidget::checkSyncTreeModel() { // make sure that the treeview shows the contents of the selected directory in the directory tree view // it can go out of sync when the dir tree model has no current index - then we use the first entry // or when the filter settings are changed QModelIndex curIdxDir = m_DirTreeView->currentIndex(); if (!curIdxDir.isValid() && m_Data->m_DirSortModel->columnCount() > 0) { m_DirTreeView->setCurrentIndex(m_Data->m_DirSortModel->index(0, 0)); curIdxDir = m_DirTreeView->currentIndex(); } const QModelIndex curIdxBase = m_Data->srcDirInd(curIdxDir); m_TreeView->setRootIndex(m_Data->m_SortModel->mapFromSource(curIdxBase)); } void MainTreeWidget::slotCommit() { m_Data->m_Model->svnWrapper()->doCommit(SelectionList()); } void MainTreeWidget::slotDirCommit() { m_Data->m_Model->svnWrapper()->doCommit(DirSelectionList()); } void MainTreeWidget::slotDirUpdate() { const SvnItemList which = DirSelectionList(); svn::Paths what; if (which.isEmpty()) { what.append(svn::Path(baseUri())); } else { what.reserve(which.size()); for (const SvnItem *item : which) what.append(svn::Path(item->fullName())); } m_Data->m_Model->svnWrapper()->makeUpdate(svn::Targets(what), svn::Revision::HEAD, svn::DepthUnknown); } void MainTreeWidget::slotRefreshItem(const QString &path) { const QModelIndex idx = m_Data->m_Model->findIndex(path); if (!idx.isValid()) return; m_Data->m_Model->emitDataChangedRow(idx); } void MainTreeWidget::checkUseNavigation(bool startup) { bool use = Kdesvnsettings::show_navigation_panel(); if (use) { checkSyncTreeModel(); } else { // tree view is the only visible view, make sure to display all m_TreeView->setRootIndex(QModelIndex()); m_TreeView->expand(QModelIndex()); } m_TreeView->setExpandsOnDoubleClick(!use); m_TreeView->setRootIsDecorated(!use); m_TreeView->setItemsExpandable(!use); QList si; if (use) { if (!startup) { si = m_ViewSplitter->sizes(); if (si.size() == 2 && si[0] < 5) { si[0] = 200; m_ViewSplitter->setSizes(si); } } } else { si << 0 << 300; m_ViewSplitter->setSizes(si); } } void MainTreeWidget::slotRepositorySettings() { if (baseUri().length() == 0) { return; } svn::InfoEntry inf; if (!m_Data->m_Model->svnWrapper()->singleInfo(baseUri(), baseRevision(), inf)) { return; } if (inf.reposRoot().isEmpty()) { KMessageBox::sorry(QApplication::activeModalWidget(), i18n("Could not retrieve repository."), i18n("SVN Error")); } else { DbSettings::showSettings(inf.reposRoot().toString(), this); } } void MainTreeWidget::slotRightProperties() { SvnItem *k = Selected(); if (!k) { return; } m_Data->m_Model->svnWrapper()->editProperties(k, isWorkingCopy() ? svn::Revision::WORKING : svn::Revision::HEAD); } void MainTreeWidget::slotLeftProperties() { SvnItem *k = DirSelected(); if (!k) { return; } m_Data->m_Model->svnWrapper()->editProperties(k, isWorkingCopy() ? svn::Revision::WORKING : svn::Revision::HEAD); } void MainTreeWidget::slotDirRecProperty() { SvnItem *k = DirSelected(); if (!k) { return; } KMessageBox::information(this, i18n("Not yet implemented"), i18n("Edit property recursively")); }