diff --git a/src/widgets/CommentsWidget.cpp b/src/widgets/CommentsWidget.cpp index 42d70cc6..e7c38657 100644 --- a/src/widgets/CommentsWidget.cpp +++ b/src/widgets/CommentsWidget.cpp @@ -1,6 +1,5 @@ #include #include -#include #include "CommentsWidget.h" #include "ui_CommentsWidget.h" diff --git a/src/widgets/SymbolsWidget.cpp b/src/widgets/SymbolsWidget.cpp index e27a7499..57eff30f 100644 --- a/src/widgets/SymbolsWidget.cpp +++ b/src/widgets/SymbolsWidget.cpp @@ -4,8 +4,111 @@ #include "MainWindow.h" #include "utils/Helpers.h" -#include +SymbolsModel::SymbolsModel(QList *symbols, QObject *parent) + : QAbstractListModel(parent), + symbols(symbols) +{ +} +int SymbolsModel::rowCount(const QModelIndex &) const +{ + return symbols->count(); +} + +int SymbolsModel::columnCount(const QModelIndex &) const +{ + return SymbolsModel::ColumnCount; +} + +QVariant SymbolsModel::data(const QModelIndex &index, int role) const +{ + if (index.row() >= symbols->count()) + return QVariant(); + + const SymbolDescription &symbol = symbols->at(index.row()); + + switch (role) { + case Qt::DisplayRole: + switch (index.column()) { + case SymbolsModel::AddressColumn: + return RAddressString(symbol.vaddr); + case SymbolsModel::TypeColumn: + return QString("%1 %2").arg(symbol.bind, symbol.type).trimmed(); + case SymbolsModel::NameColumn: + return symbol.name; + default: + return QVariant(); + } + case SymbolsModel::SymbolDescriptionRole: + return QVariant::fromValue(symbol); + default: + return QVariant(); + } +} + +QVariant SymbolsModel::headerData(int section, Qt::Orientation, int role) const +{ + switch (role) { + case Qt::DisplayRole: + switch (section) { + case SymbolsModel::AddressColumn: + return tr("Address"); + case SymbolsModel::TypeColumn: + return tr("Type"); + case SymbolsModel::NameColumn: + return tr("Name"); + default: + return QVariant(); + } + default: + return QVariant(); + } +} + +void SymbolsModel::beginReloadSymbols() +{ + beginResetModel(); +} + +void SymbolsModel::endReloadSymbols() +{ + endResetModel(); +} + +SymbolsProxyModel::SymbolsProxyModel(SymbolsModel *sourceModel, QObject *parent) + : QSortFilterProxyModel(parent) +{ + setSourceModel(sourceModel); + setFilterCaseSensitivity(Qt::CaseInsensitive); + setSortCaseSensitivity(Qt::CaseInsensitive); +} + +bool SymbolsProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const +{ + QModelIndex index = sourceModel()->index(row, 0, parent); + auto symbol = index.data(SymbolsModel::SymbolDescriptionRole).value(); + + return symbol.name.contains(filterRegExp()); +} + +bool SymbolsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const +{ + auto leftSymbol = left.data(SymbolsModel::SymbolDescriptionRole).value(); + auto rightSymbol = right.data(SymbolsModel::SymbolDescriptionRole).value(); + + switch (left.column()) { + case SymbolsModel::AddressColumn: + return leftSymbol.vaddr < rightSymbol.vaddr; + case SymbolsModel::TypeColumn: + return leftSymbol.type < rightSymbol.type; + case SymbolsModel::NameColumn: + return leftSymbol.name < rightSymbol.name; + default: + break; + } + + return false; +} SymbolsWidget::SymbolsWidget(MainWindow *main, QAction *action) : CutterDockWidget(main, action), @@ -13,41 +116,52 @@ SymbolsWidget::SymbolsWidget(MainWindow *main, QAction *action) : { ui->setupUi(this); - ui->symbolsTreeWidget->sortByColumn(2, Qt::AscendingOrder); + symbolsModel = new SymbolsModel(&symbols, this); + symbolsProxyModel = new SymbolsProxyModel(symbolsModel, this); + ui->symbolsTreeView->setModel(symbolsProxyModel); + ui->symbolsTreeView->sortByColumn(SymbolsModel::AddressColumn, Qt::AscendingOrder); + + // Ctrl-F to show/hide the filter entry + QShortcut *searchShortcut = new QShortcut(QKeySequence::Find, this); + connect(searchShortcut, &QShortcut::activated, ui->quickFilterView, &QuickFilterView::showFilter); + searchShortcut->setContext(Qt::WidgetWithChildrenShortcut); + + // Esc to clear the filter entry + QShortcut *clearShortcut = new QShortcut(QKeySequence(Qt::Key_Escape), this); + connect(clearShortcut, &QShortcut::activated, ui->quickFilterView, &QuickFilterView::clearFilter); + clearShortcut->setContext(Qt::WidgetWithChildrenShortcut); + + connect(ui->quickFilterView, SIGNAL(filterTextChanged(const QString &)), + symbolsProxyModel, SLOT(setFilterWildcard(const QString &))); + connect(ui->quickFilterView, SIGNAL(filterClosed()), ui->symbolsTreeView, SLOT(setFocus())); setScrollMode(); - connect(Core(), SIGNAL(refreshAll()), this, SLOT(fillSymbols())); + connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshSymbols())); } SymbolsWidget::~SymbolsWidget() {} -void SymbolsWidget::on_symbolsTreeWidget_itemDoubleClicked(QTreeWidgetItem *item, int column) +void SymbolsWidget::on_symbolsTreeView_doubleClicked(const QModelIndex &index) { - if (column < 0) + if (!index.isValid()) return; - // Get offset and name of item double clicked - SymbolDescription symbol = item->data(0, Qt::UserRole).value(); + auto symbol = index.data(SymbolsModel::SymbolDescriptionRole).value(); Core()->seek(symbol.vaddr); } -void SymbolsWidget::fillSymbols() +void SymbolsWidget::refreshSymbols() { - ui->symbolsTreeWidget->clear(); - for (auto symbol : Core()->getAllSymbols()) { - QTreeWidgetItem *item = new QTreeWidgetItem(); - item->setText(0, RAddressString(symbol.vaddr)); - item->setText(1, QString("%1 %2").arg(symbol.bind, symbol.type).trimmed()); - item->setText(2, symbol.name); - item->setData(0, Qt::UserRole, QVariant::fromValue(symbol)); - ui->symbolsTreeWidget->addTopLevelItem(item); - } - qhelpers::adjustColumns(ui->symbolsTreeWidget, 0); + symbolsModel->beginReloadSymbols(); + symbols = Core()->getAllSymbols(); + symbolsModel->endReloadSymbols(); + + qhelpers::adjustColumns(ui->symbolsTreeView, SymbolsModel::ColumnCount, 0); } void SymbolsWidget::setScrollMode() { - qhelpers::setVerticalScrollMode(ui->symbolsTreeWidget); + qhelpers::setVerticalScrollMode(ui->symbolsTreeView); } diff --git a/src/widgets/SymbolsWidget.h b/src/widgets/SymbolsWidget.h index 96c19446..845774a6 100644 --- a/src/widgets/SymbolsWidget.h +++ b/src/widgets/SymbolsWidget.h @@ -2,7 +2,10 @@ #define SYMBOLSWIDGET_H #include +#include +#include +#include "Cutter.h" #include "CutterDockWidget.h" class MainWindow; @@ -12,6 +15,42 @@ namespace Ui { class SymbolsWidget; } +class SymbolsModel: public QAbstractListModel +{ + Q_OBJECT + +private: + QList *symbols; + +public: + enum Column { AddressColumn = 0, TypeColumn, NameColumn, ColumnCount }; + enum Role { SymbolDescriptionRole = Qt::UserRole }; + + SymbolsModel(QList *exports, 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 beginReloadSymbols(); + void endReloadSymbols(); +}; + +class SymbolsProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + SymbolsProxyModel(SymbolsModel *sourceModel, QObject *parent = Q_NULLPTR); + +protected: + bool filterAcceptsRow(int row, const QModelIndex &parent) const override; + bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; +}; + + class SymbolsWidget : public CutterDockWidget { Q_OBJECT @@ -21,12 +60,15 @@ public: ~SymbolsWidget(); private slots: - void on_symbolsTreeWidget_itemDoubleClicked(QTreeWidgetItem *item, int column); + void on_symbolsTreeView_doubleClicked(const QModelIndex &index); - void fillSymbols(); + void refreshSymbols(); private: std::unique_ptr ui; + QList symbols; + SymbolsModel *symbolsModel; + SymbolsProxyModel *symbolsProxyModel; void setScrollMode(); }; diff --git a/src/widgets/SymbolsWidget.ui b/src/widgets/SymbolsWidget.ui index b73211bd..356fd09e 100644 --- a/src/widgets/SymbolsWidget.ui +++ b/src/widgets/SymbolsWidget.ui @@ -28,47 +28,29 @@ 0 - - - QTreeWidget::item -{ - padding-top: 1px; - padding-bottom: 1px; -} - - - - QFrame::NoFrame - - - 0 - - - 8 - + true - - - Address - - - - - Type - - - - - Name - - + + true + + + + + + + QuickFilterView + QWidget +
widgets/QuickFilterView.h
+ 1 +
+