diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b77fd7..c684785 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,83 +1,85 @@ cmake_minimum_required(VERSION 3.0) project(controlflowgraph) set(QT_MIN_VERSION "5.7.0") set(KF_MIN_VERSION "5.28.0") find_package(ECM ${KF_MIN_VERSION} REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) include(KDECompilerSettings NO_POLICY_SCOPE) include(KDEInstallDirs) include(KDECMakeSettings) include(FeatureSummary) find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED Core Widgets ) find_package(KF5 ${KF_MIN_VERSION} REQUIRED COMPONENTS I18n TextEditor ThreadWeaver ConfigWidgets ItemModels ) find_package(KDevPlatform 5.3.40 ) set_package_properties(KDevPlatform PROPERTIES TYPE REQUIRED ) find_package(GraphViz) set_package_properties(GraphViz PROPERTIES TYPE REQUIRED ) add_definitions( -DQT_DEPRECATED_WARNINGS -DQT_DISABLE_DEPRECATED_BEFORE=0x050700 -DQT_NO_SIGNALS_SLOTS_KEYWORDS -DQT_NO_URL_CAST_FROM_STRING + -DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_TO_ASCII -DQT_NO_CAST_FROM_BYTEARRAY + -DQT_USE_QSTRINGBUILDER -DQT_STRICT_ITERATORS -DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT ) include_directories( ${GraphViz_INCLUDE_DIRECTORIES} ) set(kdevcontrolflowgraphview_PART_SRCS kdevcontrolflowgraphviewplugin.cpp controlflowgraphview.cpp duchaincontrolflow.cpp dotcontrolflowgraph.cpp duchaincontrolflowjob.cpp duchaincontrolflowinternaljob.cpp controlflowgraphnavigationcontext.cpp controlflowgraphnavigationwidget.cpp controlflowgraphusescollector.cpp controlflowgraphfiledialog.cpp ) set(kdevcontrolflowgraphview_PART_UI controlflowgraphview.ui controlflowgraphexportconfiguration.ui ) ki18n_wrap_ui(kdevcontrolflowgraphview_PART_SRCS ${kdevcontrolflowgraphview_PART_UI}) kdevplatform_add_plugin(kdevcontrolflowgraphview JSON kdevcontrolflowgraphview.json SOURCES ${kdevcontrolflowgraphview_PART_SRCS}) target_link_libraries(kdevcontrolflowgraphview KF5::Parts KF5::TextEditor KF5::ThreadWeaver KDev::Interfaces KDev::Language KDev::Project KDev::Util gvc cgraph cdt) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/controlflowgraphfiledialog.cpp b/controlflowgraphfiledialog.cpp index 93685b9..c06281e 100644 --- a/controlflowgraphfiledialog.cpp +++ b/controlflowgraphfiledialog.cpp @@ -1,185 +1,183 @@ /*************************************************************************** * 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(nullptr) { - 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"; + const QStringList mimeTypes { + QStringLiteral("image/png"), + QStringLiteral("image/jpeg"), + QStringLiteral("image/gif"), + QStringLiteral("image/svg+xml"), + QStringLiteral("image/svg+xml-compressed"), + QStringLiteral("application/x-dia-diagram"), + QStringLiteral("image/x-xfig"), + QStringLiteral("application/pdf"), + QStringLiteral("text/vnd.graphviz"), + }; setMimeTypeFilters(mimeTypes); setAcceptMode(QFileDialog::AcceptSave); 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")); + m_configurationWidget->controlFlowFunctionRadioButton->setIcon(QIcon::fromTheme(QStringLiteral("flag-blue"))); + m_configurationWidget->controlFlowClassRadioButton->setIcon(QIcon::fromTheme(QStringLiteral("flag-green"))); + m_configurationWidget->controlFlowNamespaceRadioButton->setIcon(QIcon::fromTheme(QStringLiteral("flag-red"))); + m_configurationWidget->clusteringClassCheckBox->setIcon(QIcon::fromTheme(QStringLiteral("code-class"))); + m_configurationWidget->clusteringNamespaceCheckBox->setIcon(QIcon::fromTheme(QStringLiteral("namespace"))); + m_configurationWidget->clusteringProjectCheckBox->setIcon(QIcon::fromTheme(QStringLiteral("folder-development"))); + m_configurationWidget->limitMaxLevelCheckBox->setIcon(QIcon::fromTheme(QStringLiteral("zoom-fit-height"))); + m_configurationWidget->drawIncomingArcsCheckBox->setIcon(QIcon::fromTheme(QStringLiteral("draw-arrow-down"))); + m_configurationWidget->useFolderNameCheckBox->setIcon(QIcon::fromTheme(QStringLiteral("folder-favorites"))); + m_configurationWidget->useShortNamesCheckBox->setIcon(QIcon::fromTheme(QStringLiteral("application-x-arc"))); 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") - { + if (radioButton->objectName() == QLatin1String("controlFlowFunctionRadioButton")) { m_configurationWidget->clusteringClassCheckBox->setEnabled(true); m_configurationWidget->clusteringNamespaceCheckBox->setEnabled(true); } - if (radioButton->objectName() == "controlFlowClassRadioButton") - { + if (radioButton->objectName() == QLatin1String("controlFlowClassRadioButton")) { m_configurationWidget->clusteringClassCheckBox->setChecked(false); m_configurationWidget->clusteringClassCheckBox->setEnabled(false); m_configurationWidget->clusteringNamespaceCheckBox->setEnabled(true); } - if (radioButton->objectName() == "controlFlowNamespaceRadioButton") - { + if (radioButton->objectName() == QLatin1String("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/controlflowgraphnavigationcontext.cpp b/controlflowgraphnavigationcontext.cpp index 9d9dcc6..8f8eb92 100644 --- a/controlflowgraphnavigationcontext.cpp +++ b/controlflowgraphnavigationcontext.cpp @@ -1,87 +1,87 @@ /*************************************************************************** * 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 "controlflowgraphnavigationcontext.h" #include #include #include #include #include #include using namespace KDevelop; ControlFlowGraphNavigationContext::ControlFlowGraphNavigationContext(const QString &label, const ArcUses &arcUses, TopDUContextPointer topContext, AbstractNavigationContext *previousContext) : AbstractNavigationContext(topContext, previousContext), m_label(label), m_arcUses (arcUses) { } ControlFlowGraphNavigationContext::~ControlFlowGraphNavigationContext() { } QString ControlFlowGraphNavigationContext::name() const { return i18n("Control flow graph navigation widget"); } QString ControlFlowGraphNavigationContext::html(bool shorten) { clear(); AbstractNavigationContext::html(shorten); - modifyHtml() += "

"; + modifyHtml() += QLatin1String("

"); - QStringList nodes = m_label.split("->"); + QStringList nodes = m_label.split(QLatin1String("->")); if (nodes.size() < 2) - return ""; + return QString(); - modifyHtml() += importantHighlight(i18n("Uses of %1 from %2", nodes[1], nodes[0])) + "


"; + modifyHtml() += importantHighlight(i18n("Uses of %1 from %2", nodes[1], nodes[0])) + QLatin1String("
"); unsigned int i = m_arcUses.size()-1; QPair pair; QListIterator< QPair > iterator(m_arcUses); iterator.toBack(); DUChainReadLocker lock(DUChain::lock()); while (iterator.hasPrevious()) { pair = iterator.previous(); CodeRepresentation::Ptr code = createCodeRepresentation(pair.second); - modifyHtml() += "" + pair.second.toUrl().fileName() + " (" + QString::number(pair.first.start.line+1) + "): " + code->line(pair.first.start.line).trimmed().toHtmlEscaped() + "
"; + modifyHtml() += QLatin1String("") + pair.second.toUrl().fileName() + QLatin1String(" (") + QString::number(pair.first.start.line+1) + QLatin1String("): ") + code->line(pair.first.start.line).trimmed().toHtmlEscaped() + QLatin1String("
"); } - modifyHtml() += "

"; + modifyHtml() += QLatin1String("

"); return currentHtml(); } void ControlFlowGraphNavigationContext::slotAnchorClicked(const QUrl &link) { int position = link.toString().toInt(); QPair pair = m_arcUses[position]; DUChainReadLocker lock(DUChain::lock()); QUrl url(pair.second.toUrl()); CursorInRevision cursor = pair.first.start; int line = cursor.line; int column = cursor.column; lock.unlock(); ICore::self()->documentController()->openDocument(url, KTextEditor::Cursor(line, column)); } diff --git a/controlflowgraphview.cpp b/controlflowgraphview.cpp index fc715af..1b8c46a 100644 --- a/controlflowgraphview.cpp +++ b/controlflowgraphview.cpp @@ -1,321 +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(nullptr), m_dotControlFlowGraph(new DotControlFlowGraph), m_duchainControlFlow(new DUChainControlFlow(m_dotControlFlowGraph)), m_graphLocked(false) { setupUi(this); - KPluginFactory *factory = KPluginLoader("kgraphviewerpart").factory(); + KPluginFactory *factory = KPluginLoader(QStringLiteral("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(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")); + modeFunctionToolButton->setIcon(QIcon::fromTheme(QStringLiteral("code-function"))); + modeClassToolButton->setIcon(QIcon::fromTheme(QStringLiteral("code-class"))); + modeNamespaceToolButton->setIcon(QIcon::fromTheme(QStringLiteral("namespace"))); + clusteringClassToolButton->setIcon(QIcon::fromTheme(QStringLiteral("code-class"))); + clusteringNamespaceToolButton->setIcon(QIcon::fromTheme(QStringLiteral("namespace"))); + clusteringProjectToolButton->setIcon(QIcon::fromTheme(QStringLiteral("folder-development"))); + useFolderNameToolButton->setIcon(QIcon::fromTheme(QStringLiteral("folder-favorites"))); + drawIncomingArcsToolButton->setIcon(QIcon::fromTheme(QStringLiteral("draw-arrow-down"))); + maxLevelToolButton->setIcon(QIcon::fromTheme(QStringLiteral("zoom-fit-height"))); + exportToolButton->setIcon(QIcon::fromTheme(QStringLiteral("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")); + birdseyeToolButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-find"))); + usesHoverToolButton->setIcon(QIcon::fromTheme(QStringLiteral("input-mouse"))); + zoominToolButton->setIcon(QIcon::fromTheme(QStringLiteral("zoom-in"))); + zoomoutToolButton->setIcon(QIcon::fromTheme(QStringLiteral("zoom-out"))); if (ICore::self()->projectController()->projectCount() > 0) setProjectButtonsEnabled(true); - useShortNamesToolButton->setIcon(QIcon::fromTheme("application-x-arc")); + useShortNamesToolButton->setIcon(QIcon::fromTheme(QStringLiteral("application-x-arc"))); updateLockIcon(lockControlFlowGraphToolButton->isChecked()); // Control flow mode buttons signals 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, &QToolButton::toggled, this, &ControlFlowGraphView::setClusteringClass); connect(clusteringNamespaceToolButton, &QToolButton::toggled, this, &ControlFlowGraphView::setClusteringNamespace); connect(clusteringProjectToolButton, &QToolButton::toggled, this, &ControlFlowGraphView::setClusteringProject); // Configuration buttons signals 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 auto partActionCollection = m_part->actionCollection(); connect(zoomoutToolButton, &QToolButton::clicked, - partActionCollection->action("view_zoom_out"), &QAction::triggered); + partActionCollection->action(QStringLiteral("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")); + partActionCollection->action(QStringLiteral("view_zoom_in")), &QAction::triggered); + partActionCollection->action(QStringLiteral("view_bev_enabled"))->setIcon(QIcon::fromTheme(QStringLiteral("edit-find"))); + partActionCollection->action(QStringLiteral("view_bev_enabled"))->setChecked(false); + birdseyeToolButton->setDefaultAction(partActionCollection->action(QStringLiteral("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, &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, &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->setIcon(QIcon::fromTheme(checked ? QStringLiteral("document-encrypt"):QStringLiteral("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(nullptr); } diff --git a/dotcontrolflowgraph.cpp b/dotcontrolflowgraph.cpp index 9e5b52a..b317e09 100644 --- a/dotcontrolflowgraph.cpp +++ b/dotcontrolflowgraph.cpp @@ -1,195 +1,197 @@ /*************************************************************************** * 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 "dotcontrolflowgraph.h" #include #include namespace { // C interface takes char*, so to avoid deprecated cast and/or undefined behaviour, // defined the needed constants here. static char SUFFIX[] = "dot"; static char GRAPH_NAME[] = "Root_Graph"; static char LABEL[] = "label"; static char EMPTY[] = ""; static char FILLED[] = "filled"; static char FILLCOLOR[] = "fillcolor"; static char SHAPE[] = "shape"; static char STYLE[] = "style"; static char BOX[] = "box"; } QMutex DotControlFlowGraph::mutex; DotControlFlowGraph::DotControlFlowGraph() : m_rootGraph(nullptr) { m_gvc = gvContext(); } DotControlFlowGraph::~DotControlFlowGraph() { gvFreeContext(m_gvc); } void DotControlFlowGraph::graphDone() { if (m_rootGraph) { if (mutex.tryLock()) { gvLayout(m_gvc, m_rootGraph, SUFFIX); gvFreeLayout(m_gvc, m_rootGraph); mutex.unlock(); emit loadLibrary(m_rootGraph); } } } void DotControlFlowGraph::clearGraph() { if (m_rootGraph) { gvFreeLayout(m_gvc, m_rootGraph); agclose(m_rootGraph); m_rootGraph = nullptr; } m_namedGraphs.clear(); m_rootGraph = agopen(GRAPH_NAME, Agdirected, nullptr); graphDone(); } void DotControlFlowGraph::exportGraph(const QString &fileName) { if (m_rootGraph) { gvLayout(m_gvc, m_rootGraph, SUFFIX); - gvRenderFilename(m_gvc, m_rootGraph, fileName.right(fileName.size()-fileName.lastIndexOf('.')-1).toUtf8().data(), fileName.toUtf8().data()); + gvRenderFilename(m_gvc, m_rootGraph, fileName.right(fileName.size()-fileName.lastIndexOf(QLatin1Char('.'))-1).toUtf8().data(), fileName.toUtf8().data()); gvFreeLayout(m_gvc, m_rootGraph); } } void DotControlFlowGraph::prepareNewGraph() { clearGraph(); } void DotControlFlowGraph::foundRootNode(const QStringList &containers, const QString &label) { Agraph_t *graph = m_rootGraph; if (!m_rootGraph) { // This shouldn't happen, as the graph should be generated before this function // is connected. Q_ASSERT(false); return; } QString absoluteContainer; foreach (const QString& container, containers) { absoluteContainer += container; - graph = m_namedGraphs[absoluteContainer] = agsubg(graph, ("cluster_" + absoluteContainer).toUtf8().data(), 1); + graph = m_namedGraphs[absoluteContainer] = agsubg(graph, (QLatin1String("cluster_") + absoluteContainer).toUtf8().data(), 1); agsafeset(graph, LABEL, container.toUtf8().data(), EMPTY); } - Agnode_t *node = agnode(graph, (containers.join("") + label).toUtf8().data(), 1); + Agnode_t *node = agnode(graph, (containers.join(QString()) + label).toUtf8().data(), 1); agsafeset(node, SHAPE, BOX, EMPTY); QColor c = colorFromQualifiedIdentifier(label); char color[8]; std::sprintf (color, "#%02x%02x%02x", c.red(), c.green(), c.blue()); agsafeset(node, STYLE, FILLED, EMPTY); agsafeset(node, FILLCOLOR, color, EMPTY); agsafeset(node, LABEL, label.toUtf8().data(), EMPTY); } void DotControlFlowGraph::foundFunctionCall(const QStringList &sourceContainers, const QString &source, const QStringList &targetContainers, const QString &target) { if (!m_rootGraph) { // This shouldn't happen, as the graph should be generated before this function // is connected. Q_ASSERT(false); return; } Agraph_t *sourceGraph, *targetGraph, *newGraph; sourceGraph = targetGraph = m_rootGraph; QString absoluteContainer; foreach (const QString& container, sourceContainers) { Agraph_t *previousGraph = sourceGraph; absoluteContainer += container; if (!m_namedGraphs.contains(absoluteContainer)) { - newGraph = agsubg(previousGraph, ("cluster_" + absoluteContainer).toUtf8().data(), 1); + newGraph = agsubg(previousGraph, (QLatin1String("cluster_") + absoluteContainer).toUtf8().data(), 1); m_namedGraphs.insert(absoluteContainer, newGraph); agsafeset(newGraph, LABEL, container.toUtf8().data(), EMPTY); } sourceGraph = m_namedGraphs[absoluteContainer]; } absoluteContainer.clear(); foreach (const QString& container, targetContainers) { Agraph_t *previousGraph = targetGraph; absoluteContainer += container; if (!m_namedGraphs.contains(absoluteContainer)) { - Agraph_t *newGraph = agsubg(previousGraph, ("cluster_" + absoluteContainer).toUtf8().data(), 1); + Agraph_t *newGraph = agsubg(previousGraph, (QLatin1String("cluster_") + absoluteContainer).toUtf8().data(), 1); m_namedGraphs.insert(absoluteContainer, newGraph); agsafeset(newGraph, LABEL, container.toUtf8().data(), EMPTY); } targetGraph = m_namedGraphs[absoluteContainer]; } - Agnode_t* src = agnode(sourceGraph, (sourceContainers.join("") + source).toUtf8().data(), 1); - Agnode_t* tgt = agnode(targetGraph, (targetContainers.join("") + target).toUtf8().data(), 1); + Agnode_t* src = agnode(sourceGraph, (sourceContainers.join(QString()) + source).toUtf8().data(), 1); + Agnode_t* tgt = agnode(targetGraph, (targetContainers.join(QString()) + target).toUtf8().data(), 1); char color[8]; char ID[] = "id"; QColor c = colorFromQualifiedIdentifier(source); std::sprintf (color, "#%02x%02x%02x", c.red(), c.green(), c.blue()); agsafeset(src, STYLE, FILLED, EMPTY); agsafeset(src, FILLCOLOR, color, EMPTY); agsafeset(src, SHAPE, BOX, EMPTY); agsafeset(src, LABEL, source.toUtf8().data(), EMPTY); c = colorFromQualifiedIdentifier(target); std::sprintf (color, "#%02x%02x%02x", c.red(), c.green(), c.blue()); agsafeset(tgt, STYLE, FILLED, EMPTY); agsafeset(tgt, FILLCOLOR, color, EMPTY); agsafeset(tgt, SHAPE, BOX, EMPTY); agsafeset(tgt, LABEL, target.toUtf8().data(), EMPTY); Agedge_t* edge; if (sourceGraph == targetGraph) edge = agedge(sourceGraph, src, tgt, nullptr, 1); else edge = agedge(m_rootGraph, src, tgt, nullptr, 1); - agsafeset(edge, ID, (source + "->" + target).toUtf8().data(), EMPTY); + agsafeset(edge, ID, (source + QLatin1String("->") + target).toUtf8().data(), EMPTY); } const QColor& DotControlFlowGraph::colorFromQualifiedIdentifier(const QString &label) { - if (m_colorMap.contains(label.split("::")[0])) - return m_colorMap[label.split("::")[0]]; - else - return m_colorMap[label.split("::")[0]] = QColor::fromHsv(qrand() % 256, 255, 190); + const QString qid = label.split(QLatin1String("::")).value(0); + auto it = m_colorMap.find(qid); + if (it == m_colorMap.end()) { + it = m_colorMap.insert(qid, QColor::fromHsv(qrand() % 256, 255, 190)); + } + return *it; } diff --git a/duchaincontrolflow.cpp b/duchaincontrolflow.cpp index 29156e1..e4a23df 100644 --- a/duchaincontrolflow.cpp +++ b/duchaincontrolflow.cpp @@ -1,625 +1,629 @@ /*************************************************************************** * 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(nullptr), m_currentProject(nullptr), 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(nullptr) { 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); + m_identifierDeclarationMap[containers.join(QString()) + 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, &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 = nullptr; 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 = nullptr; 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, &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); + m_identifierDeclarationMap[sourceContainers.join(QString()) + 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); + m_identifierDeclarationMap[targetContainers.join(QString()) + 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); + const QString edgeTag = sourceLabel + QLatin1String("->") + targetLabel; + if (!m_arcUsesMap.values(edgeTag).contains(pair)) + m_arcUsesMap.insertMulti(edgeTag, 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); + const QString edgeTag = sourceLabel + QLatin1String("->") + targetLabel; + if (!m_arcUsesMap.values(edgeTag).contains(pair)) + m_arcUsesMap.insertMulti(edgeTag, pair); // Store method definition for navigation - m_identifierDeclarationMap[targetContainers.join("") + targetShortName] = IndexedDeclaration(declarationFromControlFlowMode(calledFunctionDefinition)); + m_identifierDeclarationMap[targetContainers.join(QString()) + 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 + if (label.contains(QLatin1String("->")) && 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 = nullptr; 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)) { if (auto* project = ICore::self()->projectController()->findProjectForUrl(definition->url().toUrl())) { containers << project->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("::")) + foreach(const QString &container, strGlobalNamespaceOrFolderNames.split(QLatin1String("::"))) 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('/')) + if (declarationUrl.endsWith(QLatin1Char('/'))) declarationUrl.chop(1); - if (declarationUrl.startsWith('/')) + if (declarationUrl.startsWith(QLatin1Char('/'))) declarationUrl.remove(0, 1); - declarationUrl = declarationUrl.replace('/', "::"); + declarationUrl = declarationUrl.replace(QLatin1Char('/'), QLatin1String("::")); 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 + "::"); + prependedQualifiedName.prepend(prefix + QLatin1String("::")); } 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()); + if (shortName.contains(container)) { + const QString containerPrefix = container + QLatin1String("::"); + shortName.remove(shortName.indexOf(containerPrefix), containerPrefix.length()); + } } return shortName; } diff --git a/kdevcontrolflowgraphviewplugin.cpp b/kdevcontrolflowgraphviewplugin.cpp index 8464333..ced773b 100644 --- a/kdevcontrolflowgraphviewplugin.cpp +++ b/kdevcontrolflowgraphviewplugin.cpp @@ -1,589 +1,589 @@ /*************************************************************************** * 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) {} QWidget* create(QWidget* parent = nullptr) override { return new ControlFlowGraphView(m_plugin, parent); } Qt::DockWidgetArea defaultPosition() override { return Qt::BottomDockWidgetArea; } QString id() const override { - return "org.kdevelop.ControlFlowGraphView"; + return QStringLiteral("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(nullptr), m_project(nullptr), m_abort(false) { core()->uiController()->addToolView(i18n("Control Flow Graph"), m_toolViewFactory); 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, &QAction::triggered, this, &KDevControlFlowGraphViewPlugin::slotExportControlFlowGraph, Qt::UniqueConnection); m_exportClassControlFlowGraph = new QAction(i18n("Export Class Control Flow Graph"), this); connect(m_exportClassControlFlowGraph, &QAction::triggered, this, &KDevControlFlowGraphViewPlugin::slotExportClassControlFlowGraph, Qt::UniqueConnection); m_exportProjectControlFlowGraph = new QAction(i18n("Export Project Control Flow Graph"), this); 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 nullptr; } KDevelop::ContextMenuExtension KDevControlFlowGraphViewPlugin::contextMenuExtension(KDevelop::Context* context, QWidget* parent) { Q_UNUSED(parent); 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(), &KTextEditor::Document::viewCreated, this, &KDevControlFlowGraphViewPlugin::viewCreated); } void KDevControlFlowGraphViewPlugin::viewCreated(KTextEditor::Document *document, KTextEditor::View *view) { Q_UNUSED(document); 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, &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, &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, &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 = nullptr; 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 = nullptr; 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 = nullptr; 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