File indexing completed on 2024-04-28 05:41:05

0001 /*
0002     Copyright (C) 2013-2014 Volker Krause <vkrause@kde.org>
0003 
0004     This program is free software; you can redistribute it and/or modify it
0005     under the terms of the GNU Library General Public License as published by
0006     the Free Software Foundation; either version 2 of the License, or (at your
0007     option) any later version.
0008 
0009     This program is distributed in the hope that it will be useful, but WITHOUT
0010     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0011     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
0012     License for more details.
0013 
0014     You should have received a copy of the GNU General Public License
0015     along with this program.  If not, see <https://www.gnu.org/licenses/>.
0016 */
0017 
0018 #include "mainwindow.h"
0019 #include "ui_mainwindow.h"
0020 
0021 #include <elf/elffileset.h>
0022 
0023 #include <checks/structurepackingcheck.h>
0024 
0025 #include <elfmodel/elfmodel.h>
0026 #include <navigator/codenavigator.h>
0027 
0028 #include <QFileDialog>
0029 #include <QMessageBox>
0030 #include <QSettings>
0031 #include <QStatusBar>
0032 #include <QStandardItemModel>
0033 
0034 #include <elf.h>
0035 
0036 static void addView(QStandardItemModel *model, const QString& iconName, const QString& title)
0037 {
0038     auto icon = QIcon::fromTheme(iconName);
0039     if (icon.isNull())
0040         icon = QIcon::fromTheme(QStringLiteral("dialog-information")); // fallback
0041     model->appendRow(new QStandardItem(icon, title));
0042 }
0043 
0044 MainWindow::MainWindow(QWidget* parent): QMainWindow(parent), ui(new Ui::MainWindow), m_elfModel(new ElfModel(this))
0045 {
0046     ui->setupUi(this);
0047 
0048     ui->elfStructureView->setModel(m_elfModel);
0049     ui->sizeTreeMapView->setModel(m_elfModel);
0050 
0051     connect(ui->stackedWidget, &QStackedWidget::currentChanged, this, &MainWindow::currentViewChanged);
0052 
0053     connect(ui->actionOpen, &QAction::triggered, this, &MainWindow::fileOpen);
0054     connect(ui->actionQuit, &QAction::triggered, this, &MainWindow::close);
0055     connect(ui->actionReopenPreviousFile, &QAction::triggered, this, &MainWindow::reloadFileOnStartup);
0056 
0057     auto viewModel = new QStandardItemModel(this);
0058     addView(viewModel, QStringLiteral("document-preview"), QStringLiteral("ELF Structure"));
0059     addView(viewModel, QStringLiteral("table"), QStringLiteral("Size Tree Map"));
0060     addView(viewModel, QStringLiteral("view-list-tree"), QStringLiteral("Dependencies"));
0061     addView(viewModel, QStringLiteral("code-class"), QStringLiteral("Data Types"));
0062     addView(viewModel, QStringLiteral("chronometer"), QStringLiteral("Performance"));
0063     addView(viewModel, QStringLiteral("dialog-warning"), QStringLiteral("Issues"));
0064     ui->viewSelector->setModel(viewModel);
0065     connect(ui->viewSelector, &SidePane::currentIndexChanged, this, [this](int index){
0066         ui->stackedWidget->setCurrentIndex(index);
0067     });
0068 
0069     ui->menuSettings->addAction(CodeNavigator::configMenu(this));
0070 
0071     restoreSettings();
0072     currentViewChanged();
0073 }
0074 
0075 MainWindow::~MainWindow()
0076 {
0077 }
0078 
0079 void MainWindow::closeEvent(QCloseEvent *event)
0080 {
0081     QSettings settings;
0082     settings.beginGroup(QStringLiteral("MainWindow"));
0083     settings.setValue(QStringLiteral("geometry"), saveGeometry());
0084     settings.setValue(QStringLiteral("windowState"), saveState());
0085     settings.setValue(QStringLiteral("currentView"), ui->stackedWidget->currentIndex());
0086     settings.endGroup();
0087     QMainWindow::closeEvent(event);
0088 }
0089 
0090 void MainWindow::fileOpen()
0091 {
0092     const QFileInfo fi(m_currentFileName);
0093 
0094     const QString fileName = QFileDialog::getOpenFileName(this, tr("Open ELF Object"), fi.path());
0095     if (fileName.isEmpty())
0096         return;
0097 
0098     m_currentFileName = fileName;
0099     loadFile(fileName);
0100 }
0101 
0102 void MainWindow::reloadFileOnStartup()
0103 {
0104     QSettings settings;
0105     settings.setValue(QStringLiteral("Settings/ReloadPreviousFile"), ui->actionReopenPreviousFile->isChecked());
0106 }
0107 
0108 void MainWindow::restoreSettings()
0109 {
0110     QSettings settings;
0111     ui->sizeTreeMapView->restoreSettings();
0112     ui->actionReopenPreviousFile->setChecked(settings.value(QStringLiteral("Settings/ReloadPreviousFile"), false).toBool());
0113     if (ui->actionReopenPreviousFile->isChecked()) {
0114         auto fileName = settings.value(QStringLiteral("Recent/PreviousFile")).toString();
0115         const QFileInfo fi(fileName);
0116         if (!fi.isReadable() || !fi.isFile())
0117             return;
0118         m_currentFileName = std::move(fileName);
0119         loadFile(m_currentFileName);
0120     }
0121 
0122     settings.beginGroup(QStringLiteral("MainWindow"));
0123     restoreGeometry(settings.value(QStringLiteral("geometry")).toByteArray());
0124     restoreState(settings.value(QStringLiteral("windowState")).toByteArray());
0125     const auto currentViewIndex = settings.value(QStringLiteral("currentView"), 0).toInt();
0126     const auto currentIdx = ui->viewSelector->model()->index(currentViewIndex, 0);
0127     ui->viewSelector->selectionModel()->select(currentIdx, QItemSelectionModel::ClearAndSelect);
0128     ui->viewSelector->selectionModel()->setCurrentIndex(currentIdx, QItemSelectionModel::ClearAndSelect);
0129     settings.endGroup();
0130 }
0131 
0132 void MainWindow::loadFile(const QString& fileName)
0133 {
0134     if (fileName.isEmpty() || windowFilePath() == fileName)
0135         return;
0136     setWindowFilePath(fileName);
0137 
0138     m_fileSet.reset(new ElfFileSet(this));
0139     m_fileSet->addFile(fileName);
0140 
0141     if (m_fileSet->size() == 0) {
0142         QMessageBox::critical(this, tr("Failed to load ELF file"), tr("Could not load %1.").arg(fileName));
0143 
0144         m_fileSet.reset();
0145 
0146         statusBar()->showMessage(tr("Failed to load %1.").arg(fileName));
0147     } else {
0148         m_fileSet->topologicalSort();
0149 
0150         QSettings settings;
0151         settings.setValue(QStringLiteral("Recent/PreviousFile"), fileName);
0152 
0153         statusBar()->showMessage(tr("Loaded %1.").arg(fileName));
0154     }
0155 
0156     m_elfModel->setFileSet(m_fileSet.get());
0157     ui->dependencyView->setFileSet(m_fileSet.get());
0158     ui->loadTimeView->setFileSet(m_fileSet.get());
0159     ui->typeView->setFileSet(m_fileSet.get());
0160     ui->issuesView->setFileSet(m_fileSet.get());
0161 }
0162 
0163 void MainWindow::currentViewChanged()
0164 {
0165     ui->menuView->clear();
0166     ui->menuView->addActions(ui->stackedWidget->currentWidget()->actions());
0167     ui->menuView->setEnabled(!ui->menuView->isEmpty());
0168 }