Refactor SectionsWidget to use Model/View archictecture (#475)

This commit is contained in:
Paul I 2018-05-03 10:52:30 +03:00 committed by xarkes
parent fd829986af
commit a30ac22056
11 changed files with 297 additions and 218 deletions

View File

@ -275,6 +275,7 @@ Q_DECLARE_METATYPE(ResourcesDescription)
Q_DECLARE_METATYPE(VTableDescription)
Q_DECLARE_METATYPE(TypeDescription)
Q_DECLARE_METATYPE(SearchDescription)
Q_DECLARE_METATYPE(SectionDescription)
class CutterCore: public QObject
{

View File

@ -120,7 +120,6 @@ SOURCES += \
widgets/PieView.cpp \
widgets/RelocsWidget.cpp \
widgets/SdbDock.cpp \
widgets/SectionsDock.cpp \
widgets/SectionsWidget.cpp \
widgets/Sidebar.cpp \
widgets/StringsWidget.cpp \
@ -192,7 +191,6 @@ HEADERS += \
widgets/PieView.h \
widgets/RelocsWidget.h \
widgets/SdbDock.h \
widgets/SectionsDock.h \
widgets/SectionsWidget.h \
widgets/Sidebar.h \
widgets/StringsWidget.h \
@ -252,7 +250,7 @@ FORMS += \
widgets/ImportsWidget.ui \
widgets/SdbDock.ui \
widgets/RelocsWidget.ui \
widgets/SectionsDock.ui \
widgets/SectionsWidget.ui \
widgets/Sidebar.ui \
widgets/StringsWidget.ui \
widgets/SymbolsWidget.ui \

View File

@ -53,7 +53,6 @@
#include "widgets/SearchWidget.h"
#include "widgets/SymbolsWidget.h"
#include "widgets/StringsWidget.h"
#include "widgets/SectionsDock.h"
#include "widgets/RelocsWidget.h"
#include "widgets/FlagsWidget.h"
#include "widgets/VisualNavbar.h"
@ -163,7 +162,7 @@ void MainWindow::initUI()
// Hide centralWidget as we do not need it
ui->centralWidget->hide();
sectionsDock = new SectionsDock(this, ui->actionSections);
sectionsDock = new SectionsWidget(this, ui->actionSections);
entrypointDock = new EntrypointWidget(this, ui->actionEntrypoints);
functionsDock = new FunctionsWidget(this, ui->actionFunctions);
importsDock = new ImportsWidget(this, ui->actionImports);

View File

@ -32,7 +32,7 @@ class Dashboard;
class QLineEdit;
class SdbDock;
class QAction;
class SectionsDock;
class SectionsWidget;
class ConsoleWidget;
class EntrypointWidget;
class DisassemblerGraphView;
@ -188,7 +188,7 @@ private:
Dashboard *dashboardDock = nullptr;
QLineEdit *gotoEntry = nullptr;
SdbDock *sdbDock = nullptr;
SectionsDock *sectionsDock = nullptr;
SectionsWidget *sectionsDock = nullptr;
ConsoleWidget *consoleDock = nullptr;
ClassesWidget *classesDock = nullptr;
ResourcesWidget *resourcesDock = nullptr;

View File

@ -121,8 +121,6 @@ bool ExportsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &rig
return leftExp.vaddr < rightExp.vaddr;
}
ExportsWidget::ExportsWidget(MainWindow *main, QAction *action) :
CutterDockWidget(main, action),
ui(new Ui::ExportsWidget)

View File

@ -16,11 +16,6 @@ namespace Ui {
class ExportsWidget;
}
class MainWindow;
class QTreeWidgetItem;
class ExportsModel : public QAbstractListModel
{
Q_OBJECT
@ -44,8 +39,6 @@ public:
void endReloadExports();
};
class ExportsProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
@ -58,8 +51,6 @@ protected:
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
};
class ExportsWidget : public CutterDockWidget
{
Q_OBJECT
@ -83,5 +74,4 @@ private:
void setScrollMode();
};
#endif // EXPORTSWIDGET_H

View File

@ -1,71 +0,0 @@
#include "SectionsDock.h"
#include "ui_SectionsDock.h"
#include "MainWindow.h"
#include "widgets/SectionsWidget.h"
#include <QMenu>
#include <QResizeEvent>
SectionsDock::SectionsDock(MainWindow *main, QAction *action) :
CutterDockWidget(main, action),
ui(new Ui::SectionsDock),
main(main)
{
ui->setupUi(this);
this->sectionsWidget = new SectionsWidget(this->main);
this->setWidget(this->sectionsWidget);
this->sectionsWidget->setContentsMargins(0, 0, 0, 5);
this->setContextMenuPolicy(Qt::CustomContextMenu);
connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
this, SLOT(showSectionsContextMenu(const QPoint &)));
}
SectionsDock::~SectionsDock() {}
void SectionsDock::showSectionsContextMenu(const QPoint &pt)
{
// Set functions popup menu
QMenu *menu = new QMenu(this);
menu->clear();
menu->addAction(ui->actionHorizontal);
menu->addAction(ui->actionVertical);
if (this->sectionsWidget->orientation() == 1) {
ui->actionHorizontal->setChecked(true);
ui->actionVertical->setChecked(false);
} else {
ui->actionVertical->setChecked(true);
ui->actionHorizontal->setChecked(false);
}
this->setContextMenuPolicy(Qt::CustomContextMenu);
menu->exec(this->mapToGlobal(pt));
delete menu;
}
void SectionsDock::resizeEvent(QResizeEvent *event)
{
if (main->responsive && isVisible()) {
if (event->size().width() >= event->size().height()) {
// Set horizontal view (list)
this->on_actionHorizontal_triggered();
} else {
// Set vertical view (Tree)
this->on_actionVertical_triggered();
}
}
QWidget::resizeEvent(event);
}
void SectionsDock::on_actionVertical_triggered()
{
this->sectionsWidget->setOrientation(Qt::Vertical);
}
void SectionsDock::on_actionHorizontal_triggered()
{
this->sectionsWidget->setOrientation(Qt::Horizontal);
}

View File

@ -1,40 +0,0 @@
#ifndef SECTIONSDOCK_H
#define SECTIONSDOCK_H
#include <memory>
#include "CutterDockWidget.h"
class MainWindow;
class SectionsWidget;
namespace Ui {
class SectionsDock;
}
class SectionsDock : public CutterDockWidget
{
Q_OBJECT
public:
explicit SectionsDock(MainWindow *main, QAction *action = nullptr);
~SectionsDock();
protected:
void resizeEvent(QResizeEvent *event) override;
private slots:
void showSectionsContextMenu(const QPoint &pt);
void on_actionVertical_triggered();
void on_actionHorizontal_triggered();
private:
std::unique_ptr<Ui::SectionsDock> ui;
MainWindow *main;
SectionsWidget *sectionsWidget;
};
#endif // SECTIONSDOCK_H

View File

@ -1,73 +1,32 @@
#include "widgets/SectionsWidget.h"
#include "widgets/PieView.h"
#include <QMenu>
#include <QSplitter>
#include <QTreeView>
#include <QResizeEvent>
#include "SectionsWidget.h"
#include "ui_SectionsWidget.h"
#include "PieView.h"
#include "MainWindow.h"
#include "utils/Helpers.h"
#include <QtWidgets>
#include <QTreeWidget>
SectionsWidget::SectionsWidget(MainWindow *main, QWidget *parent) :
QSplitter(main),
main(main)
SectionsModel::SectionsModel(QList<SectionDescription> *sections, QObject *parent)
: QAbstractListModel(parent),
sections(sections)
{
Q_UNUSED(parent);
setupViews();
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
connect(this->tree, SIGNAL(doubleClicked(const QModelIndex &)), this,
SLOT(onSectionsDoubleClicked(const QModelIndex &)));
tree->sortByColumn(0, Qt::AscendingOrder);
connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshSections()));
}
void SectionsWidget::refreshSections()
int SectionsModel::rowCount(const QModelIndex &) const
{
tree->clear();
int row = 0;
for (auto section : Core()->getAllSections()) {
fillSections(row++, section);
return sections->count();
}
qhelpers::adjustColumns(tree, 0);
}
void SectionsWidget::setupViews()
int SectionsModel::columnCount(const QModelIndex &) const
{
// Table view
this->tree = new QTreeWidget;
this->tree->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
this->tree->setIndentation(10);
// Setup TreeWidget
this->tree->setColumnCount(4);
QList<QString> headers;
headers << tr("Name") << tr("Size") << tr("Address") << tr("End Address");
this->tree->setHeaderLabels(headers);
this->tree->setFrameShape(QFrame::NoFrame);
this->tree->setSortingEnabled(true);
pieChart = new PieView;
pieChart->setFrameShape(QFrame::NoFrame);
pieChart->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
this->addWidget(this->tree);
this->addWidget(pieChart);
this->setStretchFactor(0, 4);
//this->tree->setModel(model);
pieChart->setModel(this->tree->model());
QItemSelectionModel *selectionModel = new QItemSelectionModel(this->tree->model());
this->tree->setSelectionModel(selectionModel);
pieChart->setSelectionModel(selectionModel);
return SectionsModel::ColumnCount;
}
void SectionsWidget::fillSections(int row, const SectionDescription &section)
QVariant SectionsModel::data(const QModelIndex &index, int role) const
{
// TODO: create unique colors, e. g. use HSV color space and rotate in H for 360/size
static const QList<QColor> colors = { QColor("#1ABC9C"), //TURQUOISE
@ -83,14 +42,200 @@ void SectionsWidget::fillSections(int row, const SectionDescription &section)
QColor("#95A5A6") //COBCRETE
};
QTreeWidgetItem *tempItem = new QTreeWidgetItem();
tempItem->setText(0, section.name);
tempItem->setData(1, Qt::DisplayRole, section.size);
tempItem->setTextAlignment(1, Qt::AlignRight | Qt::AlignVCenter);
tempItem->setText(2, RAddressString(section.vaddr));
tempItem->setText(3, RAddressString(section.vaddr + section.vsize));
tempItem->setData(0, Qt::DecorationRole, colors[row % colors.size()]);
this->tree->insertTopLevelItem(0, tempItem);
if (index.row() >= sections->count())
return QVariant();
const SectionDescription &section = sections->at(index.row());
switch (role) {
case Qt::DisplayRole:
switch (index.column()) {
case SectionsModel::NameColumn:
return section.name;
case SectionsModel::SizeColumn:
return section.size;
case SectionsModel::AddressColumn:
return RAddressString(section.vaddr);
case SectionsModel::EndAddressColumn:
return RAddressString(section.vaddr + section.size);
default:
return QVariant();
}
case Qt::DecorationRole:
if (index.column() == 0)
return colors[index.row() % colors.size()];
return QVariant();
case SectionsModel::SectionDescriptionRole:
return QVariant::fromValue(section);
default:
return QVariant();
}
}
QVariant SectionsModel::headerData(int section, Qt::Orientation, int role) const
{
switch (role) {
case Qt::DisplayRole:
switch (section) {
case SectionsModel::NameColumn:
return tr("Name");
case SectionsModel::SizeColumn:
return tr("Size");
case SectionsModel::AddressColumn:
return tr("Address");
case SectionsModel::EndAddressColumn:
return tr("EndAddress");
default:
return QVariant();
}
default:
return QVariant();
}
}
void SectionsModel::beginReloadSections()
{
beginResetModel();
}
void SectionsModel::endReloadSections()
{
endResetModel();
// Update PieChart
emit dataChanged(QModelIndex(), QModelIndex());
}
SectionsProxyModel::SectionsProxyModel(SectionsModel *sourceModel, QObject *parent)
: QSortFilterProxyModel(parent)
{
setSourceModel(sourceModel);
setFilterCaseSensitivity(Qt::CaseInsensitive);
setSortCaseSensitivity(Qt::CaseInsensitive);
connect(sourceModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)),
this, SLOT(onSourceModelDataChanged(QModelIndex,QModelIndex,QVector<int>)));
}
void SectionsProxyModel::onSourceModelDataChanged(const QModelIndex &topLeft,
const QModelIndex &bottomRight,
const QVector<int> &roles)
{
// Pass the signal further to update PieChart
emit dataChanged(topLeft, bottomRight, roles);
}
bool SectionsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{
auto leftSection = left.data(SectionsModel::SectionDescriptionRole).value<SectionDescription>();
auto rightSection = right.data(SectionsModel::SectionDescriptionRole).value<SectionDescription>();
switch (left.column()) {
case SectionsModel::NameColumn:
return leftSection.name < rightSection.name;
case SectionsModel::SizeColumn:
return leftSection.size < rightSection.size;
case SectionsModel::AddressColumn:
case SectionsModel::EndAddressColumn:
return leftSection.vaddr < rightSection.vaddr;
default:
break;
}
return false;
}
SectionsWidget::SectionsWidget(MainWindow *main, QAction *action) :
CutterDockWidget(main, action),
ui(new Ui::SectionsWidget),
main(main)
{
ui->setupUi(this);
sectionsModel = new SectionsModel(&sections, this);
sectionsProxyModel = new SectionsProxyModel(sectionsModel, this);
setupViews();
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
setContextMenuPolicy(Qt::CustomContextMenu);
connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
this, SLOT(showSectionsContextMenu(const QPoint &)));
connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshSections()));
}
SectionsWidget::~SectionsWidget() {}
void SectionsWidget::resizeEvent(QResizeEvent *event)
{
if (main->responsive && isVisible()) {
if (event->size().width() >= event->size().height()) {
on_actionHorizontal_triggered();
} else {
on_actionVertical_triggered();
}
}
QWidget::resizeEvent(event);
}
void SectionsWidget::showSectionsContextMenu(const QPoint &pt)
{
// Set functions popup menu
QMenu *menu = new QMenu(this);
menu->clear();
menu->addAction(ui->actionHorizontal);
menu->addAction(ui->actionVertical);
if (splitter->orientation() == 1) {
ui->actionHorizontal->setChecked(true);
ui->actionVertical->setChecked(false);
} else {
ui->actionVertical->setChecked(true);
ui->actionHorizontal->setChecked(false);
}
menu->exec(mapToGlobal(pt));
delete menu;
}
void SectionsWidget::refreshSections()
{
sectionsModel->beginReloadSections();
sections = Core()->getAllSections();
sectionsModel->endReloadSections();
qhelpers::adjustColumns(sectionsTable, SectionsModel::ColumnCount, 0);
}
void SectionsWidget::setupViews()
{
splitter = new QSplitter;
sectionsTable = new QTreeView;
sectionsPieChart = new PieView;
splitter->addWidget(sectionsTable);
splitter->addWidget(sectionsPieChart);
//splitter->setStretchFactor(0, 4);
sectionsTable->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
sectionsTable->setIndentation(10);
sectionsTable->setFrameShape(QFrame::NoFrame);
sectionsTable->setSortingEnabled(true);
sectionsTable->sortByColumn(SectionsModel::NameColumn, Qt::AscendingOrder);
connect(sectionsTable, SIGNAL(doubleClicked(const QModelIndex &)),
this, SLOT(onSectionsDoubleClicked(const QModelIndex &)));
sectionsPieChart->setFrameShape(QFrame::NoFrame);
sectionsPieChart->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
sectionsTable->setModel(sectionsProxyModel);
sectionsPieChart->setModel(sectionsProxyModel);
QItemSelectionModel *selectionModel = new QItemSelectionModel(sectionsProxyModel);
sectionsTable->setSelectionModel(selectionModel);
sectionsPieChart->setSelectionModel(selectionModel);
setWidget(splitter);
}
void SectionsWidget::onSectionsDoubleClicked(const QModelIndex &index)
@ -98,7 +243,16 @@ void SectionsWidget::onSectionsDoubleClicked(const QModelIndex &index)
if (!index.isValid())
return;
QTreeWidgetItem *section = tree->topLevelItem(index.row());
auto addr = section->text(2);
Core()->seek(addr);
auto section = index.data(SectionsModel::SectionDescriptionRole).value<SectionDescription>();
Core()->seek(section.vaddr);
}
void SectionsWidget::on_actionVertical_triggered()
{
splitter->setOrientation(Qt::Vertical);
}
void SectionsWidget::on_actionHorizontal_triggered()
{
splitter->setOrientation(Qt::Horizontal);
}

View File

@ -1,41 +1,92 @@
#ifndef SECTIONSWIDGET_H
#define SECTIONSWIDGET_H
#include <QSplitter>
#include <memory>
class MainWindow;
class QTreeWidget;
class QAbstractItemModel;
#include <QAbstractListModel>
#include <QSortFilterProxyModel>
#include "Cutter.h"
#include "CutterDockWidget.h"
class QSplitter;
class QTreeView;
class QAbstractItemView;
class QItemSelectionModel;
struct SectionDescription;
class QResizeEvent;
class MainWindow;
namespace Ui {
class SectionsWidget;
}
class SectionsWidget : public QSplitter
class SectionsModel : public QAbstractListModel
{
Q_OBJECT
private:
QList<SectionDescription> *sections;
public:
enum Column { NameColumn = 0, SizeColumn, AddressColumn, EndAddressColumn, ColumnCount };
enum Role { SectionDescriptionRole = Qt::UserRole };
SectionsModel(QList<SectionDescription> *sections, QObject *parent = Q_NULLPTR);
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
void beginReloadSections();
void endReloadSections();
};
class SectionsProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
explicit SectionsWidget(MainWindow *main, QWidget *parent = 0);
SectionsProxyModel(SectionsModel *sourceModel, QObject *parent = Q_NULLPTR);
private slots:
void onSourceModelDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight,
const QVector<int>& roles);
protected:
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
};
class SectionsWidget : public CutterDockWidget
{
Q_OBJECT
public:
explicit SectionsWidget(MainWindow *main, QAction *action = Q_NULLPTR);
~SectionsWidget();
protected:
void resizeEvent(QResizeEvent *event) override;
private slots:
void refreshSections();
void showSectionsContextMenu(const QPoint &pt);
void on_actionVertical_triggered();
void on_actionHorizontal_triggered();
void onSectionsDoubleClicked(const QModelIndex &index);
private:
QAbstractItemView *pieChart;
QItemSelectionModel *selectionModel;
std::unique_ptr<Ui::SectionsWidget> ui;
QList<SectionDescription> sections;
SectionsModel *sectionsModel;
SectionsProxyModel *sectionsProxyModel;
QSplitter *splitter;
QTreeView *sectionsTable;
QAbstractItemView *sectionsPieChart;
MainWindow *main;
QTreeWidget *tree;
void setupViews();
void fillSections(int row, const SectionDescription &section);
};
#endif // SECTIONSWIDGET_H

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SectionsDock</class>
<widget class="QDockWidget" name="SectionsDock">
<class>SectionsWidget</class>
<widget class="QDockWidget" name="SectionsWidget">
<property name="geometry">
<rect>
<x>0</x>
@ -13,7 +13,6 @@
<property name="windowTitle">
<string>Sections</string>
</property>
<widget class="QWidget" name="dockWidgetContents"/>
<action name="actionVertical">
<property name="checkable">
<bool>true</bool>