diff --git a/controlflowgraphfiledialog.cpp b/controlflowgraphfiledialog.cpp index f02daf9..bfdd14b 100644 --- a/controlflowgraphfiledialog.cpp +++ b/controlflowgraphfiledialog.cpp @@ -1,179 +1,186 @@ /*************************************************************************** * Copyright 2009 Sandro Andrade * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library 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 Library 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 "controlflowgraphfiledialog.h" #include #include #include #include "ui_controlflowgraphexportconfiguration.h" using namespace KDevelop; ControlFlowGraphFileDialog::ControlFlowGraphFileDialog(QWidget *parent, OpeningMode mode) : QFileDialog(parent, i18n("Export Control Flow Graph")), m_configurationWidget(0) { QStringList mimeTypes; mimeTypes << "image/png" << "image/jpeg" << "image/gif" << "image/svg+xml" << "image/svg+xml-compressed" << "application/x-dia-diagram" << "image/x-xfig" << "application/pdf" << "text/vnd.graphviz"; setMimeTypeFilters(mimeTypes); setAcceptMode(QFileDialog::AcceptSave); setConfirmOverwrite(true); setFileMode(QFileDialog::AnyFile); if (mode != NoConfigurationButtons) { m_configurationWidget = new Ui::ControlFlowGraphExportConfiguration; QWidget *widget = new QWidget; m_configurationWidget->setupUi(widget); m_configurationWidget->controlFlowFunctionRadioButton->setIcon(QIcon::fromTheme("flag-blue")); m_configurationWidget->controlFlowClassRadioButton->setIcon(QIcon::fromTheme("flag-green")); m_configurationWidget->controlFlowNamespaceRadioButton->setIcon(QIcon::fromTheme("flag-red")); m_configurationWidget->clusteringClassCheckBox->setIcon(QIcon::fromTheme("code-class")); m_configurationWidget->clusteringNamespaceCheckBox->setIcon(QIcon::fromTheme("namespace")); m_configurationWidget->clusteringProjectCheckBox->setIcon(QIcon::fromTheme("folder-development")); m_configurationWidget->limitMaxLevelCheckBox->setIcon(QIcon::fromTheme("zoom-fit-height")); m_configurationWidget->drawIncomingArcsCheckBox->setIcon(QIcon::fromTheme("draw-arrow-down")); m_configurationWidget->useFolderNameCheckBox->setIcon(QIcon::fromTheme("folder-favorites")); m_configurationWidget->useShortNamesCheckBox->setIcon(QIcon::fromTheme("application-x-arc")); - connect(m_configurationWidget->controlFlowFunctionRadioButton, SIGNAL(toggled(bool)), SLOT(setControlFlowMode(bool))); - connect(m_configurationWidget->controlFlowClassRadioButton, SIGNAL(toggled(bool)), SLOT(setControlFlowMode(bool))); - connect(m_configurationWidget->controlFlowNamespaceRadioButton, SIGNAL(toggled(bool)), SLOT(setControlFlowMode(bool))); - - connect(m_configurationWidget->clusteringClassCheckBox, SIGNAL(stateChanged(int)), SLOT(setClusteringModes(int))); - connect(m_configurationWidget->clusteringNamespaceCheckBox, SIGNAL(stateChanged(int)), SLOT(setClusteringModes(int))); - connect(m_configurationWidget->clusteringProjectCheckBox, SIGNAL(stateChanged(int)), SLOT(setClusteringModes(int))); - - connect(m_configurationWidget->limitMaxLevelCheckBox, SIGNAL(stateChanged(int)), SLOT(slotLimitMaxLevelChanged(int))); + connect(m_configurationWidget->controlFlowFunctionRadioButton, &QRadioButton::toggled, + this, &ControlFlowGraphFileDialog::setControlFlowMode); + connect(m_configurationWidget->controlFlowClassRadioButton, &QRadioButton::toggled, + this, &ControlFlowGraphFileDialog::setControlFlowMode); + connect(m_configurationWidget->controlFlowNamespaceRadioButton, &QRadioButton::toggled, + this, &ControlFlowGraphFileDialog::setControlFlowMode); + + connect(m_configurationWidget->clusteringClassCheckBox, &QCheckBox::stateChanged, + this, &ControlFlowGraphFileDialog::setClusteringModes); + connect(m_configurationWidget->clusteringNamespaceCheckBox, &QCheckBox::stateChanged, + this, &ControlFlowGraphFileDialog::setClusteringModes); + connect(m_configurationWidget->clusteringProjectCheckBox, &QCheckBox::stateChanged, + this, &ControlFlowGraphFileDialog::setClusteringModes); + + connect(m_configurationWidget->limitMaxLevelCheckBox, &QCheckBox::stateChanged, + this, &ControlFlowGraphFileDialog::slotLimitMaxLevelChanged); if (ICore::self()->projectController()->projectCount() > 0) { m_configurationWidget->clusteringProjectCheckBox->setEnabled(true); m_configurationWidget->useFolderNameCheckBox->setEnabled(true); } // TODO: care about native non-Qt filedialog if (layout()) { layout()->addWidget(widget); } else { qWarning() << "No access to layout of QFileDialog, cannot add custom widget"; } } } ControlFlowGraphFileDialog::~ControlFlowGraphFileDialog() { delete m_configurationWidget; } DUChainControlFlow::ControlFlowMode ControlFlowGraphFileDialog::controlFlowMode() const { return (m_configurationWidget->controlFlowFunctionRadioButton->isChecked() ? DUChainControlFlow::ControlFlowFunction : ((m_configurationWidget->controlFlowClassRadioButton->isChecked()) ? DUChainControlFlow::ControlFlowClass : DUChainControlFlow::ControlFlowNamespace) ); } DUChainControlFlow::ClusteringModes ControlFlowGraphFileDialog::clusteringModes() const { DUChainControlFlow::ClusteringModes clusteringModes; if (m_configurationWidget->clusteringClassCheckBox->isChecked()) clusteringModes |= DUChainControlFlow::ClusteringClass; if (m_configurationWidget->clusteringNamespaceCheckBox->isChecked()) clusteringModes |= DUChainControlFlow::ClusteringNamespace; if (m_configurationWidget->clusteringProjectCheckBox->isChecked()) clusteringModes |= DUChainControlFlow::ClusteringProject; return clusteringModes; } int ControlFlowGraphFileDialog::maxLevel() const { if (m_configurationWidget->limitMaxLevelCheckBox->isChecked()) return m_configurationWidget->maxLevelSpinBox->value(); else return 0; } bool ControlFlowGraphFileDialog::useFolderName() const { return m_configurationWidget->useFolderNameCheckBox->isChecked(); } bool ControlFlowGraphFileDialog::useShortNames() const { return m_configurationWidget->useShortNamesCheckBox->isChecked(); } bool ControlFlowGraphFileDialog::drawIncomingArcs() const { return m_configurationWidget->drawIncomingArcsCheckBox->isChecked(); } void ControlFlowGraphFileDialog::setControlFlowMode(bool checked) { if (checked) { QRadioButton *radioButton = qobject_cast(sender()); if (radioButton->objectName() == "controlFlowFunctionRadioButton") { m_configurationWidget->clusteringClassCheckBox->setEnabled(true); m_configurationWidget->clusteringNamespaceCheckBox->setEnabled(true); } if (radioButton->objectName() == "controlFlowClassRadioButton") { m_configurationWidget->clusteringClassCheckBox->setChecked(false); m_configurationWidget->clusteringClassCheckBox->setEnabled(false); m_configurationWidget->clusteringNamespaceCheckBox->setEnabled(true); } if (radioButton->objectName() == "controlFlowNamespaceRadioButton") { m_configurationWidget->clusteringClassCheckBox->setChecked(false); m_configurationWidget->clusteringClassCheckBox->setEnabled(false); m_configurationWidget->clusteringNamespaceCheckBox->setChecked(false); m_configurationWidget->clusteringNamespaceCheckBox->setEnabled(false); } } } void ControlFlowGraphFileDialog::setClusteringModes(int state) { Q_UNUSED(state); m_configurationWidget->useShortNamesCheckBox->setEnabled(m_configurationWidget->clusteringClassCheckBox->isChecked() || m_configurationWidget->clusteringNamespaceCheckBox->isChecked() || m_configurationWidget->clusteringProjectCheckBox->isChecked()); } void ControlFlowGraphFileDialog::slotLimitMaxLevelChanged(int state) { m_configurationWidget->maxLevelSpinBox->setEnabled((state == Qt::Checked) ? true:false); } diff --git a/controlflowgraphview.cpp b/controlflowgraphview.cpp index e9484b1..9629c46 100644 --- a/controlflowgraphview.cpp +++ b/controlflowgraphview.cpp @@ -1,298 +1,321 @@ /*************************************************************************** * Copyright 2009 Sandro Andrade * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library 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 Library 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 "controlflowgraphview.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "duchaincontrolflow.h" #include "dotcontrolflowgraph.h" #include "controlflowgraphfiledialog.h" #include "kdevcontrolflowgraphviewplugin.h" using namespace KDevelop; ControlFlowGraphView::ControlFlowGraphView(KDevControlFlowGraphViewPlugin *plugin, QWidget *parent) : QWidget(parent), m_plugin(plugin), m_part(0), m_dotControlFlowGraph(new DotControlFlowGraph), m_duchainControlFlow(new DUChainControlFlow(m_dotControlFlowGraph)), m_graphLocked(false) { setupUi(this); KPluginFactory *factory = KPluginLoader("kgraphviewerpart").factory(); if (!factory) { QMessageBox::critical((QWidget *) m_plugin->core()->uiController()->activeMainWindow(), i18n("Could not load the KGraphViewer KPart"), i18n("Unable to load KGraphViewer, please verify that a compatible version is installed.")); return; } m_part = factory->create("kgraphviewerpart", this); if (!m_part) { QMessageBox::critical((QWidget *) m_plugin->core()->uiController()->activeMainWindow(), i18n("Could not load the KGraphViewer KPart"), i18n("Unable to create a KGraphViewer instance, please verify that a compatible version is installed.")); return; } QMetaObject::invokeMethod(m_part, "setReadWrite"); verticalLayout->addWidget(m_part->widget()); modeFunctionToolButton->setIcon(QIcon::fromTheme("code-function")); modeClassToolButton->setIcon(QIcon::fromTheme("code-class")); modeNamespaceToolButton->setIcon(QIcon::fromTheme("namespace")); clusteringClassToolButton->setIcon(QIcon::fromTheme("code-class")); clusteringNamespaceToolButton->setIcon(QIcon::fromTheme("namespace")); clusteringProjectToolButton->setIcon(QIcon::fromTheme("folder-development")); useFolderNameToolButton->setIcon(QIcon::fromTheme("folder-favorites")); drawIncomingArcsToolButton->setIcon(QIcon::fromTheme("draw-arrow-down")); maxLevelToolButton->setIcon(QIcon::fromTheme("zoom-fit-height")); exportToolButton->setIcon(QIcon::fromTheme("document-export")); m_duchainControlFlow->setMaxLevel(2); birdseyeToolButton->setIcon(QIcon::fromTheme("edit-find")); usesHoverToolButton->setIcon(QIcon::fromTheme("input-mouse")); zoominToolButton->setIcon(QIcon::fromTheme("zoom-in")); zoomoutToolButton->setIcon(QIcon::fromTheme("zoom-out")); if (ICore::self()->projectController()->projectCount() > 0) setProjectButtonsEnabled(true); useShortNamesToolButton->setIcon(QIcon::fromTheme("application-x-arc")); updateLockIcon(lockControlFlowGraphToolButton->isChecked()); // Control flow mode buttons signals - connect(modeFunctionToolButton, SIGNAL(toggled(bool)), SLOT(setControlFlowFunction(bool))); - connect(modeClassToolButton, SIGNAL(toggled(bool)), SLOT(setControlFlowClass(bool))); - connect(modeNamespaceToolButton, SIGNAL(toggled(bool)), SLOT(setControlFlowNamespace(bool))); + connect(modeFunctionToolButton, &QToolButton::toggled, + this, &ControlFlowGraphView::setControlFlowFunction); + connect(modeClassToolButton, &QToolButton::toggled, + this, &ControlFlowGraphView::setControlFlowClass); + connect(modeNamespaceToolButton, &QToolButton::toggled, + this, &ControlFlowGraphView::setControlFlowNamespace); // Clustering buttons signals - connect(clusteringClassToolButton, SIGNAL(toggled(bool)), SLOT(setClusteringClass(bool))); - connect(clusteringNamespaceToolButton, SIGNAL(toggled(bool)), SLOT(setClusteringNamespace(bool))); - connect(clusteringProjectToolButton, SIGNAL(toggled(bool)), SLOT(setClusteringProject(bool))); + connect(clusteringClassToolButton, &QToolButton::toggled, + this, &ControlFlowGraphView::setClusteringClass); + connect(clusteringNamespaceToolButton, &QToolButton::toggled, + this, &ControlFlowGraphView::setClusteringNamespace); + connect(clusteringProjectToolButton, &QToolButton::toggled, + this, &ControlFlowGraphView::setClusteringProject); // Configuration buttons signals - connect(maxLevelSpinBox, SIGNAL(valueChanged(int)), SLOT(setMaxLevel(int))); - connect(maxLevelToolButton, SIGNAL(toggled(bool)), SLOT(setUseMaxLevel(bool))); - connect(drawIncomingArcsToolButton, SIGNAL(toggled(bool)), SLOT(setDrawIncomingArcs(bool))); - connect(useFolderNameToolButton, SIGNAL(toggled(bool)), SLOT(setUseFolderName(bool))); - connect(useShortNamesToolButton, SIGNAL(toggled(bool)), SLOT(setUseShortNames(bool))); - connect(lockControlFlowGraphToolButton, SIGNAL(toggled(bool)), SLOT(updateLockIcon(bool))); + connect(maxLevelSpinBox, static_cast(&QSpinBox::valueChanged), + this, &ControlFlowGraphView::setMaxLevel); + connect(maxLevelToolButton, &QToolButton::toggled, + this, &ControlFlowGraphView::setUseMaxLevel); + connect(drawIncomingArcsToolButton, &QToolButton::toggled, + this, &ControlFlowGraphView::setDrawIncomingArcs); + connect(useFolderNameToolButton, &QToolButton::toggled, + this, &ControlFlowGraphView::setUseFolderName); + connect(useShortNamesToolButton, &QToolButton::toggled, + this, &ControlFlowGraphView::setUseShortNames); + connect(lockControlFlowGraphToolButton, &QToolButton::toggled, + this, &ControlFlowGraphView::updateLockIcon); // Left buttons signals - connect(zoomoutToolButton, SIGNAL(clicked()), m_part->actionCollection()->action("view_zoom_out"), SIGNAL(triggered())); - connect(zoominToolButton, SIGNAL(clicked()), m_part->actionCollection()->action("view_zoom_in"), SIGNAL(triggered())); - m_part->actionCollection()->action("view_bev_enabled")->setIcon(QIcon::fromTheme("edit-find")); - m_part->actionCollection()->action("view_bev_enabled")->setChecked(false); - birdseyeToolButton->setDefaultAction(m_part->actionCollection()->action("view_bev_enabled")); + auto partActionCollection = m_part->actionCollection(); + connect(zoomoutToolButton, &QToolButton::clicked, + partActionCollection->action("view_zoom_out"), &QAction::triggered); + connect(zoominToolButton, &QToolButton::clicked, + partActionCollection->action("view_zoom_in"), &QAction::triggered); + partActionCollection->action("view_bev_enabled")->setIcon(QIcon::fromTheme("edit-find")); + partActionCollection->action("view_bev_enabled")->setChecked(false); + birdseyeToolButton->setDefaultAction(partActionCollection->action("view_bev_enabled")); + // TODO: create and use declared extra interface of graphviewer part + // instead of blind string-based connection connect(m_part, SIGNAL(selectionIs(const QList, const QPoint&)), m_duchainControlFlow, SLOT(slotGraphElementSelected(QList,QPoint))); connect(m_part, SIGNAL(hoverEnter(QString)), m_duchainControlFlow, SLOT(slotEdgeHover(QString))); - connect(exportToolButton, SIGNAL(clicked()), SLOT(exportControlFlowGraph())); - connect(usesHoverToolButton, SIGNAL(toggled(bool)), m_duchainControlFlow, SLOT(setShowUsesOnEdgeHover(bool))); + connect(exportToolButton, &QToolButton::clicked, + this, &ControlFlowGraphView::exportControlFlowGraph); + connect(usesHoverToolButton, &QToolButton::toggled, + m_duchainControlFlow, &DUChainControlFlow::setShowUsesOnEdgeHover); // Make sure we have a graph before we hook up signals to act on it m_dotControlFlowGraph->prepareNewGraph(); // Graph generation signals + // TODO: create and use declared extra interface of graphviewer part + // instead of blind string-based connection connect(m_dotControlFlowGraph, SIGNAL(loadLibrary(graph_t*)), m_part, SLOT(slotLoadLibrary(graph_t*))); - connect(m_duchainControlFlow, SIGNAL(startingJob()), SLOT(startingJob())); - connect(m_duchainControlFlow, SIGNAL(jobDone()), SLOT(graphDone())); + connect(m_duchainControlFlow, &DUChainControlFlow::startingJob, + this, &ControlFlowGraphView::startingJob); + connect(m_duchainControlFlow, static_cast(&DUChainControlFlow::jobDone), + this, &ControlFlowGraphView::graphDone); m_plugin->registerToolView(this); } ControlFlowGraphView::~ControlFlowGraphView() { m_plugin->unRegisterToolView(this); delete m_duchainControlFlow; delete m_dotControlFlowGraph; delete m_part; } void ControlFlowGraphView::refreshGraph() { m_duchainControlFlow->refreshGraph(); } void ControlFlowGraphView::newGraph() { m_duchainControlFlow->newGraph(); } void ControlFlowGraphView::setProjectButtonsEnabled(bool enabled) { useFolderNameToolButton->setEnabled(enabled); clusteringProjectToolButton->setEnabled(enabled); } void ControlFlowGraphView::cursorPositionChanged(KTextEditor::View *view, const KTextEditor::Cursor &cursor) { m_duchainControlFlow->cursorPositionChanged(view, cursor); } void ControlFlowGraphView::startingJob() { setEnabled(false); } void ControlFlowGraphView::graphDone() { setEnabled(true); } void ControlFlowGraphView::exportControlFlowGraph() { QPointer fileDialog; if ((fileDialog = m_plugin->exportControlFlowGraph(ControlFlowGraphFileDialog::NoConfigurationButtons)) && !fileDialog->selectedFiles().isEmpty()) { m_dotControlFlowGraph->exportGraph(fileDialog->selectedFiles()[0]); KMessageBox::information(this, i18n("Control flow graph exported"), i18n("Export Control Flow Graph")); } } void ControlFlowGraphView::updateLockIcon(bool checked) { lockControlFlowGraphToolButton->setIcon(QIcon::fromTheme(checked ? "document-encrypt":"document-decrypt")); lockControlFlowGraphToolButton->setToolTip(checked ? i18n("Unlock control flow graph"):i18n("Lock control flow graph")); m_duchainControlFlow->setLocked(checked); m_graphLocked = checked; if (!checked) m_duchainControlFlow->refreshGraph(); } void ControlFlowGraphView::setControlFlowClass(bool checked) { if (checked) { m_duchainControlFlow->setControlFlowMode(DUChainControlFlow::ControlFlowClass); m_duchainControlFlow->refreshGraph(); clusteringClassToolButton->setChecked(false); clusteringClassToolButton->setEnabled(false); clusteringNamespaceToolButton->setEnabled(true); } } void ControlFlowGraphView::setControlFlowFunction(bool checked) { if (checked) { m_duchainControlFlow->setControlFlowMode(DUChainControlFlow::ControlFlowFunction); m_duchainControlFlow->refreshGraph(); clusteringClassToolButton->setEnabled(true); clusteringNamespaceToolButton->setEnabled(true); } } void ControlFlowGraphView::setControlFlowNamespace(bool checked) { if (checked) { m_duchainControlFlow->setControlFlowMode(DUChainControlFlow::ControlFlowNamespace); m_duchainControlFlow->refreshGraph(); clusteringClassToolButton->setChecked(false); clusteringClassToolButton->setEnabled(false); clusteringNamespaceToolButton->setChecked(false); clusteringNamespaceToolButton->setEnabled(false); } } void ControlFlowGraphView::setClusteringClass(bool checked) { Q_UNUSED(checked); m_duchainControlFlow->setClusteringModes(m_duchainControlFlow->clusteringModes() ^ DUChainControlFlow::ClusteringClass); m_duchainControlFlow->refreshGraph(); useShortNamesToolButton->setEnabled(m_duchainControlFlow->clusteringModes() ? true:false); } void ControlFlowGraphView::setClusteringProject(bool checked) { Q_UNUSED(checked); m_duchainControlFlow->setClusteringModes(m_duchainControlFlow->clusteringModes() ^ DUChainControlFlow::ClusteringProject); m_duchainControlFlow->refreshGraph(); useShortNamesToolButton->setEnabled(m_duchainControlFlow->clusteringModes() ? true:false); } void ControlFlowGraphView::setClusteringNamespace(bool checked) { Q_UNUSED(checked); m_duchainControlFlow->setClusteringModes(m_duchainControlFlow->clusteringModes() ^ DUChainControlFlow::ClusteringNamespace); m_duchainControlFlow->refreshGraph(); useShortNamesToolButton->setEnabled(m_duchainControlFlow->clusteringModes() ? true:false); } void ControlFlowGraphView::setUseMaxLevel(bool checked) { maxLevelSpinBox->setEnabled(checked); setMaxLevel(checked ? maxLevelSpinBox->value():0); } void ControlFlowGraphView::setMaxLevel(int value) { m_duchainControlFlow->setMaxLevel(value); m_duchainControlFlow->refreshGraph(); } void ControlFlowGraphView::setDrawIncomingArcs(bool checked) { m_duchainControlFlow->setDrawIncomingArcs(checked); m_duchainControlFlow->refreshGraph(); } void ControlFlowGraphView::setUseFolderName(bool checked) { m_duchainControlFlow->setUseFolderName(checked); m_duchainControlFlow->refreshGraph(); } void ControlFlowGraphView::setUseShortNames(bool checked) { m_duchainControlFlow->setUseShortNames(checked); m_duchainControlFlow->refreshGraph(); } void ControlFlowGraphView::showEvent(QShowEvent *event) { Q_UNUSED(event); m_plugin->setActiveToolView(this); } void ControlFlowGraphView::hideEvent(QHideEvent *event) { Q_UNUSED(event); m_plugin->setActiveToolView(0); } diff --git a/duchaincontrolflow.cpp b/duchaincontrolflow.cpp index d689637..b48de29 100644 --- a/duchaincontrolflow.cpp +++ b/duchaincontrolflow.cpp @@ -1,620 +1,622 @@ /*************************************************************************** * Copyright 2009 Sandro Andrade * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library 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 Library 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 "duchaincontrolflow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dotcontrolflowgraph.h" #include "duchaincontrolflowjob.h" #include "controlflowgraphusescollector.h" #include "controlflowgraphnavigationwidget.h" Q_DECLARE_METATYPE(KDevelop::Use) using namespace KDevelop; DUChainControlFlow::DUChainControlFlow(DotControlFlowGraph* dotControlFlowGraph) : m_dotControlFlowGraph(dotControlFlowGraph), m_previousUppermostExecutableContext(IndexedDUContext()), m_currentView(0), m_currentProject(0), m_currentLevel(1), m_maxLevel(2), m_locked(false), m_drawIncomingArcs(true), m_useFolderName(true), m_useShortNames(true), m_ShowUsesOnEdgeHover(true), m_controlFlowMode(ControlFlowClass), m_clusteringModes(ClusteringNamespace), m_graphThreadRunning(false), m_abort(false), m_collector(0) { qRegisterMetaType("Use"); } DUChainControlFlow::~DUChainControlFlow() { KDevelop::ICore::self()->languageController()->backgroundParser()->revertAllRequests(this); delete m_collector; } void DUChainControlFlow::setControlFlowMode(ControlFlowMode controlFlowMode) { m_controlFlowMode = controlFlowMode; } void DUChainControlFlow::setClusteringModes(ClusteringModes clusteringModes) { m_clusteringModes = clusteringModes; } DUChainControlFlow::ClusteringModes DUChainControlFlow::clusteringModes() const { return m_clusteringModes; } void DUChainControlFlow::generateControlFlowForDeclaration(IndexedDeclaration idefinition, IndexedTopDUContext itopContext, IndexedDUContext iuppermostExecutableContext) { DUChainReadLocker lock(DUChain::lock()); Declaration *definition = idefinition.data(); if (!definition) return; TopDUContext *topContext = itopContext.data(); if (!topContext) return; DUContext *uppermostExecutableContext = iuppermostExecutableContext.data(); if (!uppermostExecutableContext) return; // Convert to a declaration in accordance with control flow mode (function, class or namespace) Declaration *nodeDefinition = declarationFromControlFlowMode(definition); QStringList containers; prepareContainers(containers, definition); QString shortName = shortNameFromContainers(containers, prependFolderNames(nodeDefinition)); if (m_maxLevel != 1 && !m_visitedFunctions.contains(idefinition) && nodeDefinition && nodeDefinition->internalContext()) { m_dotControlFlowGraph->foundRootNode(containers, (m_controlFlowMode == ControlFlowNamespace && nodeDefinition->internalContext() && nodeDefinition->internalContext()->type() != DUContext::Namespace) ? globalNamespaceOrFolderNames(nodeDefinition): shortName); ++m_currentLevel; m_visitedFunctions.insert(idefinition); m_identifierDeclarationMap[containers.join("") + shortName] = IndexedDeclaration(nodeDefinition); useDeclarationsFromDefinition(definition, topContext, uppermostExecutableContext); } if (m_abort) return; if (m_drawIncomingArcs) { Declaration *declaration = definition; if (declaration->isDefinition()) declaration = DUChainUtils::declarationForDefinition(declaration, topContext); if (declaration) { delete m_collector; m_collector = new ControlFlowGraphUsesCollector(declaration); m_collector->setProcessDeclarations(true); - connect(m_collector, SIGNAL(processFunctionCall(Declaration*, Declaration*, Use)), SLOT(processFunctionCall(Declaration*, Declaration*, Use))); + connect(m_collector, &ControlFlowGraphUsesCollector::processFunctionCall, + this, &DUChainControlFlow::processFunctionCall); m_collector->startCollecting(); } } m_dotControlFlowGraph->graphDone(); m_currentLevel = 1; } bool DUChainControlFlow::isLocked() { return m_locked; } void DUChainControlFlow::run() { DUChainReadLocker lock(DUChain::lock()); m_abort = false; generateControlFlowForDeclaration(m_definition, m_topContext, m_uppermostExecutableContext); } void DUChainControlFlow::cursorPositionChanged(KTextEditor::View *view, const KTextEditor::Cursor &cursor) { if (!m_graphThreadRunning) { if (m_locked) return; if (!view->document()) return; DUChainReadLocker lock(DUChain::lock()); TopDUContext *topContext = DUChainUtils::standardContextForUrl(view->document()->url()); if (!topContext) return; DUContext *context = topContext->findContextAt(topContext->transformToLocalRevision(cursor)); if (!context) { newGraph(); m_previousUppermostExecutableContext = IndexedDUContext(); return; } // If cursor is in a method arguments context change it to internal context if (context && context->type() == DUContext::Function && context->importers().size() == 1) context = context->importers()[0]; Declaration *declarationUnderCursor = DUChainUtils::itemUnderCursor(view->document()->url(), cursor).declaration; if (declarationUnderCursor && (!context || context->type() != DUContext::Other) && declarationUnderCursor->internalContext()) context = declarationUnderCursor->internalContext(); if (!context || context->type() != DUContext::Other) { // If there is a previous graph if (!(m_previousUppermostExecutableContext == IndexedDUContext())) { newGraph(); m_previousUppermostExecutableContext = IndexedDUContext(); } return; } m_currentContext = IndexedDUContext(context); m_currentView = view; m_topContext = IndexedTopDUContext(topContext); m_currentProject = ICore::self()->projectController()->findProjectForUrl(m_currentView->document()->url()); m_includeDirectories.clear(); // Invoke includeDirectories in advance. Running it in the background thread may crash because // of thread-safety issues in KConfig / CMakeUtils. if (m_currentProject) { KDevelop::ProjectBaseItem *project_item = m_currentProject->projectItem(); IBuildSystemManager *buildSystemManager = 0; if (project_item && (buildSystemManager = m_currentProject->buildSystemManager())) m_includeDirectories = buildSystemManager->includeDirectories(project_item); } // Navigate to uppermost executable context DUContext *uppermostExecutableContext = m_currentContext.data(); if (!uppermostExecutableContext) return; while (uppermostExecutableContext->parentContext() && uppermostExecutableContext->parentContext()->type() == DUContext::Other) uppermostExecutableContext = uppermostExecutableContext->parentContext(); // If cursor is in the same function definition if (IndexedDUContext(uppermostExecutableContext) == m_previousUppermostExecutableContext) return; m_previousUppermostExecutableContext = IndexedDUContext(uppermostExecutableContext); // Get the definition Declaration* definition = 0; if (!uppermostExecutableContext || !uppermostExecutableContext->owner()) return; else definition = uppermostExecutableContext->owner(); if (!definition) return; newGraph(); m_dotControlFlowGraph->prepareNewGraph(); m_definition = IndexedDeclaration(definition); m_uppermostExecutableContext = IndexedDUContext(uppermostExecutableContext); m_graphThreadRunning = true; DUChainControlFlowJob *job = new DUChainControlFlowJob(context->scopeIdentifier().toString(), this); - connect (job, SIGNAL(result(KJob*)), SLOT(jobDone(KJob*))); + connect(job, &DUChainControlFlowJob::result, + this, static_cast(&DUChainControlFlow::jobDone)); emit startingJob(); ICore::self()->runController()->registerJob(job); } else { qDebug() << "Control flow thread already running"; } } void DUChainControlFlow::processFunctionCall(Declaration *source, Declaration *target, const Use &use) { FunctionDefinition *calledFunctionDefinition; DUContext *calledFunctionContext; DUChainReadLocker lock(DUChain::lock()); // Convert to a declaration in accordance with control flow mode (function, class or namespace) Declaration *nodeSource = declarationFromControlFlowMode(source); Declaration *nodeTarget = declarationFromControlFlowMode(target); // Try to acquire the called function definition calledFunctionDefinition = FunctionDefinition::definition(target); QStringList sourceContainers, targetContainers; prepareContainers(sourceContainers, source); prepareContainers(targetContainers, target); QString sourceLabel = shortNameFromContainers(sourceContainers, (m_controlFlowMode == ControlFlowNamespace && (nodeSource->internalContext() && nodeSource->internalContext()->type() != DUContext::Namespace)) ? globalNamespaceOrFolderNames(nodeSource) : prependFolderNames(nodeSource)); QString targetLabel = shortNameFromContainers(targetContainers, (m_controlFlowMode == ControlFlowNamespace && (nodeTarget->internalContext() && nodeTarget->internalContext()->type() != DUContext::Namespace)) ? globalNamespaceOrFolderNames(nodeTarget) : prependFolderNames(nodeTarget)); QString sourceShortName = shortNameFromContainers(sourceContainers, prependFolderNames(nodeSource)); QString targetShortName = shortNameFromContainers(targetContainers, prependFolderNames(nodeTarget)); if (sender() && dynamic_cast(sender())) { sourceContainers.prepend(i18n("Uses of %1", targetLabel)); m_identifierDeclarationMap[sourceContainers.join("") + sourceShortName] = IndexedDeclaration(nodeSource); } IndexedDeclaration ideclaration = IndexedDeclaration(calledFunctionDefinition); m_dotControlFlowGraph->foundFunctionCall(sourceContainers, sourceLabel, targetContainers, targetLabel); if (calledFunctionDefinition) calledFunctionContext = calledFunctionDefinition->internalContext(); else { // Store method declaration for navigation m_identifierDeclarationMap[targetContainers.join("") + targetShortName] = IndexedDeclaration(nodeTarget); // Store use for edge inspection QPair pair(use.m_range, source->url()); if (!m_arcUsesMap.values(sourceLabel + "->" + targetLabel).contains(pair)) m_arcUsesMap.insertMulti(sourceLabel + "->" + targetLabel, pair); return; } // Store use for edge inspection QPair pair(use.m_range, source->url()); if (!m_arcUsesMap.values(sourceLabel + "->" + targetLabel).contains(pair)) m_arcUsesMap.insertMulti(sourceLabel + "->" + targetLabel, pair); // Store method definition for navigation m_identifierDeclarationMap[targetContainers.join("") + targetShortName] = IndexedDeclaration(declarationFromControlFlowMode(calledFunctionDefinition)); if (calledFunctionContext && (m_currentLevel < m_maxLevel || m_maxLevel == 0)) { // For prevent endless loop in recursive methods if (!m_visitedFunctions.contains(ideclaration)) { ++m_currentLevel; m_visitedFunctions.insert(ideclaration); // Recursive call for method invocation useDeclarationsFromDefinition(calledFunctionDefinition, calledFunctionDefinition->topContext(), calledFunctionContext); } } } void DUChainControlFlow::updateToolTip(const QString &edge, const QPoint& point, QWidget *partWidget) { ControlFlowGraphNavigationWidget *navigationWidget = new ControlFlowGraphNavigationWidget(edge, m_arcUsesMap.values(edge)); KDevelop::NavigationToolTip *usesToolTip = new KDevelop::NavigationToolTip( partWidget, point, navigationWidget); usesToolTip->resize(navigationWidget->sizeHint() + QSize(10, 10)); ActiveToolTip::showToolTip(usesToolTip, 100, edge); } void DUChainControlFlow::slotGraphElementSelected(QList list, const QPoint& point) { Q_UNUSED(point) if (!list.isEmpty()) { QString label = list[0]; Declaration *declaration = m_identifierDeclarationMap[label].data(); DUChainReadLocker lock(DUChain::lock()); if (declaration) // Node click, jump to definition/declaration { QUrl url = declaration->url().toUrl(); CursorInRevision cursor = declaration->range().start; int line = cursor.line; int column = cursor.column; lock.unlock(); ICore::self()->documentController()->openDocument(url, KTextEditor::Cursor(line, column)); } } } void DUChainControlFlow::slotEdgeHover(QString label) { if (label.contains("->") && m_ShowUsesOnEdgeHover) // Edge click, show uses contained in the edge { KParts::ReadOnlyPart *part = dynamic_cast(sender()); if (!part) return; updateToolTip(label, QCursor::pos(), part->widget()); } } void DUChainControlFlow::setLocked(bool locked) { m_locked = locked; } void DUChainControlFlow::setUseFolderName(bool useFolderName) { m_useFolderName = useFolderName; } void DUChainControlFlow::setUseShortNames(bool useShortNames) { m_useShortNames = useShortNames; } void DUChainControlFlow::setDrawIncomingArcs(bool drawIncomingArcs) { m_drawIncomingArcs = drawIncomingArcs; } void DUChainControlFlow::setMaxLevel(int maxLevel) { m_maxLevel = maxLevel; } void DUChainControlFlow::setShowUsesOnEdgeHover(bool checked) { m_ShowUsesOnEdgeHover = checked; } void DUChainControlFlow::refreshGraph() { if (!m_locked) { if(ICore::self()->documentController()->activeDocument() && ICore::self()->documentController()->activeDocument()->textDocument()) { { DUChainReadLocker lock(DUChain::lock()); m_previousUppermostExecutableContext = IndexedDUContext(); } KTextEditor::View *view = ICore::self()->documentController()->activeDocument()->activeTextView(); cursorPositionChanged(view, view->cursorPosition()); } } } void DUChainControlFlow::newGraph() { m_visitedFunctions.clear(); m_identifierDeclarationMap.clear(); m_arcUsesMap.clear(); m_currentProject = 0; m_dotControlFlowGraph->clearGraph(); } void DUChainControlFlow::jobDone (KJob* job) { m_graphThreadRunning = false; job->deleteLater(); emit jobDone(); } void DUChainControlFlow::useDeclarationsFromDefinition (Declaration *definition, TopDUContext *topContext, DUContext *context) { if (!topContext) return; const Use *uses = context->uses(); unsigned int usesCount = context->usesCount(); QVector subContexts = context->childContexts(); QVector::iterator subContextsIterator = subContexts.begin(); QVector::iterator subContextsEnd = subContexts.end(); Declaration *declaration; for (unsigned int i = 0; i < usesCount; ++i) { if (m_abort) return; declaration = topContext->usedDeclarationForIndex(uses[i].m_declarationIndex); if (declaration && declaration->type()) { if (subContextsIterator != subContextsEnd) { if (uses[i].m_range.start < (*subContextsIterator)->range().start) processFunctionCall(definition, declaration, uses[i]); else if ((*subContextsIterator)->type() == DUContext::Other) { // Recursive call for sub-contexts useDeclarationsFromDefinition(definition, topContext, *subContextsIterator); ++subContextsIterator; --i; } } else processFunctionCall(definition, declaration, uses[i]); } } while (subContextsIterator != subContextsEnd) if ((*subContextsIterator)->type() == DUContext::Other) { // Recursive call for remaining sub-contexts useDeclarationsFromDefinition(definition, topContext, *subContextsIterator); ++subContextsIterator; } } Declaration *DUChainControlFlow::declarationFromControlFlowMode(Declaration *definitionDeclaration) { Declaration *nodeDeclaration = definitionDeclaration; if (m_controlFlowMode != ControlFlowFunction) { if (nodeDeclaration->isDefinition()) nodeDeclaration = DUChainUtils::declarationForDefinition(nodeDeclaration, nodeDeclaration->topContext()); if (!nodeDeclaration || !nodeDeclaration->context() || !nodeDeclaration->context()->owner()) return definitionDeclaration; while (nodeDeclaration->context() && nodeDeclaration->context()->owner() && ((m_controlFlowMode == ControlFlowClass && nodeDeclaration->context() && nodeDeclaration->context()->type() == DUContext::Class) || (m_controlFlowMode == ControlFlowNamespace && ( (nodeDeclaration->context() && nodeDeclaration->context()->type() == DUContext::Class) || (nodeDeclaration->context() && nodeDeclaration->context()->type() == DUContext::Namespace)) ))) nodeDeclaration = nodeDeclaration->context()->owner(); } return nodeDeclaration; } void DUChainControlFlow::prepareContainers(QStringList &containers, Declaration* definition) { ControlFlowMode originalControlFlowMode = m_controlFlowMode; QString strGlobalNamespaceOrFolderNames; // Handling project clustering if (m_clusteringModes.testFlag(ClusteringProject) && ICore::self()->projectController()->findProjectForUrl(definition->url().str())) containers << ICore::self()->projectController()->findProjectForUrl(definition->url().str())->name(); // Handling namespace clustering if (m_clusteringModes.testFlag(ClusteringNamespace)) { m_controlFlowMode = ControlFlowNamespace; Declaration *namespaceDefinition = declarationFromControlFlowMode(definition); strGlobalNamespaceOrFolderNames = ((namespaceDefinition->internalContext() && namespaceDefinition->internalContext()->type() != DUContext::Namespace) ? globalNamespaceOrFolderNames(namespaceDefinition): shortNameFromContainers(containers, prependFolderNames(namespaceDefinition))); foreach(const QString &container, strGlobalNamespaceOrFolderNames.split("::")) containers << container; } // Handling class clustering if (m_clusteringModes.testFlag(ClusteringClass)) { m_controlFlowMode = ControlFlowClass; Declaration *classDefinition = declarationFromControlFlowMode(definition); if (classDefinition->internalContext() && classDefinition->internalContext()->type() == DUContext::Class) containers << shortNameFromContainers(containers, prependFolderNames(classDefinition)); } m_controlFlowMode = originalControlFlowMode; } QString DUChainControlFlow::globalNamespaceOrFolderNames(Declaration *declaration) { if (m_useFolderName && m_currentProject && m_includeDirectories.count() > 0) { int minLength = std::numeric_limits::max(); QString folderName, smallestDirectory, declarationUrl = declaration->url().str(); foreach (const Path &url, m_includeDirectories) { QString urlString = url.toLocalFile(); if (urlString.length() <= minLength && declarationUrl.startsWith(urlString)) { smallestDirectory = urlString; minLength = urlString.length(); } } declarationUrl = declarationUrl.remove(0, smallestDirectory.length()); declarationUrl = declarationUrl.remove(declaration->url().str()); if (declarationUrl.endsWith('/')) declarationUrl.chop(1); if (declarationUrl.startsWith('/')) declarationUrl.remove(0, 1); declarationUrl = declarationUrl.replace('/', "::"); if (!declarationUrl.isEmpty()) return declarationUrl; } return i18n("Global Namespace"); } QString DUChainControlFlow::prependFolderNames(Declaration *declaration) { QString prependedQualifiedName = declaration->qualifiedIdentifier().toString(); if (m_useFolderName) { ControlFlowMode originalControlFlowMode = m_controlFlowMode; m_controlFlowMode = ControlFlowNamespace; Declaration *namespaceDefinition = declarationFromControlFlowMode(declaration); m_controlFlowMode = originalControlFlowMode; QString prefix = globalNamespaceOrFolderNames(namespaceDefinition); if (namespaceDefinition && namespaceDefinition->internalContext() && namespaceDefinition->internalContext()->type() != DUContext::Namespace && prefix != i18n("Global Namespace")) prependedQualifiedName.prepend(prefix + "::"); } return prependedQualifiedName; } QString DUChainControlFlow::shortNameFromContainers(const QList &containers, const QString &qualifiedIdentifier) { QString shortName = qualifiedIdentifier; if (m_useShortNames) { foreach(const QString &container, containers) if (shortName.contains(container)) shortName.remove(shortName.indexOf(container + "::"), (container + "::").length()); } return shortName; } diff --git a/duchaincontrolflowjob.cpp b/duchaincontrolflowjob.cpp index 1f9e1b0..61ab6b9 100644 --- a/duchaincontrolflowjob.cpp +++ b/duchaincontrolflowjob.cpp @@ -1,93 +1,94 @@ /*************************************************************************** * Copyright 2009 Sandro Andrade * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library 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 Library 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 "duchaincontrolflowjob.h" #include #include #include #include #include #include DUChainControlFlowJob::DUChainControlFlowJob(const QString &jobName, DUChainControlFlow *duchainControlFlow) : m_duchainControlFlow(duchainControlFlow), m_plugin(0), m_internalJob(0), m_controlFlowJobType(DUChainControlFlowInternalJob::ControlFlowJobInteractive) { init(jobName); } DUChainControlFlowJob::DUChainControlFlowJob(const QString &jobName, KDevControlFlowGraphViewPlugin *plugin) : m_duchainControlFlow(0), m_plugin(plugin), m_internalJob(0), m_controlFlowJobType(DUChainControlFlowInternalJob::ControlFlowJobInteractive) { init(jobName); } void DUChainControlFlowJob::init(const QString &jobName) { setObjectName(i18n("Generating control flow graph for %1", jobName)); setCapabilities(Killable); setAutoDelete(false); ICore::self()->uiController()->registerStatus(this); } DUChainControlFlowJob::~DUChainControlFlowJob() { } QString DUChainControlFlowJob::statusName() const { return i18n("Control Flow Graph"); } void DUChainControlFlowJob::setControlFlowJobType(DUChainControlFlowInternalJob::ControlFlowJobType controlFlowJobType) { m_controlFlowJobType = controlFlowJobType; } void DUChainControlFlowJob::start() { emit showProgress(this, 0, 0, 0); emit showMessage(this, objectName()); m_internalJob = new DUChainControlFlowInternalJob(m_duchainControlFlow, m_plugin); m_internalJob->setControlFlowJobType(m_controlFlowJobType); - connect(m_internalJob, SIGNAL(done()), SLOT(done())); + connect(m_internalJob, &DUChainControlFlowInternalJob::done, + this, &DUChainControlFlowJob::done); ThreadWeaver::Queue::instance()->enqueue(ThreadWeaver::JobPointer(m_internalJob)); } bool DUChainControlFlowJob::doKill() { m_internalJob->requestAbort(); return false; } void DUChainControlFlowJob::done() { emit hideProgress(this); emit clearMessage(this); emitResult(); } diff --git a/kdevcontrolflowgraphviewplugin.cpp b/kdevcontrolflowgraphviewplugin.cpp index 9c5c828..ef67fba 100644 --- a/kdevcontrolflowgraphviewplugin.cpp +++ b/kdevcontrolflowgraphviewplugin.cpp @@ -1,579 +1,587 @@ /*************************************************************************** * Copyright 2009 Sandro Andrade * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library 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 Library 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 "kdevcontrolflowgraphviewplugin.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "duchaincontrolflow.h" #include "dotcontrolflowgraph.h" #include "controlflowgraphview.h" #include "duchaincontrolflowjob.h" using namespace KDevelop; K_PLUGIN_FACTORY_WITH_JSON(ControlFlowGraphViewFactory, "kdevcontrolflowgraphview.json", registerPlugin();) class KDevControlFlowGraphViewFactory: public KDevelop::IToolViewFactory{ public: KDevControlFlowGraphViewFactory(KDevControlFlowGraphViewPlugin *plugin) : m_plugin(plugin) {} virtual QWidget* create(QWidget *parent = 0) { return new ControlFlowGraphView(m_plugin, parent); } virtual Qt::DockWidgetArea defaultPosition() { return Qt::BottomDockWidgetArea; } virtual QString id() const { return "org.kdevelop.ControlFlowGraphView"; } private: KDevControlFlowGraphViewPlugin *m_plugin; }; KDevControlFlowGraphViewPlugin::KDevControlFlowGraphViewPlugin (QObject *parent, const QVariantList &) : KDevelop::IPlugin (QStringLiteral("kdevcontrolflowgraphview"), parent), m_toolViewFactory(new KDevControlFlowGraphViewFactory(this)), m_activeToolView(0), m_project(0), m_abort(false) { core()->uiController()->addToolView(i18n("Control Flow Graph"), m_toolViewFactory); - QObject::connect(core()->documentController(), SIGNAL(textDocumentCreated(KDevelop::IDocument*)), - SLOT(textDocumentCreated(KDevelop::IDocument*))); - QObject::connect(core()->projectController(), SIGNAL(projectOpened(KDevelop::IProject*)), - SLOT(projectOpened(KDevelop::IProject*))); - QObject::connect(core()->projectController(), SIGNAL(projectClosed(KDevelop::IProject*)), - SLOT(projectClosed(KDevelop::IProject*))); - QObject::connect(core()->languageController()->backgroundParser(), SIGNAL(parseJobFinished(KDevelop::ParseJob*)), - SLOT(parseJobFinished(KDevelop::ParseJob*))); + connect(core()->documentController(), &IDocumentController::textDocumentCreated, + this, &KDevControlFlowGraphViewPlugin::textDocumentCreated); + connect(core()->projectController(), &IProjectController::projectOpened, + this, &KDevControlFlowGraphViewPlugin::projectOpened); + connect(core()->projectController(), &IProjectController::projectClosed, + this, &KDevControlFlowGraphViewPlugin::projectClosed); + connect(core()->languageController()->backgroundParser(), &BackgroundParser::parseJobFinished, + this, &KDevControlFlowGraphViewPlugin::parseJobFinished); m_exportControlFlowGraph = new QAction(i18n("Export Control Flow Graph"), this); - connect(m_exportControlFlowGraph, SIGNAL(triggered(bool)), SLOT(slotExportControlFlowGraph(bool)), Qt::UniqueConnection); + connect(m_exportControlFlowGraph, &QAction::triggered, + this, &KDevControlFlowGraphViewPlugin::slotExportControlFlowGraph, Qt::UniqueConnection); m_exportClassControlFlowGraph = new QAction(i18n("Export Class Control Flow Graph"), this); - connect(m_exportClassControlFlowGraph, SIGNAL(triggered(bool)), SLOT(slotExportClassControlFlowGraph(bool)), Qt::UniqueConnection); + connect(m_exportClassControlFlowGraph, &QAction::triggered, + this, &KDevControlFlowGraphViewPlugin::slotExportClassControlFlowGraph, Qt::UniqueConnection); m_exportProjectControlFlowGraph = new QAction(i18n("Export Project Control Flow Graph"), this); - connect(m_exportProjectControlFlowGraph, SIGNAL(triggered(bool)), SLOT(slotExportProjectControlFlowGraph(bool)), Qt::UniqueConnection); + connect(m_exportProjectControlFlowGraph, &QAction::triggered, + this, &KDevControlFlowGraphViewPlugin::slotExportProjectControlFlowGraph, Qt::UniqueConnection); } KDevControlFlowGraphViewPlugin::~KDevControlFlowGraphViewPlugin() { } QString KDevControlFlowGraphViewPlugin::statusName() const { return i18n("Control Flow Graph"); } void KDevControlFlowGraphViewPlugin::unload() { // When calling removeToolView all existing views are destroyed and their destructor invoke unRegisterToolView. core()->uiController()->removeToolView(m_toolViewFactory); } void KDevControlFlowGraphViewPlugin::registerToolView(ControlFlowGraphView *view) { m_toolViews << view; } void KDevControlFlowGraphViewPlugin::unRegisterToolView(ControlFlowGraphView *view) { m_toolViews.removeAll(view); } QPointer KDevControlFlowGraphViewPlugin::exportControlFlowGraph(ControlFlowGraphFileDialog::OpeningMode mode) { QPointer fileDialog = new ControlFlowGraphFileDialog((QWidget *) ICore::self()->uiController()->activeMainWindow(), mode); if (fileDialog->exec() == QDialog::Accepted) { if (fileDialog && !fileDialog->selectedFiles().isEmpty()) { QString fileName = fileDialog->selectedFiles()[0]; if (!fileName.isEmpty()) return fileDialog; } } return 0; } KDevelop::ContextMenuExtension KDevControlFlowGraphViewPlugin::contextMenuExtension(KDevelop::Context* context) { KDevelop::ContextMenuExtension extension; if (context->hasType(Context::CodeContext) || context->hasType(Context::EditorContext)) { KDevelop::DeclarationContext *codeContext = dynamic_cast(context); if (!codeContext) return extension; DUChainReadLocker readLock(DUChain::lock()); Declaration *declaration(codeContext->declaration().data()); // Insert action for generating control flow graph from method if (declaration && declaration->type() && (declaration->isDefinition() || FunctionDefinition::definition(declaration))) { m_exportControlFlowGraph->setData(QVariant::fromValue(DUChainBasePointer(declaration))); extension.addAction(KDevelop::ContextMenuExtension::ExtensionGroup, m_exportControlFlowGraph); } // Insert action for generating control flow graph for the whole class else if (declaration && declaration->kind() == Declaration::Type && declaration->internalContext() && declaration->internalContext()->type() == DUContext::Class) { m_exportClassControlFlowGraph->setData(QVariant::fromValue(DUChainBasePointer(declaration))); extension.addAction(KDevelop::ContextMenuExtension::ExtensionGroup, m_exportClassControlFlowGraph); } } else if (context->hasType(Context::ProjectItemContext)) { KDevelop::ProjectItemContext *projectItemContext = dynamic_cast(context); if (projectItemContext) { QList items = projectItemContext->items(); foreach(ProjectBaseItem *item, items) { ProjectFolderItem *folder = item->folder(); if (folder && !folder->parent()) { m_exportProjectControlFlowGraph->setData(QVariant::fromValue(folder->project()->name())); extension.addAction(KDevelop::ContextMenuExtension::ExtensionGroup, m_exportProjectControlFlowGraph); } } } } return extension; } void KDevControlFlowGraphViewPlugin::projectOpened(KDevelop::IProject* project) { Q_UNUSED(project); foreach (ControlFlowGraphView *controlFlowGraphView, m_toolViews) controlFlowGraphView->setProjectButtonsEnabled(true); refreshActiveToolView(); } void KDevControlFlowGraphViewPlugin::projectClosed(KDevelop::IProject* project) { Q_UNUSED(project); if (core()->projectController()->projectCount() == 0) { foreach (ControlFlowGraphView *controlFlowGraphView, m_toolViews) { controlFlowGraphView->setProjectButtonsEnabled(false); controlFlowGraphView->newGraph(); } } refreshActiveToolView(); } void KDevControlFlowGraphViewPlugin::parseJobFinished(KDevelop::ParseJob* parseJob) { if (core()->documentController()->activeDocument() && parseJob->document().toUrl() == core()->documentController()->activeDocument()->url()) refreshActiveToolView(); } void KDevControlFlowGraphViewPlugin::textDocumentCreated(KDevelop::IDocument *document) { - connect(document->textDocument(), SIGNAL(viewCreated(KTextEditor::Document*, KTextEditor::View*)), - SLOT(viewCreated(KTextEditor::Document*, KTextEditor::View*))); + connect(document->textDocument(), &KTextEditor::Document::viewCreated, + this, &KDevControlFlowGraphViewPlugin::viewCreated); } void KDevControlFlowGraphViewPlugin::viewCreated(KTextEditor::Document *document, KTextEditor::View *view) { Q_UNUSED(document); - connect(view, SIGNAL(cursorPositionChanged(KTextEditor::View*, KTextEditor::Cursor)), - SLOT(cursorPositionChanged(KTextEditor::View*, KTextEditor::Cursor))); - connect(view, SIGNAL(destroyed(QObject*)), SLOT(viewDestroyed(QObject*))); - connect(view, SIGNAL(focusIn(KTextEditor::View*)), SLOT(focusIn(KTextEditor::View*))); + connect(view, &KTextEditor::View::cursorPositionChanged, + this, &KDevControlFlowGraphViewPlugin::cursorPositionChanged); + connect(view, &KTextEditor::View::destroyed, + this, &KDevControlFlowGraphViewPlugin::viewDestroyed); + connect(view, &KTextEditor::View::focusIn, + this, &KDevControlFlowGraphViewPlugin::focusIn); } void KDevControlFlowGraphViewPlugin::viewDestroyed(QObject *object) { Q_UNUSED(object); if (!core()->documentController()->activeDocument() && m_activeToolView) m_activeToolView->newGraph(); } void KDevControlFlowGraphViewPlugin::focusIn(KTextEditor::View *view) { if (view) cursorPositionChanged(view, view->cursorPosition()); } void KDevControlFlowGraphViewPlugin::cursorPositionChanged(KTextEditor::View *view, const KTextEditor::Cursor &cursor) { if (m_activeToolView) m_activeToolView->cursorPositionChanged(view, cursor); } void KDevControlFlowGraphViewPlugin::refreshActiveToolView() { if (m_activeToolView) m_activeToolView->refreshGraph(); } void KDevControlFlowGraphViewPlugin::slotExportControlFlowGraph(bool value) { // Export graph for a given function Q_UNUSED(value); if (m_duchainControlFlow || m_dotControlFlowGraph) { KMessageBox::error((QWidget *) core()->uiController()->activeMainWindow(), i18n("There is a graph being currently exported. Please stop it before requiring a new one")); return; } DUChainReadLocker lock(DUChain::lock()); Q_ASSERT(qobject_cast(sender())); QAction *action = static_cast(sender()); Q_ASSERT(action->data().canConvert()); DeclarationPointer declarationPointer = qvariant_cast(action->data()).dynamicCast(); Declaration *declaration = declarationPointer.data(); if (!declaration) { KMessageBox::error((QWidget *) core()->uiController()->activeMainWindow(), i18n("Could not generate function control flow graph")); return; } if (!declaration->isDefinition()) { declaration = FunctionDefinition::definition(declaration); if (!declaration || !declaration->internalContext()) { KMessageBox::error((QWidget *) core()->uiController()->activeMainWindow(), i18n("Could not generate control flow graph")); return; } } if ((m_fileDialog = exportControlFlowGraph())) { DUChainControlFlowJob *job = new DUChainControlFlowJob(declaration->qualifiedIdentifier().toString(), this); job->setControlFlowJobType(DUChainControlFlowInternalJob::ControlFlowJobBatchForFunction); m_ideclaration = IndexedDeclaration(declaration); - connect (job, SIGNAL(result(KJob*)), SLOT(generationDone(KJob*))); + connect(job, &DUChainControlFlowJob::result, + this, &KDevControlFlowGraphViewPlugin::generationDone); ICore::self()->runController()->registerJob(job); } action->setData(QVariant::fromValue(DUChainBasePointer())); } void KDevControlFlowGraphViewPlugin::slotExportClassControlFlowGraph(bool value) { // Export graph for all functions of a given class - individual per-function graphs will be merged Q_UNUSED(value); if (m_duchainControlFlow || m_dotControlFlowGraph) { KMessageBox::error((QWidget *) core()->uiController()->activeMainWindow(), i18n("There is a graph being currently exported. Please stop it before requiring a new one")); return; } DUChainReadLocker lock(DUChain::lock()); Q_ASSERT(qobject_cast(sender())); QAction *action = static_cast(sender()); Q_ASSERT(action->data().canConvert()); DeclarationPointer declarationPointer = qvariant_cast(action->data()).dynamicCast(); Declaration *declaration = declarationPointer.data(); if (!declaration) { KMessageBox::error((QWidget *) core()->uiController()->activeMainWindow(), i18n("Could not generate class control flow graph")); return; } if ((m_fileDialog = exportControlFlowGraph(ControlFlowGraphFileDialog::ForClassConfigurationButtons))) { DUChainControlFlowJob *job = new DUChainControlFlowJob(declaration->qualifiedIdentifier().toString(), this); job->setControlFlowJobType(DUChainControlFlowInternalJob::ControlFlowJobBatchForClass); m_ideclaration = IndexedDeclaration(declaration); - connect (job, SIGNAL(result(KJob*)), SLOT(generationDone(KJob*))); + connect(job, &DUChainControlFlowJob::result, + this, &KDevControlFlowGraphViewPlugin::generationDone); ICore::self()->runController()->registerJob(job); } action->setData(QVariant::fromValue(DUChainBasePointer())); } void KDevControlFlowGraphViewPlugin::slotExportProjectControlFlowGraph(bool value) { // Export graph for all classes of a given project - individual per-class graphs will be merged Q_UNUSED(value); if (m_duchainControlFlow || m_dotControlFlowGraph) { KMessageBox::error((QWidget *) core()->uiController()->activeMainWindow(), i18n("There is a graph being currently exported. Please stop it before requiring a new one")); return; } Q_ASSERT(qobject_cast(sender())); QAction *action = static_cast(sender()); Q_ASSERT(action->data().canConvert()); QString projectName = qvariant_cast(action->data()); if (projectName.isEmpty()) { KMessageBox::error((QWidget *) core()->uiController()->activeMainWindow(), i18n("Could not generate project control flow graph - project name empty")); return; } IProject *project = core()->projectController()->findProjectByName(projectName); if (!project) { KMessageBox::error((QWidget *) core()->uiController()->activeMainWindow(), i18n("Could not generate project control flow graph - project not found")); return; } if ((m_fileDialog = exportControlFlowGraph(ControlFlowGraphFileDialog::ForClassConfigurationButtons))) { DUChainControlFlowJob *job = new DUChainControlFlowJob(projectName, this); job->setControlFlowJobType(DUChainControlFlowInternalJob::ControlFlowJobBatchForProject); m_project = project; - connect (job, SIGNAL(result(KJob*)), SLOT(generationDone(KJob*))); + connect(job, &DUChainControlFlowJob::result, + this, &KDevControlFlowGraphViewPlugin::generationDone); ICore::self()->runController()->registerJob(job); } action->setData(QVariant::fromValue(QString())); } void KDevControlFlowGraphViewPlugin::generateControlFlowGraph() { DUChainReadLocker readLock(DUChain::lock()); Declaration *declaration = m_ideclaration.data(); if (!declaration) return; m_abort = false; m_dotControlFlowGraph = new DotControlFlowGraph; m_duchainControlFlow = new DUChainControlFlow(m_dotControlFlowGraph); configureDuchainControlFlow(m_duchainControlFlow, m_dotControlFlowGraph, m_fileDialog); m_duchainControlFlow->generateControlFlowForDeclaration(m_ideclaration, IndexedTopDUContext(declaration->topContext()), IndexedDUContext(declaration->internalContext())); exportGraph(); } void KDevControlFlowGraphViewPlugin::generateClassControlFlowGraph() { DUChainReadLocker readLock(DUChain::lock()); Declaration *declaration = m_ideclaration.data(); if (!declaration) return; m_abort = false; m_dotControlFlowGraph = new DotControlFlowGraph; m_duchainControlFlow = new DUChainControlFlow(m_dotControlFlowGraph); configureDuchainControlFlow(m_duchainControlFlow, m_dotControlFlowGraph, m_fileDialog); if (!declaration->isForwardDeclaration() && declaration->internalContext()) { int i = 0; int max = declaration->internalContext()->localDeclarations().size(); // For each function declaration ClassFunctionDeclaration *functionDeclaration; foreach (Declaration *decl, declaration->internalContext()->localDeclarations()) { if (m_abort) break; emit showProgress(this, 0, max-1, i); emit showMessage(this, i18n("Generating graph for function %1", decl->identifier().toString())); ++i; if ((functionDeclaration = dynamic_cast(decl))) { Declaration *functionDefinition = FunctionDefinition::definition(functionDeclaration); if (functionDefinition) m_duchainControlFlow->generateControlFlowForDeclaration(IndexedDeclaration(functionDefinition), IndexedTopDUContext(functionDefinition->topContext()), IndexedDUContext(functionDefinition->internalContext())); } } } if (!m_abort && !m_fileDialog->selectedFiles().isEmpty()) { emit showMessage(this, i18n("Saving file %1", m_fileDialog->selectedFiles()[0])); exportGraph(); } emit hideProgress(this); emit clearMessage(this); } void KDevControlFlowGraphViewPlugin::generateProjectControlFlowGraph() { if (!m_project) return; m_abort = false; m_dotControlFlowGraph = new DotControlFlowGraph; m_duchainControlFlow = new DUChainControlFlow(m_dotControlFlowGraph); configureDuchainControlFlow(m_duchainControlFlow, m_dotControlFlowGraph, m_fileDialog); DUChainReadLocker readLock(DUChain::lock()); int i = 0; int max = m_project->fileSet().size(); // For each source file foreach(const IndexedString &file, m_project->fileSet()) { emit showProgress(this, 0, max-1, i); ++i; uint codeModelItemCount = 0; const CodeModelItem *codeModelItems = 0; CodeModel::self().items(file, codeModelItemCount, codeModelItems); for (uint codeModelItemIndex = 0; codeModelItemIndex < codeModelItemCount; ++codeModelItemIndex) { const CodeModelItem &item = codeModelItems[codeModelItemIndex]; if ((item.kind & CodeModelItem::Class) && !item.id.identifier().last().toString().isEmpty()) { uint declarationCount = 0; const IndexedDeclaration *declarations = 0; PersistentSymbolTable::self().declarations(item.id.identifier(), declarationCount, declarations); // For each class declaration for (uint j = 0; j < declarationCount; ++j) { Declaration *declaration = dynamic_cast(declarations[j].declaration()); if (declaration && !declaration->isForwardDeclaration() && declaration->internalContext()) { // For each function declaration ClassFunctionDeclaration *functionDeclaration; foreach (Declaration *decl, declaration->internalContext()->localDeclarations()) { emit showMessage(this, i18n("Generating graph for %1 - %2", file.str(), decl->qualifiedIdentifier().toString())); if ((functionDeclaration = dynamic_cast(decl))) { if (m_abort) break; Declaration *functionDefinition = FunctionDefinition::definition(functionDeclaration); if (functionDefinition) m_duchainControlFlow->generateControlFlowForDeclaration(IndexedDeclaration(functionDefinition), IndexedTopDUContext(functionDefinition->topContext()), IndexedDUContext(functionDefinition->internalContext())); } } if (m_abort) break; } } if (m_abort) break; } } if (m_abort) break; } if (!m_abort && !m_fileDialog->selectedFiles().isEmpty()) { emit showMessage(this, i18n("Saving file %1", m_fileDialog->selectedFiles()[0])); exportGraph(); } m_project = 0; emit hideProgress(this); emit clearMessage(this); } void KDevControlFlowGraphViewPlugin::requestAbort() { m_abort = true; } void KDevControlFlowGraphViewPlugin::setActiveToolView(ControlFlowGraphView *activeToolView) { m_activeToolView = activeToolView; refreshActiveToolView(); } void KDevControlFlowGraphViewPlugin::generationDone(KJob *job) { job->deleteLater(); delete m_dotControlFlowGraph; delete m_duchainControlFlow; if (!m_abort) KMessageBox::information((QWidget *) (core()->uiController()->activeMainWindow()), i18n("Control flow graph exported"), i18n("Export Control Flow Graph")); } void KDevControlFlowGraphViewPlugin::exportGraph() { DotControlFlowGraph::mutex.lock(); if (!m_fileDialog->selectedFiles().isEmpty()) { m_dotControlFlowGraph->exportGraph(m_fileDialog->selectedFiles()[0]); } DotControlFlowGraph::mutex.unlock(); } void KDevControlFlowGraphViewPlugin::configureDuchainControlFlow(DUChainControlFlow *duchainControlFlow, DotControlFlowGraph *dotControlFlowGraph, ControlFlowGraphFileDialog *fileDialog) { duchainControlFlow->setControlFlowMode(fileDialog->controlFlowMode()); duchainControlFlow->setClusteringModes(fileDialog->clusteringModes()); duchainControlFlow->setMaxLevel(fileDialog->maxLevel()); duchainControlFlow->setUseFolderName(fileDialog->useFolderName()); duchainControlFlow->setUseShortNames(fileDialog->useShortNames()); duchainControlFlow->setDrawIncomingArcs(fileDialog->drawIncomingArcs()); dotControlFlowGraph->prepareNewGraph(); } #include "kdevcontrolflowgraphviewplugin.moc" \ No newline at end of file