📄 revgraphview.cpp
字号:
/*************************************************************************** * Copyright (C) 2006-2007 by Rajko Albrecht * * ral@alwins-world.de * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/#include "revgraphview.h"#include "graphtreelabel.h"#include "pannerview.h"#include "graphtree_defines.h"#include "src/settings/kdesvnsettings.h"#include "../stopdlg.h"#include "src/svnqt/client.hpp"#include <kapp.h>#include <kdebug.h>#include <ktempfile.h>#include <ktempdir.h>#include <kprocess.h>#include <klocale.h>#include <kfiledialog.h>#include <kmessagebox.h>#include <qtooltip.h>#include <qwmatrix.h>#include <qpopupmenu.h>#include <qpainter.h>#include <qregexp.h>#include <math.h>#define LABEL_WIDTH 160#define LABEL_HEIGHT 90class GraphViewTip:public QToolTip{public: GraphViewTip( QWidget* p ):QToolTip(p) {} virtual ~GraphViewTip(){}protected: void maybeTip( const QPoint & );};void GraphViewTip::maybeTip( const QPoint & pos){ if (!parentWidget()->inherits( "RevGraphView" )) return; RevGraphView* cgv = (RevGraphView*)parentWidget(); QPoint cPos = cgv->viewportToContents(pos); QCanvasItemList l = cgv->canvas()->collisions(cPos); if (l.count() == 0) return; QCanvasItem* i = l.first(); if (i->rtti() == GRAPHTREE_LABEL) { GraphTreeLabel*tl = (GraphTreeLabel*)i; QString nm = tl->nodename(); QString tipStr = cgv->toolTip(nm); if (tipStr.length()>0) { QPoint vPosTL = cgv->contentsToViewport(i->boundingRect().topLeft()); QPoint vPosBR = cgv->contentsToViewport(i->boundingRect().bottomRight()); tip(QRect(vPosTL, vPosBR), tipStr); } }}RevGraphView::RevGraphView(QObject*aListener,svn::Client*_client,QWidget * parent, const char * name, WFlags f) : QCanvasView(parent,name,f){ m_Canvas = 0L; m_Client = _client; m_Listener = aListener; dotTmpFile = 0; m_Selected = 0; renderProcess = 0; m_Marker = 0; m_Tip = new GraphViewTip(this); m_CompleteView = new PannerView(this); m_CompleteView->setVScrollBarMode(QScrollView::AlwaysOff); m_CompleteView->setHScrollBarMode(QScrollView::AlwaysOff); m_CompleteView->raise(); m_CompleteView->hide(); connect(this, SIGNAL(contentsMoving(int,int)), this, SLOT(contentsMovingSlot(int,int))); connect(m_CompleteView, SIGNAL(zoomRectMoved(int,int)), this, SLOT(zoomRectMoved(int,int))); connect(m_CompleteView, SIGNAL(zoomRectMoveFinished()), this, SLOT(zoomRectMoveFinished())); m_LastAutoPosition = TopLeft; _isMoving = false; _noUpdateZoomerPos = false; m_LabelMap[""]="";}RevGraphView::~RevGraphView(){ setCanvas(0); delete m_Canvas; delete dotTmpFile; delete m_CompleteView; delete m_Tip; delete renderProcess;}void RevGraphView::showText(const QString&s){ clear(); m_Canvas = new QCanvas(QApplication::desktop()->width(), QApplication::desktop()->height()); QCanvasText* t = new QCanvasText(s, m_Canvas); t->move(5, 5); t->show(); center(0,0); setCanvas(m_Canvas); m_Canvas->update(); m_CompleteView->hide();}void RevGraphView::clear(){ if (m_Selected) { m_Selected->setSelected(false); m_Selected=0; } if (m_Marker) { m_Marker->hide(); delete m_Marker; m_Marker=0; } if (!m_Canvas) return; delete m_Canvas; m_Canvas = 0; setCanvas(0); m_CompleteView->setCanvas(0);}void RevGraphView::beginInsert(){ viewport()->setUpdatesEnabled(false);}void RevGraphView::endInsert(){ if (m_Canvas) { _cvZoom = 0; updateSizes(); m_Canvas->update(); } viewport()->setUpdatesEnabled(true);}void RevGraphView::readDotOutput(KProcess*,char * buffer,int buflen){ dotOutput+=QString::fromLocal8Bit(buffer, buflen);}void RevGraphView::dotExit(KProcess*p){ if (p!=renderProcess)return; // remove line breaks when lines to long QRegExp endslash("\\\\\\n"); dotOutput.replace(endslash,""); double scale = 1.0, scaleX = 1.0, scaleY = 1.0; double dotWidth, dotHeight; QTextStream* dotStream; dotStream = new QTextStream(dotOutput, IO_ReadOnly); QString line,cmd; int lineno=0; clear(); beginInsert(); /* mostly taken from kcachegrind */ while (1) { line = dotStream->readLine(); if (line.isNull()) break; lineno++; if (line.isEmpty()) continue; QTextStream lineStream(line, IO_ReadOnly); lineStream >> cmd; if (cmd == "stop") {break; } if (cmd == "graph") { lineStream >> scale >> dotWidth >> dotHeight; scaleX = scale * 60; scaleY = scale * 100; int w = (int)(scaleX * dotWidth); int h = (int)(scaleY * dotHeight); _xMargin = 50; if (w < QApplication::desktop()->width()) _xMargin += (QApplication::desktop()->width()-w)/2; _yMargin = 50; if (h < QApplication::desktop()->height()) _yMargin += (QApplication::desktop()->height()-h)/2; m_Canvas = new QCanvas(int(w+2*_xMargin), int(h+2*_yMargin)); continue; } if ((cmd != "node") && (cmd != "edge")) { kdWarning() << "Ignoring unknown command '" << cmd << "' from dot (" << dotTmpFile->name() << ":" << lineno << ")" << endl; continue; } if (cmd=="node") { QString nodeName, label; QString _x,_y,_w,_h; double x, y, width, height; lineStream >> nodeName >> _x >> _y >> _w >> _h; x=_x.toDouble(); y=_y.toDouble(); width=_w.toDouble(); height=_h.toDouble(); // better here 'cause dot may scramble utf8 labels so we regenerate it better // and do not read it in. label = getLabelstring(nodeName); int xx = (int)(scaleX * x + _xMargin); int yy = (int)(scaleY * (dotHeight - y) + _yMargin); int w = (int)(scaleX * width); int h = (int)(scaleY * height); QRect r(xx-w/2, yy-h/2, w, h); GraphTreeLabel*t=new GraphTreeLabel(label,nodeName,r,m_Canvas); if (isStart(nodeName)) { ensureVisible(r.x(),r.y()); } t->setBgColor(getBgColor(nodeName)); t->setZ(1.0); t->show(); m_NodeList[nodeName]=t; } else { QString node1Name, node2Name, label; QString _x,_y; double x, y; QPointArray pa; int points, i; lineStream >> node1Name >> node2Name; lineStream >> points; pa.resize(points); for (i=0;i<points;++i) { if (lineStream.atEnd()) break; lineStream >> _x >> _y; x=_x.toDouble(); y=_y.toDouble(); int xx = (int)(scaleX * x + _xMargin); int yy = (int)(scaleY * (dotHeight - y) + _yMargin); if (0) qDebug(" P %d: ( %f / %f ) => ( %d / %d)", i, x, y, xx, yy); pa.setPoint(i, xx, yy); } if (i < points) { qDebug("CallGraphView: Can't read %d spline points (%d)", points, lineno); continue; } GraphEdge * n = new GraphEdge(m_Canvas); QColor arrowColor = Qt::black; n->setPen(QPen(arrowColor,1)); n->setControlPoints(pa,false); n->setZ(0.5); n->show(); /* arrow */ QPoint arrowDir; int indexHead = -1; QMap<QString,GraphTreeLabel*>::Iterator it; it = m_NodeList.find(node2Name); if (it!=m_NodeList.end()) { it.data()->setSource(node1Name); } it = m_NodeList.find(node1Name); if (it!=m_NodeList.end()) { GraphTreeLabel*tlab = it.data(); if (tlab) { QPoint toCenter = tlab->rect().center(); int dx0 = pa.point(0).x() - toCenter.x(); int dy0 = pa.point(0).y() - toCenter.y(); int dx1 = pa.point(points-1).x() - toCenter.x(); int dy1 = pa.point(points-1).y() - toCenter.y(); if (dx0*dx0+dy0*dy0 > dx1*dx1+dy1*dy1) { // start of spline is nearer to call target node indexHead=-1; while(arrowDir.isNull() && (indexHead<points-2)) { indexHead++; arrowDir = pa.point(indexHead) - pa.point(indexHead+1); } } } } if (arrowDir.isNull()) { indexHead = points; // sometimes the last spline points from dot are the same... while(arrowDir.isNull() && (indexHead>1)) { indexHead--; arrowDir = pa.point(indexHead) - pa.point(indexHead-1); } } if (!arrowDir.isNull()) { arrowDir *= 10.0/sqrt(double(arrowDir.x()*arrowDir.x() + arrowDir.y()*arrowDir.y())); QPointArray a(3); a.setPoint(0, pa.point(indexHead) + arrowDir); a.setPoint(1, pa.point(indexHead) + QPoint(arrowDir.y()/2, -arrowDir.x()/2)); a.setPoint(2, pa.point(indexHead) + QPoint(-arrowDir.y()/2, arrowDir.x()/2)); GraphEdgeArrow* aItem = new GraphEdgeArrow(n,m_Canvas); aItem->setPoints(a); aItem->setBrush(arrowColor); aItem->setZ(1.5); aItem->show();// sItem->setArrow(aItem); } } } if (!m_Canvas) { QString s = i18n("Error running the graph layouting tool.\n"); s += i18n("Please check that 'dot' is installed (package GraphViz)."); showText(s); } else { setCanvas(m_Canvas); m_CompleteView->setCanvas(m_Canvas); } endInsert(); delete p; renderProcess=0;}bool RevGraphView::isStart(const QString&nodeName)const{ bool res = false; trevTree::ConstIterator it; it = m_Tree.find(nodeName); if (it==m_Tree.end()) { return res; } switch (it.data().Action) { case 'A': res = true; break; } return res;}char RevGraphView::getAction(const QString&nodeName)const{ trevTree::ConstIterator it; it = m_Tree.find(nodeName); if (it==m_Tree.end()) { return (char)0; } return it.data().Action;}QColor RevGraphView::getBgColor(const QString&nodeName)const{ trevTree::ConstIterator it; it = m_Tree.find(nodeName); QColor res = Qt::white; if (it==m_Tree.end()) { return res; } switch (it.data().Action) { case 'D': res = Kdesvnsettings::tree_delete_color(); break; case 'R': case 'M': res = Kdesvnsettings::tree_modify_color(); break; case 'A': res = Kdesvnsettings::tree_add_color(); break; case 'C': case 1: res = Kdesvnsettings::tree_copy_color(); break; case 2: res = Kdesvnsettings::tree_rename_color(); break; default: res = Kdesvnsettings::tree_modify_color(); break; } return res;}const QString&RevGraphView::getLabelstring(const QString&nodeName){ QMap<QString,QString>::ConstIterator nIt; nIt = m_LabelMap.find(nodeName); if (nIt!=m_LabelMap.end()) { return nIt.data(); } trevTree::ConstIterator it1; it1 = m_Tree.find(nodeName); if (it1==m_Tree.end()) { return m_LabelMap[""]; } QString res; switch (it1.data().Action) { case 'D': res = i18n("Deleted at revision %1").arg(it1.data().rev); break; case 'A': res = i18n("Added at revision %1 as %2") .arg(it1.data().rev) .arg(it1.data().name); break; case 'C': case 1: res = i18n("Copied to %1 at revision %2").arg(it1.data().name).arg(it1.data().rev); break; case 2: res = i18n("Renamed to %1 at revision %2").arg(it1.data().name).arg(it1.data().rev); break; case 'M': res = i18n("Modified at revision %1").arg(it1.data().rev); break; case 'R': res = i18n("Replaced at revision %1").arg(it1.data().rev); break; default: res=i18n("Revision %1").arg(it1.data().rev); break; } m_LabelMap[nodeName]=res; return m_LabelMap[nodeName];}void RevGraphView::dumpRevtree(){ delete dotTmpFile; clear(); dotOutput = ""; dotTmpFile = new KTempFile(QString::null,".dot"); dotTmpFile->setAutoDelete(true); QTextStream* stream = dotTmpFile->textStream(); if (!stream) { showText(i18n("Could not open tempfile %1 for writing.").arg(dotTmpFile->name())); return; } *stream << "digraph \"callgraph\" {\n"; *stream << " bgcolor=\"transparent\";\n"; int dir = Kdesvnsettings::tree_direction(); *stream << QString(" rankdir=\""); switch (dir) { case 3: *stream << "TB"; break; case 2: *stream << "RL"; break; case 1: *stream << "BT"; break; case 0: default: *stream << "LR"; break; } *stream << "\";\n";
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -