diff options
Diffstat (limited to 'view/kernelcache/ui/kctriage.h')
| -rw-r--r-- | view/kernelcache/ui/kctriage.h | 238 |
1 files changed, 163 insertions, 75 deletions
diff --git a/view/kernelcache/ui/kctriage.h b/view/kernelcache/ui/kctriage.h index 07c24fad..7d8bdfc1 100644 --- a/view/kernelcache/ui/kctriage.h +++ b/view/kernelcache/ui/kctriage.h @@ -1,25 +1,26 @@ -#include <kernelcacheapi.h> -#include <binaryninjaapi.h> -#include "uitypes.h" -#include "viewframe.h" -#include "animation.h" -#include "uicontext.h" - -#include <QTableView> -#include <QStandardItemModel> -#include <QSortFilterProxyModel> #include <QHeaderView> -#include "filter.h" +#include <QSortFilterProxyModel> +#include <QStandardItemModel> #include <QStyledItemDelegate> - +#include <QTableView> +#include <binaryninjaapi.h> +#include <kernelcacheapi.h> +#include <progresstask.h> +#include "filter.h" #include "ui/fontsettings.h" +#include "uicontext.h" +#include "uitypes.h" +#include "viewframe.h" #ifndef BINARYNINJA_KCTRIAGE_H #define BINARYNINJA_KCTRIAGE_H + +using namespace KernelCacheAPI; + + class AddressColorDelegate : public QStyledItemDelegate { - public: explicit AddressColorDelegate(QObject* parent = nullptr) : QStyledItemDelegate(parent) {} @@ -28,6 +29,7 @@ public: QStyleOptionViewItem opt = option; initStyleOption(&opt, index); + opt.font = getMonospaceFont(qobject_cast<QWidget*>(parent())); opt.palette.setColor(QPalette::Text, getThemeColor(BNThemeColor::AddressColor)); opt.displayAlignment = Qt::AlignCenter | Qt::AlignVCenter; @@ -36,6 +38,21 @@ public: }; +class MonospaceFontDelegate : public QStyledItemDelegate { +public: + explicit MonospaceFontDelegate(QObject* parent = nullptr) : QStyledItemDelegate(parent) {} + + void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override { + QStyleOptionViewItem opt = option; + initStyleOption(&opt, index); + + opt.font = getMonospaceFont(qobject_cast<QWidget*>(parent())); + + QStyledItemDelegate::paint(painter, opt, index); + } +}; + + class FilterableTableView : public QTableView, public FilterTarget { Q_OBJECT @@ -73,7 +90,10 @@ public: void scrollToFirstItem() override { if (model()->rowCount() > 0) { - scrollTo(model()->index(0, 0)); + QModelIndex top = indexAt(rect().topLeft()); + if (top.isValid()) { + scrollTo(top); + } } } @@ -86,109 +106,177 @@ public: void selectFirstItem() override { if (model()->rowCount() > 0) { - QModelIndex firstIndex = model()->index(0, 0); - selectionModel()->select(firstIndex, QItemSelectionModel::ClearAndSelect); + QModelIndex top = indexAt(rect().topLeft()); + if (top.isValid()) { + selectionModel()->select(top, QItemSelectionModel::ClearAndSelect); + setCurrentIndex(top); + } } } void activateFirstItem() override { if (model()->rowCount() > 0) { - QModelIndex firstIndex = model()->index(0, 0); - setCurrentIndex(firstIndex); - emit activated(firstIndex); - } - } - - bool eventFilter(QObject* obj, QEvent* event) override { - if (event->type() == QEvent::KeyPress) { - auto keyEvent = dynamic_cast<QKeyEvent*>(event); - if (keyEvent->key() == Qt::Key_Escape) { - clearSelection(); - return true; - } - if (keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return) { - emit activated(currentIndex()); - return true; + QModelIndex topLeft = indexAt(rect().topLeft()); + if (topLeft.isValid()) { + setCurrentIndex(topLeft); + emit activated(topLeft); } } - return QTableView::eventFilter(obj, event); } signals: void filterTextChanged(const QString& text); }; -class SymbolTableView; - -class SymbolTableModel : public QAbstractTableModel { - Q_OBJECT - SymbolTableView* m_parent; - QFont m_font; - std::string m_filter; - std::vector<KernelCacheAPI::KCSymbol> m_symbols; +class SymbolTableProxyModel : public QSortFilterProxyModel +{ +Q_OBJECT public: - explicit SymbolTableModel(SymbolTableView* parent); + SymbolTableProxyModel(QObject* parent = nullptr) : QSortFilterProxyModel(parent), m_timer(new QTimer(this)) + { + m_timer->setSingleShot(true); + connect(m_timer, &QTimer::timeout, this, &SymbolTableProxyModel::delayedFilterChanged); + } + + void setFilterString(const QString& filter) + { + QRegularExpression newRegEx(QRegularExpression::escape(filter), QRegularExpression::CaseInsensitiveOption); + if (m_filter != newRegEx) { + m_filter = std::move(newRegEx); + m_timer->start(200); + } + } - int rowCount(const QModelIndex& parent) const override; - int columnCount(const QModelIndex& parent) const override; - QVariant data(const QModelIndex& index, int role) const override; - QVariant headerData(int section, Qt::Orientation orientation, int role) const override; - void updateSymbols(); - void setFilter(std::string text); +protected: + bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override + { + if (m_filter.pattern().isEmpty()) + return true; - const KernelCacheAPI::KCSymbol& symbolAt(int row) const; + for (int column = 0; column < sourceModel()->columnCount(source_parent); ++column) + { + QModelIndex index = sourceModel()->index(source_row, column, source_parent); + QString data = sourceModel()->data(index).toString(); + if (m_filter.match(data).hasMatch()) + return true; + } + return false; + } + +private slots: + void delayedFilterChanged() + { + invalidateFilter(); + } + +private: + QRegularExpression m_filter; + QTimer* m_timer; }; class SymbolTableView : public QTableView, public FilterTarget { - Q_OBJECT +Q_OBJECT friend class SymbolTableModel; std::vector<KernelCacheAPI::KCSymbol> m_symbols; - - SymbolTableModel* m_model; + QStandardItemModel* m_model; + SymbolTableProxyModel* m_proxyModel; public: - SymbolTableView(QWidget* parent, Ref<KernelCacheAPI::KernelCache> cache); - ~SymbolTableView() override; + SymbolTableView(QWidget* parent, Ref<KernelCache>& cache) + : QTableView(parent), m_model(new QStandardItemModel(this)), m_proxyModel(new SymbolTableProxyModel(this)) + { + m_proxyModel->setSourceModel(m_model); + setModel(m_proxyModel); - void scrollToFirstItem() override { - if (model()->rowCount() > 0) { - scrollTo(model()->index(0, 0)); - } + // Set up the headers + m_model->setColumnCount(3); + m_model->setHorizontalHeaderLabels({"Address", "Name", "Image"}); + setFont(getMonospaceFont(parent)); + setItemDelegateForColumn(0, new AddressColorDelegate(this)); + setItemDelegateForColumn(1, new MonospaceFontDelegate(this)); + setItemDelegateForColumn(2, new MonospaceFontDelegate(this)); + + // Configure view settings + horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents); + horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch); + horizontalHeader()->setSectionResizeMode(2, QHeaderView::Stretch); + setEditTriggers(QAbstractItemView::NoEditTriggers); + setSelectionBehavior(QAbstractItemView::SelectRows); + setSelectionMode(QAbstractItemView::SingleSelection); + verticalHeader()->setVisible(false); + + setSortingEnabled(true); + + BackgroundThread::create(this)->thenBackground([this, cache](){ + m_symbols = cache->LoadAllSymbolsAndWait(); + })->thenMainThread([this](){ + updateSymbols(); + })->start(); } - void scrollToCurrentItem() override { - QModelIndex currentIndex = selectionModel()->currentIndex(); - if (currentIndex.isValid()) { - scrollTo(currentIndex); + ~SymbolTableView() override = default; + + void updateSymbols() + { + m_model->removeRows(0, m_model->rowCount()); + for (const auto& symbol : m_symbols) + { + QList<QStandardItem*> row; + row << new QStandardItem(QString("0x%1").arg(symbol.address, 0, 16)) + << new QStandardItem(QString::fromStdString(symbol.name)) + << new QStandardItem(QString::fromStdString(symbol.image)); + m_model->appendRow(row); } } - void selectFirstItem() override { - if (model()->rowCount() > 0) { - QModelIndex firstIndex = model()->index(0, 0); - selectionModel()->select(firstIndex, QItemSelectionModel::ClearAndSelect); - } + KernelCacheAPI::KCSymbol getSymbolAtRow(int row) const + { + QModelIndex proxyIndex = m_proxyModel->index(row, 0); + QModelIndex sourceIndex = m_proxyModel->mapToSource(proxyIndex); + return m_symbols[sourceIndex.row()]; } - void activateFirstItem() override { - if (model()->rowCount() > 0) { - QModelIndex firstIndex = model()->index(0, 0); - setCurrentIndex(firstIndex); - emit activated(firstIndex); + void scrollToFirstItem() override + { + scrollToTop(); + } + + void scrollToCurrentItem() override + { + scrollTo(selectionModel()->currentIndex()); + } + + void selectFirstItem() override + { + if (m_proxyModel->rowCount() > 0) { + QModelIndex idx = m_proxyModel->index(0, 0); + if (idx.isValid()) { + selectionModel()->select(idx, QItemSelectionModel::ClearAndSelect); + setCurrentIndex(idx); + } } } - KernelCacheAPI::KCSymbol getSymbolAtRow(int row) const + void activateFirstItem() override { - return m_model->symbolAt(row); + if (m_proxyModel->rowCount() > 0) { + QModelIndex idx = m_proxyModel->index(0, 0); + if (idx.isValid()) { + setCurrentIndex(idx); + emit activated(idx); + } + } } - void setFilter(const std::string& filter) override; + void setFilter(const std::string& text) override + { + m_proxyModel->setFilterString(QString::fromStdString(text)); + } }; |
