summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--view/kernelcache/ui/kctriage.cpp179
-rw-r--r--view/kernelcache/ui/kctriage.h238
-rw-r--r--view/sharedcache/ui/dsctriage.cpp149
-rw-r--r--view/sharedcache/ui/dsctriage.h73
-rw-r--r--view/sharedcache/ui/symboltable.cpp51
-rw-r--r--view/sharedcache/ui/symboltable.h47
6 files changed, 371 insertions, 366 deletions
diff --git a/view/kernelcache/ui/kctriage.cpp b/view/kernelcache/ui/kctriage.cpp
index 7b3cf9f0..aa2d7bb1 100644
--- a/view/kernelcache/ui/kctriage.cpp
+++ b/view/kernelcache/ui/kctriage.cpp
@@ -1,152 +1,14 @@
-#include "globalarea.h"
-#include "kctriage.h"
-#include "progresstask.h"
-#include "ui/fontsettings.h"
#include <QMessageBox>
#include <QPainter>
#include <cmath>
+#include "globalarea.h"
+#include "kctriage.h"
+#include "ui/fontsettings.h"
using namespace BinaryNinja;
using namespace KernelCacheAPI;
-SymbolTableModel::SymbolTableModel(SymbolTableView* parent)
- : QAbstractTableModel(parent), m_parent(parent) {
- // TODO: Need to implement updating this font if it is changed by the user
- m_font = getMonospaceFont(parent);
-}
-
-
-int SymbolTableModel::rowCount(const QModelIndex& parent) const {
- Q_UNUSED(parent);
- return static_cast<int>(m_symbols.size());
-}
-
-
-int SymbolTableModel::columnCount(const QModelIndex& parent) const {
- Q_UNUSED(parent);
- // We have 3 columns: Address, Name, and Image
- return 3;
-}
-
-
-QVariant SymbolTableModel::data(const QModelIndex& index, int role) const {
- if (!index.isValid() || (role != Qt::DisplayRole && role != Qt::FontRole)) {
- return QVariant();
- }
-
- const KCSymbol& symbol = m_symbols.at(index.row());
-
- switch (role)
- {
- case Qt::DisplayRole:
- {
- switch (index.column()) {
- case 0: // Address column
- return QString("0x%1").arg(symbol.address, 0, 16); // Display address as hexadecimal
- case 1: // Name column
- return QString::fromStdString(symbol.name);
- case 2: // Image column
- return QString::fromStdString(symbol.image);
- default:
- return QVariant();
- }
- }
- case Qt::FontRole:
- return m_font;
- default:
- return QVariant();
- }
-}
-
-
-QVariant SymbolTableModel::headerData(int section, Qt::Orientation orientation, int role) const {
- if (role != Qt::DisplayRole || orientation != Qt::Horizontal) {
- return QVariant();
- }
-
- switch (section) {
- case 0:
- return QString("Address");
- case 1:
- return QString("Name");
- case 2:
- return QString("Image");
- default:
- return QVariant();
- }
-}
-
-
-void SymbolTableModel::updateSymbols() {
- m_symbols = m_parent->m_symbols;
- setFilter(m_filter);
-}
-
-
-const KCSymbol& SymbolTableModel::symbolAt(int row) const {
- return m_symbols.at(row);
-}
-
-
-void SymbolTableModel::setFilter(std::string text)
-{
- beginResetModel();
-
- m_filter = text;
- m_symbols.clear();
-
- if (!m_filter.empty())
- {
- m_symbols.reserve(m_parent->m_symbols.size());
- for (const auto& symbol : m_parent->m_symbols)
- if (symbol.name.find(m_filter) != std::string::npos)
- m_symbols.push_back(symbol);
- m_symbols.shrink_to_fit();
- }
- else
- {
- m_symbols = m_parent->m_symbols;
- }
-
- endResetModel();
-}
-
-
-SymbolTableView::SymbolTableView(QWidget* parent, Ref<KernelCache> cache)
- : QTableView(parent), m_model(new SymbolTableModel(this)) {
-
- // Set up the filter model
- setModel(m_model);
-
- // Configure view settings
- horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
- setEditTriggers(QAbstractItemView::NoEditTriggers);
- setSelectionBehavior(QAbstractItemView::SelectRows);
- setSelectionMode(QAbstractItemView::SingleSelection);
-
- setSortingEnabled(true);
-
- BackgroundThread::create(this)->thenBackground([this, cache](){
- // LogInfo("Symbol Search: Loading symbols...");
- m_symbols = cache->LoadAllSymbolsAndWait();
- // LogInfo("Symbol Search: Loaded 0x%zx symbols", m_symbols.size());
- })->thenMainThread([this](){
- m_model->updateSymbols();
- })->start();
-}
-
-
-SymbolTableView::~SymbolTableView() {
- delete m_model;
-}
-
-
-void SymbolTableView::setFilter(const std::string& filter) {
- m_model->setFilter(filter);
-}
-
-
KCTriageViewType::KCTriageViewType()
: ViewType("KCTriage", "Kernel Cache Triage")
{}
@@ -194,8 +56,6 @@ KCTriageView::KCTriageView(QWidget* parent, BinaryViewRef data) : QWidget(parent
m_layout->addWidget(m_triageTabs);
setLayout(m_layout);
- // XXX: RefreshData
-
m_triageTabs->selectWidget(defaultWidget);
}
@@ -261,18 +121,18 @@ QWidget* KCTriageView::initImageTable()
auto loadImageButton = new QPushButton();
connect(loadImageButton, &QPushButton::clicked, [this](bool) {
// Collect only visible selected rows
- QModelIndexList visibleSelectedRows;
+ QModelIndexList selected;
for (const auto& index : m_imageTable->selectionModel()->selectedRows()) {
if (!m_imageTable->isRowHidden(index.row())) {
- visibleSelectedRows.append(index);
+ selected.append(index);
}
}
- if (visibleSelectedRows.empty())
+ if (selected.empty())
return;
- for (const auto& selection : visibleSelectedRows) {
- auto name = selection.data().toString().toStdString();
+ for (const auto& selection : selected) {
+ auto name = m_imageModel->item(selection.row(), 1)->text().toStdString();
WorkerPriorityEnqueue([this, name]() { m_cache->LoadImageWithInstallName(name); });
}
});
@@ -284,7 +144,7 @@ QWidget* KCTriageView::initImageTable()
});
connect(m_imageTable, &FilterableTableView::activated, this, [=](const QModelIndex& index) {
- auto selected = m_imageModel->item(index.row(), 0);
+ auto selected = m_imageModel->item(index.row(), 1);
auto name = selected->text().toStdString();
WorkerPriorityEnqueue([this, name]() { m_cache->LoadImageWithInstallName(name); });
});
@@ -292,12 +152,11 @@ QWidget* KCTriageView::initImageTable()
auto loadImageLayout = new QVBoxLayout;
loadImageLayout->addWidget(loadImageFilterEdit);
loadImageLayout->addWidget(m_imageTable);
- loadImageLayout->addWidget(loadImageButton);
- auto buttonLayout = new QHBoxLayout;
- buttonLayout->addWidget(loadImageButton);
- buttonLayout->setAlignment(Qt::AlignLeft);
- loadImageLayout->addLayout(buttonLayout);
+ auto loadImageFooterLayout = new QHBoxLayout;
+ loadImageFooterLayout->addWidget(loadImageButton);
+ loadImageFooterLayout->setAlignment(Qt::AlignLeft);
+ loadImageLayout->addLayout(loadImageFooterLayout);
auto loadImageWidget = new QWidget;
loadImageWidget->setLayout(loadImageLayout);
@@ -306,7 +165,7 @@ QWidget* KCTriageView::initImageTable()
m_imageTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
- m_imageTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed);
+ m_imageTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
m_imageTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch);
m_imageTable->setSelectionBehavior(QAbstractItemView::SelectRows);
@@ -332,9 +191,6 @@ void KCTriageView::initSymbolTable()
m_symbolTable->setFilter(filter.toStdString());
});
- // Apply custom column styling
- m_symbolTable->setItemDelegateForColumn(0, new AddressColorDelegate(m_symbolTable));
-
auto symbolLayout = new QVBoxLayout;
symbolLayout->addWidget(symbolFilterEdit);
symbolLayout->addWidget(m_symbolTable);
@@ -342,13 +198,6 @@ void KCTriageView::initSymbolTable()
auto symbolWidget = new QWidget;
symbolWidget->setLayout(symbolLayout);
- m_symbolTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents); // Address
- m_symbolTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch); // Name
- m_symbolTable->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Stretch); // Image
-
- m_symbolTable->setSelectionBehavior(QAbstractItemView::SelectRows);
- m_symbolTable->setSelectionMode(QAbstractItemView::SingleSelection);
-
std::function<void(uint64_t)> navigateToAddress = [=](uint64_t addr) {
ExecuteOnMainThread([addr, this](){
if (Settings::Instance()->Get<bool>("ui.view.graph.preferred"))
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));
+ }
};
diff --git a/view/sharedcache/ui/dsctriage.cpp b/view/sharedcache/ui/dsctriage.cpp
index e2cf8147..54fe4ed0 100644
--- a/view/sharedcache/ui/dsctriage.cpp
+++ b/view/sharedcache/ui/dsctriage.cpp
@@ -1,11 +1,10 @@
+#include <QHeaderView>
+#include <QMessageBox>
+#include <utility>
#include "dsctriage.h"
#include "globalarea.h"
-#include "progresstask.h"
#include "symboltable.h"
#include "ui/fontsettings.h"
-#include <QHeaderView>
-#include <QMessageBox>
-#include <utility>
using namespace BinaryNinja;
using namespace SharedCacheAPI;
@@ -28,7 +27,6 @@ QWidget* DSCTriageViewType::create(BinaryViewRef data, ViewFrame* viewFrame)
{
if (data->GetTypeName() != VIEW_NAME)
return nullptr;
- // TODO: Check for dyld start. Then continue.
return new DSCTriageView(viewFrame, data);
}
@@ -134,36 +132,36 @@ void DSCTriageView::loadImagesWithAddr(const std::vector<uint64_t>& addresses, b
QWidget* DSCTriageView::initImageTable()
{
- auto loadImageTable = new FilterableTableView(this);
+ m_imageTable = new FilterableTableView(this);
- m_imageModel = new QStandardItemModel(0, 3, loadImageTable);
+ m_imageModel = new QStandardItemModel(0, 3, m_imageTable);
m_imageModel->setHorizontalHeaderLabels({"Address", "Loaded", "Name"});
// Apply custom column styling
- loadImageTable->setItemDelegateForColumn(0, new AddressColorDelegate(loadImageTable));
- loadImageTable->setItemDelegateForColumn(1, new LoadedDelegate(loadImageTable));
+ m_imageTable->setItemDelegateForColumn(0, new AddressColorDelegate(m_imageTable));
+ m_imageTable->setItemDelegateForColumn(1, new LoadedDelegate(m_imageTable));
// Context menu
- loadImageTable->setContextMenuPolicy(Qt::CustomContextMenu);
- connect(loadImageTable, &QWidget::customContextMenuRequested, [this, loadImageTable](const QPoint &pos) {
- QMenu contextMenu(tr("Load Image Actions"), loadImageTable);
+ m_imageTable->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(m_imageTable, &QWidget::customContextMenuRequested, [this](const QPoint &pos) {
+ QMenu contextMenu(tr("Load Image Actions"), m_imageTable);
// Get number of selected images
- auto selected = loadImageTable->selectionModel()->selectedRows();
+ auto selected = m_imageTable->selectionModel()->selectedRows();
int selectedCount = 0;
std::vector<uint64_t> addresses;
for (const auto& idx : selected)
{
// Skip rows hidden by the filter
- if (loadImageTable->isRowHidden(idx.row()))
+ if (m_imageTable->isRowHidden(idx.row()))
continue;
addresses.push_back(idx.data().toString().toULongLong(nullptr, 16));
selectedCount++;
}
- QAction noSelectionAction("No Images Selected", loadImageTable);
- QAction loadImagesAction("", loadImageTable);
- QAction loadImagesWithDepsAction("", loadImageTable);
+ QAction noSelectionAction("No Images Selected", m_imageTable);
+ QAction loadImagesAction("", m_imageTable);
+ QAction loadImagesWithDepsAction("", m_imageTable);
if (selectedCount == 0)
{
noSelectionAction.setEnabled(false);
@@ -188,20 +186,25 @@ QWidget* DSCTriageView::initImageTable()
contextMenu.addAction(&loadImagesWithDepsAction);
}
- contextMenu.exec(loadImageTable->viewport()->mapToGlobal(pos));
+ contextMenu.exec(m_imageTable->viewport()->mapToGlobal(pos));
});
auto loadImageButton = new QPushButton();
- connect(loadImageButton, &QPushButton::clicked, [this, loadImageTable](bool) {
- auto selected = loadImageTable->selectionModel()->selectedRows();
+ connect(loadImageButton, &QPushButton::clicked, [this](bool) {
+ // Collect only visible selected rows
+ QModelIndexList selected;
+ for (const auto& index : m_imageTable->selectionModel()->selectedRows()) {
+ if (!m_imageTable->isRowHidden(index.row())) {
+ selected.append(index);
+ }
+ }
+
+ if (selected.empty())
+ return;
+
std::vector<uint64_t> addresses;
for (const auto& idx : selected)
- {
- // Skip rows hidden by the filter
- if (loadImageTable->isRowHidden(idx.row()))
- continue;
addresses.push_back(idx.data().toString().toULongLong(nullptr, 16));
- }
loadImagesWithAddr(addresses);
});
loadImageButton->setText("Load Selected");
@@ -213,43 +216,43 @@ QWidget* DSCTriageView::initImageTable()
refreshDataButton->setText("Refresh");
} // refreshDataButton
- auto loadImageFilterEdit = new FilterEdit(loadImageTable);
- connect(loadImageFilterEdit, &FilterEdit::textChanged, [loadImageTable](const QString& filter) {
- loadImageTable->setFilter(filter.toStdString());
+ auto loadImageFilterEdit = new FilterEdit(m_imageTable);
+ connect(loadImageFilterEdit, &FilterEdit::textChanged, [this](const QString& filter) {
+ m_imageTable->setFilter(filter.toStdString());
});
- connect(loadImageTable, &FilterableTableView::activated, this, [=](const QModelIndex& index) {
+ connect(m_imageTable, &FilterableTableView::activated, this, [=](const QModelIndex& index) {
auto addr = m_imageModel->item(index.row(), 0)->text().toULongLong(nullptr, 16);
loadImagesWithAddr({addr});
});
auto loadImageLayout = new QVBoxLayout;
loadImageLayout->addWidget(loadImageFilterEdit);
- loadImageLayout->addWidget(loadImageTable);
+ loadImageLayout->addWidget(m_imageTable);
- auto buttonLayout = new QHBoxLayout;
- buttonLayout->addWidget(loadImageButton);
- buttonLayout->addWidget(refreshDataButton);
- buttonLayout->setAlignment(Qt::AlignLeft);
- loadImageLayout->addLayout(buttonLayout);
+ auto loadImageFooterLayout = new QHBoxLayout;
+ loadImageFooterLayout->addWidget(loadImageButton);
+ loadImageFooterLayout->addWidget(refreshDataButton);
+ loadImageFooterLayout->setAlignment(Qt::AlignLeft);
+ loadImageLayout->addLayout(loadImageFooterLayout);
auto loadImageWidget = new QWidget;
loadImageWidget->setLayout(loadImageLayout);
- loadImageTable->setModel(m_imageModel);
+ m_imageTable->setModel(m_imageModel);
- loadImageTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
+ m_imageTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
- loadImageTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed);
- loadImageTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
- loadImageTable->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Stretch);
+ m_imageTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed);
+ m_imageTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
+ m_imageTable->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Stretch);
- loadImageTable->setSelectionBehavior(QAbstractItemView::SelectRows);
- loadImageTable->setSelectionMode(QAbstractItemView::ExtendedSelection);
+ m_imageTable->setSelectionBehavior(QAbstractItemView::SelectRows);
+ m_imageTable->setSelectionMode(QAbstractItemView::ExtendedSelection);
- loadImageTable->setSortingEnabled(true);
+ m_imageTable->setSortingEnabled(true);
- loadImageTable->verticalHeader()->setVisible(false);
+ m_imageTable->verticalHeader()->setVisible(false);
m_triageTabs->addTab(loadImageWidget, "Images");
m_triageTabs->setCanCloseTab(loadImageWidget, false);
@@ -262,51 +265,43 @@ void DSCTriageView::initSymbolTable()
{
m_symbolTable = new SymbolTableView(this);
+ // Apply custom column styling
+ m_symbolTable->setItemDelegateForColumn(0, new AddressColorDelegate(m_symbolTable));
+
auto symbolFilterEdit = new FilterEdit(m_symbolTable);
connect(symbolFilterEdit, &FilterEdit::textChanged, [this](const QString& filter) {
m_symbolTable->setFilter(filter.toStdString());
});
- // Apply custom column styling
- m_symbolTable->setItemDelegateForColumn(0, new AddressColorDelegate(m_symbolTable));
-
auto loadSymbolImageButton = new QPushButton();
- {
- connect(loadSymbolImageButton, &QPushButton::clicked,
- [this](bool) {
- auto selected = m_symbolTable->selectionModel()->selectedRows();
- std::vector<uint64_t> addresses;
- for (const auto& row : selected)
- addresses.push_back(row.data().toString().toULongLong(nullptr, 16));
- loadImagesWithAddr(addresses);
- });
- loadSymbolImageButton->setText("Load Image");
- } // loadImageButton
+ connect(loadSymbolImageButton, &QPushButton::clicked, [this](bool) {
+ auto selected = m_symbolTable->selectionModel()->selectedRows();
+ std::vector<uint64_t> addresses;
+ for (const auto& row : selected)
+ addresses.push_back(row.data().toString().toULongLong(nullptr, 16));
+ loadImagesWithAddr(addresses);
+ });
+ loadSymbolImageButton->setText("Load Image");
// Shows the current selected rows image name.
- auto currentImageLabel = new QLabel(this); {
- currentImageLabel->setText("");
- currentImageLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
- }
-
- // Update the label whenever the selection changes.
- connect(m_symbolTable->selectionModel(), &QItemSelectionModel::currentRowChanged, this,
- [this, currentImageLabel](const QModelIndex &current, const QModelIndex &) {
- auto symbol = m_symbolTable->getSymbolAtRow(current.row());
- auto controller = SharedCacheController::GetController(*this->m_data);
- if (!controller)
- return;
- auto image = controller->GetImageContaining(symbol.address);
- if (image)
- currentImageLabel->setText("Image: " + QString::fromStdString(image->name));
- else
- currentImageLabel->setText("");
- });
+ auto currentImageLabel = new QLabel(this);
+ currentImageLabel->setText("");
+ currentImageLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ connect(m_symbolTable->selectionModel(), &QItemSelectionModel::currentRowChanged, this, [this, currentImageLabel](const QModelIndex &current, const QModelIndex &) {
+ auto symbol = m_symbolTable->getSymbolAtRow(current.row());
+ auto controller = SharedCacheController::GetController(*this->m_data);
+ if (!controller)
+ return;
+ auto image = controller->GetImageContaining(symbol.address);
+ if (image)
+ currentImageLabel->setText("Image: " + QString::fromStdString(image->name));
+ else
+ currentImageLabel->setText("");
+ });
auto symbolFooterLayout = new QHBoxLayout;
symbolFooterLayout->addWidget(loadSymbolImageButton);
symbolFooterLayout->addWidget(currentImageLabel);
-
symbolFooterLayout->setAlignment(Qt::AlignLeft);
auto symbolLayout = new QVBoxLayout;
diff --git a/view/sharedcache/ui/dsctriage.h b/view/sharedcache/ui/dsctriage.h
index a16457e3..e847625e 100644
--- a/view/sharedcache/ui/dsctriage.h
+++ b/view/sharedcache/ui/dsctriage.h
@@ -1,24 +1,29 @@
-#include <binaryninjaapi.h>
+#include <QHeaderView>
#include <QItemDelegate>
+#include <QPainter>
+#include <QSortFilterProxyModel>
+#include <QStandardItemModel>
#include <QStyledItemDelegate>
-
-#include "uitypes.h"
-#include "viewframe.h"
-#include "animation.h"
-#include "uicontext.h"
-
+#include <QTableView>
+#include <binaryninjaapi.h>
+#include <progresstask.h>
+#include <sharedcacheapi.h>
#include "filter.h"
#include "symboltable.h"
-
#include "ui/fontsettings.h"
+#include "uicontext.h"
+#include "uitypes.h"
+#include "viewframe.h"
#ifndef BINARYNINJA_DSCTRIAGE_H
#define BINARYNINJA_DSCTRIAGE_H
+using namespace BinaryNinja;
+using namespace SharedCacheAPI;
+
class AddressColorDelegate : public QStyledItemDelegate
{
-
public:
explicit AddressColorDelegate(QObject* parent = nullptr) : QStyledItemDelegate(parent) {}
@@ -27,6 +32,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;
@@ -130,7 +136,10 @@ public:
void scrollToFirstItem() override {
if (model()->rowCount() > 0) {
- scrollTo(model()->index(0, 0));
+ QModelIndex top = indexAt(rect().topLeft());
+ if (top.isValid()) {
+ scrollTo(top);
+ }
}
}
@@ -143,34 +152,39 @@ 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);
}
+// 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;
+// }
+// }
+// return QTableView::eventFilter(obj, event);
+// }
+
signals:
void filterTextChanged(const QString& text);
};
@@ -184,6 +198,7 @@ class DSCTriageView : public QWidget, public View, public UIContextNotification
SplitTabWidget* m_triageTabs;
DockableTabCollection* m_triageCollection;
+ FilterableTableView* m_imageTable;
QStandardItemModel* m_imageModel;
SymbolTableView* m_symbolTable;
diff --git a/view/sharedcache/ui/symboltable.cpp b/view/sharedcache/ui/symboltable.cpp
index 1df7d529..b0247f2b 100644
--- a/view/sharedcache/ui/symboltable.cpp
+++ b/view/sharedcache/ui/symboltable.cpp
@@ -10,23 +10,27 @@
using namespace BinaryNinja;
using namespace SharedCacheAPI;
+
SymbolTableModel::SymbolTableModel(SymbolTableView* parent)
: QAbstractTableModel(parent), m_parent(parent) {
// TODO: Need to implement updating this font if it is changed by the user
m_font = getMonospaceFont(parent);
}
+
int SymbolTableModel::rowCount(const QModelIndex& parent) const {
Q_UNUSED(parent);
return static_cast<int>(m_modelSymbols.size());
}
+
int SymbolTableModel::columnCount(const QModelIndex& parent) const {
Q_UNUSED(parent);
// We have 3 columns: Address, Type, Name
return 3;
}
+
QVariant SymbolTableModel::data(const QModelIndex& index, int role) const {
if (!index.isValid() || (role != Qt::DisplayRole && role != Qt::FontRole)) {
return QVariant();
@@ -58,6 +62,7 @@ QVariant SymbolTableModel::data(const QModelIndex& index, int role) const {
}
}
+
QVariant SymbolTableModel::headerData(int section, Qt::Orientation orientation, int role) const {
if (role != Qt::DisplayRole || orientation != Qt::Horizontal) {
return QVariant();
@@ -75,12 +80,58 @@ QVariant SymbolTableModel::headerData(int section, Qt::Orientation orientation,
}
}
+
+void SymbolTableModel::sort(int column, Qt::SortOrder order)
+{
+ beginResetModel();
+
+ std::function<bool(const CacheSymbol&, const CacheSymbol&)> comparator;
+
+ switch (column)
+ {
+ case 0: // Address column
+ comparator = [](const CacheSymbol& a, const CacheSymbol& b) {
+ return a.address < b.address;
+ };
+ break;
+ case 1: // Type column
+ comparator = [](const CacheSymbol& a, const CacheSymbol& b) {
+ return GetSymbolTypeAsString(a.type) < GetSymbolTypeAsString(b.type);
+ };
+ break;
+ case 2: // Name column
+ comparator = [](const CacheSymbol& a, const CacheSymbol& b) {
+ return a.name < b.name;
+ };
+ break;
+ default:
+ endResetModel();
+ return;
+ }
+
+ if (order == Qt::DescendingOrder)
+ {
+ std::sort(m_modelSymbols.begin(), m_modelSymbols.end(),
+ [&comparator](const CacheSymbol& a, const CacheSymbol& b) {
+ return comparator(b, a);
+ });
+ }
+ else
+ {
+ std::sort(m_modelSymbols.begin(), m_modelSymbols.end(), comparator);
+ }
+
+ endResetModel();
+}
+
+
void SymbolTableModel::updateSymbols(std::vector<CacheSymbol>&& symbols)
{
m_preparedSymbols = symbols;
setFilter(m_filter);
}
+
const CacheSymbol& SymbolTableModel::symbolAt(int row) const
{
return m_modelSymbols.at(row);
diff --git a/view/sharedcache/ui/symboltable.h b/view/sharedcache/ui/symboltable.h
index 2fc5c892..51ba88f0 100644
--- a/view/sharedcache/ui/symboltable.h
+++ b/view/sharedcache/ui/symboltable.h
@@ -2,12 +2,13 @@
#include <sharedcacheapi.h>
#include "viewframe.h"
-#include "animation.h"
#include <QTableView>
#include <QStandardItemModel>
#include "filter.h"
+#ifndef BINARYNINJA_DSCSYMBOLTABLE_H
+#define BINARYNINJA_DSCSYMBOLTABLE_H
class SymbolTableView;
@@ -29,9 +30,11 @@ public:
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 sort(int column, Qt::SortOrder order) override;
void updateSymbols(std::vector<SharedCacheAPI::CacheSymbol>&& symbols);
void setFilter(std::string text);
const SharedCacheAPI::CacheSymbol& symbolAt(int row) const;
+
};
@@ -44,45 +47,46 @@ Q_OBJECT
public:
explicit SymbolTableView(QWidget* parent);
-
~SymbolTableView() override;
+ // Call this to populate the symbols from the given view.
+ void populateSymbols(BinaryNinja::BinaryView& view);
+
void scrollToFirstItem() override
{
- if (model()->rowCount() > 0)
- {
- scrollTo(model()->index(0, 0));
+ if (model()->rowCount() > 0) {
+ QModelIndex top = indexAt(rect().topLeft());
+ if (top.isValid())
+ scrollTo(top);
}
}
- // Call this to populate the symbols from the given view.
- void populateSymbols(BinaryNinja::BinaryView& view);
-
void scrollToCurrentItem() override
{
QModelIndex currentIndex = selectionModel()->currentIndex();
if (currentIndex.isValid())
- {
scrollTo(currentIndex);
- }
}
void selectFirstItem() override
{
- if (model()->rowCount() > 0)
- {
- QModelIndex firstIndex = model()->index(0, 0);
- selectionModel()->select(firstIndex, QItemSelectionModel::ClearAndSelect);
+ if (model()->rowCount() > 0) {
+ 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);
+ if (model()->rowCount() > 0) {
+ QModelIndex topLeft = indexAt(rect().topLeft());
+ if (topLeft.isValid()) {
+ setCurrentIndex(topLeft);
+ emit activated(topLeft);
+ }
}
}
@@ -92,4 +96,7 @@ public:
}
void setFilter(const std::string& filter) override;
-}; \ No newline at end of file
+};
+
+
+#endif // BINARYNINJA_DSCSYMBOLTABLE_H