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 + -
显示快捷键?