graphwidget.cpp

来自「linux下的sourceinsight」· C++ 代码 · 共 1,163 行 · 第 1/3 页

CPP
1,163
字号
/*************************************************************************** * * Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: *  * 1. Redistributions of source code must retain the above copyright *    notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright *    notice, this list of conditions and the following disclaimer in the *    documentation and/or other materials provided with the distribution. *  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ***************************************************************************/#include <math.h>#include <stdlib.h>#include <qfile.h>#include <qpainter.h>#include <qtooltip.h>#include <klocale.h>#include <kmessagebox.h>#include "graphwidget.h"#include "graphnode.h"#include "graphedge.h"#include "kscopeconfig.h"#include "queryviewdlg.h"#include "encoder.h"#include "progressdlg.h"const char* GRAPH_DIRS[] = { "TB", "LR", "BT", "RL" };const char TMP_TMPL[] = "/tmp/kscope_dot.XXXXXX";#define TMP_TMPL_SIZE	(sizeof(TMP_TMPL) + 1)/** * Displays a tool tip on the graph. * Note that we cannot use the standard tool tip class here, since graph * items are neither rectangular nor is their position known in advance. * @author	Elad Lahav */class GraphTip : public QToolTip{public:	/**	 * Class constructor.	 * @param	pWidget	Owner graph widget	 */	GraphTip(GraphWidget* pWidget) : QToolTip(pWidget->viewport()),		m_pGraphWidget(pWidget) {}		/**	 * Class destructor.	 */	virtual ~GraphTip() {}	protected:	/**	 * Called when the pre-conditions for a tool tip are met.	 * Asks the owner for a tip to display and, if one is returned, shows	 * the tool tip.	 * @param	ptPos	Current mouse position	 */	virtual void maybeTip(const QPoint& ptPos) {		QString sText;		QRect rc;				// Display a tip, if required by the owner		sText = m_pGraphWidget->getTip(ptPos, rc);		if (sText != QString::null)			tip(rc, sText);	}	private:	/** The parent graph widget. */	GraphWidget* m_pGraphWidget;};/** * Provides a menu separator with text. * The separator is added with QMenuData::insertItem(QWidget*). * @author Elad Lahav */class MenuLabel : public QLabel{public:	/**	 * Class constructor.	 * @param	sText	The text to display	 * @param	pParent	The parent widget	 */	MenuLabel(const QString& sText, QWidget* pParent) :		QLabel(sText, pParent) {		// Set the appropriate visual properties		setFrameShape(MenuBarPanel);		setAlignment(AlignHCenter | AlignVCenter);		setIndent(0);	}};ArrowInfo GraphWidget::s_ai;/** * Class constructor. * @param	pParent	The parent widget * @param	szName	The widget's name */GraphWidget::GraphWidget(QWidget* pParent, const char* szName) :	QCanvasView(pParent, szName),	m_progress(this),	m_dot(this),	m_dZoom(1.0),	m_nMaxNodeDegree(10), // will be overriden by CallTreeDlg	m_nMultiCallNum(0),	m_pProgressDlg(NULL)					 {	// Automatically delete nodes when they are removed	m_dictNodes.setAutoDelete(true);		// Create a canvas	setCanvas(new QCanvas(this));	canvas()->setBackgroundColor(Config().getColor(KScopeConfig::GraphBack));	// Create a persistent Cscope process	m_pCscope = new CscopeFrontend();		// Add records output by the Cscope process	connect(m_pCscope, SIGNAL(dataReady(FrontendToken*)), this,		SLOT(slotDataReady(FrontendToken*)));		// Display query progress information	connect(m_pCscope, SIGNAL(progress(int, int)), this,		SLOT(slotProgress(int, int)));				// Draw the graph when the process has finished	connect(m_pCscope, SIGNAL(finished(uint)), this,		SLOT(slotFinished(uint)));		// Show a multi-call node when a query results in too many records	connect(m_pCscope, SIGNAL(aborted()), this,		SLOT(slotAborted()));		// Redraw the graph when Dot exits	connect(&m_dot, SIGNAL(finished(uint)), this, SLOT(slotDotFinished()));			// Create the node popup menu	m_pNodePopup = new QPopupMenu(this);		m_pNodePopup->insertItem(new MenuLabel(i18n("<b>Called Functions</b>"),		m_pNodePopup));	m_pNodePopup->insertItem(i18n("Show"), this,		SLOT(slotShowCalled()));	m_pNodePopup->insertItem(i18n("List/Filter..."), this,		SLOT(slotListCalled()));	m_pNodePopup->insertItem(i18n("Hide"), this,		SLOT(slotHideCalled()));		m_pNodePopup->insertItem(new MenuLabel(i18n("<b>Calling Functions</b>"),		m_pNodePopup));	m_pNodePopup->insertItem(i18n("Show"), this,		SLOT(slotShowCalling()));	m_pNodePopup->insertItem(i18n("List/Filter..."), this,		SLOT(slotListCalling()));	m_pNodePopup->insertItem(i18n("Hide"), this,		SLOT(slotHideCalling()));		m_pNodePopup->insertItem(new MenuLabel(i18n("<b>This Function</b>"),		m_pNodePopup));	m_pNodePopup->insertItem(i18n("Find Definition"), this,		SLOT(slotFindDef()));	m_pNodePopup->insertItem(i18n("Remove"), this, SLOT(slotRemoveNode()));			// Create the multi-call node popup menu	m_pMultiCallPopup = new QPopupMenu(this);	m_pMultiCallPopup->insertItem(i18n("List..."), this,		SLOT(slotMultiCallDetails()));	m_pMultiCallPopup->insertSeparator();	m_pMultiCallPopup->insertItem(i18n("Remove"), this,		SLOT(slotRemoveNode()));		// Create the edge menu	m_pEdgePopup = new QPopupMenu(this);	m_pEdgePopup->insertItem(i18n("Open Call"), this, SLOT(slotOpenCall()));			(void)new GraphTip(this);}/** * Class destructor. */GraphWidget::~GraphWidget(){}/** * Creates a root node for the graph. * The root node defines the connected component which is always displayed * (all other connected components are removed when they are no longer * strongly connected to the root). * @param	sFunc	The function name for the root node */void GraphWidget::setRoot(const QString& sFunc){	// Insert a new node to the graph	addNode(sFunc);	draw();}/** * Locates a node by its name and, if one does not exist, creates a new node. * @param	sFunc	The name of a function * @return	The node corresponding to the given name */GraphNode* GraphWidget::addNode(const QString& sFunc, bool bMultiCall){	GraphNode* pNode;		// Look for a node with the given name	if ((pNode = m_dictNodes.find(sFunc)) == NULL) {		// Node not found, create it		pNode = new GraphNode(canvas(), sFunc, bMultiCall);		m_dictNodes.insert(sFunc, pNode);	}		// Return the found/created node	return pNode;}/** * Adds a call to the graph. * A call is made between two functions, the caller and the callee. * @param	data	Contains information on the call */void GraphWidget::addCall(const CallData& data){	GraphNode* pCaller, * pCallee;	GraphEdge* pEdge;		// Find the relevant nodes (create new nodes if necessary)	pCaller = addNode(data.m_sCaller);	pCallee = addNode(data.m_sCallee);		// Create a new edge	pEdge = pCaller->addOutEdge(pCallee);	pEdge->setCallInfo(data.m_sFile, data.m_sLine, data.m_sText);}/** * Creates a special node representing multiple calls to/from a function. * Such a node is creates when the number of calls to/from a function exceeds * a certain number. Thus the graph does not become too cluttered. * A multiple call node can be replaced by some/all of the actual calls by * using the "Details..." action in the node's popup menu. * @param	sFunc	The parent function * @param	bCalled	true if the multiple calls are called from that function, *					false if they are calling the function */void GraphWidget::addMultiCall(const QString& sFunc, bool bCalled){	QString sMulti;	GraphNode* pCaller, * pCallee;	GraphEdge* pEdge;		// Create a unique name for the new node.	// The name is of the form 0XXX, where XXX is a hexadecimal number. 	// We assume that no function starts with a digit, and that there are no	// more than 0xfff multi-call nodes in the graph.	sMulti.sprintf("0%.3x", m_nMultiCallNum);	m_nMultiCallNum = (m_nMultiCallNum + 1) & 0xfff;		// Find the relevant nodes (create new nodes if necessary)	if (bCalled) {		pCaller = addNode(sFunc);		pCallee = addNode(sMulti, true);	}	else {		pCaller = addNode(sMulti, true);		pCallee = addNode(sFunc);	}		// Create a new edge	pEdge = pCaller->addOutEdge(pCallee);}/** * Draws the graph on the canvas using the graphviz engine. * A new canvas is created, so all items need to be regenerated. * TODO: Can we use the same canvas and only reposition existing items? */void GraphWidget::draw(){	QWMatrix mtx;	char szTempFile[TMP_TMPL_SIZE];	int nFd;	FILE* pFile;		// Do nothing if drawing process has already started	if (m_dot.isRunning())		return;		// Apply the zoom factor	mtx.scale(m_dZoom, m_dZoom);	setWorldMatrix(mtx);	// Do not draw until the Dot process finishes	setUpdatesEnabled(false);	// Open a temporary file	strcpy(szTempFile, TMP_TMPL);	nFd = mkstemp(szTempFile);	if ((pFile = fdopen(nFd, "w")) == NULL)		return;		// Remember the file name (so it can be deleted later)	m_sDrawFilePath = szTempFile;		// Write the graph contents to the temporary file	{		QTextStream str(pFile, IO_WriteOnly);		write(str, "graph", "--", false);	}		// Close the file	fclose(pFile);		// Draw the graph	if (m_dot.run(szTempFile)) {		// Create the progress dialogue		m_pProgressDlg = new ProgressDlg(i18n("KScope"), 			i18n("Generating graph, please wait"), this);		m_pProgressDlg->setMinimumDuration(1000);		m_pProgressDlg->setValue(0);				// TODO:		// Implement cancel (what do we do when the drawing process is 		// terminated, even though the nodes and edges were already added by		// Cscope?)		// m_pProgressDlg->setAllowCancel(true);	}}/** * Stores a graph on a file. * The file uses the dot language to describe the graph. * @param	pFile	An open file to write to */void GraphWidget::save(FILE* pFile){	// Write the graph using the dot language	QTextStream str(pFile, IO_WriteOnly);	write(str, "digraph", "->", true);}/** * Exports a graph to a dot file. * @param	sFile	The full path of the file to export to */void GraphWidget::save(const QString& sFile){	QFile file(sFile);		// Open/create the file	if (!file.open(IO_WriteOnly))		return;			QTextStream str(&file);	write(str, "digraph", "->", false);}/** * Changes the zoom factor. * @param	bIn	true to zoom in, false to zoom out */void GraphWidget::zoom(bool bIn){	QWMatrix mtx;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?