1 /*
2     Copyright (C) 2013-2014 Volker Krause <vkrause@kde.org>
3 
4     This program is free software; you can redistribute it and/or modify it
5     under the terms of the GNU Library General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or (at your
7     option) any later version.
8 
9     This program is distributed in the hope that it will be useful, but WITHOUT
10     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
12     License for more details.
13 
14     You should have received a copy of the GNU General Public License
15     along with this program.  If not, see <https://www.gnu.org/licenses/>.
16 */
17 
18 #include "mainwindow.h"
19 #include "ui_mainwindow.h"
20 
21 #include <elf/elffileset.h>
22 
23 #include <checks/structurepackingcheck.h>
24 
25 #include <elfmodel/elfmodel.h>
26 #include <navigator/codenavigator.h>
27 
28 #include <QFileDialog>
29 #include <QMessageBox>
30 #include <QSettings>
31 #include <QStatusBar>
32 #include <QStandardItemModel>
33 
34 #include <elf.h>
35 
addView(QStandardItemModel * model,const QString & iconName,const QString & title)36 static void addView(QStandardItemModel *model, const QString& iconName, const QString& title)
37 {
38     auto icon = QIcon::fromTheme(iconName);
39     if (icon.isNull())
40         icon = QIcon::fromTheme(QStringLiteral("dialog-information")); // fallback
41     model->appendRow(new QStandardItem(icon, title));
42 }
43 
MainWindow(QWidget * parent)44 MainWindow::MainWindow(QWidget* parent): QMainWindow(parent), ui(new Ui::MainWindow), m_elfModel(new ElfModel(this))
45 {
46     ui->setupUi(this);
47 
48     ui->elfStructureView->setModel(m_elfModel);
49     ui->sizeTreeMapView->setModel(m_elfModel);
50 
51     connect(ui->stackedWidget, &QStackedWidget::currentChanged, this, &MainWindow::currentViewChanged);
52 
53     connect(ui->actionOpen, &QAction::triggered, this, &MainWindow::fileOpen);
54     connect(ui->actionQuit, &QAction::triggered, this, &MainWindow::close);
55     connect(ui->actionReopenPreviousFile, &QAction::triggered, this, &MainWindow::reloadFileOnStartup);
56 
57     auto viewModel = new QStandardItemModel(this);
58     addView(viewModel, QStringLiteral("document-preview"), QStringLiteral("ELF Structure"));
59     addView(viewModel, QStringLiteral("table"), QStringLiteral("Size Tree Map"));
60     addView(viewModel, QStringLiteral("view-list-tree"), QStringLiteral("Dependencies"));
61     addView(viewModel, QStringLiteral("code-class"), QStringLiteral("Data Types"));
62     addView(viewModel, QStringLiteral("chronometer"), QStringLiteral("Performance"));
63     addView(viewModel, QStringLiteral("dialog-warning"), QStringLiteral("Issues"));
64     ui->viewSelector->setModel(viewModel);
65     connect(ui->viewSelector, &SidePane::currentIndexChanged, this, [this](int index){
66         ui->stackedWidget->setCurrentIndex(index);
67     });
68 
69     ui->menuSettings->addAction(CodeNavigator::configMenu(this));
70 
71     restoreSettings();
72     currentViewChanged();
73 }
74 
~MainWindow()75 MainWindow::~MainWindow()
76 {
77 }
78 
closeEvent(QCloseEvent * event)79 void MainWindow::closeEvent(QCloseEvent *event)
80 {
81     QSettings settings;
82     settings.beginGroup(QStringLiteral("MainWindow"));
83     settings.setValue(QStringLiteral("geometry"), saveGeometry());
84     settings.setValue(QStringLiteral("windowState"), saveState());
85     settings.setValue(QStringLiteral("currentView"), ui->stackedWidget->currentIndex());
86     settings.endGroup();
87     QMainWindow::closeEvent(event);
88 }
89 
fileOpen()90 void MainWindow::fileOpen()
91 {
92     const QFileInfo fi(m_currentFileName);
93 
94     const QString fileName = QFileDialog::getOpenFileName(this, tr("Open ELF Object"), fi.path());
95     if (fileName.isEmpty())
96         return;
97 
98     m_currentFileName = fileName;
99     loadFile(fileName);
100 }
101 
reloadFileOnStartup()102 void MainWindow::reloadFileOnStartup()
103 {
104     QSettings settings;
105     settings.setValue(QStringLiteral("Settings/ReloadPreviousFile"), ui->actionReopenPreviousFile->isChecked());
106 }
107 
restoreSettings()108 void MainWindow::restoreSettings()
109 {
110     QSettings settings;
111     ui->sizeTreeMapView->restoreSettings();
112     ui->actionReopenPreviousFile->setChecked(settings.value(QStringLiteral("Settings/ReloadPreviousFile"), false).toBool());
113     if (ui->actionReopenPreviousFile->isChecked()) {
114         auto fileName = settings.value(QStringLiteral("Recent/PreviousFile")).toString();
115         const QFileInfo fi(fileName);
116         if (!fi.isReadable() || !fi.isFile())
117             return;
118         m_currentFileName = std::move(fileName);
119         loadFile(m_currentFileName);
120     }
121 
122     settings.beginGroup(QStringLiteral("MainWindow"));
123     restoreGeometry(settings.value(QStringLiteral("geometry")).toByteArray());
124     restoreState(settings.value(QStringLiteral("windowState")).toByteArray());
125     const auto currentViewIndex = settings.value(QStringLiteral("currentView"), 0).toInt();
126     const auto currentIdx = ui->viewSelector->model()->index(currentViewIndex, 0);
127     ui->viewSelector->selectionModel()->select(currentIdx, QItemSelectionModel::ClearAndSelect);
128     ui->viewSelector->selectionModel()->setCurrentIndex(currentIdx, QItemSelectionModel::ClearAndSelect);
129     settings.endGroup();
130 }
131 
loadFile(const QString & fileName)132 void MainWindow::loadFile(const QString& fileName)
133 {
134     if (fileName.isEmpty() || windowFilePath() == fileName)
135         return;
136     setWindowFilePath(fileName);
137 
138     m_fileSet.reset(new ElfFileSet(this));
139     m_fileSet->addFile(fileName);
140 
141     if (m_fileSet->size() == 0) {
142         QMessageBox::critical(this, tr("Failed to load ELF file"), tr("Could not load %1.").arg(fileName));
143 
144         m_fileSet.reset();
145 
146         statusBar()->showMessage(tr("Failed to load %1.").arg(fileName));
147     } else {
148         m_fileSet->topologicalSort();
149 
150         QSettings settings;
151         settings.setValue(QStringLiteral("Recent/PreviousFile"), fileName);
152 
153         statusBar()->showMessage(tr("Loaded %1.").arg(fileName));
154     }
155 
156     m_elfModel->setFileSet(m_fileSet.get());
157     ui->dependencyView->setFileSet(m_fileSet.get());
158     ui->loadTimeView->setFileSet(m_fileSet.get());
159     ui->typeView->setFileSet(m_fileSet.get());
160     ui->issuesView->setFileSet(m_fileSet.get());
161 }
162 
currentViewChanged()163 void MainWindow::currentViewChanged()
164 {
165     ui->menuView->clear();
166     ui->menuView->addActions(ui->stackedWidget->currentWidget()->actions());
167     ui->menuView->setEnabled(!ui->menuView->isEmpty());
168 }
169