diff --git a/AUTHORS b/AUTHORS index 6440127..4b012f7 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,6 +1,6 @@ Main author: Milian Wolff Contributions by: Andrei Nistor -Arnold Dumas +Arnold Dumas diff --git a/app/documentwidget.cpp b/app/documentwidget.cpp index e8d2074..5e26f2e 100644 --- a/app/documentwidget.cpp +++ b/app/documentwidget.cpp @@ -1,306 +1,306 @@ /* This file is part of Massif Visualizer Copyright 2010 Milian Wolff - Copyright 2013 Arnold Dumas + Copyright 2013 Arnold Dumas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "documentwidget.h" #include #include "massifdata/filedata.h" #include "massifdata/parser.h" #include "massifdata/parseworker.h" #include "massifdata/snapshotitem.h" #include "massifdata/treeleafitem.h" #include "massifdata/util.h" #include #include #include // forward include not available until later KDE versions... #include #include #include #include #include #include #include #include #include #include #include "charttab.h" #include "allocatorstab.h" #ifdef HAVE_KGRAPHVIEWER #include "callgraphtab.h" #include #include #include #endif using namespace Massif; DocumentWidget::DocumentWidget(const QUrl& file, const QStringList& customAllocators, KXMLGUIClient* guiParent, QWidget* parent) : QWidget(parent) , KXMLGUIClient(guiParent) , m_data(0) , m_parseWorker(new ParseWorker) , m_file(file) , m_currentTab(0) , m_stackedWidget(new QStackedWidget(this)) , m_tabs(new QTabWidget(m_stackedWidget)) , m_errorMessage(0) , m_loadingMessage(0) , m_loadingProgressBar(0) , m_stopParserButton(0) , m_isLoaded(false) { connect(m_parseWorker, &ParseWorker::finished, this, &DocumentWidget::parserFinished); connect(m_parseWorker, &ParseWorker::error, this, &DocumentWidget::showError); connect(m_parseWorker, &ParseWorker::progressRange, this, &DocumentWidget::setRange); connect(m_parseWorker, &ParseWorker::progress, this, &DocumentWidget::setProgress); // Create dedicated thread for this document. // TODO: use ThreadWeaver QThread* thread = new QThread(this); thread->start(); m_parseWorker->moveToThread(thread); m_parseWorker->parse(file, customAllocators); setXMLFile(QStringLiteral("documentwidgetui.rc"), true); // Set m_stackedWidget as the main widget. setLayout(new QVBoxLayout(this)); layout()->addWidget(m_stackedWidget); m_tabs->setTabPosition(QTabWidget::South); m_stackedWidget->addWidget(m_tabs); // Second widget : loadingPage QWidget* loadingPage = new QWidget(m_stackedWidget); QVBoxLayout* verticalLayout = new QVBoxLayout(loadingPage); QSpacerItem* upperSpacerItem = new QSpacerItem(20, 247, QSizePolicy::Minimum, QSizePolicy::Expanding); verticalLayout->addItem(upperSpacerItem); m_loadingMessage = new QLabel(loadingPage); m_loadingMessage->setText(i18n("loading file %1...", file.toString())); m_loadingMessage->setAlignment(Qt::AlignCenter); verticalLayout->addWidget(m_loadingMessage); m_loadingProgressBar = new QProgressBar(loadingPage); m_loadingProgressBar->setValue(24); m_loadingProgressBar->setRange(0, 0); verticalLayout->addWidget(m_loadingProgressBar); QWidget* stopParserWidget = new QWidget(loadingPage); stopParserWidget->setLayoutDirection(Qt::LeftToRight); QHBoxLayout* stopParserWidgetLayout = new QHBoxLayout(stopParserWidget); m_stopParserButton = new QToolButton(stopParserWidget); m_stopParserButton->setObjectName(QStringLiteral("stopParsing")); m_stopParserButton->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); m_stopParserButton->setIcon(QIcon::fromTheme(QStringLiteral("process-stop"))); m_stopParserButton->setIconSize(QSize(48, 48)); connect(m_stopParserButton, &QToolButton::clicked, this, &DocumentWidget::requestClose); stopParserWidgetLayout->addWidget(m_stopParserButton); verticalLayout->addWidget(stopParserWidget); QSpacerItem* bottomSpacerItem = new QSpacerItem(20, 230, QSizePolicy::Minimum, QSizePolicy::Expanding); verticalLayout->addItem(bottomSpacerItem); m_stackedWidget->addWidget(loadingPage); // By default we show the loadingPage. m_stackedWidget->setCurrentIndex(1); } DocumentWidget::~DocumentWidget() { stopParser(); if (m_data) { delete m_data; m_data = 0; m_file.clear(); } } FileData* DocumentWidget::data() const { return m_data; } QUrl DocumentWidget::file() const { return m_file; } bool DocumentWidget::isLoaded() const { return m_isLoaded; } void DocumentWidget::settingsChanged() { for (int i = 0; i < m_tabs->count(); ++i) { static_cast(m_tabs->widget(i))->settingsChanged(); } } void DocumentWidget::parserFinished(const QUrl& file, FileData* data) { Q_ASSERT(data->peak()); qDebug() << "loaded massif file:" << file; qDebug() << "description:" << data->description(); qDebug() << "command:" << data->cmd(); qDebug() << "time unit:" << data->timeUnit(); qDebug() << "snapshots:" << data->snapshots().size(); qDebug() << "peak: snapshot #" << data->peak()->number() << "after" << data->peak()->time() << data->timeUnit(); qDebug() << "peak cost:" << prettyCost(data->peak()->memHeap()) << "heap" << prettyCost(data->peak()->memHeapExtra()) << "heap extra" << prettyCost(data->peak()->memStacks()) << "stacks"; m_data = data; m_file = file; m_tabs->addTab(new ChartTab(m_data, this, this), QIcon::fromTheme(QStringLiteral("office-chart-area-stacked")), i18n("Memory Chart")); #ifdef HAVE_KGRAPHVIEWER static KPluginFactory *factory = KPluginLoader("kgraphviewerpart").factory(); if (factory) { KParts::ReadOnlyPart* part = factory->create("kgraphviewerpart", this); if (part) { m_tabs->addTab(new CallGraphTab(m_data, part, this, this), QIcon::fromTheme(QStringLiteral("kgraphviewer")), i18n("Callgraph")); } } #endif m_tabs->addTab(new AllocatorsTab(m_data, this, this), QIcon::fromTheme(QStringLiteral("view-list-text")), i18n("Allocators")); for (int i = 0; i < m_tabs->count(); ++i) { DocumentTabInterface* tab = static_cast(m_tabs->widget(i)); connect(tab, &DocumentTabInterface::modelItemSelected, this, &DocumentWidget::modelItemSelected); connect(tab, &DocumentTabInterface::contextMenuRequested, this, &DocumentWidget::contextMenuRequested); } m_tabs->setCurrentIndex(0); connect(m_tabs, &QTabWidget::currentChanged, this, &DocumentWidget::slotTabChanged); slotTabChanged(0); m_isLoaded = true; // Switch to the display page and notify that everything is setup. m_stackedWidget->setCurrentIndex(0); emit loadingFinished(); } void DocumentWidget::addGuiActions(KXMLGUIFactory* factory) { factory->addClient(this); // ensure only the current tab's client is in the factory // otherwise the actions from the other tabs are visible const int current = m_tabs->currentIndex(); for (int i = 0; i < m_tabs->count(); ++i) { if (i != current) { factory->removeClient(static_cast(m_tabs->widget(i))); } } } void DocumentWidget::clearGuiActions(KXMLGUIFactory* factory) { factory->removeClient(this); } void DocumentWidget::slotTabChanged(int tab) { if (!factory()) { return; } if (m_currentTab) { factory()->removeClient(m_currentTab); } if (tab >= 0 && tab < m_tabs->count()) { m_currentTab = static_cast(m_tabs->widget(tab)); factory()->addClient(m_currentTab); } } void DocumentWidget::selectModelItem(const ModelItem& item) { for (int i = 0; i < m_tabs->count(); ++i) { static_cast(m_tabs->widget(i))->selectModelItem(item); } } void DocumentWidget::setProgress(int value) { m_loadingProgressBar->setValue(value); } void DocumentWidget::setRange(int minimum, int maximum) { m_loadingProgressBar->setRange(minimum, maximum); } void DocumentWidget::showError(const QString& title, const QString& error) { if (!m_errorMessage) { m_errorMessage = new KMessageWidget(m_stackedWidget); m_stackedWidget->addWidget(m_errorMessage); m_errorMessage->setWordWrap(true); m_errorMessage->setMessageType(KMessageWidget::Error); m_errorMessage->setCloseButtonVisible(false); } m_errorMessage->setText(QString::fromLatin1("%1

%2

").arg(title).arg(error)); m_stackedWidget->setCurrentWidget(m_errorMessage); } void DocumentWidget::stopParser() { if (!m_parseWorker) { return; } QThread* thread = m_parseWorker->thread(); m_parseWorker->stop(); m_parseWorker->deleteLater(); m_parseWorker = 0; thread->quit(); thread->wait(); } diff --git a/app/documentwidget.h b/app/documentwidget.h index 0c3cced..b43d358 100644 --- a/app/documentwidget.h +++ b/app/documentwidget.h @@ -1,105 +1,105 @@ /* This file is part of Massif Visualizer Copyright 2010 Milian Wolff - Copyright 2013 Arnold Dumas + Copyright 2013 Arnold Dumas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DOCUMENTWIDGET_H #define DOCUMENTWIDGET_H #include #include #include #include #include "visualizer/modelitem.h" class DocumentTabInterface; class QMenu; class QLabel; class QProgressBar; class QToolButton; class QStackedWidget; class QTabWidget; class KMessageWidget; namespace Massif { class FileData; class ParseWorker; } class DocumentWidget : public QWidget, public KXMLGUIClient { Q_OBJECT public: explicit DocumentWidget(const QUrl &file, const QStringList& customAllocators, KXMLGUIClient* guiParent, QWidget* parent = 0); ~DocumentWidget(); Massif::FileData* data() const; QUrl file() const; bool isLoaded() const; void settingsChanged(); void addGuiActions(KXMLGUIFactory* factory); void clearGuiActions(KXMLGUIFactory* factory); void selectModelItem(const Massif::ModelItem& item); Q_SIGNALS: void loadingFinished(); void modelItemSelected(const Massif::ModelItem& item); void contextMenuRequested(const Massif::ModelItem& item, QMenu* menu); void requestClose(); private Q_SLOTS: void stopParser(); void parserFinished(const QUrl& file, Massif::FileData* data); void setProgress(int value); void setRange(int minimum, int maximum); void showError(const QString& title, const QString& error); void slotTabChanged(int tab); private: Massif::FileData* m_data; Massif::ParseWorker* m_parseWorker; QUrl m_file; DocumentTabInterface* m_currentTab; QStackedWidget* m_stackedWidget; QTabWidget* m_tabs; KMessageWidget* m_errorMessage; QLabel* m_loadingMessage; QProgressBar* m_loadingProgressBar; QToolButton* m_stopParserButton; bool m_isLoaded; }; #endif // DOCUMENTWIDGET_H diff --git a/app/main.cpp b/app/main.cpp index ed689fc..4cc39c2 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -1,78 +1,78 @@ /* This file is part of Massif Visualizer Copyright 2010 Milian Wolff This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include #include "mainwindow.h" int main( int argc, char *argv[] ) { QApplication app(argc, argv); KLocalizedString::setApplicationDomain("massif-visualizer"); KAboutData aboutData(QStringLiteral("massif-visualizer"), i18n("Massif Visualizer"), QStringLiteral("0.7"), i18n("A visualizer for output generated by Valgrind's massif tool."), KAboutLicense::LGPL, i18n("Copyright 2010-2015, Milian Wolff "), QString(), QStringLiteral("massif-visualizer@kde.org")); aboutData.addAuthor(i18n("Milian Wolff"), i18n("Original author, maintainer"), QStringLiteral("mail@milianw.de"), QStringLiteral("http://milianw.de")); aboutData.addAuthor(i18n("Arnold Dumas"), i18n("Multiple document interface, bug fixes"), - QStringLiteral("contact@arnolddumas.fr"), QStringLiteral("http://arnolddumas.fr")); + QStringLiteral("arnold@dumas.at"), QStringLiteral("http://arnold.dumas.at")); aboutData.setOrganizationDomain("kde.org"); KAboutData::setApplicationData(aboutData); app.setApplicationName(aboutData.componentName()); app.setApplicationDisplayName(aboutData.displayName()); app.setOrganizationDomain(aboutData.organizationDomain()); app.setWindowIcon(QIcon::fromTheme(QStringLiteral("office-chart-area"))); app.setApplicationVersion(aboutData.version()); QCommandLineParser parser; KAboutData::setApplicationData(aboutData); parser.addVersionOption(); parser.addHelpOption(); aboutData.setupCommandLine(&parser); parser.addPositionalArgument(QStringLiteral("files"), i18n( "Files to load" ), i18n("[FILE...]")); parser.process(app); aboutData.processCommandLine(&parser); Massif::MainWindow* window = new Massif::MainWindow; foreach (const QString &file, parser.positionalArguments()) { window->openFile(QUrl::fromUserInput(file, QDir::currentPath(), QUrl::AssumeLocalFile)); } window->show(); return app.exec(); } diff --git a/app/mainwindow.cpp b/app/mainwindow.cpp index bfcafa6..c134283 100644 --- a/app/mainwindow.cpp +++ b/app/mainwindow.cpp @@ -1,525 +1,525 @@ /* This file is part of Massif Visualizer Copyright 2010 Milian Wolff - Copyright 2013 Arnold Dumas + Copyright 2013 Arnold Dumas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "mainwindow.h" #include "massifdata/filedata.h" #include "massifdata/snapshotitem.h" #include "massifdata/treeleafitem.h" #include "massifdata/util.h" #include "visualizer/totalcostmodel.h" #include "visualizer/detailedcostmodel.h" #include "visualizer/datatreemodel.h" #include "visualizer/filtereddatatreemodel.h" #include "visualizer/dotgraphgenerator.h" #include "massif-visualizer-settings.h" #include "configdialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_KGRAPHVIEWER #include #endif using namespace Massif; // Helper function static KConfigGroup allocatorConfig() { return KSharedConfig::openConfig()->group("Allocators"); } MainWindow::MainWindow(QWidget* parent, Qt::WindowFlags f) : KParts::MainWindow(parent, f) , m_recentFiles(0) , m_close(0) , m_allocatorModel(new QStringListModel(this)) , m_newAllocator(0) , m_removeAllocator(0) , m_shortenTemplates(0) , m_selectPeak(0) , m_currentDocument(0) , m_dataTreeModel(new DataTreeModel(this)) , m_dataTreeFilterModel(new FilteredDataTreeModel(m_dataTreeModel)) , m_settingSelection(false) { ui.setupUi(this); setWindowTitle(i18n("Massif Visualizer")); //BEGIN KGraphViewer bool haveGraphViewer = false; // NOTE: just check if kgraphviewer is available at runtime. // The former logic has been moved to DocumentWidget constructor. #ifdef HAVE_KGRAPHVIEWER KPluginFactory *factory = KPluginLoader("kgraphviewerpart").factory(); if (factory) { KParts::ReadOnlyPart* readOnlyPart = factory->create("kgraphviewerpart", this); if (readOnlyPart) { readOnlyPart->widget()->hide(); haveGraphViewer = true; } } #endif if (!haveGraphViewer) { // cleanup UI when we installed with kgraphviewer but it's not available at runtime KToolBar* callgraphToolbar = toolBar(QStringLiteral("callgraphToolBar")); removeToolBar(callgraphToolbar); delete callgraphToolbar; } //END KGraphViewer ui.documents->setMovable(true); ui.documents->setTabsClosable(true); connect(ui.documents, &QTabWidget::currentChanged, this, &MainWindow::documentChanged); connect(ui.documents, &QTabWidget::tabCloseRequested, this, &MainWindow::closeFileTab); //BEGIN custom allocators tabifyDockWidget(ui.allocatorDock, ui.dataTreeDock); ui.allocatorView->setModel(m_allocatorModel); int iconSize = style()->pixelMetric(QStyle::PM_SmallIconSize); ui.dockMenuBar->setIconSize(QSize(iconSize, iconSize)); ui.dockMenuBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); ui.dockMenuBar->setFloatable(false); ui.dockMenuBar->setMovable(false); KConfigGroup cfg = allocatorConfig(); m_allocatorModel->setStringList(cfg.entryMap().values()); connect(m_allocatorModel, &QStringListModel::modelReset, this, &MainWindow::allocatorsChanged); connect(m_allocatorModel, &QStringListModel::dataChanged, this, &MainWindow::allocatorsChanged); connect(ui.dataTreeView, &QTreeView::customContextMenuRequested, this, &MainWindow::dataTreeContextMenuRequested); ui.dataTreeView->setContextMenuPolicy(Qt::CustomContextMenu); connect(ui.allocatorView, &QTreeView::customContextMenuRequested, this, &MainWindow::allocatorViewContextMenuRequested); ui.allocatorView->setContextMenuPolicy(Qt::CustomContextMenu); //END custom allocators setupActions(); setupGUI(StandardWindowOptions(Default ^ StatusBar)); statusBar()->hide(); ui.dataTreeView->setModel(m_dataTreeFilterModel); connect(ui.filterDataTree, &KLineEdit::textChanged, m_dataTreeFilterModel, &FilteredDataTreeModel::setFilter); connect(ui.dataTreeView->selectionModel(), &QItemSelectionModel::currentChanged, this, &MainWindow::treeSelectionChanged); // open page ui.stackedWidget->setCurrentWidget(ui.openPage); } MainWindow::~MainWindow() { while (ui.documents->count()) { closeCurrentFile(); } m_recentFiles->saveEntries(KSharedConfig::openConfig()->group( QString() )); } void MainWindow::setupActions() { QAction* openFile = KStandardAction::open(this, SLOT(openFile()), actionCollection()); m_recentFiles = KStandardAction::openRecent(this, SLOT(openFile(QUrl)), actionCollection()); m_recentFiles->loadEntries(KSharedConfig::openConfig()->group( QString() )); QAction* reload = KStandardAction::redisplay(this, SLOT(reloadCurrentFile()), actionCollection()); actionCollection()->addAction(QStringLiteral("file_reload"), reload); reload->setEnabled(false); m_close = KStandardAction::close(this, SLOT(closeCurrentFile()), actionCollection()); m_close->setEnabled(false); KStandardAction::quit(qApp, SLOT(closeAllWindows()), actionCollection()); KStandardAction::preferences(this, SLOT(preferences()), actionCollection()); m_shortenTemplates = new QAction(QIcon::fromTheme(QStringLiteral("shortentemplates")), i18n("Shorten Templates"), actionCollection()); m_shortenTemplates->setCheckable(true); m_shortenTemplates->setChecked(Settings::shortenTemplates()); connect(m_shortenTemplates, &QAction::toggled, this, &MainWindow::slotShortenTemplates); actionCollection()->addAction(QStringLiteral("shorten_templates"), m_shortenTemplates); m_selectPeak = new QAction(QIcon::fromTheme(QStringLiteral("flag-red")), i18n("Select peak snapshot"), actionCollection()); connect(m_selectPeak, &QAction::triggered, this, &MainWindow::selectPeakSnapshot); actionCollection()->addAction(QStringLiteral("selectPeak"), m_selectPeak); m_selectPeak->setEnabled(false); //BEGIN custom allocators m_newAllocator = new QAction(QIcon::fromTheme(QStringLiteral("list-add")), i18n("add"), ui.allocatorDock); m_newAllocator->setToolTip(i18n("add custom allocator")); connect(m_newAllocator, &QAction::triggered, this, &MainWindow::slotNewAllocator); ui.dockMenuBar->addAction(m_newAllocator); m_removeAllocator = new QAction(QIcon::fromTheme(QStringLiteral("list-remove")), i18n("remove"), ui.allocatorDock); m_newAllocator->setToolTip(i18n("remove selected allocator")); connect(m_removeAllocator, &QAction::triggered, this, &MainWindow::slotRemoveAllocator); connect(ui.allocatorView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MainWindow::allocatorSelectionChanged); m_removeAllocator->setEnabled(false); ui.dockMenuBar->addAction(m_removeAllocator); m_markCustomAllocator = new QAction(i18n("mark as custom allocator"), ui.allocatorDock); connect(m_markCustomAllocator, &QAction::triggered, this, &MainWindow::slotMarkCustomAllocator, Qt::QueuedConnection); //END custom allocators //dock actions actionCollection()->addAction(QStringLiteral("toggleDataTree"), ui.dataTreeDock->toggleViewAction()); actionCollection()->addAction(QStringLiteral("toggleAllocators"), ui.allocatorDock->toggleViewAction()); //open page actions ui.openFile->setDefaultAction(openFile); ui.openFile->setText(i18n("Open Massif Data File")); ui.openFile->setIconSize(QSize(48, 48)); } void MainWindow::preferences() { if (ConfigDialog::isShown()) { return; } ConfigDialog* dlg = new ConfigDialog(this); connect(dlg, &ConfigDialog::settingsChanged, this, &MainWindow::settingsChanged); dlg->show(); } void MainWindow::settingsChanged() { if (Settings::self()->shortenTemplates() != m_shortenTemplates->isChecked()) { m_shortenTemplates->setChecked(Settings::self()->shortenTemplates()); } Settings::self()->save(); if (m_currentDocument) { m_currentDocument->settingsChanged(); } ui.dataTreeView->viewport()->update(); } void MainWindow::openFile() { const QList files = QFileDialog::getOpenFileUrls(this, i18n("Open Massif Output File"), QUrl(), i18n("Massif data files (massif.out.*)")); foreach (const QUrl& file, files) { openFile(file); } } void MainWindow::reloadCurrentFile() { if (m_currentDocument->file().isValid()) { openFile(QUrl(m_currentDocument->file())); } } void MainWindow::openFile(const QUrl& file) { Q_ASSERT(file.isValid()); // Is file already opened ? int indexToInsert = -1; for (int i = 0; i < ui.documents->count(); ++i) { if (qobject_cast(ui.documents->widget(i))->file() == file) { indexToInsert = i; break; } } DocumentWidget* documentWidget = new DocumentWidget(file, m_allocatorModel->stringList(), this, this); if (indexToInsert != -1) { // Remove existing instance of the file. ui.documents->setCurrentIndex(indexToInsert); closeCurrentFile(); // Insert the new tab at the correct position. ui.documents->insertTab(indexToInsert, documentWidget, file.fileName()); ui.documents->setCurrentIndex(indexToInsert); } else { const int idx = ui.documents->addTab(documentWidget, file.fileName()); ui.documents->setCurrentIndex(idx); } connect(documentWidget, &DocumentWidget::loadingFinished, this, &MainWindow::documentChanged); connect(documentWidget, &DocumentWidget::requestClose, this, &MainWindow::closeRequested); m_recentFiles->addUrl(file); ui.stackedWidget->setCurrentWidget(ui.displayPage); } void MainWindow::treeSelectionChanged(const QModelIndex& now, const QModelIndex& before) { if (!m_currentDocument || m_settingSelection || now == before) { return; } m_settingSelection = true; const ModelItem& item = now.data(DataTreeModel::ModelItemRole).value(); m_currentDocument->selectModelItem(item); m_settingSelection = false; } void MainWindow::modelItemSelected(const ModelItem& item) { if (!m_currentDocument || m_settingSelection) { return; } m_settingSelection = true; const QModelIndex& newIndex = m_dataTreeFilterModel->mapFromSource( m_dataTreeModel->indexForItem(item) ); ui.dataTreeView->selectionModel()->clearSelection(); ui.dataTreeView->selectionModel()->setCurrentIndex(newIndex, QItemSelectionModel::Select | QItemSelectionModel::Rows); ui.dataTreeView->scrollTo(ui.dataTreeView->selectionModel()->currentIndex()); m_currentDocument->selectModelItem(item); m_settingSelection = false; } void MainWindow::selectPeakSnapshot() { ui.dataTreeView->selectionModel()->clearSelection(); ui.dataTreeView->selectionModel()->setCurrentIndex( m_dataTreeFilterModel->mapFromSource( m_dataTreeModel->indexForSnapshot(m_currentDocument->data()->peak())), QItemSelectionModel::Select | QItemSelectionModel::Rows ); } void MainWindow::closeCurrentFile() { closeFileTab(ui.documents->currentIndex()); } void MainWindow::closeRequested() { DocumentWidget* widget = qobject_cast(sender()); Q_ASSERT(widget); closeFileTab(ui.documents->indexOf(widget)); } void MainWindow::closeFileTab(int idx) { Q_ASSERT(idx != -1); ui.documents->widget(idx)->deleteLater(); ui.documents->removeTab(idx); } void MainWindow::allocatorsChanged() { KConfigGroup cfg = allocatorConfig(); cfg.deleteGroup(); int i = 0; foreach(const QString& allocator, m_allocatorModel->stringList()) { if (allocator.isEmpty()) { m_allocatorModel->removeRow(i); continue; } cfg.writeEntry(QString::number(i++), allocator); } cfg.sync(); if (m_currentDocument) { reloadCurrentFile(); } } void MainWindow::allocatorSelectionChanged() { m_removeAllocator->setEnabled(ui.allocatorView->selectionModel()->hasSelection()); } void MainWindow::slotNewAllocator() { QString allocator = QInputDialog::getText(this, i18n("Add Custom Allocator"), i18n("allocator:")); if (allocator.isEmpty()) { return; } if (!m_allocatorModel->stringList().contains(allocator)) { m_allocatorModel->setStringList(m_allocatorModel->stringList() << allocator); } } void MainWindow::slotRemoveAllocator() { Q_ASSERT(ui.allocatorView->selectionModel()->hasSelection()); foreach(const QModelIndex& idx, ui.allocatorView->selectionModel()->selectedRows()) { m_allocatorModel->removeRow(idx.row(), idx.parent()); } allocatorsChanged(); } void MainWindow::slotMarkCustomAllocator() { const QString allocator = m_markCustomAllocator->data().toString(); Q_ASSERT(!allocator.isEmpty()); if (!m_allocatorModel->stringList().contains(allocator)) { m_allocatorModel->setStringList(m_allocatorModel->stringList() << allocator); } } void MainWindow::allocatorViewContextMenuRequested(const QPoint& pos) { const QModelIndex idx = ui.allocatorView->indexAt(pos); QMenu menu; menu.addAction(m_newAllocator); if (idx.isValid()) { menu.addAction(m_removeAllocator); } menu.exec(ui.allocatorView->mapToGlobal(pos)); } void MainWindow::dataTreeContextMenuRequested(const QPoint& pos) { const QModelIndex idx = ui.dataTreeView->indexAt(pos); const TreeLeafItem* item = idx.data(DataTreeModel::TreeItemRole).value(); if (!item) { return; } QMenu menu; contextMenuRequested(ModelItem(item, 0), &menu); menu.exec(ui.dataTreeView->mapToGlobal(pos)); } void MainWindow::contextMenuRequested(const ModelItem& item, QMenu* menu) { if (!item.first) { // only handle context menu on tree-leaf items for now return; } QByteArray func = functionInLabel(item.first->label()); if (func.length() > 40) { func.resize(40); func.append("..."); } menu->setTitle(QString::fromUtf8(func)); m_markCustomAllocator->setData(item.first->label()); menu->addAction(m_markCustomAllocator); } void MainWindow::slotShortenTemplates(bool shorten) { if (shorten == Settings::self()->shortenTemplates()) { return; } Settings::self()->setShortenTemplates(shorten); settingsChanged(); } void MainWindow::updateWindowTitle() { if (m_currentDocument && m_currentDocument->isLoaded()) { setWindowTitle(i18n("Massif Visualizer - evaluation of %1 (%2)", m_currentDocument->data()->cmd(), m_currentDocument->file().fileName())); } else { setWindowTitle(i18n("Massif Visualizer")); } } void MainWindow::documentChanged() { // required to prevent GUI flickering when changing documents // the changing actions in the toolbar are really flickering bad otherwise setUpdatesEnabled(false); if (m_currentDocument) { m_dataTreeModel->setSource(0); m_dataTreeFilterModel->setFilter(QString()); m_currentDocument->clearGuiActions(guiFactory()); disconnect(m_currentDocument, &DocumentWidget::modelItemSelected, this, &MainWindow::modelItemSelected); disconnect(m_currentDocument, &DocumentWidget::contextMenuRequested, this, &MainWindow::contextMenuRequested); } m_currentDocument = qobject_cast(ui.documents->currentWidget()); updateWindowTitle(); actionCollection()->action(QStringLiteral("file_reload"))->setEnabled(m_currentDocument && m_currentDocument->isLoaded()); m_close->setEnabled(m_currentDocument); m_selectPeak->setEnabled(m_currentDocument && m_currentDocument->isLoaded()); if (!m_currentDocument) { ui.stackedWidget->setCurrentWidget(ui.openPage); setUpdatesEnabled(true); return; } else { m_dataTreeModel->setSource(m_currentDocument->data()); m_currentDocument->addGuiActions(guiFactory()); connect(m_currentDocument, &DocumentWidget::modelItemSelected, this, &MainWindow::modelItemSelected); connect(m_currentDocument, &DocumentWidget::contextMenuRequested, this, &MainWindow::contextMenuRequested); } setUpdatesEnabled(true); } diff --git a/app/mainwindow.h b/app/mainwindow.h index ab524e6..5cc021f 100644 --- a/app/mainwindow.h +++ b/app/mainwindow.h @@ -1,145 +1,145 @@ /* This file is part of Massif Visualizer Copyright 2010 Milian Wolff - Copyright 2013 Arnold Dumas + Copyright 2013 Arnold Dumas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef MASSIF_MAINWINDOW_H #define MASSIF_MAINWINDOW_H #include #include #include "ui_mainwindow.h" #include "documentwidget.h" class QAction; class QSpinBox; class QStringListModel; namespace KChart { class Chart; class HeaderFooter; class Plotter; class CartesianAxis; class Legend; class BarDiagram; } class KRecentFilesAction; #ifdef HAVE_KGRAPHVIEWER namespace KGraphViewer { class KGraphViewerInterface; } #endif namespace Massif { class FilteredDataTreeModel; class DataTreeModel; class SnapshotItem; class TreeLeafItem; class MainWindow : public KParts::MainWindow { Q_OBJECT public: MainWindow(QWidget* parent = 0, Qt::WindowFlags f = 0); virtual ~MainWindow(); void setupActions(); public Q_SLOTS: /** * Open a dialog to pick a massif output file(s) to display. */ void openFile(); /** * Opens @p file as massif output file and visualize it. */ void openFile(const QUrl& file); /** * reload currently opened file */ void reloadCurrentFile(); /** * Close currently opened file. */ void closeCurrentFile(); private Q_SLOTS: void closeRequested(); void closeFileTab(int idx); void preferences(); void settingsChanged(); void treeSelectionChanged(const QModelIndex& now, const QModelIndex& before); void selectPeakSnapshot(); void modelItemSelected(const Massif::ModelItem& item); void contextMenuRequested(const Massif::ModelItem& item, QMenu* menu); void documentChanged(); void allocatorsChanged(); void allocatorSelectionChanged(); void dataTreeContextMenuRequested(const QPoint &pos); void slotNewAllocator(); void slotRemoveAllocator(); /// operates on data of @c m_markCustomAllocator void slotMarkCustomAllocator(); void allocatorViewContextMenuRequested(const QPoint &pos); void slotShortenTemplates(bool); private: void updateWindowTitle(); // Helper Ui::MainWindow ui; KRecentFilesAction* m_recentFiles; QAction* m_close; QStringListModel* m_allocatorModel; QAction* m_newAllocator; QAction* m_removeAllocator; QAction* m_markCustomAllocator; QAction* m_shortenTemplates; QAction* m_selectPeak; DocumentWidget* m_currentDocument; Massif::DataTreeModel* m_dataTreeModel; Massif::FilteredDataTreeModel* m_dataTreeFilterModel; bool m_settingSelection; }; } #endif // MASSIF_MAINWINDOW_H