diff options
| author | Mason Reed <mason@vector35.com> | 2025-10-07 14:32:48 -0400 |
|---|---|---|
| committer | Mason Reed <mason@vector35.com> | 2025-10-07 14:33:34 -0400 |
| commit | 4a227e0523391bc026bf96c3c31d44a39cac1701 (patch) | |
| tree | cc603b899a446fb469a0be3ae4c2c77e6351c091 /plugins | |
| parent | 3e24f6ab0b373d8718e26a19ac296044cc0d19c6 (diff) | |
[WARP] Format UI plugin
Diffstat (limited to 'plugins')
| -rw-r--r-- | plugins/warp/ui/containers.cpp | 471 | ||||
| -rw-r--r-- | plugins/warp/ui/containers.h | 245 | ||||
| -rw-r--r-- | plugins/warp/ui/matched.cpp | 139 | ||||
| -rw-r--r-- | plugins/warp/ui/matched.h | 16 | ||||
| -rw-r--r-- | plugins/warp/ui/matches.cpp | 281 | ||||
| -rw-r--r-- | plugins/warp/ui/matches.h | 26 | ||||
| -rw-r--r-- | plugins/warp/ui/plugin.cpp | 114 | ||||
| -rw-r--r-- | plugins/warp/ui/plugin.h | 52 | ||||
| -rw-r--r-- | plugins/warp/ui/shared/constraint.cpp | 178 | ||||
| -rw-r--r-- | plugins/warp/ui/shared/constraint.h | 66 | ||||
| -rw-r--r-- | plugins/warp/ui/shared/fetchdialog.cpp | 325 | ||||
| -rw-r--r-- | plugins/warp/ui/shared/fetchdialog.h | 48 | ||||
| -rw-r--r-- | plugins/warp/ui/shared/fetcher.cpp | 115 | ||||
| -rw-r--r-- | plugins/warp/ui/shared/fetcher.h | 45 | ||||
| -rw-r--r-- | plugins/warp/ui/shared/function.cpp | 498 | ||||
| -rw-r--r-- | plugins/warp/ui/shared/function.h | 177 | ||||
| -rw-r--r-- | plugins/warp/ui/shared/misc.cpp | 186 | ||||
| -rw-r--r-- | plugins/warp/ui/shared/misc.h | 126 | ||||
| -rw-r--r-- | plugins/warp/ui/shared/search.cpp | 496 | ||||
| -rw-r--r-- | plugins/warp/ui/shared/search.h | 291 |
20 files changed, 1936 insertions, 1959 deletions
diff --git a/plugins/warp/ui/containers.cpp b/plugins/warp/ui/containers.cpp index 689d695f..f800f977 100644 --- a/plugins/warp/ui/containers.cpp +++ b/plugins/warp/ui/containers.cpp @@ -1,274 +1,283 @@ #include "containers.h" -QVariant WarpSourcesModel::data(const QModelIndex &index, int role) const +QVariant WarpSourcesModel::data(const QModelIndex& index, int role) const { - if (!index.isValid()) - return {}; - if (index.row() < 0 || index.row() >= rowCount()) - return {}; + if (!index.isValid()) + return {}; + if (index.row() < 0 || index.row() >= rowCount()) + return {}; - const auto &r = m_rows[static_cast<size_t>(index.row())]; + const auto& r = m_rows[static_cast<size_t>(index.row())]; - // Build a small two-dot status icon (left: writable, right: uncommitted) - auto statusIcon = [](bool writable, bool uncommitted) -> QIcon { - static QIcon cache[2][2]; // [writable][uncommitted] - QIcon &cached = cache[writable ? 1 : 0][uncommitted ? 1 : 0]; - if (!cached.isNull()) - return cached; + // Build a small two-dot status icon (left: writable, right: uncommitted) + auto statusIcon = [](bool writable, bool uncommitted) -> QIcon { + static QIcon cache[2][2]; // [writable][uncommitted] + QIcon& cached = cache[writable ? 1 : 0][uncommitted ? 1 : 0]; + if (!cached.isNull()) + return cached; - const int w = 16, h = 12, radius = 4; - QPixmap pm(w, h); - pm.fill(Qt::transparent); - QPainter p(&pm); - p.setRenderHint(QPainter::Antialiasing, true); + const int w = 16, h = 12, radius = 4; + QPixmap pm(w, h); + pm.fill(Qt::transparent); + QPainter p(&pm); + p.setRenderHint(QPainter::Antialiasing, true); - // Colors - QColor writableOn(76, 175, 80); // green - QColor writableOff(158, 158, 158); // grey - QColor uncommittedOn(255, 193, 7); // amber - QColor uncommittedOff(158, 158, 158); // grey + // Colors + QColor writableOn(76, 175, 80); // green + QColor writableOff(158, 158, 158); // grey + QColor uncommittedOn(255, 193, 7); // amber + QColor uncommittedOff(158, 158, 158); // grey - // Left dot: writable - p.setBrush(writable ? writableOn : writableOff); - p.setPen(Qt::NoPen); - p.drawEllipse(QPoint(4, h / 2), radius, radius); + // Left dot: writable + p.setBrush(writable ? writableOn : writableOff); + p.setPen(Qt::NoPen); + p.drawEllipse(QPoint(4, h / 2), radius, radius); - // Right dot: uncommitted - p.setBrush(uncommitted ? uncommittedOn : uncommittedOff); - p.drawEllipse(QPoint(w - 6, h / 2), radius, radius); + // Right dot: uncommitted + p.setBrush(uncommitted ? uncommittedOn : uncommittedOff); + p.drawEllipse(QPoint(w - 6, h / 2), radius, radius); - p.end(); - cached = QIcon(pm); - return cached; - }; + p.end(); + cached = QIcon(pm); + return cached; + }; - if (role == Qt::DecorationRole && index.column() == PathCol) - { - return statusIcon(r.writable, r.uncommitted); - } + if (role == Qt::DecorationRole && index.column() == PathCol) + { + return statusIcon(r.writable, r.uncommitted); + } - if (role == Qt::ToolTipRole && index.column() == PathCol) - { - QStringList parts; - parts << (r.writable ? "Writable" : "Read-only"); - parts << (r.uncommitted ? "Uncommitted changes" : "No uncommitted changes"); - return parts.join(" • "); - } + if (role == Qt::ToolTipRole && index.column() == PathCol) + { + QStringList parts; + parts << (r.writable ? "Writable" : "Read-only"); + parts << (r.uncommitted ? "Uncommitted changes" : "No uncommitted changes"); + return parts.join(" • "); + } - if (role == Qt::DisplayRole) - { - switch (index.column()) - { - case GuidCol: return r.guid; - case PathCol: return r.path; - case WritableCol: return r.writable ? "Yes" : "No"; - case UncommittedCol: return r.uncommitted ? "Yes" : "No"; - default: return {}; - } - } + if (role == Qt::DisplayRole) + { + switch (index.column()) + { + case GuidCol: + return r.guid; + case PathCol: + return r.path; + case WritableCol: + return r.writable ? "Yes" : "No"; + case UncommittedCol: + return r.uncommitted ? "Yes" : "No"; + default: + return {}; + } + } - if (role == Qt::CheckStateRole) - { - // Optional: expose as checkboxes if someone ever shows these columns - switch (index.column()) - { - case WritableCol: return r.writable ? Qt::Checked : Qt::Unchecked; - case UncommittedCol: return r.uncommitted ? Qt::Checked : Qt::Unchecked; - default: break; - } - } + if (role == Qt::CheckStateRole) + { + // Optional: expose as checkboxes if someone ever shows these columns + switch (index.column()) + { + case WritableCol: + return r.writable ? Qt::Checked : Qt::Unchecked; + case UncommittedCol: + return r.uncommitted ? Qt::Checked : Qt::Unchecked; + default: + break; + } + } - return {}; + return {}; } -WarpContainerWidget::WarpContainerWidget(Warp::Ref<Warp::Container> container, QWidget *parent) : QWidget(parent) +WarpContainerWidget::WarpContainerWidget(Warp::Ref<Warp::Container> container, QWidget* parent) : QWidget(parent) { - m_container = std::move(container); - auto *layout = new QVBoxLayout(this); - layout->setContentsMargins(0, 0, 0, 0); - m_tabs = new QTabWidget(this); - layout->addWidget(m_tabs); + m_container = std::move(container); + auto* layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + m_tabs = new QTabWidget(this); + layout->addWidget(m_tabs); - // Sources tab - m_sourcesPage = new QWidget(this); - auto *sourcesLayout = new QVBoxLayout(m_sourcesPage); - m_sourcesView = new QTableView(m_sourcesPage); - m_sourcesModel = new WarpSourcesModel(m_sourcesPage); - m_sourcesModel->setContainer(m_container); - m_sourcesView->setModel(m_sourcesModel); - m_sourcesView->horizontalHeader()->setStretchLastSection(true); - m_sourcesView->setSelectionBehavior(QAbstractItemView::SelectRows); - m_sourcesView->setSelectionMode(QAbstractItemView::SingleSelection); + // Sources tab + m_sourcesPage = new QWidget(this); + auto* sourcesLayout = new QVBoxLayout(m_sourcesPage); + m_sourcesView = new QTableView(m_sourcesPage); + m_sourcesModel = new WarpSourcesModel(m_sourcesPage); + m_sourcesModel->setContainer(m_container); + m_sourcesView->setModel(m_sourcesModel); + m_sourcesView->horizontalHeader()->setStretchLastSection(true); + m_sourcesView->setSelectionBehavior(QAbstractItemView::SelectRows); + m_sourcesView->setSelectionMode(QAbstractItemView::SingleSelection); - // Make the table look like a simple list that shows only the source path - m_sourcesView->setShowGrid(false); - m_sourcesView->verticalHeader()->setVisible(false); - m_sourcesView->horizontalHeader()->setVisible(false); - m_sourcesView->setAlternatingRowColors(false); - m_sourcesView->setEditTriggers(QAbstractItemView::NoEditTriggers); - m_sourcesView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - m_sourcesView->setWordWrap(false); - m_sourcesView->setIconSize(QSize(16, 12)); - // Ensure long paths truncate from the left: "...tail/of/the/path" - m_sourcesView->setTextElideMode(Qt::ElideLeft); - // Hide GUID column, keep only the Path column visible - m_sourcesView->setColumnHidden(WarpSourcesModel::GuidCol, true); - // Also hide boolean columns; their state is shown as an icon next to the path - m_sourcesView->setColumnHidden(WarpSourcesModel::WritableCol, true); - m_sourcesView->setColumnHidden(WarpSourcesModel::UncommittedCol, true); - // Ensure the remaining (Path) column fills the width - m_sourcesView->horizontalHeader()->setSectionResizeMode(WarpSourcesModel::PathCol, QHeaderView::Stretch); + // Make the table look like a simple list that shows only the source path + m_sourcesView->setShowGrid(false); + m_sourcesView->verticalHeader()->setVisible(false); + m_sourcesView->horizontalHeader()->setVisible(false); + m_sourcesView->setAlternatingRowColors(false); + m_sourcesView->setEditTriggers(QAbstractItemView::NoEditTriggers); + m_sourcesView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + m_sourcesView->setWordWrap(false); + m_sourcesView->setIconSize(QSize(16, 12)); + // Ensure long paths truncate from the left: "...tail/of/the/path" + m_sourcesView->setTextElideMode(Qt::ElideLeft); + // Hide GUID column, keep only the Path column visible + m_sourcesView->setColumnHidden(WarpSourcesModel::GuidCol, true); + // Also hide boolean columns; their state is shown as an icon next to the path + m_sourcesView->setColumnHidden(WarpSourcesModel::WritableCol, true); + m_sourcesView->setColumnHidden(WarpSourcesModel::UncommittedCol, true); + // Ensure the remaining (Path) column fills the width + m_sourcesView->horizontalHeader()->setSectionResizeMode(WarpSourcesModel::PathCol, QHeaderView::Stretch); - // Per-item context menu - m_sourcesView->setContextMenuPolicy(Qt::CustomContextMenu); - connect(m_sourcesView, &QWidget::customContextMenuRequested, this, [this](const QPoint &pos) { - QMenu menu(m_sourcesView); - const QModelIndex index = m_sourcesView->indexAt(pos); + // Per-item context menu + m_sourcesView->setContextMenuPolicy(Qt::CustomContextMenu); + connect(m_sourcesView, &QWidget::customContextMenuRequested, this, [this](const QPoint& pos) { + QMenu menu(m_sourcesView); + const QModelIndex index = m_sourcesView->indexAt(pos); - if (!index.isValid()) - { - QAction *actAdd = menu.addAction(tr("Add Source")); - QAction *chosen = menu.exec(m_sourcesView->viewport()->mapToGlobal(pos)); - if (!chosen) - return; - if (chosen == actAdd) - { - std::string sourceName; - if (!BinaryNinja::GetTextLineInput(sourceName, "Source name:", "Add Source")) - return; - if (const auto sourceId = m_container->AddSource(sourceName); !sourceId.has_value()) - { - BinaryNinja::LogAlertF("Failed to add source: {}", sourceName); - return; - } - m_sourcesModel->reload(); - } - } else - { - m_sourcesView->setCurrentIndex(index.sibling(index.row(), WarpSourcesModel::PathCol)); + if (!index.isValid()) + { + QAction* actAdd = menu.addAction(tr("Add Source")); + QAction* chosen = menu.exec(m_sourcesView->viewport()->mapToGlobal(pos)); + if (!chosen) + return; + if (chosen == actAdd) + { + std::string sourceName; + if (!BinaryNinja::GetTextLineInput(sourceName, "Source name:", "Add Source")) + return; + if (const auto sourceId = m_container->AddSource(sourceName); !sourceId.has_value()) + { + BinaryNinja::LogAlertF("Failed to add source: {}", sourceName); + return; + } + m_sourcesModel->reload(); + } + } + else + { + m_sourcesView->setCurrentIndex(index.sibling(index.row(), WarpSourcesModel::PathCol)); - const int row = index.row(); - const QModelIndex pathIdx = m_sourcesModel->index(row, WarpSourcesModel::PathCol); - const QModelIndex guidIdx = m_sourcesModel->index(row, WarpSourcesModel::GuidCol); - const QString path = m_sourcesModel->data(pathIdx, Qt::DisplayRole).toString(); - const QFileInfo fi(path); - - const QString guid = m_sourcesModel->data(guidIdx, Qt::DisplayRole).toString(); + const int row = index.row(); + const QModelIndex pathIdx = m_sourcesModel->index(row, WarpSourcesModel::PathCol); + const QModelIndex guidIdx = m_sourcesModel->index(row, WarpSourcesModel::GuidCol); + const QString path = m_sourcesModel->data(pathIdx, Qt::DisplayRole).toString(); + const QFileInfo fi(path); - QAction *actReveal = menu.addAction(tr("Reveal in File Browser")); - actReveal->setEnabled(fi.exists()); - QAction *actCopyPath = menu.addAction(tr("Copy Path")); - QAction *actCopyGuid = menu.addAction(tr("Copy GUID")); - - QAction *chosen = menu.exec(m_sourcesView->viewport()->mapToGlobal(pos)); - if (!chosen) - return; - if (chosen == actCopyPath) - QGuiApplication::clipboard()->setText(path); - else if (chosen == actCopyGuid) - QGuiApplication::clipboard()->setText(guid); - else if (chosen == actReveal) - QDesktopServices::openUrl(QUrl::fromLocalFile(fi.absoluteFilePath())); - } - }); + const QString guid = m_sourcesModel->data(guidIdx, Qt::DisplayRole).toString(); + QAction* actReveal = menu.addAction(tr("Reveal in File Browser")); + actReveal->setEnabled(fi.exists()); + QAction* actCopyPath = menu.addAction(tr("Copy Path")); + QAction* actCopyGuid = menu.addAction(tr("Copy GUID")); - sourcesLayout->addWidget(m_sourcesView); - m_tabs->addTab(m_sourcesPage, tr("Sources")); + QAction* chosen = menu.exec(m_sourcesView->viewport()->mapToGlobal(pos)); + if (!chosen) + return; + if (chosen == actCopyPath) + QGuiApplication::clipboard()->setText(path); + else if (chosen == actCopyGuid) + QGuiApplication::clipboard()->setText(guid); + else if (chosen == actReveal) + QDesktopServices::openUrl(QUrl::fromLocalFile(fi.absoluteFilePath())); + } + }); - // Search tab - m_searchTab = new WarpSearchWidget(m_container, this); - m_tabs->addTab(m_searchTab, tr("Search")); - // Periodic refresh timer for the Sources view - m_refreshTimer = new QTimer(this); - m_refreshTimer->setInterval(5000); - connect(m_refreshTimer, &QTimer::timeout, this, [this]() { - // Only refresh if the widget and the Sources page are actually visible - if (!this->isVisible() || !m_sourcesPage || !m_sourcesPage->isVisible()) - return; + sourcesLayout->addWidget(m_sourcesView); + m_tabs->addTab(m_sourcesPage, tr("Sources")); - // Preserve selection by GUID across reloads - QString currentGuid; - if (const QModelIndex currentIdx = m_sourcesView->currentIndex(); currentIdx.isValid()) - { - const int row = currentIdx.row(); - const QModelIndex guidIdx = m_sourcesModel->index(row, WarpSourcesModel::GuidCol); - currentGuid = m_sourcesModel->data(guidIdx, Qt::DisplayRole).toString(); - } + // Search tab + m_searchTab = new WarpSearchWidget(m_container, this); + m_tabs->addTab(m_searchTab, tr("Search")); - m_sourcesModel->reload(); + // Periodic refresh timer for the Sources view + m_refreshTimer = new QTimer(this); + m_refreshTimer->setInterval(5000); + connect(m_refreshTimer, &QTimer::timeout, this, [this]() { + // Only refresh if the widget and the Sources page are actually visible + if (!this->isVisible() || !m_sourcesPage || !m_sourcesPage->isVisible()) + return; - if (!currentGuid.isEmpty()) - { - for (int r = 0; r < m_sourcesModel->rowCount(); ++r) - { - const QModelIndex gIdx = m_sourcesModel->index(r, WarpSourcesModel::GuidCol); - if (m_sourcesModel->data(gIdx, Qt::DisplayRole).toString() == currentGuid) - { - m_sourcesView->setCurrentIndex(m_sourcesModel->index(r, WarpSourcesModel::PathCol)); - break; - } - } - } - }); - m_refreshTimer->start(); + // Preserve selection by GUID across reloads + QString currentGuid; + if (const QModelIndex currentIdx = m_sourcesView->currentIndex(); currentIdx.isValid()) + { + const int row = currentIdx.row(); + const QModelIndex guidIdx = m_sourcesModel->index(row, WarpSourcesModel::GuidCol); + currentGuid = m_sourcesModel->data(guidIdx, Qt::DisplayRole).toString(); + } - // Optional: force a refresh when switching back to the Sources tab - connect(m_tabs, &QTabWidget::currentChanged, this, [this](const int idx) { - QWidget *w = m_tabs->widget(idx); - if (w == m_sourcesPage) - m_sourcesModel->reload(); - }); + m_sourcesModel->reload(); + + if (!currentGuid.isEmpty()) + { + for (int r = 0; r < m_sourcesModel->rowCount(); ++r) + { + const QModelIndex gIdx = m_sourcesModel->index(r, WarpSourcesModel::GuidCol); + if (m_sourcesModel->data(gIdx, Qt::DisplayRole).toString() == currentGuid) + { + m_sourcesView->setCurrentIndex(m_sourcesModel->index(r, WarpSourcesModel::PathCol)); + break; + } + } + } + }); + m_refreshTimer->start(); + + // Optional: force a refresh when switching back to the Sources tab + connect(m_tabs, &QTabWidget::currentChanged, this, [this](const int idx) { + QWidget* w = m_tabs->widget(idx); + if (w == m_sourcesPage) + m_sourcesModel->reload(); + }); } -WarpContainersPane::WarpContainersPane(QWidget *parent) : QWidget(parent) +WarpContainersPane::WarpContainersPane(QWidget* parent) : QWidget(parent) { - auto *splitter = new QSplitter(Qt::Vertical, this); - splitter->setContentsMargins(0, 0, 0, 0); - auto *mainLayout = new QVBoxLayout(this); - mainLayout->setContentsMargins(0, 0, 0, 0); - mainLayout->setSpacing(0); - mainLayout->addWidget(splitter); - auto newPalette = palette(); - newPalette.setColor(QPalette::Window, getThemeColor(SidebarWidgetBackgroundColor)); - setAutoFillBackground(true); - setPalette(newPalette); + auto* splitter = new QSplitter(Qt::Vertical, this); + splitter->setContentsMargins(0, 0, 0, 0); + auto* mainLayout = new QVBoxLayout(this); + mainLayout->setContentsMargins(0, 0, 0, 0); + mainLayout->setSpacing(0); + mainLayout->addWidget(splitter); + auto newPalette = palette(); + newPalette.setColor(QPalette::Window, getThemeColor(SidebarWidgetBackgroundColor)); + setAutoFillBackground(true); + setPalette(newPalette); - // List on top - m_list = new QListWidget(splitter); - m_list->setSelectionMode(QAbstractItemView::SingleSelection); - m_list->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - m_list->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents); - m_list->setUniformItemSizes(true); + // List on top + m_list = new QListWidget(splitter); + m_list->setSelectionMode(QAbstractItemView::SingleSelection); + m_list->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + m_list->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents); + m_list->setUniformItemSizes(true); - // Make names larger and show end of long strings (elide at the start) - { - QFont f = m_list->font(); - f.setPointSizeF(f.pointSizeF() + 2.0); // bump size - m_list->setFont(f); - m_list->setTextElideMode(Qt::ElideLeft); - } + // Make names larger and show end of long strings (elide at the start) + { + QFont f = m_list->font(); + f.setPointSizeF(f.pointSizeF() + 2.0); // bump size + m_list->setFont(f); + m_list->setTextElideMode(Qt::ElideLeft); + } - // Container view (tabs) below - m_stack = new QStackedWidget(splitter); - m_stack->setContentsMargins(0, 0, 0, 0); + // Container view (tabs) below + m_stack = new QStackedWidget(splitter); + m_stack->setContentsMargins(0, 0, 0, 0); - splitter->setStretchFactor(0, 0); // list: minimal growth - splitter->setStretchFactor(1, 1); // stack: takes remaining space - splitter->setCollapsible(0, false); - splitter->setCollapsible(1, false); + splitter->setStretchFactor(0, 0); // list: minimal growth + splitter->setStretchFactor(1, 1); // stack: takes remaining space + splitter->setCollapsible(0, false); + splitter->setCollapsible(1, false); - populate(); + populate(); - connect(m_list, &QListWidget::currentRowChanged, this, [this](int row) { - if (row >= 0 && row < m_stack->count()) - m_stack->setCurrentIndex(row); - }); + connect(m_list, &QListWidget::currentRowChanged, this, [this](int row) { + if (row >= 0 && row < m_stack->count()) + m_stack->setCurrentIndex(row); + }); - // Select the first container if available - if (m_list->count() > 0) - { - m_list->setCurrentRow(0); - } + // Select the first container if available + if (m_list->count() > 0) + { + m_list->setCurrentRow(0); + } } diff --git a/plugins/warp/ui/containers.h b/plugins/warp/ui/containers.h index 80ffa12f..d5e82242 100644 --- a/plugins/warp/ui/containers.h +++ b/plugins/warp/ui/containers.h @@ -14,162 +14,165 @@ class WarpSourcesModel final : public QAbstractTableModel { - Q_OBJECT + Q_OBJECT public: - enum Columns : int - { - GuidCol = 0, - PathCol, - WritableCol, - UncommittedCol, - ColumnCount - }; + enum Columns : int + { + GuidCol = 0, + PathCol, + WritableCol, + UncommittedCol, + ColumnCount + }; - explicit WarpSourcesModel(QObject *parent = nullptr) - : QAbstractTableModel(parent) - { - } + explicit WarpSourcesModel(QObject* parent = nullptr) : QAbstractTableModel(parent) {} - void setContainer(Warp::Ref<Warp::Container> container) - { - m_container = std::move(container); - reload(); - } + void setContainer(Warp::Ref<Warp::Container> container) + { + m_container = std::move(container); + reload(); + } - void reload() - { - // Fetch synchronously (can be adapted to async if needed) - beginResetModel(); - m_rows.clear(); - for (const auto &src: m_container->GetSources()) - { - QString guid = QString::fromStdString(src.ToString()); - QString path = QString::fromStdString(m_container->SourcePath(src).value_or(std::string{})); - bool writable = m_container->IsSourceWritable(src); - bool uncommitted = m_container->IsSourceUncommitted(src); - m_rows.push_back({guid, path, writable, uncommitted}); - } - endResetModel(); - } + void reload() + { + // Fetch synchronously (can be adapted to async if needed) + beginResetModel(); + m_rows.clear(); + for (const auto& src : m_container->GetSources()) + { + QString guid = QString::fromStdString(src.ToString()); + QString path = QString::fromStdString(m_container->SourcePath(src).value_or(std::string {})); + bool writable = m_container->IsSourceWritable(src); + bool uncommitted = m_container->IsSourceUncommitted(src); + m_rows.push_back({guid, path, writable, uncommitted}); + } + endResetModel(); + } - int rowCount(const QModelIndex &parent = QModelIndex()) const override - { - if (parent.isValid()) return 0; - return static_cast<int>(m_rows.size()); - } + int rowCount(const QModelIndex& parent = QModelIndex()) const override + { + if (parent.isValid()) + return 0; + return static_cast<int>(m_rows.size()); + } - int columnCount(const QModelIndex &parent = QModelIndex()) const override - { - Q_UNUSED(parent); - return ColumnCount; - } + int columnCount(const QModelIndex& parent = QModelIndex()) const override + { + Q_UNUSED(parent); + return ColumnCount; + } - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; - QVariant headerData(int section, Qt::Orientation orientation, int role) const override - { - if (orientation == Qt::Horizontal && role == Qt::DisplayRole) - { - switch (section) - { - case GuidCol: return "Source GUID"; - case PathCol: return "Path"; - case WritableCol: return "Writable"; - case UncommittedCol: return "Uncommitted"; - default: return {}; - } - } - return {}; - } + QVariant headerData(int section, Qt::Orientation orientation, int role) const override + { + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) + { + switch (section) + { + case GuidCol: + return "Source GUID"; + case PathCol: + return "Path"; + case WritableCol: + return "Writable"; + case UncommittedCol: + return "Uncommitted"; + default: + return {}; + } + } + return {}; + } private: - struct Row - { - QString guid; - QString path; - bool writable; - bool uncommitted; - }; + struct Row + { + QString guid; + QString path; + bool writable; + bool uncommitted; + }; - std::vector<Row> m_rows; - Warp::Ref<Warp::Container> m_container; + std::vector<Row> m_rows; + Warp::Ref<Warp::Container> m_container; }; class WarpContainerWidget : public QWidget { - Q_OBJECT + Q_OBJECT public: - explicit WarpContainerWidget(Warp::Ref<Warp::Container> container, QWidget *parent = nullptr); + explicit WarpContainerWidget(Warp::Ref<Warp::Container> container, QWidget* parent = nullptr); private: - Warp::Ref<Warp::Container> m_container; + Warp::Ref<Warp::Container> m_container; - QTabWidget *m_tabs = nullptr; + QTabWidget* m_tabs = nullptr; - // Sources - QTableView *m_sourcesView = nullptr; - WarpSourcesModel *m_sourcesModel = nullptr; - QWidget* m_sourcesPage = nullptr; - QTimer* m_refreshTimer = nullptr; + // Sources + QTableView* m_sourcesView = nullptr; + WarpSourcesModel* m_sourcesModel = nullptr; + QWidget* m_sourcesPage = nullptr; + QTimer* m_refreshTimer = nullptr; - // Search - WarpSearchWidget *m_searchTab = nullptr; + // Search + WarpSearchWidget* m_searchTab = nullptr; }; class WarpContainersPane : public QWidget { - Q_OBJECT + Q_OBJECT public: - explicit WarpContainersPane(QWidget *parent = nullptr); + explicit WarpContainersPane(QWidget* parent = nullptr); - void refresh() - { - // Clear and repopulate from current container list - m_list->clear(); - while (m_stack->count() > 0) - { - QWidget *w = m_stack->widget(0); - m_stack->removeWidget(w); - delete w; - } - m_containers.clear(); - populate(); - if (m_list->count() > 0) - m_list->setCurrentRow(0); - } + void refresh() + { + // Clear and repopulate from current container list + m_list->clear(); + while (m_stack->count() > 0) + { + QWidget* w = m_stack->widget(0); + m_stack->removeWidget(w); + delete w; + } + m_containers.clear(); + populate(); + if (m_list->count() > 0) + m_list->setCurrentRow(0); + } private: - void populate() - { - // Retrieve all available containers - const auto all = Warp::Container::All(); - m_containers = all; // copy vector<Ref<Container>> + void populate() + { + // Retrieve all available containers + const auto all = Warp::Container::All(); + m_containers = all; // copy vector<Ref<Container>> - for (const auto &c: m_containers) - { - const QString name = QString::fromStdString(c->GetName()); - auto *item = new QListWidgetItem(name, m_list); - item->setSizeHint(QSize(item->sizeHint().width(), itemHeightPx())); - auto *widget = new WarpContainerWidget(c, m_stack); - m_stack->addWidget(widget); - } + for (const auto& c : m_containers) + { + const QString name = QString::fromStdString(c->GetName()); + auto* item = new QListWidgetItem(name, m_list); + item->setSizeHint(QSize(item->sizeHint().width(), itemHeightPx())); + auto* widget = new WarpContainerWidget(c, m_stack); + m_stack->addWidget(widget); + } - // Visual style: behave like a vertical tab bar - // m_list->setFrameShape(QFrame::NoFrame); - // m_list->setSpacing(0); - } + // Visual style: behave like a vertical tab bar + // m_list->setFrameShape(QFrame::NoFrame); + // m_list->setSpacing(0); + } - static int itemHeightPx() - { - // A reasonable, readable height per entry - return 28; - } + static int itemHeightPx() + { + // A reasonable, readable height per entry + return 28; + } private: - QListWidget *m_list = nullptr; - QStackedWidget *m_stack = nullptr; - std::vector<Warp::Ref<Warp::Container> > m_containers; + QListWidget* m_list = nullptr; + QStackedWidget* m_stack = nullptr; + std::vector<Warp::Ref<Warp::Container>> m_containers; }; diff --git a/plugins/warp/ui/matched.cpp b/plugins/warp/ui/matched.cpp index 618c1b4c..aff6779f 100644 --- a/plugins/warp/ui/matched.cpp +++ b/plugins/warp/ui/matched.cpp @@ -4,88 +4,85 @@ #include "theme.h" -const char *WARP_APPLY_ACTIVITY = "analysis.warp.apply"; +const char* WARP_APPLY_ACTIVITY = "analysis.warp.apply"; WarpMatchedWidget::WarpMatchedWidget(BinaryViewRef current) { - m_current = current; - // Create the QT stuff - QGridLayout *layout = new QGridLayout(this); - layout->setContentsMargins(2, 2, 2, 2); - layout->setSpacing(2); - auto newPalette = palette(); - newPalette.setColor(QPalette::Window, getThemeColor(SidebarWidgetBackgroundColor)); - setAutoFillBackground(true); - setPalette(newPalette); + m_current = current; + // Create the QT stuff + QGridLayout* layout = new QGridLayout(this); + layout->setContentsMargins(2, 2, 2, 2); + layout->setSpacing(2); + auto newPalette = palette(); + newPalette.setColor(QPalette::Window, getThemeColor(SidebarWidgetBackgroundColor)); + setAutoFillBackground(true); + setPalette(newPalette); - // TODO: Split horizontally if the widget is displayed in a sidebar that is vertically challenged. - m_splitter = new QSplitter(Qt::Vertical); - m_splitter->setContentsMargins(0, 0, 0, 0); + // TODO: Split horizontally if the widget is displayed in a sidebar that is vertically challenged. + m_splitter = new QSplitter(Qt::Vertical); + m_splitter->setContentsMargins(0, 0, 0, 0); - // Add a widget to display the matches. - m_tableWidget = new WarpFunctionTableWidget(this); - m_tableWidget->setContentsMargins(0, 0, 0, 0); - m_splitter->addWidget(m_tableWidget); + // Add a widget to display the matches. + m_tableWidget = new WarpFunctionTableWidget(this); + m_tableWidget->setContentsMargins(0, 0, 0, 0); + m_splitter->addWidget(m_tableWidget); - // Toggle the applying workflow, this workflow sets all the data for the function based on the matched function data. - m_tableWidget->RegisterContextMenuAction("Toggle Application", - [this](WarpFunctionItem *, std::optional<uint64_t> address) { - if (!address.has_value()) - return; - for (const auto &func: m_current->GetAnalysisFunctionsForAddress( - *address)) - { - const bool previous = BinaryNinja::Settings::Instance()->Get<bool>( - WARP_APPLY_ACTIVITY, func); - BinaryNinja::Settings::Instance()->Set( - WARP_APPLY_ACTIVITY, !previous, func); - func->Reanalyze(); - } - }); + // Toggle the applying workflow, this workflow sets all the data for the function based on the matched function + // data. + m_tableWidget->RegisterContextMenuAction( + "Toggle Application", [this](WarpFunctionItem*, std::optional<uint64_t> address) { + if (!address.has_value()) + return; + for (const auto& func : m_current->GetAnalysisFunctionsForAddress(*address)) + { + const bool previous = BinaryNinja::Settings::Instance()->Get<bool>(WARP_APPLY_ACTIVITY, func); + BinaryNinja::Settings::Instance()->Set(WARP_APPLY_ACTIVITY, !previous, func); + func->Reanalyze(); + } + }); - layout->addWidget(m_splitter, 1, 0, 1, 5); - setLayout(layout); + layout->addWidget(m_splitter, 1, 0, 1, 5); + setLayout(layout); - Update(); + Update(); - connect(m_tableWidget->GetTableView(), &QTableView::clicked, this, - [this](const QModelIndex &index) { - if (m_current == nullptr) - return; - if (!index.isValid()) - return; - const QModelIndex sourceIndex = m_tableWidget->GetProxyModel()->mapToSource(index); - if (!sourceIndex.isValid()) - return; - auto selectedItem = m_tableWidget->GetModel()->GetAddress(sourceIndex); - if (!selectedItem.has_value()) - return; - // Navigate to the address in the view, so the user feels like they are doing something. - auto currentView = m_current->GetCurrentView(); - m_current->Navigate(currentView, selectedItem.value()); - }); + connect(m_tableWidget->GetTableView(), &QTableView::clicked, this, [this](const QModelIndex& index) { + if (m_current == nullptr) + return; + if (!index.isValid()) + return; + const QModelIndex sourceIndex = m_tableWidget->GetProxyModel()->mapToSource(index); + if (!sourceIndex.isValid()) + return; + auto selectedItem = m_tableWidget->GetModel()->GetAddress(sourceIndex); + if (!selectedItem.has_value()) + return; + // Navigate to the address in the view, so the user feels like they are doing something. + auto currentView = m_current->GetCurrentView(); + m_current->Navigate(currentView, selectedItem.value()); + }); } void WarpMatchedWidget::Update() { - m_tableWidget->GetTableView()->setSortingEnabled(false); - m_tableWidget->GetTableView()->setEnabled(false); - m_tableWidget->GetProxyModel()->setDynamicSortFilter(false); - m_tableWidget->GetTableView()->setUpdatesEnabled(false); - m_tableWidget->GetTableView()->setModel(nullptr); - m_tableWidget->GetProxyModel()->setSourceModel(nullptr); - for (const auto &analysisFunction: m_current->GetAnalysisFunctionList()) - { - if (const auto &matchedFunction = Warp::Function::GetMatched(*analysisFunction)) - { - uint64_t startAddress = analysisFunction->GetStart(); - m_tableWidget->InsertFunction(startAddress, new WarpFunctionItem(matchedFunction, analysisFunction)); - } - } - m_tableWidget->GetTableView()->setModel(m_tableWidget->GetProxyModel()); - m_tableWidget->GetProxyModel()->setSourceModel(m_tableWidget->GetModel()); - m_tableWidget->GetProxyModel()->setDynamicSortFilter(true); - m_tableWidget->GetTableView()->setEnabled(true); - m_tableWidget->GetTableView()->setSortingEnabled(true); - m_tableWidget->GetTableView()->setUpdatesEnabled(true); + m_tableWidget->GetTableView()->setSortingEnabled(false); + m_tableWidget->GetTableView()->setEnabled(false); + m_tableWidget->GetProxyModel()->setDynamicSortFilter(false); + m_tableWidget->GetTableView()->setUpdatesEnabled(false); + m_tableWidget->GetTableView()->setModel(nullptr); + m_tableWidget->GetProxyModel()->setSourceModel(nullptr); + for (const auto& analysisFunction : m_current->GetAnalysisFunctionList()) + { + if (const auto& matchedFunction = Warp::Function::GetMatched(*analysisFunction)) + { + uint64_t startAddress = analysisFunction->GetStart(); + m_tableWidget->InsertFunction(startAddress, new WarpFunctionItem(matchedFunction, analysisFunction)); + } + } + m_tableWidget->GetTableView()->setModel(m_tableWidget->GetProxyModel()); + m_tableWidget->GetProxyModel()->setSourceModel(m_tableWidget->GetModel()); + m_tableWidget->GetProxyModel()->setDynamicSortFilter(true); + m_tableWidget->GetTableView()->setEnabled(true); + m_tableWidget->GetTableView()->setSortingEnabled(true); + m_tableWidget->GetTableView()->setUpdatesEnabled(true); } diff --git a/plugins/warp/ui/matched.h b/plugins/warp/ui/matched.h index 5e333e25..70c624d5 100644 --- a/plugins/warp/ui/matched.h +++ b/plugins/warp/ui/matched.h @@ -6,22 +6,22 @@ class WarpMatchedFunctionTableWidget : public WarpFunctionTableWidget { - Q_OBJECT + Q_OBJECT }; class WarpMatchedWidget : public QWidget { - Q_OBJECT - BinaryViewRef m_current; + Q_OBJECT + BinaryViewRef m_current; - QSplitter *m_splitter; + QSplitter* m_splitter; - WarpFunctionTableWidget *m_tableWidget; + WarpFunctionTableWidget* m_tableWidget; public: - explicit WarpMatchedWidget(BinaryViewRef current); + explicit WarpMatchedWidget(BinaryViewRef current); - ~WarpMatchedWidget() override = default; + ~WarpMatchedWidget() override = default; - void Update(); + void Update(); }; diff --git a/plugins/warp/ui/matches.cpp b/plugins/warp/ui/matches.cpp index b03d72c6..5ec3da1a 100644 --- a/plugins/warp/ui/matches.cpp +++ b/plugins/warp/ui/matches.cpp @@ -13,178 +13,179 @@ WarpCurrentFunctionWidget::WarpCurrentFunctionWidget() { - // We must explicitly support no current function. - m_current = nullptr; + // We must explicitly support no current function. + m_current = nullptr; - m_logger = new BinaryNinja::Logger("WARP UI"); + m_logger = new BinaryNinja::Logger("WARP UI"); - // Create the QT stuff - QGridLayout *layout = new QGridLayout(this); - layout->setContentsMargins(2, 2, 2, 2); - layout->setSpacing(2); - auto newPalette = palette(); - newPalette.setColor(QPalette::Window, getThemeColor(SidebarWidgetBackgroundColor)); - setAutoFillBackground(true); - setPalette(newPalette); + // Create the QT stuff + QGridLayout* layout = new QGridLayout(this); + layout->setContentsMargins(2, 2, 2, 2); + layout->setSpacing(2); + auto newPalette = palette(); + newPalette.setColor(QPalette::Window, getThemeColor(SidebarWidgetBackgroundColor)); + setAutoFillBackground(true); + setPalette(newPalette); - // TODO: Split horizontally if the widget is displayed in a sidebar that is vertically challenged. - m_splitter = new QSplitter(Qt::Vertical); - m_splitter->setContentsMargins(0, 0, 0, 0); + // TODO: Split horizontally if the widget is displayed in a sidebar that is vertically challenged. + m_splitter = new QSplitter(Qt::Vertical); + m_splitter->setContentsMargins(0, 0, 0, 0); - // Add a widget to display the matches. - m_tableWidget = new WarpFunctionTableWidget(this); - m_tableWidget->setContentsMargins(0, 0, 0, 0); - m_splitter->addWidget(m_tableWidget); + // Add a widget to display the matches. + m_tableWidget = new WarpFunctionTableWidget(this); + m_tableWidget->setContentsMargins(0, 0, 0, 0); + m_splitter->addWidget(m_tableWidget); - // Add a widget to display the info about the selected function match. - m_infoWidget = new WarpFunctionInfoWidget(this); - m_infoWidget->setContentsMargins(0, 0, 0, 0); - m_splitter->addWidget(m_infoWidget); + // Add a widget to display the info about the selected function match. + m_infoWidget = new WarpFunctionInfoWidget(this); + m_infoWidget->setContentsMargins(0, 0, 0, 0); + m_splitter->addWidget(m_infoWidget); - layout->addWidget(m_splitter, 1, 0, 1, 5); - setLayout(layout); + layout->addWidget(m_splitter, 1, 0, 1, 5); + setLayout(layout); - m_tableWidget->RegisterContextMenuAction("Apply", [this](WarpFunctionItem *item, std::optional<uint64_t>) { - if (item == nullptr) - return; - Warp::Ref<Warp::Function> selectedFunction = item->GetFunction(); - if (!selectedFunction) - return; - selectedFunction->Apply(*m_current); - // Update analysis so that the selected function shows. - m_current->GetView()->UpdateAnalysis(); - // So it shows visually as selected. - m_tableWidget->GetModel()->SetMatchedFunction(selectedFunction); - }); - m_tableWidget->RegisterContextMenuAction("Search for Source", - [this](WarpFunctionItem *item, std::optional<uint64_t>) { - // Apply the source as the filter. - if (const auto source = item->GetSource(); source) - m_tableWidget->setFilter(source->ToString()); - }); + m_tableWidget->RegisterContextMenuAction("Apply", [this](WarpFunctionItem* item, std::optional<uint64_t>) { + if (item == nullptr) + return; + Warp::Ref<Warp::Function> selectedFunction = item->GetFunction(); + if (!selectedFunction) + return; + selectedFunction->Apply(*m_current); + // Update analysis so that the selected function shows. + m_current->GetView()->UpdateAnalysis(); + // So it shows visually as selected. + m_tableWidget->GetModel()->SetMatchedFunction(selectedFunction); + }); + m_tableWidget->RegisterContextMenuAction( + "Search for Source", [this](WarpFunctionItem* item, std::optional<uint64_t>) { + // Apply the source as the filter. + if (const auto source = item->GetSource(); source) + m_tableWidget->setFilter(source->ToString()); + }); - connect(m_tableWidget->GetTableView(), &QTableView::clicked, this, - [this](const QModelIndex &index) { - if (m_current == nullptr) - return; - if (!index.isValid()) - return; - const QModelIndex sourceIndex = m_tableWidget->GetProxyModel()->mapToSource(index); - if (!sourceIndex.isValid()) - return; - auto selectedItem = m_tableWidget->GetModel()->GetItem(sourceIndex); - // Access the first column in the row - if (!selectedItem) - return; - m_infoWidget->SetFunction(selectedItem->GetFunction()); - m_infoWidget->UpdateInfo(); - }); + connect(m_tableWidget->GetTableView(), &QTableView::clicked, this, [this](const QModelIndex& index) { + if (m_current == nullptr) + return; + if (!index.isValid()) + return; + const QModelIndex sourceIndex = m_tableWidget->GetProxyModel()->mapToSource(index); + if (!sourceIndex.isValid()) + return; + auto selectedItem = m_tableWidget->GetModel()->GetItem(sourceIndex); + // Access the first column in the row + if (!selectedItem) + return; + m_infoWidget->SetFunction(selectedItem->GetFunction()); + m_infoWidget->UpdateInfo(); + }); - connect(m_tableWidget->GetTableView(), &QTableView::doubleClicked, this, [=, this](const QModelIndex &index) { - if (m_current == nullptr) - return; - // Get the selected row for the given index. - if (!index.isValid()) - return; - const QModelIndex sourceIndex = m_tableWidget->GetProxyModel()->mapToSource(index); - if (!sourceIndex.isValid()) - return; - auto selectedItem = m_tableWidget->GetModel()->GetItem(sourceIndex); - // Access the first column in the row - if (!selectedItem) - return; - Warp::Ref<Warp::Function> selectedFunction = selectedItem->GetFunction(); + connect(m_tableWidget->GetTableView(), &QTableView::doubleClicked, this, [=, this](const QModelIndex& index) { + if (m_current == nullptr) + return; + // Get the selected row for the given index. + if (!index.isValid()) + return; + const QModelIndex sourceIndex = m_tableWidget->GetProxyModel()->mapToSource(index); + if (!sourceIndex.isValid()) + return; + auto selectedItem = m_tableWidget->GetModel()->GetItem(sourceIndex); + // Access the first column in the row + if (!selectedItem) + return; + Warp::Ref<Warp::Function> selectedFunction = selectedItem->GetFunction(); - // Actually apply the newly selected function. - selectedFunction->Apply(*m_current); + // Actually apply the newly selected function. + selectedFunction->Apply(*m_current); - // Update analysis so that the selected function shows. - m_current->GetView()->UpdateAnalysis(); + // Update analysis so that the selected function shows. + m_current->GetView()->UpdateAnalysis(); - // So it shows visually as selected. - m_tableWidget->GetModel()->SetMatchedFunction(selectedFunction); - }); + // So it shows visually as selected. + m_tableWidget->GetModel()->SetMatchedFunction(selectedFunction); + }); } void WarpCurrentFunctionWidget::SetFetcher(std::shared_ptr<WarpFetcher> fetcher) { - m_fetcher = fetcher; + m_fetcher = fetcher; } void WarpCurrentFunctionWidget::SetCurrentFunction(FunctionRef current) { - if (m_current == current) - return; - m_current = current; - m_infoWidget->SetAnalysisFunction(m_current); + if (m_current == current) + return; + m_current = current; + m_infoWidget->SetAnalysisFunction(m_current); - // If we have a fetcher we should also let it know to try and fetch the possible functions from the containers. - if (current && m_fetcher) - { - m_fetcher->AddPendingFunction(current); + // If we have a fetcher we should also let it know to try and fetch the possible functions from the containers. + if (current && m_fetcher) + { + m_fetcher->AddPendingFunction(current); - // TODO: Automatically fetch the function, I need to figure out how to make this debounce correctly so that all requests are processed in line. - if (!m_fetcher->m_requestInProgress.exchange(true)) - { - BinaryNinja::WorkerPriorityEnqueue([this]() { - BinaryNinja::Ref bgTask = new BinaryNinja::BackgroundTask("Fetching WARP Functions...", true); - const auto allowedTags = GetAllowedTagsFromView(m_current->GetView()); - m_fetcher->FetchPendingFunctions(allowedTags); - bgTask->Finish(); - }); - } - } + // TODO: Automatically fetch the function, I need to figure out how to make this debounce correctly so that all + // requests are processed in line. + if (!m_fetcher->m_requestInProgress.exchange(true)) + { + BinaryNinja::WorkerPriorityEnqueue([this]() { + BinaryNinja::Ref bgTask = new BinaryNinja::BackgroundTask("Fetching WARP Functions...", true); + const auto allowedTags = GetAllowedTagsFromView(m_current->GetView()); + m_fetcher->FetchPendingFunctions(allowedTags); + bgTask->Finish(); + }); + } + } - UpdateMatches(); + UpdateMatches(); } void WarpCurrentFunctionWidget::UpdateMatches() { - if (!m_current) - return; - const auto guid = Warp::GetAnalysisFunctionGUID(*m_current); - if (!guid.has_value()) - return; + if (!m_current) + return; + const auto guid = Warp::GetAnalysisFunctionGUID(*m_current); + if (!guid.has_value()) + return; - // Set the matched function for highlighting. - Warp::Ref<Warp::Function> matchedFunction = Warp::Function::GetMatched(*m_current); - m_tableWidget->GetModel()->SetMatchedFunction(matchedFunction); + // Set the matched function for highlighting. + Warp::Ref<Warp::Function> matchedFunction = Warp::Function::GetMatched(*m_current); + m_tableWidget->GetModel()->SetMatchedFunction(matchedFunction); - // We swapped functions, reset the info widget to the default state with new analysis function. - m_infoWidget->SetFunction(matchedFunction); - m_infoWidget->UpdateInfo(); + // We swapped functions, reset the info widget to the default state with new analysis function. + m_infoWidget->SetFunction(matchedFunction); + m_infoWidget->UpdateInfo(); - Warp::Ref<Warp::Target> target = Warp::Target::FromPlatform(*m_current->GetPlatform()); + Warp::Ref<Warp::Target> target = Warp::Target::FromPlatform(*m_current->GetPlatform()); - // Add all the possible matches for the current function to the model. - QVector<WarpFunctionItem *> matches; - bool matchedFuncAdded = false; - // TODO: When we add in the networked container we need to update this stuff on a separate thread and show a spinny thing. - for (const auto &container: Warp::Container::All()) - { - for (const auto &source: container->GetSourcesWithFunctionGUID(*target, guid.value())) - { - for (const auto &function: container->GetFunctionsWithGUID(*target, source, guid.value())) - { - // TODO: This does not work. - if (matchedFunction && BNWARPFunctionsEqual(function->m_object, matchedFunction->m_object)) - matchedFuncAdded = true; - auto item = new WarpFunctionItem(function, m_current); - item->SetContainer(container); - item->SetSource(source); - matches.emplace_back(item); - } - } - } + // Add all the possible matches for the current function to the model. + QVector<WarpFunctionItem*> matches; + bool matchedFuncAdded = false; + // TODO: When we add in the networked container we need to update this stuff on a separate thread and show a spinny + // thing. + for (const auto& container : Warp::Container::All()) + { + for (const auto& source : container->GetSourcesWithFunctionGUID(*target, guid.value())) + { + for (const auto& function : container->GetFunctionsWithGUID(*target, source, guid.value())) + { + // TODO: This does not work. + if (matchedFunction && BNWARPFunctionsEqual(function->m_object, matchedFunction->m_object)) + matchedFuncAdded = true; + auto item = new WarpFunctionItem(function, m_current); + item->SetContainer(container); + item->SetSource(source); + matches.emplace_back(item); + } + } + } - // Add the matched function unconditionally, assuming it has not been found in a container. - // NOTE: This happens when you load from a database for example. - if (matchedFunction && !matchedFuncAdded) - { - auto item = new WarpFunctionItem(matchedFunction, m_current); - matches.emplace_back(item); - } + // Add the matched function unconditionally, assuming it has not been found in a container. + // NOTE: This happens when you load from a database for example. + if (matchedFunction && !matchedFuncAdded) + { + auto item = new WarpFunctionItem(matchedFunction, m_current); + matches.emplace_back(item); + } - m_tableWidget->SetFunctions(matches); + m_tableWidget->SetFunctions(matches); } diff --git a/plugins/warp/ui/matches.h b/plugins/warp/ui/matches.h index 88926add..6a2061ef 100644 --- a/plugins/warp/ui/matches.h +++ b/plugins/warp/ui/matches.h @@ -9,28 +9,28 @@ class WarpCurrentFunctionWidget : public QWidget { - Q_OBJECT - FunctionRef m_current; + Q_OBJECT + FunctionRef m_current; - QSplitter *m_splitter; + QSplitter* m_splitter; - WarpFunctionTableWidget *m_tableWidget; - WarpFunctionInfoWidget *m_infoWidget; + WarpFunctionTableWidget* m_tableWidget; + WarpFunctionInfoWidget* m_infoWidget; - LoggerRef m_logger; + LoggerRef m_logger; - std::shared_ptr<WarpFetcher> m_fetcher; + std::shared_ptr<WarpFetcher> m_fetcher; public: - explicit WarpCurrentFunctionWidget(); + explicit WarpCurrentFunctionWidget(); - ~WarpCurrentFunctionWidget() override = default; + ~WarpCurrentFunctionWidget() override = default; - void SetFetcher(std::shared_ptr<WarpFetcher> fetcher); + void SetFetcher(std::shared_ptr<WarpFetcher> fetcher); - void SetCurrentFunction(FunctionRef current); + void SetCurrentFunction(FunctionRef current); - FunctionRef GetCurrentFunction() { return m_current; }; + FunctionRef GetCurrentFunction() { return m_current; }; - void UpdateMatches(); + void UpdateMatches(); }; diff --git a/plugins/warp/ui/plugin.cpp b/plugins/warp/ui/plugin.cpp index b1cc9a5b..e8088d7c 100644 --- a/plugins/warp/ui/plugin.cpp +++ b/plugins/warp/ui/plugin.cpp @@ -10,7 +10,7 @@ using namespace BinaryNinja; -QIcon GetColoredIcon(const QString &iconPath, const QColor &color) +QIcon GetColoredIcon(const QString& iconPath, const QColor& color) { auto pixmap = QPixmap(iconPath); auto mask = pixmap.createMaskFromColor(QColor(0, 0, 0), Qt::MaskInColor); @@ -24,7 +24,7 @@ Ref<BackgroundTask> GetMatcherTask() // TODO: What happens if we have multiple views open matching? This fails. // Look for the matcher background task to determine if we are stopping or starting it. Ref<BackgroundTask> matcherTask = nullptr; - for (const auto &task: BackgroundTask::GetRunningTasks()) + for (const auto& task : BackgroundTask::GetRunningTasks()) { std::string progressText = task->GetProgressText(); if (progressText.find("Matching on WARP") != std::string::npos) @@ -35,22 +35,29 @@ Ref<BackgroundTask> GetMatcherTask() void ShowNetworkNotice() { - // By default, network access is disabled for WARP, this function will show the user a notice to enable it and restart. + // By default, network access is disabled for WARP, this function will show the user a notice to enable it and + // restart. const auto settings = Settings::Instance(); const bool networkNoticeShown = QSettings().value("warp/NetworkNoticeShown", false).toBool(); QSettings().setValue("warp/NetworkNoticeShown", true); if (!networkNoticeShown && settings->Contains("network.enableWARP") && !settings->Get<bool>("network.enableWARP")) { - const bool enable = ShowMessageBox("Enable WARP Network Access?", - "Network access is disabled by default. Enable WARP network features now?\n\n" - "You can change this later in Settings.", - YesNoButtonSet, InformationIcon) == YesButton; + const bool enable = + ShowMessageBox("Enable WARP Network Access?", + "Network access is disabled by default. Enable WARP network features now?\n\n" + "You can change this later in Settings.", + YesNoButtonSet, InformationIcon) + == YesButton; settings->Set("network.enableWARP", enable); // TODO: Add a notifyRestartRequired call here if (enable) - ShowMessageBox("WARP Network Enabled", "Please restart Binary Ninja to allow WARP to make requests to the server.", OKButtonSet, InformationIcon); + ShowMessageBox("WARP Network Enabled", + "Please restart Binary Ninja to allow WARP to make requests to the server.", OKButtonSet, + InformationIcon); else - ShowMessageBox("WARP Network Disabled", "WARP network access will remain disabled. You can enable it later from Settings.", OKButtonSet, InformationIcon); + ShowMessageBox("WARP Network Disabled", + "WARP network access will remain disabled. You can enable it later from Settings.", OKButtonSet, + InformationIcon); } } @@ -63,24 +70,24 @@ WarpSidebarWidget::WarpSidebarWidget(BinaryViewRef data) : SidebarWidget("WARP") ShowNetworkNotice(); m_headerWidget = new QWidget(); - QHBoxLayout *headerLayout = new QHBoxLayout(); + QHBoxLayout* headerLayout = new QHBoxLayout(); headerLayout->setContentsMargins(0, 0, 0, 0); headerLayout->setSpacing(0); - QToolBar *headerToolbar = new QToolBar(this); + QToolBar* headerToolbar = new QToolBar(this); headerToolbar->setContentsMargins(0, 0, 0, 0); headerToolbar->setIconSize(QSize(20, 20)); auto fetchIcon = GetColoredIcon(":/icons/images/arrow-pull.png", getThemeColor(BlueStandardHighlightColor)); auto fetchAction = headerToolbar->addAction(fetchIcon, "Fetch data from WARP containers", [this]() { - UIActionHandler *handler = m_currentFrame->getCurrentViewInterface()->actionHandler(); + UIActionHandler* handler = m_currentFrame->getCurrentViewInterface()->actionHandler(); handler->executeAction("WARP\\Fetch"); }); fetchAction->setToolTip("Fetch data from WARP containers"); auto commitIcon = GetColoredIcon(":/icons/images/arrow-push.png", getThemeColor(BlueStandardHighlightColor)); auto commitAction = headerToolbar->addAction(commitIcon, "Commit a WARP file to a source", [this]() { - UIActionHandler *handler = m_currentFrame->getCurrentViewInterface()->actionHandler(); + UIActionHandler* handler = m_currentFrame->getCurrentViewInterface()->actionHandler(); handler->executeAction("WARP\\Commit File"); }); commitAction->setToolTip("Commit a WARP file to a source"); @@ -90,14 +97,14 @@ WarpSidebarWidget::WarpSidebarWidget(BinaryViewRef data) : SidebarWidget("WARP") auto loadIcon = GetColoredIcon(":/icons/images/file-add.png", getThemeColor(BlueStandardHighlightColor)); auto loadAction = headerToolbar->addAction(loadIcon, "Load Signature File", [this]() { - UIActionHandler *handler = m_currentFrame->getCurrentViewInterface()->actionHandler(); + UIActionHandler* handler = m_currentFrame->getCurrentViewInterface()->actionHandler(); handler->executeAction("WARP\\Load File"); }); loadAction->setToolTip("Load a signature file to match against"); auto saveIcon = GetColoredIcon(":/icons/images/edit.png", getThemeColor(BlueStandardHighlightColor)); auto saveAction = headerToolbar->addAction(saveIcon, "Create Signature File", [this]() { - UIActionHandler *handler = m_currentFrame->getCurrentViewInterface()->actionHandler(); + UIActionHandler* handler = m_currentFrame->getCurrentViewInterface()->actionHandler(); handler->executeAction("WARP\\Create\\From Current View"); }); saveAction->setToolTip("Save data to a signature file"); @@ -105,10 +112,10 @@ WarpSidebarWidget::WarpSidebarWidget(BinaryViewRef data) : SidebarWidget("WARP") headerToolbar->addSeparator(); static auto matcherStopIcon = GetColoredIcon(":/icons/images/stop.png", getThemeColor(RedStandardHighlightColor)); - static auto matcherStartIcon = GetColoredIcon(":/icons/images/start.png", - getThemeColor(GreenStandardHighlightColor)); + static auto matcherStartIcon = + GetColoredIcon(":/icons/images/start.png", getThemeColor(GreenStandardHighlightColor)); m_matcherAction = headerToolbar->addAction(matcherStartIcon, "Run Matcher", [this]() { - UIActionHandler *handler = m_currentFrame->getCurrentViewInterface()->actionHandler(); + UIActionHandler* handler = m_currentFrame->getCurrentViewInterface()->actionHandler(); if (Ref<BackgroundTask> matcherTask = GetMatcherTask()) matcherTask->Cancel(); else if (!isMatcherRunning) @@ -120,9 +127,7 @@ WarpSidebarWidget::WarpSidebarWidget(BinaryViewRef data) : SidebarWidget("WARP") m_matcherAction->setToolTip("Run the matcher on all functions"); auto refreshIcon = GetColoredIcon(":/icons/images/refresh.png", getThemeColor(BlueStandardHighlightColor)); - auto refreshAction = headerToolbar->addAction(refreshIcon, "Refresh the view data", [this]() { - Update(); - }); + auto refreshAction = headerToolbar->addAction(refreshIcon, "Refresh the view data", [this]() { Update(); }); refreshAction->setToolTip("Refresh the sidebar data"); // Push the toolbar to the right using a stretch space. @@ -130,31 +135,31 @@ WarpSidebarWidget::WarpSidebarWidget(BinaryViewRef data) : SidebarWidget("WARP") headerLayout->addWidget(headerToolbar, 0); m_headerWidget->setLayout(headerLayout); - QFrame *currentFunctionFrame = new QFrame(this); + QFrame* currentFunctionFrame = new QFrame(this); m_currentFunctionWidget = new WarpCurrentFunctionWidget(); - QVBoxLayout *currentFunctionLayout = new QVBoxLayout(); + QVBoxLayout* currentFunctionLayout = new QVBoxLayout(); currentFunctionLayout->setContentsMargins(0, 0, 0, 0); currentFunctionLayout->setSpacing(0); currentFunctionLayout->addWidget(m_currentFunctionWidget); currentFunctionFrame->setLayout(currentFunctionLayout); - QFrame *matchedFrame = new QFrame(this); + QFrame* matchedFrame = new QFrame(this); m_matchedWidget = new WarpMatchedWidget(m_data); - QVBoxLayout *matchedLayout = new QVBoxLayout(); + QVBoxLayout* matchedLayout = new QVBoxLayout(); matchedLayout->setContentsMargins(0, 0, 0, 0); matchedLayout->setSpacing(0); matchedLayout->addWidget(m_matchedWidget); matchedFrame->setLayout(matchedLayout); - QFrame *containerFrame = new QFrame(this); + QFrame* containerFrame = new QFrame(this); m_containerWidget = new WarpContainersPane(); - QVBoxLayout *containerLayout = new QVBoxLayout(); + QVBoxLayout* containerLayout = new QVBoxLayout(); containerLayout->setContentsMargins(0, 0, 0, 0); containerLayout->setSpacing(0); containerLayout->addWidget(m_containerWidget); containerFrame->setLayout(containerLayout); - QVBoxLayout *layout = new QVBoxLayout(this); + QVBoxLayout* layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0); @@ -167,11 +172,7 @@ WarpSidebarWidget::WarpSidebarWidget(BinaryViewRef data) : SidebarWidget("WARP") this->setLayout(layout); // Do a full update if analysis has been done, otherwise we may persist old data and not have new data. - m_analysisEvent = new AnalysisCompletionEvent(m_data, [this]() { - ExecuteOnMainThread([this]() { - Update(); - }); - }); + m_analysisEvent = new AnalysisCompletionEvent(m_data, [this]() { ExecuteOnMainThread([this]() { Update(); }); }); const std::shared_ptr<WarpFetcher> fetcher = WarpFetcher::Global(); fetcher->AddCompletionCallback([this]() { @@ -188,9 +189,7 @@ WarpSidebarWidget::~WarpSidebarWidget() m_analysisEvent->Cancel(); } -void WarpSidebarWidget::focus() -{ -} +void WarpSidebarWidget::focus() {} void WarpSidebarWidget::Update() { @@ -203,15 +202,16 @@ void WarpSidebarWidget::Update() void WarpSidebarWidget::setMatcherActionIcon(bool running) { static auto matcherStopIcon = GetColoredIcon(":/icons/images/stop.png", getThemeColor(RedStandardHighlightColor)); - static auto matcherStartIcon = GetColoredIcon(":/icons/images/start.png", - getThemeColor(GreenStandardHighlightColor)); + static auto matcherStartIcon = + GetColoredIcon(":/icons/images/start.png", getThemeColor(GreenStandardHighlightColor)); isMatcherRunning = running; if (running) { m_matcherAction->setIcon(matcherStopIcon); m_matcherAction->setToolTip("Stop the matcher"); m_matcherAction->setIconText("Stop Matcher"); - } else + } + else { m_matcherAction->setIcon(matcherStartIcon); m_matcherAction->setToolTip("Run the matcher on all functions"); @@ -219,7 +219,7 @@ void WarpSidebarWidget::setMatcherActionIcon(bool running) } } -void WarpSidebarWidget::notifyViewChanged(ViewFrame *view) +void WarpSidebarWidget::notifyViewChanged(ViewFrame* view) { if (!view) return; @@ -230,7 +230,7 @@ void WarpSidebarWidget::notifyViewChanged(ViewFrame *view) // TODO: We need to set some stuff here prolly. } -void WarpSidebarWidget::notifyViewLocationChanged(View *view, const ViewLocation &location) +void WarpSidebarWidget::notifyViewLocationChanged(View* view, const ViewLocation& location) { // Warp sidebar really should only update if it is visible, otherwise its a waste of cycles. if (!this->isVisible()) @@ -241,25 +241,23 @@ void WarpSidebarWidget::notifyViewLocationChanged(View *view, const ViewLocation m_currentFunctionWidget->SetCurrentFunction(function); } -WarpSidebarWidgetType::WarpSidebarWidgetType() : SidebarWidgetType(QImage(":/icons/images/warp.png"), "WARP") -{ - -} - +WarpSidebarWidgetType::WarpSidebarWidgetType() : SidebarWidgetType(QImage(":/icons/images/warp.png"), "WARP") {} -extern "C" { -BN_DECLARE_UI_ABI_VERSION -BINARYNINJAPLUGIN void CorePluginDependencies() +extern "C" { - // We must have WARP to enable this plugin! - AddRequiredPluginDependency("warp_ninja"); -} + BN_DECLARE_UI_ABI_VERSION -BINARYNINJAPLUGIN bool UIPluginInit() -{ - RegisterWarpFetchFunctionsCommand(); - Sidebar::addSidebarWidgetType(new WarpSidebarWidgetType()); - return true; -} + BINARYNINJAPLUGIN void CorePluginDependencies() + { + // We must have WARP to enable this plugin! + AddRequiredPluginDependency("warp_ninja"); + } + + BINARYNINJAPLUGIN bool UIPluginInit() + { + RegisterWarpFetchFunctionsCommand(); + Sidebar::addSidebarWidgetType(new WarpSidebarWidgetType()); + return true; + } } diff --git a/plugins/warp/ui/plugin.h b/plugins/warp/ui/plugin.h index 56b31711..7f406e19 100644 --- a/plugins/warp/ui/plugin.h +++ b/plugins/warp/ui/plugin.h @@ -8,48 +8,48 @@ class WarpSidebarWidget : public SidebarWidget { - Q_OBJECT - BinaryNinja::Ref<BinaryNinja::Logger> m_logger; - BinaryViewRef m_data; - ViewFrame *m_currentFrame; - QWidget *m_headerWidget; + Q_OBJECT + BinaryNinja::Ref<BinaryNinja::Logger> m_logger; + BinaryViewRef m_data; + ViewFrame* m_currentFrame; + QWidget* m_headerWidget; - BinaryNinja::Ref<BinaryNinja::AnalysisCompletionEvent> m_analysisEvent; - QAction *m_matcherAction; - bool isMatcherRunning = false; + BinaryNinja::Ref<BinaryNinja::AnalysisCompletionEvent> m_analysisEvent; + QAction* m_matcherAction; + bool isMatcherRunning = false; - WarpCurrentFunctionWidget *m_currentFunctionWidget; - WarpMatchedWidget *m_matchedWidget; - WarpContainersPane *m_containerWidget; + WarpCurrentFunctionWidget* m_currentFunctionWidget; + WarpMatchedWidget* m_matchedWidget; + WarpContainersPane* m_containerWidget; public: - explicit WarpSidebarWidget(BinaryViewRef data); + explicit WarpSidebarWidget(BinaryViewRef data); - ~WarpSidebarWidget() override; + ~WarpSidebarWidget() override; - QWidget *headerWidget() override { return m_headerWidget; } + QWidget* headerWidget() override { return m_headerWidget; } - void focus() override; + void focus() override; - void Update(); + void Update(); - void setMatcherActionIcon(bool running); + void setMatcherActionIcon(bool running); - void notifyViewChanged(ViewFrame *) override; + void notifyViewChanged(ViewFrame*) override; - void notifyViewLocationChanged(View *, const ViewLocation &) override; + void notifyViewLocationChanged(View*, const ViewLocation&) override; }; class WarpSidebarWidgetType : public SidebarWidgetType { public: - WarpSidebarWidgetType(); + WarpSidebarWidgetType(); - SidebarWidgetLocation defaultLocation() const override { return SidebarWidgetLocation::RightContent; } - SidebarContextSensitivity contextSensitivity() const override { return PerViewTypeSidebarContext; } + SidebarWidgetLocation defaultLocation() const override { return SidebarWidgetLocation::RightContent; } + SidebarContextSensitivity contextSensitivity() const override { return PerViewTypeSidebarContext; } - WarpSidebarWidget *createWidget(ViewFrame *viewFrame, BinaryViewRef data) override - { - return new WarpSidebarWidget(data); - } + WarpSidebarWidget* createWidget(ViewFrame* viewFrame, BinaryViewRef data) override + { + return new WarpSidebarWidget(data); + } }; diff --git a/plugins/warp/ui/shared/constraint.cpp b/plugins/warp/ui/shared/constraint.cpp index c871fc68..39c9c7d3 100644 --- a/plugins/warp/ui/shared/constraint.cpp +++ b/plugins/warp/ui/shared/constraint.cpp @@ -5,127 +5,127 @@ #include "theme.h" -WarpConstraintItem::WarpConstraintItem(const Warp::Constraint &constraint) : m_constraint(constraint) +WarpConstraintItem::WarpConstraintItem(const Warp::Constraint& constraint) : m_constraint(constraint) { - QString guidStr = QString::fromStdString(constraint.guid.ToString()); - if (const auto offset = constraint.offset; offset) - guidStr += QString(" @ %1").arg(*offset, 0, 16); - setText(guidStr); + QString guidStr = QString::fromStdString(constraint.guid.ToString()); + if (const auto offset = constraint.offset; offset) + guidStr += QString(" @ %1").arg(*offset, 0, 16); + setText(guidStr); } -WarpConstraintItemModel::WarpConstraintItemModel(const QStringList &labels, QObject *parent) +WarpConstraintItemModel::WarpConstraintItemModel(const QStringList& labels, QObject* parent) { - this->setHorizontalHeaderLabels(labels); + this->setHorizontalHeaderLabels(labels); } -void WarpConstraintItemModel::AddItem(WarpConstraintItem *item) +void WarpConstraintItemModel::AddItem(WarpConstraintItem* item) { - QList<QStandardItem *> row = {}; - row.insert(COL_CONSTRAINT_ITEM, item); - appendRow(row); + QList<QStandardItem*> row = {}; + row.insert(COL_CONSTRAINT_ITEM, item); + appendRow(row); } -WarpConstraintItem *WarpConstraintItemModel::GetItem(const QModelIndex &index) const +WarpConstraintItem* WarpConstraintItemModel::GetItem(const QModelIndex& index) const { - if (!index.isValid()) - return nullptr; - return dynamic_cast<WarpConstraintItem *>(item(index.row(), COL_CONSTRAINT_ITEM)); + if (!index.isValid()) + return nullptr; + return dynamic_cast<WarpConstraintItem*>(item(index.row(), COL_CONSTRAINT_ITEM)); } -QVariant WarpConstraintItemModel::data(const QModelIndex &index, int role) const +QVariant WarpConstraintItemModel::data(const QModelIndex& index, int role) const { - // Highlight constraints that are found in analysis. - if (role == Qt::BackgroundRole) - { - if (const auto item = GetItem(index); item) - { - auto itemConstraint = item->GetConstraint(); - // TODO: We really should store the guid in a hashmap or something instead of looping over it for every item. - for (const auto &constraint: m_matchedConstraints) - { - if (constraint.guid == itemConstraint.guid) - { - static QColor matchedColor = getThemeColor(GreenStandardHighlightColor); - matchedColor.setAlpha(100); - if (constraint.offset == itemConstraint.offset) - matchedColor.setAlpha(128); - return matchedColor; - } - } - } - } + // Highlight constraints that are found in analysis. + if (role == Qt::BackgroundRole) + { + if (const auto item = GetItem(index); item) + { + auto itemConstraint = item->GetConstraint(); + // TODO: We really should store the guid in a hashmap or something instead of looping over it for every + // item. + for (const auto& constraint : m_matchedConstraints) + { + if (constraint.guid == itemConstraint.guid) + { + static QColor matchedColor = getThemeColor(GreenStandardHighlightColor); + matchedColor.setAlpha(100); + if (constraint.offset == itemConstraint.offset) + matchedColor.setAlpha(128); + return matchedColor; + } + } + } + } - return QStandardItemModel::data(index, role); + return QStandardItemModel::data(index, role); } -WarpConstraintTableWidget::WarpConstraintTableWidget(QWidget *parent) +WarpConstraintTableWidget::WarpConstraintTableWidget(QWidget* parent) { - QGridLayout *layout = new QGridLayout(this); - layout->setContentsMargins(2, 2, 2, 2); - layout->setVerticalSpacing(4); + QGridLayout* layout = new QGridLayout(this); + layout->setContentsMargins(2, 2, 2, 2); + layout->setVerticalSpacing(4); - m_table = new QTableView(this); - m_model = new WarpConstraintItemModel({"Constraint"}, this); - m_proxyModel = new GenericTextFilterModel(this); - m_proxyModel->setSourceModel(m_model); - m_table->setModel(m_proxyModel); + m_table = new QTableView(this); + m_model = new WarpConstraintItemModel({"Constraint"}, this); + m_proxyModel = new GenericTextFilterModel(this); + m_proxyModel->setSourceModel(m_model); + m_table->setModel(m_proxyModel); - m_filterEdit = new FilterEdit(this); - m_filterView = new FilteredView(this, m_table, this, m_filterEdit); - m_filterView->setFilterPlaceholderText("Search constraints"); + m_filterEdit = new FilterEdit(this); + m_filterView = new FilteredView(this, m_table, this, m_filterEdit); + m_filterView->setFilterPlaceholderText("Search constraints"); - layout->addWidget(m_filterEdit, 0, 0, 1, 5); - layout->addWidget(m_table, 1, 0, 1, 5); + layout->addWidget(m_filterEdit, 0, 0, 1, 5); + layout->addWidget(m_table, 1, 0, 1, 5); - // Make the table look nice. - m_table->horizontalHeader()->setStretchLastSection(true); - m_table->verticalHeader()->hide(); - m_table->setSelectionBehavior(QAbstractItemView::SelectRows); - m_table->setSelectionMode(QAbstractItemView::SingleSelection); - m_table->setEditTriggers(QAbstractItemView::NoEditTriggers); - m_table->setFocusPolicy(Qt::NoFocus); - m_table->setShowGrid(false); - m_table->setAlternatingRowColors(false); - m_table->setSortingEnabled(true); - // NOTE: We only have a single column right now, so disable the header. - m_table->horizontalHeader()->hide(); - // Decrease row height to make it look nice. - m_table->verticalHeader()->setDefaultSectionSize(30); + // Make the table look nice. + m_table->horizontalHeader()->setStretchLastSection(true); + m_table->verticalHeader()->hide(); + m_table->setSelectionBehavior(QAbstractItemView::SelectRows); + m_table->setSelectionMode(QAbstractItemView::SingleSelection); + m_table->setEditTriggers(QAbstractItemView::NoEditTriggers); + m_table->setFocusPolicy(Qt::NoFocus); + m_table->setShowGrid(false); + m_table->setAlternatingRowColors(false); + m_table->setSortingEnabled(true); + // NOTE: We only have a single column right now, so disable the header. + m_table->horizontalHeader()->hide(); + // Decrease row height to make it look nice. + m_table->verticalHeader()->setDefaultSectionSize(30); - // Make the highlight less bright. - QPalette palette = m_table->palette(); - palette.setColor(QPalette::Highlight, getThemeColor(SelectionColor)); - m_table->setPalette(palette); + // Make the highlight less bright. + QPalette palette = m_table->palette(); + palette.setColor(QPalette::Highlight, getThemeColor(SelectionColor)); + m_table->setPalette(palette); } -void WarpConstraintTableWidget::SetConstraints(QVector<WarpConstraintItem *> constraints) +void WarpConstraintTableWidget::SetConstraints(QVector<WarpConstraintItem*> constraints) { - // Clear matches as they are no longer valid. - m_model->clear(); - m_model->setRowCount(0); + // Clear matches as they are no longer valid. + m_model->clear(); + m_model->setRowCount(0); - // Temporarily disable sorting so we can add rows faster - m_table->setModel(m_model); - m_table->setSortingEnabled(false); - m_table->setEnabled(false); + // Temporarily disable sorting so we can add rows faster + m_table->setModel(m_model); + m_table->setSortingEnabled(false); + m_table->setEnabled(false); - for (const auto &constraint: constraints) - m_model->AddItem(constraint); + for (const auto& constraint : constraints) + m_model->AddItem(constraint); - // We are done, re-enable table. - m_table->setEnabled(true); - m_table->setModel(m_proxyModel); - m_table->setSortingEnabled(true); + // We are done, re-enable table. + m_table->setEnabled(true); + m_table->setModel(m_proxyModel); + m_table->setSortingEnabled(true); } -void WarpConstraintTableWidget::SetMatchedConstraints( - const std::vector<Warp::Constraint> &analysisConstraints) +void WarpConstraintTableWidget::SetMatchedConstraints(const std::vector<Warp::Constraint>& analysisConstraints) { - m_model->SetMatchedConstraints(analysisConstraints); + m_model->SetMatchedConstraints(analysisConstraints); } -void WarpConstraintTableWidget::setFilter(const std::string &filter) +void WarpConstraintTableWidget::setFilter(const std::string& filter) { - m_proxyModel->setFilterFixedString(QString::fromStdString(filter)); - m_filterView->showFilter(QString::fromStdString(filter)); + m_proxyModel->setFilterFixedString(QString::fromStdString(filter)); + m_filterView->showFilter(QString::fromStdString(filter)); } diff --git a/plugins/warp/ui/shared/constraint.h b/plugins/warp/ui/shared/constraint.h index 2f064836..edee36e5 100644 --- a/plugins/warp/ui/shared/constraint.h +++ b/plugins/warp/ui/shared/constraint.h @@ -9,70 +9,62 @@ class WarpConstraintItem : public QStandardItem { - Warp::Constraint m_constraint; + Warp::Constraint m_constraint; public: - WarpConstraintItem(const Warp::Constraint &constraint); + WarpConstraintItem(const Warp::Constraint& constraint); - Warp::Constraint GetConstraint() { return m_constraint; } + Warp::Constraint GetConstraint() { return m_constraint; } }; class WarpConstraintItemModel : public QStandardItemModel { - Q_OBJECT + Q_OBJECT - // The current analysis constraints used to highlight matching constraints. - std::vector<Warp::Constraint> m_matchedConstraints; + // The current analysis constraints used to highlight matching constraints. + std::vector<Warp::Constraint> m_matchedConstraints; public: - WarpConstraintItemModel(const QStringList &labels, QObject *parent); + WarpConstraintItemModel(const QStringList& labels, QObject* parent); - static constexpr int COL_CONSTRAINT_ITEM = 0; + static constexpr int COL_CONSTRAINT_ITEM = 0; - void AddItem(WarpConstraintItem *item); + void AddItem(WarpConstraintItem* item); - WarpConstraintItem *GetItem(const QModelIndex &index) const; + WarpConstraintItem* GetItem(const QModelIndex& index) const; - QVariant data(const QModelIndex &index, int role) const override; + QVariant data(const QModelIndex& index, int role) const override; - void SetMatchedConstraints(const std::vector<Warp::Constraint> &analysisConstraints) - { - m_matchedConstraints = analysisConstraints; - } + void SetMatchedConstraints(const std::vector<Warp::Constraint>& analysisConstraints) + { + m_matchedConstraints = analysisConstraints; + } }; class WarpConstraintTableWidget : public QWidget, public FilterTarget { - Q_OBJECT + Q_OBJECT - QTableView *m_table; - WarpConstraintItemModel *m_model; - GenericTextFilterModel *m_proxyModel; - FilterEdit *m_filterEdit; - FilteredView *m_filterView; + QTableView* m_table; + WarpConstraintItemModel* m_model; + GenericTextFilterModel* m_proxyModel; + FilterEdit* m_filterEdit; + FilteredView* m_filterView; public: - explicit WarpConstraintTableWidget(QWidget *parent = nullptr); + explicit WarpConstraintTableWidget(QWidget* parent = nullptr); - void SetConstraints(QVector<WarpConstraintItem *> constraints); + void SetConstraints(QVector<WarpConstraintItem*> constraints); - void SetMatchedConstraints(const std::vector<Warp::Constraint> &analysisConstraints); + void SetMatchedConstraints(const std::vector<Warp::Constraint>& analysisConstraints); - void setFilter(const std::string &) override; + void setFilter(const std::string&) override; - void scrollToFirstItem() override - { - } + void scrollToFirstItem() override {} - void scrollToCurrentItem() override - { - } + void scrollToCurrentItem() override {} - void selectFirstItem() override - { - } + void selectFirstItem() override {} - void activateFirstItem() override - { - } + void activateFirstItem() override {} }; diff --git a/plugins/warp/ui/shared/fetchdialog.cpp b/plugins/warp/ui/shared/fetchdialog.cpp index 1d18aa92..328f00c2 100644 --- a/plugins/warp/ui/shared/fetchdialog.cpp +++ b/plugins/warp/ui/shared/fetchdialog.cpp @@ -11,236 +11,231 @@ using namespace BinaryNinja; -static void AddListItem(QListWidget *list, const QString &value) +static void AddListItem(QListWidget* list, const QString& value) { - if (value.trimmed().isEmpty()) - return; - // Avoid duplicates - for (int i = 0; i < list->count(); ++i) - if (list->item(i)->text().compare(value, Qt::CaseInsensitive) == 0) - return; - list->addItem(value.trimmed()); + if (value.trimmed().isEmpty()) + return; + // Avoid duplicates + for (int i = 0; i < list->count(); ++i) + if (list->item(i)->text().compare(value, Qt::CaseInsensitive) == 0) + return; + list->addItem(value.trimmed()); } -WarpFetchDialog::WarpFetchDialog(BinaryViewRef bv, - std::shared_ptr<WarpFetcher> fetcher, - QWidget *parent) - : QDialog(parent), m_fetchProcessor(std::move(fetcher)), m_bv(std::move(bv)) +WarpFetchDialog::WarpFetchDialog(BinaryViewRef bv, std::shared_ptr<WarpFetcher> fetcher, QWidget* parent) : + QDialog(parent), m_fetchProcessor(std::move(fetcher)), m_bv(std::move(bv)) { - setWindowTitle("Fetch WARP Functions"); + setWindowTitle("Fetch WARP Functions"); - auto form = new QFormLayout(); - m_containerCombo = new QComboBox(this); - populateContainers(); - m_containerCombo->addItem("All Containers"); // index 0 for "all" - for (const auto &c: m_containers) - m_containerCombo->addItem(QString::fromStdString(c->GetName())); + auto form = new QFormLayout(); + m_containerCombo = new QComboBox(this); + populateContainers(); + m_containerCombo->addItem("All Containers"); // index 0 for "all" + for (const auto& c : m_containers) + m_containerCombo->addItem(QString::fromStdString(c->GetName())); - // Tags editor - m_tagsList = new QListWidget(this); - m_addTagBtn = new QPushButton(this); - m_addTagBtn->setText("+"); - m_addTagBtn->setToolTip("Add tag"); - m_removeTagBtn = new QPushButton(this); - m_removeTagBtn->setText("-"); - m_removeTagBtn->setToolTip("Remove selected tag(s)"); - m_resetTagBtn = new QPushButton(this); - m_resetTagBtn->setText("Reset"); - m_resetTagBtn->setToolTip("Reset tags to: official, trusted"); - auto tagBtnRow = new QHBoxLayout(); - tagBtnRow->addWidget(m_addTagBtn); - tagBtnRow->addWidget(m_removeTagBtn); - tagBtnRow->addWidget(m_resetTagBtn); - tagBtnRow->addStretch(); - auto tagCol = new QVBoxLayout(); - tagCol->addWidget(m_tagsList); - tagCol->addLayout(tagBtnRow); - auto tagWrapper = new QWidget(this); - tagWrapper->setLayout(tagCol); + // Tags editor + m_tagsList = new QListWidget(this); + m_addTagBtn = new QPushButton(this); + m_addTagBtn->setText("+"); + m_addTagBtn->setToolTip("Add tag"); + m_removeTagBtn = new QPushButton(this); + m_removeTagBtn->setText("-"); + m_removeTagBtn->setToolTip("Remove selected tag(s)"); + m_resetTagBtn = new QPushButton(this); + m_resetTagBtn->setText("Reset"); + m_resetTagBtn->setToolTip("Reset tags to: official, trusted"); + auto tagBtnRow = new QHBoxLayout(); + tagBtnRow->addWidget(m_addTagBtn); + tagBtnRow->addWidget(m_removeTagBtn); + tagBtnRow->addWidget(m_resetTagBtn); + tagBtnRow->addStretch(); + auto tagCol = new QVBoxLayout(); + tagCol->addWidget(m_tagsList); + tagCol->addLayout(tagBtnRow); + auto tagWrapper = new QWidget(this); + tagWrapper->setLayout(tagCol); - // Make tags list compact with a fixed maximum height and no vertical expansion - m_tagsList->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents); - m_tagsList->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); - m_tagsList->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - m_tagsList->setMaximumHeight(120); - m_tagsList->setToolTip("A source must have atleast ONE of these tags to be considered"); + // Make tags list compact with a fixed maximum height and no vertical expansion + m_tagsList->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents); + m_tagsList->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + m_tagsList->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + m_tagsList->setMaximumHeight(120); + m_tagsList->setToolTip("A source must have atleast ONE of these tags to be considered"); - // Defaults from processor tags - for (const auto &t: GetAllowedTagsFromView(m_bv)) - AddListItem(m_tagsList, QString::fromStdString(t)); + // Defaults from processor tags + for (const auto& t : GetAllowedTagsFromView(m_bv)) + AddListItem(m_tagsList, QString::fromStdString(t)); - // Batch size and matcher checkbox - m_batchSize = new QSpinBox(this); - m_batchSize->setRange(10, 1000); - m_batchSize->setValue(GetBatchSizeFromView(m_bv)); - m_batchSize->setToolTip("Number of functions to fetch in each batch"); + // Batch size and matcher checkbox + m_batchSize = new QSpinBox(this); + m_batchSize->setRange(10, 1000); + m_batchSize->setValue(GetBatchSizeFromView(m_bv)); + m_batchSize->setToolTip("Number of functions to fetch in each batch"); - m_rerunMatcher = new QCheckBox("Re-run matcher after fetch", this); - m_rerunMatcher->setChecked(true); + m_rerunMatcher = new QCheckBox("Re-run matcher after fetch", this); + m_rerunMatcher->setChecked(true); - m_clearProcessed = new QCheckBox("Refetch all functions", this); - m_clearProcessed->setToolTip("Clears the processed cache before fetching again, this will refetch all functions in the view"); - m_clearProcessed->setChecked(false); + m_clearProcessed = new QCheckBox("Refetch all functions", this); + m_clearProcessed->setToolTip( + "Clears the processed cache before fetching again, this will refetch all functions in the view"); + m_clearProcessed->setChecked(false); - form->addRow(new QLabel("Container: "), m_containerCombo); - form->addRow(new QLabel("Allowed Tags: "), tagWrapper); - form->addRow(new QLabel("Batch Size: "), m_batchSize); - form->addRow(m_rerunMatcher); - form->addRow(m_clearProcessed); + form->addRow(new QLabel("Container: "), m_containerCombo); + form->addRow(new QLabel("Allowed Tags: "), tagWrapper); + form->addRow(new QLabel("Batch Size: "), m_batchSize); + form->addRow(m_rerunMatcher); + form->addRow(m_clearProcessed); - auto buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); - connect(buttons, &QDialogButtonBox::accepted, this, &WarpFetchDialog::onAccept); - connect(buttons, &QDialogButtonBox::rejected, this, &WarpFetchDialog::onReject); + auto buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); + connect(buttons, &QDialogButtonBox::accepted, this, &WarpFetchDialog::onAccept); + connect(buttons, &QDialogButtonBox::rejected, this, &WarpFetchDialog::onReject); - auto root = new QVBoxLayout(this); - root->addLayout(form); - root->addWidget(buttons); - setLayout(root); + auto root = new QVBoxLayout(this); + root->addLayout(form); + root->addWidget(buttons); + setLayout(root); - // Wire buttons - connect(m_addTagBtn, &QPushButton::clicked, this, &WarpFetchDialog::onAddTag); - connect(m_removeTagBtn, &QPushButton::clicked, this, &WarpFetchDialog::onRemoveTag); - connect(m_resetTagBtn, &QPushButton::clicked, this, &WarpFetchDialog::onResetTags); + // Wire buttons + connect(m_addTagBtn, &QPushButton::clicked, this, &WarpFetchDialog::onAddTag); + connect(m_removeTagBtn, &QPushButton::clicked, this, &WarpFetchDialog::onRemoveTag); + connect(m_resetTagBtn, &QPushButton::clicked, this, &WarpFetchDialog::onResetTags); } void WarpFetchDialog::populateContainers() { - m_containers = Warp::Container::All(); + m_containers = Warp::Container::All(); } void WarpFetchDialog::onAddTag() { - bool ok = false; - const auto text = QInputDialog::getText(this, "Add Tag", "Tag:", QLineEdit::Normal, {}, &ok); - if (ok) - AddListItem(m_tagsList, text); + bool ok = false; + const auto text = QInputDialog::getText(this, "Add Tag", "Tag:", QLineEdit::Normal, {}, &ok); + if (ok) + AddListItem(m_tagsList, text); } void WarpFetchDialog::onRemoveTag() { - for (auto *item: m_tagsList->selectedItems()) - delete item; + for (auto* item : m_tagsList->selectedItems()) + delete item; } void WarpFetchDialog::onResetTags() { - m_tagsList->clear(); - AddListItem(m_tagsList, "official"); - AddListItem(m_tagsList, "trusted"); + m_tagsList->clear(); + AddListItem(m_tagsList, "official"); + AddListItem(m_tagsList, "trusted"); } std::vector<Warp::SourceTag> WarpFetchDialog::collectTags() const { - std::vector<Warp::SourceTag> out; - out.reserve(m_tagsList->count()); - for (int i = 0; i < m_tagsList->count(); ++i) - out.emplace_back(m_tagsList->item(i)->text().trimmed().toStdString()); - return out; + std::vector<Warp::SourceTag> out; + out.reserve(m_tagsList->count()); + for (int i = 0; i < m_tagsList->count(); ++i) + out.emplace_back(m_tagsList->item(i)->text().trimmed().toStdString()); + return out; } void WarpFetchDialog::onAccept() { - const int idx = m_containerCombo->currentIndex(); - std::optional<size_t> containerIndex; - if (idx > 0) // 0 == All Containers - containerIndex = static_cast<size_t>(idx - 1); + const int idx = m_containerCombo->currentIndex(); + std::optional<size_t> containerIndex; + if (idx > 0) // 0 == All Containers + containerIndex = static_cast<size_t>(idx - 1); - const auto batch = static_cast<size_t>(m_batchSize->value()); - const bool rerun = m_rerunMatcher->isChecked(); + const auto batch = static_cast<size_t>(m_batchSize->value()); + const bool rerun = m_rerunMatcher->isChecked(); - const auto tags = collectTags(); - // Persist tags to the view settings. - SetTagsToView(m_bv, tags); + const auto tags = collectTags(); + // Persist tags to the view settings. + SetTagsToView(m_bv, tags); - if (m_clearProcessed->isChecked()) - m_fetchProcessor->ClearProcessed(); + if (m_clearProcessed->isChecked()) + m_fetchProcessor->ClearProcessed(); - // Execute the network fetch in batches - runBatchedFetch(containerIndex, tags, batch, rerun); + // Execute the network fetch in batches + runBatchedFetch(containerIndex, tags, batch, rerun); - accept(); + accept(); } void WarpFetchDialog::onReject() { - const auto tags = collectTags(); - // Persist tags to the view settings. - SetTagsToView(m_bv, tags); - reject(); + const auto tags = collectTags(); + // Persist tags to the view settings. + SetTagsToView(m_bv, tags); + reject(); } -void WarpFetchDialog::runBatchedFetch(const std::optional<size_t> &containerIndex, - const std::vector<Warp::SourceTag> &allowedTags, - size_t batchSize, - bool rerunMatcher) +void WarpFetchDialog::runBatchedFetch(const std::optional<size_t>& containerIndex, + const std::vector<Warp::SourceTag>& allowedTags, size_t batchSize, bool rerunMatcher) { - if (!m_bv) - return; - // Collect functions in the view and enqueue them to the shared fetcher - std::vector<Ref<Function>> funcs = m_bv->GetAnalysisFunctionList(); - if (funcs.empty()) - return; - const size_t totalFuncs = funcs.size(); - const size_t totalBatches = (totalFuncs + batchSize - 1) / batchSize; + if (!m_bv) + return; + // Collect functions in the view and enqueue them to the shared fetcher + std::vector<Ref<Function>> funcs = m_bv->GetAnalysisFunctionList(); + if (funcs.empty()) + return; + const size_t totalFuncs = funcs.size(); + const size_t totalBatches = (totalFuncs + batchSize - 1) / batchSize; - // Create a background task to show progress in the UI - Ref<BackgroundTask> task = new BackgroundTask("Fetching WARP functions (0 / " + std::to_string(totalBatches) + ")", false); + // Create a background task to show progress in the UI + Ref<BackgroundTask> task = + new BackgroundTask("Fetching WARP functions (0 / " + std::to_string(totalBatches) + ")", false); - auto fetcher = m_fetchProcessor; - auto bv = m_bv; + auto fetcher = m_fetchProcessor; + auto bv = m_bv; - // TODO: Too many captures in this thing lol. - WorkerInteractiveEnqueue([fetcher, bv, funcs = std::move(funcs), batchSize, rerunMatcher, task, allowedTags]() mutable { - size_t processed = 0; - size_t batchIndex = 0; + // TODO: Too many captures in this thing lol. + WorkerInteractiveEnqueue( + [fetcher, bv, funcs = std::move(funcs), batchSize, rerunMatcher, task, allowedTags]() mutable { + size_t processed = 0; + size_t batchIndex = 0; - while (processed < funcs.size()) - { - const size_t remaining = funcs.size() - processed; - const size_t thisBatchCount = std::min(batchSize, remaining); + while (processed < funcs.size()) + { + const size_t remaining = funcs.size() - processed; + const size_t thisBatchCount = std::min(batchSize, remaining); - for (size_t i = 0; i < thisBatchCount; ++i) - fetcher->AddPendingFunction(funcs[processed + i]); + for (size_t i = 0; i < thisBatchCount; ++i) + fetcher->AddPendingFunction(funcs[processed + i]); - fetcher->FetchPendingFunctions(allowedTags); + fetcher->FetchPendingFunctions(allowedTags); - ++batchIndex; - processed += thisBatchCount; + ++batchIndex; + processed += thisBatchCount; - task->SetProgressText("Fetching WARP functions (" + std::to_string(batchIndex) + " / " + std::to_string((funcs.size() + batchSize - 1) / batchSize) + ")"); - } + task->SetProgressText("Fetching WARP functions (" + std::to_string(batchIndex) + " / " + + std::to_string((funcs.size() + batchSize - 1) / batchSize) + ")"); + } - task->Finish(); - // TODO: Print how long it took? - Logger("WARP Fetcher").LogInfo("Finished fetching WARP functions..."); + task->Finish(); + // TODO: Print how long it took? + Logger("WARP Fetcher").LogInfo("Finished fetching WARP functions..."); - if (rerunMatcher && bv) - Warp::RunMatcher(*bv); - }); + if (rerunMatcher && bv) + Warp::RunMatcher(*bv); + }); } void RegisterWarpFetchFunctionsCommand() { - // Register a UI action and bind it globally. Add it to the Tools menu. - const QString actionName = "WARP\\Fetch"; - if (!UIAction::isActionRegistered(actionName)) - UIAction::registerAction(actionName); + // Register a UI action and bind it globally. Add it to the Tools menu. + const QString actionName = "WARP\\Fetch"; + if (!UIAction::isActionRegistered(actionName)) + UIAction::registerAction(actionName); - UIActionHandler::globalActions()->bindAction( - actionName, - UIAction( - [](const UIActionContext &context) { - if (const BinaryViewRef bv = context.binaryView; bv) - { - WarpFetchDialog dlg(bv, WarpFetcher::Global(), nullptr); - dlg.exec(); - } - }, - [](const UIActionContext &context) { - return context.binaryView != nullptr; - } - ) - ); + UIActionHandler::globalActions()->bindAction(actionName, + UIAction( + [](const UIActionContext& context) { + if (const BinaryViewRef bv = context.binaryView; bv) + { + WarpFetchDialog dlg(bv, WarpFetcher::Global(), nullptr); + dlg.exec(); + } + }, + [](const UIActionContext& context) { return context.binaryView != nullptr; })); - Menu::mainMenu("Plugins")->addAction(actionName, "Plugins"); + Menu::mainMenu("Plugins")->addAction(actionName, "Plugins"); } diff --git a/plugins/warp/ui/shared/fetchdialog.h b/plugins/warp/ui/shared/fetchdialog.h index a99eef19..c642d31e 100644 --- a/plugins/warp/ui/shared/fetchdialog.h +++ b/plugins/warp/ui/shared/fetchdialog.h @@ -13,49 +13,45 @@ class WarpFetchDialog : public QDialog { - Q_OBJECT + Q_OBJECT - QComboBox *m_containerCombo; + QComboBox* m_containerCombo; - QListWidget *m_tagsList; - QPushButton *m_addTagBtn; - QPushButton *m_removeTagBtn; - QPushButton *m_resetTagBtn; + QListWidget* m_tagsList; + QPushButton* m_addTagBtn; + QPushButton* m_removeTagBtn; + QPushButton* m_resetTagBtn; - QSpinBox *m_batchSize; - QCheckBox *m_rerunMatcher; - QCheckBox *m_clearProcessed; + QSpinBox* m_batchSize; + QCheckBox* m_rerunMatcher; + QCheckBox* m_clearProcessed; - std::vector<Warp::Ref<Warp::Container> > m_containers; + std::vector<Warp::Ref<Warp::Container>> m_containers; - std::shared_ptr<WarpFetcher> m_fetchProcessor; - BinaryViewRef m_bv; + std::shared_ptr<WarpFetcher> m_fetchProcessor; + BinaryViewRef m_bv; public: - explicit WarpFetchDialog(BinaryViewRef bv, - std::shared_ptr<WarpFetcher> fetchProcessor, - QWidget *parent = nullptr); + explicit WarpFetchDialog(BinaryViewRef bv, std::shared_ptr<WarpFetcher> fetchProcessor, QWidget* parent = nullptr); private slots: - void onAddTag(); + void onAddTag(); - void onRemoveTag(); + void onRemoveTag(); - void onResetTags(); + void onResetTags(); - void onAccept(); + void onAccept(); - void onReject(); + void onReject(); private: - void populateContainers(); + void populateContainers(); - std::vector<Warp::SourceTag> collectTags() const; + std::vector<Warp::SourceTag> collectTags() const; - void runBatchedFetch(const std::optional<size_t> &containerIndex, - const std::vector<Warp::SourceTag> &allowedTags, - size_t batchSize, - bool rerunMatcher); + void runBatchedFetch(const std::optional<size_t>& containerIndex, const std::vector<Warp::SourceTag>& allowedTags, + size_t batchSize, bool rerunMatcher); }; void RegisterWarpFetchFunctionsCommand(); diff --git a/plugins/warp/ui/shared/fetcher.cpp b/plugins/warp/ui/shared/fetcher.cpp index 299050a5..1a262878 100644 --- a/plugins/warp/ui/shared/fetcher.cpp +++ b/plugins/warp/ui/shared/fetcher.cpp @@ -2,89 +2,90 @@ WarpFetcher::WarpFetcher() { - m_logger = new BinaryNinja::Logger("WARP Fetcher"); + m_logger = new BinaryNinja::Logger("WARP Fetcher"); } -void WarpFetcher::AddPendingFunction(const FunctionRef &func) +void WarpFetcher::AddPendingFunction(const FunctionRef& func) { - std::lock_guard<std::mutex> lock(m_requestMutex); - const auto guid = Warp::GetAnalysisFunctionGUID(*func); - if (!guid.has_value() || m_processedGuids.contains(*guid)) - return; - m_pendingRequests.push_back(func); + std::lock_guard<std::mutex> lock(m_requestMutex); + const auto guid = Warp::GetAnalysisFunctionGUID(*func); + if (!guid.has_value() || m_processedGuids.contains(*guid)) + return; + m_pendingRequests.push_back(func); } std::vector<FunctionRef> WarpFetcher::FlushPendingFunctions() { - std::lock_guard<std::mutex> lock(m_requestMutex); - std::vector<FunctionRef> requests = std::move(m_pendingRequests); - m_pendingRequests.clear(); - return requests; + std::lock_guard<std::mutex> lock(m_requestMutex); + std::vector<FunctionRef> requests = std::move(m_pendingRequests); + m_pendingRequests.clear(); + return requests; } void WarpFetcher::ExecuteCompletionCallback() { - BinaryNinja::ExecuteOnMainThread([this]() { - // TODO: Holding the mutex here is dangerous! - std::lock_guard<std::mutex> lock(m_requestMutex); - m_completionCallbacks.erase( - std::ranges::remove_if(m_completionCallbacks, - [](const auto &cb) { return cb() == RemoveCallback; }).begin(), - m_completionCallbacks.end()); - }); + BinaryNinja::ExecuteOnMainThread([this]() { + // TODO: Holding the mutex here is dangerous! + std::lock_guard<std::mutex> lock(m_requestMutex); + m_completionCallbacks.erase( + std::ranges::remove_if(m_completionCallbacks, [](const auto& cb) { return cb() == RemoveCallback; }) + .begin(), + m_completionCallbacks.end()); + }); } std::shared_ptr<WarpFetcher> WarpFetcher::Global() { - static auto global = std::make_shared<WarpFetcher>(); - return global; + static auto global = std::make_shared<WarpFetcher>(); + return global; } void WarpFetcher::FetchPendingFunctions(const std::vector<Warp::SourceTag>& allowedTags) { - m_requestInProgress = true; - const auto requests = FlushPendingFunctions(); - if (requests.empty()) - { - m_logger->LogDebug("No pending requests to fetch... skipping"); - m_requestInProgress = false; - return; - } + m_requestInProgress = true; + const auto requests = FlushPendingFunctions(); + if (requests.empty()) + { + m_logger->LogDebug("No pending requests to fetch... skipping"); + m_requestInProgress = false; + return; + } - const auto start_time = std::chrono::high_resolution_clock::now(); + const auto start_time = std::chrono::high_resolution_clock::now(); - // Because we must fetch for a single target we map the function guids to the associated platform to perform fetches for each. - std::map<PlatformRef, std::vector<Warp::FunctionGUID>> platformMappedGuids; - for (const auto &func: requests) - { - const auto guid = Warp::GetAnalysisFunctionGUID(*func); - if (!guid.has_value()) - continue; - auto platform = func->GetPlatform(); - platformMappedGuids[platform].push_back(guid.value()); - } + // Because we must fetch for a single target we map the function guids to the associated platform to perform fetches + // for each. + std::map<PlatformRef, std::vector<Warp::FunctionGUID>> platformMappedGuids; + for (const auto& func : requests) + { + const auto guid = Warp::GetAnalysisFunctionGUID(*func); + if (!guid.has_value()) + continue; + auto platform = func->GetPlatform(); + platformMappedGuids[platform].push_back(guid.value()); + } - for (const auto &[platform, guids] : platformMappedGuids) - { - m_logger->LogDebugF("Fetching {} functions for platform {}", guids.size(), platform->GetName()); - auto target = Warp::Target::FromPlatform(*platform); - for (const auto &container: Warp::Container::All()) - container->FetchFunctions(*target, guids, allowedTags); + for (const auto& [platform, guids] : platformMappedGuids) + { + m_logger->LogDebugF("Fetching {} functions for platform {}", guids.size(), platform->GetName()); + auto target = Warp::Target::FromPlatform(*platform); + for (const auto& container : Warp::Container::All()) + container->FetchFunctions(*target, guids, allowedTags); - std::lock_guard<std::mutex> lock(m_requestMutex); - for (const auto &guid: guids) - m_processedGuids.insert(guid); - } + std::lock_guard<std::mutex> lock(m_requestMutex); + for (const auto& guid : guids) + m_processedGuids.insert(guid); + } - m_requestInProgress = false; - ExecuteCompletionCallback(); - const auto end_time = std::chrono::high_resolution_clock::now(); - const std::chrono::duration<double> elapsed_time = end_time - start_time; - m_logger->LogDebug("Fetch batch took %f seconds", elapsed_time.count()); + m_requestInProgress = false; + ExecuteCompletionCallback(); + const auto end_time = std::chrono::high_resolution_clock::now(); + const std::chrono::duration<double> elapsed_time = end_time - start_time; + m_logger->LogDebug("Fetch batch took %f seconds", elapsed_time.count()); } void WarpFetcher::ClearProcessed() { - m_logger->LogInfoF("Clearing {} processed functions from cache...", m_processedGuids.size()); - m_processedGuids.clear(); + m_logger->LogInfoF("Clearing {} processed functions from cache...", m_processedGuids.size()); + m_processedGuids.clear(); } diff --git a/plugins/warp/ui/shared/fetcher.h b/plugins/warp/ui/shared/fetcher.h index 917ac912..52d0a0e2 100644 --- a/plugins/warp/ui/shared/fetcher.h +++ b/plugins/warp/ui/shared/fetcher.h @@ -12,43 +12,44 @@ enum WarpFetchCompletionStatus { - KeepCallback, - RemoveCallback, + KeepCallback, + RemoveCallback, }; // Responsible for fetching data from the containers, to later be queried from the container interface. class WarpFetcher { - LoggerRef m_logger; + LoggerRef m_logger; - std::mutex m_requestMutex; - std::vector<FunctionRef> m_pendingRequests; - std::unordered_set<Warp::FunctionGUID> m_processedGuids; + std::mutex m_requestMutex; + std::vector<FunctionRef> m_pendingRequests; + std::unordered_set<Warp::FunctionGUID> m_processedGuids; - // List of callbacks to call when done fetching data, assume that others are using this as well. - std::vector<std::function<WarpFetchCompletionStatus()> > m_completionCallbacks; + // List of callbacks to call when done fetching data, assume that others are using this as well. + std::vector<std::function<WarpFetchCompletionStatus()>> m_completionCallbacks; public: - explicit WarpFetcher(); + explicit WarpFetcher(); - // The global fetcher instance, this is used for the fetch dialog and the sidebar. - static std::shared_ptr<WarpFetcher> Global(); + // The global fetcher instance, this is used for the fetch dialog and the sidebar. + static std::shared_ptr<WarpFetcher> Global(); - std::atomic<bool> m_requestInProgress = false; + std::atomic<bool> m_requestInProgress = false; - void AddCompletionCallback(std::function<WarpFetchCompletionStatus()> cb) - { - std::lock_guard<std::mutex> lock(m_requestMutex); - m_completionCallbacks.push_back(std::move(cb)); - } + void AddCompletionCallback(std::function<WarpFetchCompletionStatus()> cb) + { + std::lock_guard<std::mutex> lock(m_requestMutex); + m_completionCallbacks.push_back(std::move(cb)); + } - void AddPendingFunction(const FunctionRef &func); + void AddPendingFunction(const FunctionRef& func); - void FetchPendingFunctions(const std::vector<Warp::SourceTag>& allowedTags); + void FetchPendingFunctions(const std::vector<Warp::SourceTag>& allowedTags); + + void ClearProcessed(); - void ClearProcessed(); private: - std::vector<FunctionRef> FlushPendingFunctions(); + std::vector<FunctionRef> FlushPendingFunctions(); - void ExecuteCompletionCallback(); + void ExecuteCompletionCallback(); }; diff --git a/plugins/warp/ui/shared/function.cpp b/plugins/warp/ui/shared/function.cpp index e1609e0b..e177c285 100644 --- a/plugins/warp/ui/shared/function.cpp +++ b/plugins/warp/ui/shared/function.cpp @@ -9,342 +9,338 @@ #include "constraint.h" #include "misc.h" -WarpFunctionItem::WarpFunctionItem(Warp::Ref<Warp::Function> function, - BinaryNinja::Ref<BinaryNinja::Function> analysisFunction) +WarpFunctionItem::WarpFunctionItem( + Warp::Ref<Warp::Function> function, BinaryNinja::Ref<BinaryNinja::Function> analysisFunction) { - m_function = function; + m_function = function; - BinaryNinja::Ref<BinaryNinja::Symbol> symbol = m_function->GetSymbol(*analysisFunction); - std::string symbolName = symbol->GetShortName(); - setText(QString::fromStdString(symbolName)); + BinaryNinja::Ref<BinaryNinja::Symbol> symbol = m_function->GetSymbol(*analysisFunction); + std::string symbolName = symbol->GetShortName(); + setText(QString::fromStdString(symbolName)); - // Serialize the tokens to make it accessible via QModelIndex. - // We will take these tokens and then user them in our custom item delegate. - TokenData tokenData = TokenData(symbolName); - if (BinaryNinja::Ref<BinaryNinja::Type> type = m_function->GetType(*analysisFunction)) - tokenData = TokenData(*type, symbolName); - setData(QVariant::fromValue(tokenData), Qt::UserRole); + // Serialize the tokens to make it accessible via QModelIndex. + // We will take these tokens and then user them in our custom item delegate. + TokenData tokenData = TokenData(symbolName); + if (BinaryNinja::Ref<BinaryNinja::Type> type = m_function->GetType(*analysisFunction)) + tokenData = TokenData(*type, symbolName); + setData(QVariant::fromValue(tokenData), Qt::UserRole); } -void WarpFunctionItem::SetContainer(const Warp::Ref<Warp::Container> &container) +void WarpFunctionItem::SetContainer(const Warp::Ref<Warp::Container>& container) { - m_container = container; + m_container = container; - // Add the container string to data so the filter model picks it up. - auto containerName = m_container->GetName(); - setData(QString::fromStdString(containerName), Qt::UserRole + 2); + // Add the container string to data so the filter model picks it up. + auto containerName = m_container->GetName(); + setData(QString::fromStdString(containerName), Qt::UserRole + 2); } void WarpFunctionItem::SetSource(Warp::Source source) { - m_source = source; + m_source = source; - // Add the source string to data so the filter model picks it up. - std::string sourceStr = m_source->ToString(); - setData(QString::fromStdString(sourceStr), Qt::UserRole + 1); + // Add the source string to data so the filter model picks it up. + std::string sourceStr = m_source->ToString(); + setData(QString::fromStdString(sourceStr), Qt::UserRole + 1); } -WarpFunctionItemModel::WarpFunctionItemModel(const QStringList &labels, QObject *parent) +WarpFunctionItemModel::WarpFunctionItemModel(const QStringList& labels, QObject* parent) { - this->setHorizontalHeaderLabels(labels); + this->setHorizontalHeaderLabels(labels); } -void WarpFunctionItemModel::AppendFunction(WarpFunctionItem *item) +void WarpFunctionItemModel::AppendFunction(WarpFunctionItem* item) { - QList<QStandardItem *> row = {}; - row.insert(COL_FUNCTION_ITEM, item); - appendRow(row); + QList<QStandardItem*> row = {}; + row.insert(COL_FUNCTION_ITEM, item); + appendRow(row); } -void WarpFunctionItemModel::InsertFunction(uint64_t address, WarpFunctionItem *item) +void WarpFunctionItemModel::InsertFunction(uint64_t address, WarpFunctionItem* item) { - // Update item if already available, this lets us keep the model - const auto iter = m_insertableFunctionRows.find(address); - if (iter != m_insertableFunctionRows.end()) - { - setItem(iter->second, COL_FUNCTION_ITEM, item); - return; - } + // Update item if already available, this lets us keep the model + const auto iter = m_insertableFunctionRows.find(address); + if (iter != m_insertableFunctionRows.end()) + { + setItem(iter->second, COL_FUNCTION_ITEM, item); + return; + } - AppendFunction(item); - m_insertableFunctionRows[address] = rowCount() - 1; + AppendFunction(item); + m_insertableFunctionRows[address] = rowCount() - 1; } -WarpFunctionItem *WarpFunctionItemModel::GetItem(const QModelIndex &index) const +WarpFunctionItem* WarpFunctionItemModel::GetItem(const QModelIndex& index) const { - if (!index.isValid()) - return nullptr; - return dynamic_cast<WarpFunctionItem *>(item(index.row(), COL_FUNCTION_ITEM)); + if (!index.isValid()) + return nullptr; + return dynamic_cast<WarpFunctionItem*>(item(index.row(), COL_FUNCTION_ITEM)); } -std::optional<uint64_t> WarpFunctionItemModel::GetAddress(const QModelIndex &index) const +std::optional<uint64_t> WarpFunctionItemModel::GetAddress(const QModelIndex& index) const { - if (!index.isValid()) - return std::nullopt; - // TODO: This is a hack, this means we must enumerate all rows to get the address. - for (const auto &[addr, row]: m_insertableFunctionRows) - if (row == index.row()) - return addr; - return std::nullopt; + if (!index.isValid()) + return std::nullopt; + // TODO: This is a hack, this means we must enumerate all rows to get the address. + for (const auto& [addr, row] : m_insertableFunctionRows) + if (row == index.row()) + return addr; + return std::nullopt; } -QVariant WarpFunctionItemModel::data(const QModelIndex &index, int role) const +QVariant WarpFunctionItemModel::data(const QModelIndex& index, int role) const { - if (role == Qt::BackgroundRole) - { - auto itemFunction = GetItem(index); - // Check if we have a valid item and it's the matched function - if (m_matchedFunction && itemFunction) - { - // TODO: Why wont == go to the correct call??? - if (BNWARPFunctionsEqual(itemFunction->GetFunction()->m_object, m_matchedFunction->m_object)) - { - // TODO: Better color? - static QColor matchedColor = getThemeColor(BlueStandardHighlightColor); - matchedColor.setAlpha(128); - return matchedColor; - } - } - } + if (role == Qt::BackgroundRole) + { + auto itemFunction = GetItem(index); + // Check if we have a valid item and it's the matched function + if (m_matchedFunction && itemFunction) + { + // TODO: Why wont == go to the correct call??? + if (BNWARPFunctionsEqual(itemFunction->GetFunction()->m_object, m_matchedFunction->m_object)) + { + // TODO: Better color? + static QColor matchedColor = getThemeColor(BlueStandardHighlightColor); + matchedColor.setAlpha(128); + return matchedColor; + } + } + } - if (role == Qt::DisplayRole) - { - // We really only use this for searching as we have TokenData for our delegate. - WarpFunctionItem *item = GetItem(index); - if (!item) - return QVariant(); - TokenData tokenData = item->data(Qt::UserRole).value<TokenData>(); - // Add the function guid so we can filter by that. - QString text = tokenData.toString() + " " + QString::fromStdString(item->GetFunction()->GetGUID().ToString()); - if (auto source = item->GetSource(); source) - { - // Add the source guid so we can also filter by that. - std::string sourceStr = source->ToString(); - text = text + " " + QString::fromStdString(sourceStr); - } - return text; - } + if (role == Qt::DisplayRole) + { + // We really only use this for searching as we have TokenData for our delegate. + WarpFunctionItem* item = GetItem(index); + if (!item) + return QVariant(); + TokenData tokenData = item->data(Qt::UserRole).value<TokenData>(); + // Add the function guid so we can filter by that. + QString text = tokenData.toString() + " " + QString::fromStdString(item->GetFunction()->GetGUID().ToString()); + if (auto source = item->GetSource(); source) + { + // Add the source guid so we can also filter by that. + std::string sourceStr = source->ToString(); + text = text + " " + QString::fromStdString(sourceStr); + } + return text; + } - return QStandardItemModel::data(index, role); + return QStandardItemModel::data(index, role); } -bool WarpFunctionFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +bool WarpFunctionFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const { - const QString filterString = filterRegularExpression().pattern(); - if (filterString.isEmpty()) - return true; + const QString filterString = filterRegularExpression().pattern(); + if (filterString.isEmpty()) + return true; - // Filter on the first column only, this contains our actual function. - auto index = sourceModel()->index(sourceRow, 0, sourceParent); - auto data = QRegularExpression::escape(index.data().toString()); - if (data.contains(filterString, Qt::CaseInsensitive)) - return true; - return false; + // Filter on the first column only, this contains our actual function. + auto index = sourceModel()->index(sourceRow, 0, sourceParent); + auto data = QRegularExpression::escape(index.data().toString()); + if (data.contains(filterString, Qt::CaseInsensitive)) + return true; + return false; } -bool WarpFunctionFilterModel::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const +bool WarpFunctionFilterModel::lessThan(const QModelIndex& sourceLeft, const QModelIndex& sourceRight) const { - // TODO: When we make the stuff _actually_ sortable. - return sourceLeft.row() < sourceRight.row(); + // TODO: When we make the stuff _actually_ sortable. + return sourceLeft.row() < sourceRight.row(); } -WarpFunctionTableWidget::WarpFunctionTableWidget(QWidget *parent) : QWidget(parent) +WarpFunctionTableWidget::WarpFunctionTableWidget(QWidget* parent) : QWidget(parent) { - QGridLayout *layout = new QGridLayout(this); - layout->setContentsMargins(0, 0, 0, 0); - layout->setSpacing(2); + QGridLayout* layout = new QGridLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(2); - m_table = new QTableView(this); - m_model = new WarpFunctionItemModel({"Function"}, this); - m_proxyModel = new WarpFunctionFilterModel(this); - m_proxyModel->setSourceModel(m_model); - m_table->setModel(m_proxyModel); + m_table = new QTableView(this); + m_model = new WarpFunctionItemModel({"Function"}, this); + m_proxyModel = new WarpFunctionFilterModel(this); + m_proxyModel->setSourceModel(m_model); + m_table->setModel(m_proxyModel); - m_filterEdit = new FilterEdit(this); - m_filterView = new FilteredView(this, m_table, this, m_filterEdit); - m_filterView->setFilterPlaceholderText("Search functions (By GUID, name or source)"); + m_filterEdit = new FilterEdit(this); + m_filterView = new FilteredView(this, m_table, this, m_filterEdit); + m_filterView->setFilterPlaceholderText("Search functions (By GUID, name or source)"); - layout->addWidget(m_filterEdit, 0, 0, 1, 5); - layout->addWidget(m_table, 1, 0, 1, 5); + layout->addWidget(m_filterEdit, 0, 0, 1, 5); + layout->addWidget(m_table, 1, 0, 1, 5); - // Make the table look nice. - m_table->horizontalHeader()->setStretchLastSection(true); - m_table->verticalHeader()->hide(); - m_table->setSelectionBehavior(QAbstractItemView::SelectRows); - m_table->setSelectionMode(QAbstractItemView::SingleSelection); - m_table->setEditTriggers(QAbstractItemView::NoEditTriggers); - m_table->setFocusPolicy(Qt::NoFocus); - m_table->setShowGrid(false); - m_table->setAlternatingRowColors(false); - m_table->setSortingEnabled(true); - // NOTE: We only have a single column right now, so disable header. - m_table->horizontalHeader()->hide(); - // Decrease row height to make it look nice. - m_table->verticalHeader()->setDefaultSectionSize(30); + // Make the table look nice. + m_table->horizontalHeader()->setStretchLastSection(true); + m_table->verticalHeader()->hide(); + m_table->setSelectionBehavior(QAbstractItemView::SelectRows); + m_table->setSelectionMode(QAbstractItemView::SingleSelection); + m_table->setEditTriggers(QAbstractItemView::NoEditTriggers); + m_table->setFocusPolicy(Qt::NoFocus); + m_table->setShowGrid(false); + m_table->setAlternatingRowColors(false); + m_table->setSortingEnabled(true); + // NOTE: We only have a single column right now, so disable header. + m_table->horizontalHeader()->hide(); + // Decrease row height to make it look nice. + m_table->verticalHeader()->setDefaultSectionSize(30); - // Make the highlight less bright. - QPalette palette = m_table->palette(); - palette.setColor(QPalette::Highlight, getThemeColor(SelectionColor)); - m_table->setPalette(palette); + // Make the highlight less bright. + QPalette palette = m_table->palette(); + palette.setColor(QPalette::Highlight, getThemeColor(SelectionColor)); + m_table->setPalette(palette); - TokenDataDelegate *tokenDelegate = new TokenDataDelegate(this); - // NOTE: Column 0 is assumed to be the function with the token data. - m_table->setItemDelegateForColumn(0, tokenDelegate); + TokenDataDelegate* tokenDelegate = new TokenDataDelegate(this); + // NOTE: Column 0 is assumed to be the function with the token data. + m_table->setItemDelegateForColumn(0, tokenDelegate); - AddressColorDelegate *addressDelegate = new AddressColorDelegate(this); - // NOTE: Column 1 is assumed to be the function address. - m_table->setItemDelegateForColumn(1, addressDelegate); + AddressColorDelegate* addressDelegate = new AddressColorDelegate(this); + // NOTE: Column 1 is assumed to be the function address. + m_table->setItemDelegateForColumn(1, addressDelegate); - // Add a dynamic context menu to the table. - // NOTE: This is a bit stupid, I am sure there is a better way to do this in QT. - m_contextMenu = new QMenu(this); - RegisterContextMenuAction("Copy Name", [](WarpFunctionItem *item, std::optional<uint64_t>) { - QClipboard *clipboard = QGuiApplication::clipboard(); - clipboard->setText(item->text()); - }); - RegisterContextMenuAction("Copy GUID", [](WarpFunctionItem *item, std::optional<uint64_t>) { - QClipboard *clipboard = QGuiApplication::clipboard(); - Warp::Ref<Warp::Function> function = item->GetFunction(); - std::string guidStr = function->GetGUID().ToString(); - clipboard->setText(QString::fromStdString(guidStr)); - }); + // Add a dynamic context menu to the table. + // NOTE: This is a bit stupid, I am sure there is a better way to do this in QT. + m_contextMenu = new QMenu(this); + RegisterContextMenuAction("Copy Name", [](WarpFunctionItem* item, std::optional<uint64_t>) { + QClipboard* clipboard = QGuiApplication::clipboard(); + clipboard->setText(item->text()); + }); + RegisterContextMenuAction("Copy GUID", [](WarpFunctionItem* item, std::optional<uint64_t>) { + QClipboard* clipboard = QGuiApplication::clipboard(); + Warp::Ref<Warp::Function> function = item->GetFunction(); + std::string guidStr = function->GetGUID().ToString(); + clipboard->setText(QString::fromStdString(guidStr)); + }); - m_table->setContextMenuPolicy(Qt::CustomContextMenu); - connect(m_table, &QTableView::customContextMenuRequested, this, [&](QPoint pos) { - const QModelIndex index = m_table->indexAt(pos); - if (!index.isValid()) - return; - const QModelIndex sourceIndex = m_proxyModel->mapToSource(index); - WarpFunctionItem *item = m_model->GetItem(sourceIndex); - if (!item || !item->GetFunction()) - return; + m_table->setContextMenuPolicy(Qt::CustomContextMenu); + connect(m_table, &QTableView::customContextMenuRequested, this, [&](QPoint pos) { + const QModelIndex index = m_table->indexAt(pos); + if (!index.isValid()) + return; + const QModelIndex sourceIndex = m_proxyModel->mapToSource(index); + WarpFunctionItem* item = m_model->GetItem(sourceIndex); + if (!item || !item->GetFunction()) + return; - // Execute the menu and get the selected action - const QAction *selectedAction = m_contextMenu->exec(m_table->viewport()->mapToGlobal(pos)); - if (!selectedAction) - return; + // Execute the menu and get the selected action + const QAction* selectedAction = m_contextMenu->exec(m_table->viewport()->mapToGlobal(pos)); + if (!selectedAction) + return; - const auto name = selectedAction->text(); - const auto iter = m_contextMenuActions.find(name); - if (iter != m_contextMenuActions.end()) - iter->second(item, m_model->GetAddress(sourceIndex)); - }); + const auto name = selectedAction->text(); + const auto iter = m_contextMenuActions.find(name); + if (iter != m_contextMenuActions.end()) + iter->second(item, m_model->GetAddress(sourceIndex)); + }); } -void WarpFunctionTableWidget::RegisterContextMenuAction(const QString &name, - const std::function<void( - WarpFunctionItem *, std::optional<uint64_t>)> &callback) +void WarpFunctionTableWidget::RegisterContextMenuAction( + const QString& name, const std::function<void(WarpFunctionItem*, std::optional<uint64_t>)>& callback) { - m_contextMenu->addAction(name); - m_contextMenuActions[name] = callback; + m_contextMenu->addAction(name); + m_contextMenuActions[name] = callback; } -void WarpFunctionTableWidget::SetFunctions(QVector<WarpFunctionItem *> functions) +void WarpFunctionTableWidget::SetFunctions(QVector<WarpFunctionItem*> functions) { - // Clear matches as they are no longer valid. - m_model->clear(); - m_model->setRowCount(0); + // Clear matches as they are no longer valid. + m_model->clear(); + m_model->setRowCount(0); - // Temporarily disable sorting so we can add rows faster - m_table->setModel(m_model); - m_table->setSortingEnabled(false); - m_table->setEnabled(false); + // Temporarily disable sorting so we can add rows faster + m_table->setModel(m_model); + m_table->setSortingEnabled(false); + m_table->setEnabled(false); - for (const auto &function: functions) - m_model->AppendFunction(function); + for (const auto& function : functions) + m_model->AppendFunction(function); - // We are done, re-enable table. - m_table->setEnabled(true); - m_table->setModel(m_proxyModel); - m_table->setSortingEnabled(true); + // We are done, re-enable table. + m_table->setEnabled(true); + m_table->setModel(m_proxyModel); + m_table->setSortingEnabled(true); - // Update the filter text with the new count of functions. - m_filterView->setFilterPlaceholderText(QString("Search %1 functions").arg(m_model->rowCount())); + // Update the filter text with the new count of functions. + m_filterView->setFilterPlaceholderText(QString("Search %1 functions").arg(m_model->rowCount())); } -void WarpFunctionTableWidget::InsertFunction(uint64_t address, WarpFunctionItem *function) +void WarpFunctionTableWidget::InsertFunction(uint64_t address, WarpFunctionItem* function) { - m_model->InsertFunction(address, function); + m_model->InsertFunction(address, function); } -void WarpFunctionTableWidget::setFilter(const std::string &filter) +void WarpFunctionTableWidget::setFilter(const std::string& filter) { - m_proxyModel->setFilterFixedString(QString::fromStdString(filter)); - m_filterView->showFilter(QString::fromStdString(filter)); + m_proxyModel->setFilterFixedString(QString::fromStdString(filter)); + m_filterView->showFilter(QString::fromStdString(filter)); } -WarpFunctionInfoWidget::WarpFunctionInfoWidget(QWidget *parent) - : QWidget(parent) +WarpFunctionInfoWidget::WarpFunctionInfoWidget(QWidget* parent) : QWidget(parent) { - // Create a tab widget - QTabWidget *tabWidget = new QTabWidget(this); - tabWidget->setContentsMargins(0, 0, 0, 0); + // Create a tab widget + QTabWidget* tabWidget = new QTabWidget(this); + tabWidget->setContentsMargins(0, 0, 0, 0); - // Create tables for the "Constraints", "Comments", and "Variables" tabs - m_commentsTable = new QTableView(this); - // m_variablesTable = new QTableView(this); + // Create tables for the "Constraints", "Comments", and "Variables" tabs + m_commentsTable = new QTableView(this); + // m_variablesTable = new QTableView(this); - // TODO: On click navigate to where the constraint is located. - m_constraintsTable = new WarpConstraintTableWidget(this); - tabWidget->addTab(m_constraintsTable, "Constraints"); + // TODO: On click navigate to where the constraint is located. + m_constraintsTable = new WarpConstraintTableWidget(this); + tabWidget->addTab(m_constraintsTable, "Constraints"); - // Set up comments tab - m_commentsModel = new QStandardItemModel(this); - m_commentsModel->setHorizontalHeaderLabels({"Offset", "Text"}); - m_commentsModel->setColumnCount(2); - m_commentsTable->setModel(m_commentsModel); - m_commentsTable->horizontalHeader()->setStretchLastSection(true); - m_commentsTable->horizontalHeader()->setSelectionBehavior(QAbstractItemView::SelectRows); - m_commentsTable->horizontalHeader()->setSelectionMode(QAbstractItemView::SingleSelection); - m_commentsTable->horizontalHeader()->setEditTriggers(QAbstractItemView::NoEditTriggers); - m_commentsTable->verticalHeader()->hide(); - m_commentsTable->horizontalHeader()->hide(); - tabWidget->addTab(m_commentsTable, "Comments"); + // Set up comments tab + m_commentsModel = new QStandardItemModel(this); + m_commentsModel->setHorizontalHeaderLabels({"Offset", "Text"}); + m_commentsModel->setColumnCount(2); + m_commentsTable->setModel(m_commentsModel); + m_commentsTable->horizontalHeader()->setStretchLastSection(true); + m_commentsTable->horizontalHeader()->setSelectionBehavior(QAbstractItemView::SelectRows); + m_commentsTable->horizontalHeader()->setSelectionMode(QAbstractItemView::SingleSelection); + m_commentsTable->horizontalHeader()->setEditTriggers(QAbstractItemView::NoEditTriggers); + m_commentsTable->verticalHeader()->hide(); + m_commentsTable->horizontalHeader()->hide(); + tabWidget->addTab(m_commentsTable, "Comments"); - // Set up variables tab - // m_variablesTable->setModel(new QStandardItemModel(this)); - // TODO: Add variables to data. - // tabWidget->addTab(m_variablesTable, "Variables"); + // Set up variables tab + // m_variablesTable->setModel(new QStandardItemModel(this)); + // TODO: Add variables to data. + // tabWidget->addTab(m_variablesTable, "Variables"); - // Add the tab widget to this widget's layout - QVBoxLayout *layout = new QVBoxLayout(this); - layout->setContentsMargins(0, 0, 0, 0); - layout->setSpacing(0); - layout->addWidget(tabWidget); + // Add the tab widget to this widget's layout + QVBoxLayout* layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + layout->addWidget(tabWidget); - setLayout(layout); + setLayout(layout); } void WarpFunctionInfoWidget::UpdateInfo() { - m_commentsModel->clear(); - m_commentsModel->setRowCount(0); - m_constraintsTable->SetMatchedConstraints({}); - m_constraintsTable->SetConstraints({}); + m_commentsModel->clear(); + m_commentsModel->setRowCount(0); + m_constraintsTable->SetMatchedConstraints({}); + m_constraintsTable->SetConstraints({}); - Warp::Ref<Warp::Function> function = GetFunction(); - if (!function) - return; + Warp::Ref<Warp::Function> function = GetFunction(); + if (!function) + return; - // Set the analysis constraints if there is an analysis function. - if (const auto analysisFunction = GetAnalysisFunction()) - { - const auto analysisConstraints = Warp::Function::Get(*analysisFunction)->GetConstraints(); - m_constraintsTable->SetMatchedConstraints(analysisConstraints); - } + // Set the analysis constraints if there is an analysis function. + if (const auto analysisFunction = GetAnalysisFunction()) + { + const auto analysisConstraints = Warp::Function::Get(*analysisFunction)->GetConstraints(); + m_constraintsTable->SetMatchedConstraints(analysisConstraints); + } - // Add all the constraints for the current function to the model. - QVector<WarpConstraintItem *> constraints; - for (const auto &constraint: function->GetConstraints()) - constraints.push_back(new WarpConstraintItem(constraint)); - m_constraintsTable->SetConstraints(constraints); + // Add all the constraints for the current function to the model. + QVector<WarpConstraintItem*> constraints; + for (const auto& constraint : function->GetConstraints()) + constraints.push_back(new WarpConstraintItem(constraint)); + m_constraintsTable->SetConstraints(constraints); - // Add all the comments to the model. - for (const auto &comment: function->GetComments()) - { - m_commentsModel->appendRow({ - new QStandardItem(QString("0x%1").arg(comment.offset, 0, 16)), - new QStandardItem(QString::fromStdString(comment.text)) - }); - } + // Add all the comments to the model. + for (const auto& comment : function->GetComments()) + { + m_commentsModel->appendRow({new QStandardItem(QString("0x%1").arg(comment.offset, 0, 16)), + new QStandardItem(QString::fromStdString(comment.text))}); + } } diff --git a/plugins/warp/ui/shared/function.h b/plugins/warp/ui/shared/function.h index 90d52092..5cfaff75 100644 --- a/plugins/warp/ui/shared/function.h +++ b/plugins/warp/ui/shared/function.h @@ -10,159 +10,148 @@ class WarpFunctionItem : public QStandardItem { - Warp::Ref<Warp::Function> m_function; + Warp::Ref<Warp::Function> m_function; - // Optional attached data used to show/manage the function. - Warp::Ref<Warp::Container> m_container; - std::optional<Warp::Source> m_source; + // Optional attached data used to show/manage the function. + Warp::Ref<Warp::Container> m_container; + std::optional<Warp::Source> m_source; public: - WarpFunctionItem(Warp::Ref<Warp::Function> function, - BinaryNinja::Ref<BinaryNinja::Function> analysisFunction); + WarpFunctionItem(Warp::Ref<Warp::Function> function, BinaryNinja::Ref<BinaryNinja::Function> analysisFunction); - void SetContainer(const Warp::Ref<Warp::Container> &container); + void SetContainer(const Warp::Ref<Warp::Container>& container); - void SetSource(Warp::Source source); + void SetSource(Warp::Source source); - Warp::Ref<Warp::Function> GetFunction() { return m_function; } - Warp::Ref<Warp::Container> GetContainer() { return m_container; } - std::optional<Warp::Source> GetSource() { return m_source; } + Warp::Ref<Warp::Function> GetFunction() { return m_function; } + Warp::Ref<Warp::Container> GetContainer() { return m_container; } + std::optional<Warp::Source> GetSource() { return m_source; } }; class WarpFunctionItemModel : public QStandardItemModel { - Q_OBJECT + Q_OBJECT - // The current matched function, used to highlight currently. - Warp::Ref<Warp::Function> m_matchedFunction; + // The current matched function, used to highlight currently. + Warp::Ref<Warp::Function> m_matchedFunction; - // Mapping of function start address to the row index. - // This is used to identify unique functions for updating instead of resetting the entire model. - std::unordered_map<uint64_t, int> m_insertableFunctionRows; + // Mapping of function start address to the row index. + // This is used to identify unique functions for updating instead of resetting the entire model. + std::unordered_map<uint64_t, int> m_insertableFunctionRows; public: - WarpFunctionItemModel(const QStringList &labels, QObject *parent); + WarpFunctionItemModel(const QStringList& labels, QObject* parent); - static constexpr int COL_FUNCTION_ITEM = 0; - static constexpr int COL_ADDRESS_ITEM = 1; + static constexpr int COL_FUNCTION_ITEM = 0; + static constexpr int COL_ADDRESS_ITEM = 1; - void AppendFunction(WarpFunctionItem *item); + void AppendFunction(WarpFunctionItem* item); - void InsertFunction(uint64_t address, WarpFunctionItem *item); + void InsertFunction(uint64_t address, WarpFunctionItem* item); - WarpFunctionItem *GetItem(const QModelIndex &index) const; + WarpFunctionItem* GetItem(const QModelIndex& index) const; - std::optional<uint64_t> GetAddress(const QModelIndex &index) const; + std::optional<uint64_t> GetAddress(const QModelIndex& index) const; - QVariant data(const QModelIndex &index, int role) const override; + QVariant data(const QModelIndex& index, int role) const override; - void SetMatchedFunction(const Warp::Ref<Warp::Function> &matchedFunction) - { - Warp::Ref<Warp::Function> previousMatchedFunction = m_matchedFunction; - m_matchedFunction = matchedFunction; + void SetMatchedFunction(const Warp::Ref<Warp::Function>& matchedFunction) + { + Warp::Ref<Warp::Function> previousMatchedFunction = m_matchedFunction; + m_matchedFunction = matchedFunction; - // Make sure to refresh the highlights so we don't keep the highlights from the previous function. - if (previousMatchedFunction) - { - const QModelIndex topLeft = index(0, 0); - const QModelIndex bottomRight = index(rowCount() - 1, 0); - emit dataChanged(topLeft, bottomRight); - } - } + // Make sure to refresh the highlights so we don't keep the highlights from the previous function. + if (previousMatchedFunction) + { + const QModelIndex topLeft = index(0, 0); + const QModelIndex bottomRight = index(rowCount() - 1, 0); + emit dataChanged(topLeft, bottomRight); + } + } }; class WarpFunctionFilterModel : public QSortFilterProxyModel { - Q_OBJECT + Q_OBJECT public: - WarpFunctionFilterModel(QObject *parent): QSortFilterProxyModel(parent) - { - } + WarpFunctionFilterModel(QObject* parent) : QSortFilterProxyModel(parent) {} - ~WarpFunctionFilterModel() override = default; + ~WarpFunctionFilterModel() override = default; - bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; + bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override; - bool lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const override; + bool lessThan(const QModelIndex& sourceLeft, const QModelIndex& sourceRight) const override; }; class WarpFunctionTableWidget : public QWidget, public FilterTarget { - Q_OBJECT + Q_OBJECT - QTableView *m_table; - WarpFunctionItemModel *m_model; - WarpFunctionFilterModel *m_proxyModel; - FilterEdit *m_filterEdit; - FilteredView *m_filterView; - QMenu *m_contextMenu; - std::map<QString, std::function<void(WarpFunctionItem *, std::optional<uint64_t>)> > m_contextMenuActions; + QTableView* m_table; + WarpFunctionItemModel* m_model; + WarpFunctionFilterModel* m_proxyModel; + FilterEdit* m_filterEdit; + FilteredView* m_filterView; + QMenu* m_contextMenu; + std::map<QString, std::function<void(WarpFunctionItem*, std::optional<uint64_t>)>> m_contextMenuActions; public: - explicit WarpFunctionTableWidget(QWidget *parent = nullptr); + explicit WarpFunctionTableWidget(QWidget* parent = nullptr); - // TODO: Invert this and provide OnCallback functions that wrap the connect call. - QTableView *GetTableView() const { return m_table; } - WarpFunctionItemModel *GetModel() const { return m_model; } - WarpFunctionFilterModel *GetProxyModel() const { return m_proxyModel; } + // TODO: Invert this and provide OnCallback functions that wrap the connect call. + QTableView* GetTableView() const { return m_table; } + WarpFunctionItemModel* GetModel() const { return m_model; } + WarpFunctionFilterModel* GetProxyModel() const { return m_proxyModel; } - void RegisterContextMenuAction(const QString &name, - const std::function<void(WarpFunctionItem *, std::optional<uint64_t>)> &callback); + void RegisterContextMenuAction( + const QString& name, const std::function<void(WarpFunctionItem*, std::optional<uint64_t>)>& callback); - void SetFunctions(QVector<WarpFunctionItem *> functions); + void SetFunctions(QVector<WarpFunctionItem*> functions); - void InsertFunction(uint64_t address, WarpFunctionItem *function); + void InsertFunction(uint64_t address, WarpFunctionItem* function); - void setFilter(const std::string &) override; + void setFilter(const std::string&) override; - void scrollToFirstItem() override - { - } + void scrollToFirstItem() override {} - void scrollToCurrentItem() override - { - } + void scrollToCurrentItem() override {} - void selectFirstItem() override - { - } + void selectFirstItem() override {} - void activateFirstItem() override - { - } + void activateFirstItem() override {} }; class WarpFunctionInfoWidget : public QWidget { - Q_OBJECT + Q_OBJECT - Warp::Ref<Warp::Function> m_function; - BinaryNinja::Ref<BinaryNinja::Function> m_analysisFunction; + Warp::Ref<Warp::Function> m_function; + BinaryNinja::Ref<BinaryNinja::Function> m_analysisFunction; - // Optionally provide this information to show the source information. - Warp::Ref<Warp::Container> m_container; - std::string source; + // Optionally provide this information to show the source information. + Warp::Ref<Warp::Container> m_container; + std::string source; - WarpConstraintTableWidget *m_constraintsTable; + WarpConstraintTableWidget* m_constraintsTable; - QTableView *m_commentsTable; - QStandardItemModel *m_commentsModel; + QTableView* m_commentsTable; + QStandardItemModel* m_commentsModel; - QTableView *m_variablesTable; + QTableView* m_variablesTable; public: - explicit WarpFunctionInfoWidget(QWidget *parent = nullptr); + explicit WarpFunctionInfoWidget(QWidget* parent = nullptr); - Warp::Ref<Warp::Function> GetFunction() { return m_function; } - void SetFunction(Warp::Ref<Warp::Function> function) { m_function = function; }; + Warp::Ref<Warp::Function> GetFunction() { return m_function; } + void SetFunction(Warp::Ref<Warp::Function> function) { m_function = function; }; - void SetAnalysisFunction(BinaryNinja::Ref<BinaryNinja::Function> analysisFunction) - { - m_analysisFunction = analysisFunction; - }; - BinaryNinja::Ref<BinaryNinja::Function> GetAnalysisFunction() { return m_analysisFunction; } + void SetAnalysisFunction(BinaryNinja::Ref<BinaryNinja::Function> analysisFunction) + { + m_analysisFunction = analysisFunction; + }; + BinaryNinja::Ref<BinaryNinja::Function> GetAnalysisFunction() { return m_analysisFunction; } - // TODO: Make this private? - void UpdateInfo(); + // TODO: Make this private? + void UpdateInfo(); }; diff --git a/plugins/warp/ui/shared/misc.cpp b/plugins/warp/ui/shared/misc.cpp index bbe3ab24..f84237c9 100644 --- a/plugins/warp/ui/shared/misc.cpp +++ b/plugins/warp/ui/shared/misc.cpp @@ -8,133 +8,135 @@ #include "render.h" #include "theme.h" -TokenData::TokenData(const std::string &name) +TokenData::TokenData(const std::string& name) { - tokens.emplace_back(255, TextToken, name); + tokens.emplace_back(255, TextToken, name); } -TokenData::TokenData(const BinaryNinja::Type &type, const std::string& name) +TokenData::TokenData(const BinaryNinja::Type& type, const std::string& name) { - for (const auto &token: type.GetTokensBeforeName()) - tokens.emplace_back(token); - tokens.emplace_back(255, TextToken, " "); - tokens.emplace_back(255, TextToken, name); - for (const auto &token: type.GetTokensAfterName()) - tokens.emplace_back(token); + for (const auto& token : type.GetTokensBeforeName()) + tokens.emplace_back(token); + tokens.emplace_back(255, TextToken, " "); + tokens.emplace_back(255, TextToken, name); + for (const auto& token : type.GetTokensAfterName()) + tokens.emplace_back(token); } -void TokenDataDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +void TokenDataDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { - painter->save(); + painter->save(); - auto tokenData = index.data(Qt::UserRole).value<TokenData>(); + auto tokenData = index.data(Qt::UserRole).value<TokenData>(); - // Draw either the selected row or background color. - QVariant background = index.data(Qt::BackgroundRole); - if (background.canConvert<QBrush>()) - painter->fillRect(option.rect, background.value<QBrush>()); - else if (option.state & QStyle::State_Selected) - painter->fillRect(option.rect, option.palette.highlight()); - painter->translate(option.rect.topLeft()); + // Draw either the selected row or background color. + QVariant background = index.data(Qt::BackgroundRole); + if (background.canConvert<QBrush>()) + painter->fillRect(option.rect, background.value<QBrush>()); + else if (option.state & QStyle::State_Selected) + painter->fillRect(option.rect, option.palette.highlight()); + painter->translate(option.rect.topLeft()); - auto renderContext = RenderContext(const_cast<QWidget *>(option.widget)); - renderContext.init(*painter); - HighlightTokenState highlightState; - renderContext.drawDisassemblyLine(*painter, 5, 5, {tokenData.tokens.begin(), tokenData.tokens.end()}, - highlightState); + auto renderContext = RenderContext(const_cast<QWidget*>(option.widget)); + renderContext.init(*painter); + HighlightTokenState highlightState; + renderContext.drawDisassemblyLine( + *painter, 5, 5, {tokenData.tokens.begin(), tokenData.tokens.end()}, highlightState); - painter->restore(); + painter->restore(); } -QSize TokenDataDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const +QSize TokenDataDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { - auto tokenData = index.data(Qt::UserRole).value<TokenData>(); - auto renderContext = RenderContext(const_cast<QWidget *>(option.widget)); - QFontMetrics fontMetrics = QFontMetrics(renderContext.getFont()); - QString line = ""; - for (const auto &token: tokenData.tokens) - line += token.text; - int width = qMax(0, fontMetrics.horizontalAdvance(line)); - return QSize(width, renderContext.getFontHeight()); + auto tokenData = index.data(Qt::UserRole).value<TokenData>(); + auto renderContext = RenderContext(const_cast<QWidget*>(option.widget)); + QFontMetrics fontMetrics = QFontMetrics(renderContext.getFont()); + QString line = ""; + for (const auto& token : tokenData.tokens) + line += token.text; + int width = qMax(0, fontMetrics.horizontalAdvance(line)); + return QSize(width, renderContext.getFontHeight()); } -void AddressColorDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +void AddressColorDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { - QStyleOptionViewItem opt = option; - initStyleOption(&opt, index); + 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; + opt.font = getMonospaceFont(qobject_cast<QWidget*>(parent())); + opt.palette.setColor(QPalette::Text, getThemeColor(BNThemeColor::AddressColor)); + opt.displayAlignment = Qt::AlignCenter | Qt::AlignVCenter; - QStyledItemDelegate::paint(painter, opt, index); + QStyledItemDelegate::paint(painter, opt, index); } -bool GenericTextFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +bool GenericTextFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const { - auto filterString = filterRegularExpression().pattern(); - if (filterString.isEmpty()) - return true; + auto filterString = filterRegularExpression().pattern(); + if (filterString.isEmpty()) + return true; - for (int i = 0; i < sourceModel()->columnCount(); i++) - { - auto index = sourceModel()->index(sourceRow, i, sourceParent); - auto data = QRegularExpression::escape(index.data().toString()); - if (data.contains(filterString, Qt::CaseInsensitive)) - return true; - } + for (int i = 0; i < sourceModel()->columnCount(); i++) + { + auto index = sourceModel()->index(sourceRow, i, sourceParent); + auto data = QRegularExpression::escape(index.data().toString()); + if (data.contains(filterString, Qt::CaseInsensitive)) + return true; + } - return false; + return false; } -bool GenericTextFilterModel::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const +bool GenericTextFilterModel::lessThan(const QModelIndex& sourceLeft, const QModelIndex& sourceRight) const { - auto leftData = sourceLeft.data().toString(); - auto rightData = sourceRight.data().toString(); - return QString::localeAwareCompare(leftData, rightData) < 0; + auto leftData = sourceLeft.data().toString(); + auto rightData = sourceRight.data().toString(); + return QString::localeAwareCompare(leftData, rightData) < 0; } -ParsedQuery::ParsedQuery(const QString &rawQuery) +ParsedQuery::ParsedQuery(const QString& rawQuery) { - query = rawQuery; - qualifiers = {}; + query = rawQuery; + qualifiers = {}; - // Regex capturing: key, and value (bare, quoted and unquoted) - static QRegularExpression re(R"((^|\s)([A-Za-z][A-Za-z0-9_-]*)\s*:\s*(?:\"([^\"]*)\"|'([^']*)'|(\S+)))", - QRegularExpression::CaseInsensitiveOption); + // Regex capturing: key, and value (bare, quoted and unquoted) + static QRegularExpression re(R"((^|\s)([A-Za-z][A-Za-z0-9_-]*)\s*:\s*(?:\"([^\"]*)\"|'([^']*)'|(\S+)))", + QRegularExpression::CaseInsensitiveOption); - // Collect matches to remove later (from end to start to keep indices stable) - struct Span - { - qsizetype start; - qsizetype length; - QString key; - QString value; - }; - QVector<Span> spans; + // Collect matches to remove later (from end to start to keep indices stable) + struct Span + { + qsizetype start; + qsizetype length; + QString key; + QString value; + }; + QVector<Span> spans; - auto it = re.globalMatch(rawQuery); - while (it.hasNext()) - { - const auto m = it.next(); - const QString key = m.captured(2).toLower(); - // Value can be in group 3 (double quotes), 4 (single quotes), or 5 (bare) - QString val = m.captured(3); - if (val.isNull() || val.isEmpty()) val = m.captured(4); - if (val.isNull() || val.isEmpty()) val = m.captured(5); - spans.push_back(Span{m.capturedStart(0), m.capturedLength(0), key, val}); - } + auto it = re.globalMatch(rawQuery); + while (it.hasNext()) + { + const auto m = it.next(); + const QString key = m.captured(2).toLower(); + // Value can be in group 3 (double quotes), 4 (single quotes), or 5 (bare) + QString val = m.captured(3); + if (val.isNull() || val.isEmpty()) + val = m.captured(4); + if (val.isNull() || val.isEmpty()) + val = m.captured(5); + spans.push_back(Span {m.capturedStart(0), m.capturedLength(0), key, val}); + } - // Keep only the last value per key (last occurrence wins, do not accumulate) - for (const auto &s: spans) - qualifiers[s.key] = s.value; + // Keep only the last value per key (last occurrence wins, do not accumulate) + for (const auto& s : spans) + qualifiers[s.key] = s.value; - // Remove matched spans from the text (replace with a single space to preserve boundaries) - std::sort(spans.begin(), spans.end(), [](const Span &a, const Span &b) { return a.start > b.start; }); - for (const auto &s: spans) - query.replace(s.start, s.length, QStringLiteral(" ")); + // Remove matched spans from the text (replace with a single space to preserve boundaries) + std::sort(spans.begin(), spans.end(), [](const Span& a, const Span& b) { return a.start > b.start; }); + for (const auto& s : spans) + query.replace(s.start, s.length, QStringLiteral(" ")); - // Normalize whitespace - query = query.simplified(); + // Normalize whitespace + query = query.simplified(); } diff --git a/plugins/warp/ui/shared/misc.h b/plugins/warp/ui/shared/misc.h index 57d71070..9d187594 100644 --- a/plugins/warp/ui/shared/misc.h +++ b/plugins/warp/ui/shared/misc.h @@ -14,103 +14,93 @@ // Used to serialize into the item data for rendering with TokenDataDelegate. struct TokenData { - QVector<BinaryNinja::InstructionTextToken> tokens{}; + QVector<BinaryNinja::InstructionTextToken> tokens {}; - TokenData() = default; + TokenData() = default; - TokenData(const std::string& name); + TokenData(const std::string& name); - TokenData(const BinaryNinja::Type &type, const std::string& name); + TokenData(const BinaryNinja::Type& type, const std::string& name); - TokenData(const std::vector<BinaryNinja::InstructionTextToken> &tokens) - { - for (const auto &token: tokens) - this->tokens.push_back(token); - } + TokenData(const std::vector<BinaryNinja::InstructionTextToken>& tokens) + { + for (const auto& token : tokens) + this->tokens.push_back(token); + } - TokenData(const BinaryNinja::InstructionTextToken &token) - { - this->tokens.push_back(token); - } + TokenData(const BinaryNinja::InstructionTextToken& token) { this->tokens.push_back(token); } - QString toString() const - { - QStringList tokenStrings; - for (const auto &token: tokens) - { - tokenStrings.append(QString::fromStdString(token.text)); - } - return tokenStrings.join(""); - } + QString toString() const + { + QStringList tokenStrings; + for (const auto& token : tokens) + { + tokenStrings.append(QString::fromStdString(token.text)); + } + return tokenStrings.join(""); + } }; Q_DECLARE_METATYPE(TokenData) class TokenDataDelegate final : public QStyledItemDelegate { - Q_OBJECT + Q_OBJECT public: - explicit TokenDataDelegate(QObject *parent = nullptr) : QStyledItemDelegate(parent) - { - } + explicit TokenDataDelegate(QObject* parent = nullptr) : QStyledItemDelegate(parent) {} - void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; - QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; + QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override; }; class AddressColorDelegate final : public QStyledItemDelegate { - Q_OBJECT + Q_OBJECT public: - explicit AddressColorDelegate(QObject *parent = nullptr) : QStyledItemDelegate(parent) - { - } + explicit AddressColorDelegate(QObject* parent = nullptr) : QStyledItemDelegate(parent) {} - void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; }; class GenericTextFilterModel : public QSortFilterProxyModel { - Q_OBJECT + Q_OBJECT public: - GenericTextFilterModel(QObject *parent) : QSortFilterProxyModel(parent) - { - } + GenericTextFilterModel(QObject* parent) : QSortFilterProxyModel(parent) {} - ~GenericTextFilterModel() override = default; + ~GenericTextFilterModel() override = default; - bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; + bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override; - bool lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const override; + bool lessThan(const QModelIndex& sourceLeft, const QModelIndex& sourceRight) const override; }; // Used to parse qualifiers out of a user-supplied string (or "query") struct ParsedQuery { - // The actual query, without the qualifiers like source:<uuid> - QString query; - // The qualifiers used to build other optional parts of the query. - QHash<QString, QString> qualifiers; + // The actual query, without the qualifiers like source:<uuid> + QString query; + // The qualifiers used to build other optional parts of the query. + QHash<QString, QString> qualifiers; - ParsedQuery(QString query, const QHash<QString, QString> &qualifiers) - : query(std::move(query)), qualifiers(qualifiers) - { - } + ParsedQuery(QString query, const QHash<QString, QString>& qualifiers) : + query(std::move(query)), qualifiers(qualifiers) + {} - explicit ParsedQuery(const QString &rawQuery); + explicit ParsedQuery(const QString& rawQuery); - [[nodiscard]] std::optional<QString> GetValue(const QString &key) const - { - const auto it = qualifiers.constFind(key); - if (it == qualifiers.constEnd() || it->isEmpty()) - return std::nullopt; - return it.value(); - } + [[nodiscard]] std::optional<QString> GetValue(const QString& key) const + { + const auto it = qualifiers.constFind(key); + if (it == qualifiers.constEnd() || it->isEmpty()) + return std::nullopt; + return it.value(); + } }; constexpr const char* ALLOWED_TAGS_SETTING = "warp.fetcher.allowedSourceTags"; @@ -118,24 +108,24 @@ constexpr const char* BATCH_SIZE_SETTING = "warp.fetcher.fetchBatchSize"; inline std::vector<Warp::SourceTag> GetAllowedTagsFromView(const BinaryViewRef& view) { - auto settings = BinaryNinja::Settings::Instance(); - if (!settings->Contains(ALLOWED_TAGS_SETTING)) - return {}; - return settings->Get<std::vector<std::string>>(ALLOWED_TAGS_SETTING, view); + auto settings = BinaryNinja::Settings::Instance(); + if (!settings->Contains(ALLOWED_TAGS_SETTING)) + return {}; + return settings->Get<std::vector<std::string>>(ALLOWED_TAGS_SETTING, view); } inline void SetTagsToView(const BinaryViewRef& view, const std::vector<Warp::SourceTag>& tags) { - auto settings = BinaryNinja::Settings::Instance(); - if (!settings->Contains(ALLOWED_TAGS_SETTING)) - return; - settings->Set(ALLOWED_TAGS_SETTING, tags, view); + auto settings = BinaryNinja::Settings::Instance(); + if (!settings->Contains(ALLOWED_TAGS_SETTING)) + return; + settings->Set(ALLOWED_TAGS_SETTING, tags, view); } inline int GetBatchSizeFromView(const BinaryViewRef& view) { - auto settings = BinaryNinja::Settings::Instance(); - if (!settings->Contains(BATCH_SIZE_SETTING)) - return 100; - return settings->Get<uint64_t>(BATCH_SIZE_SETTING, view); + auto settings = BinaryNinja::Settings::Instance(); + if (!settings->Contains(BATCH_SIZE_SETTING)) + return 100; + return settings->Get<uint64_t>(BATCH_SIZE_SETTING, view); }
\ No newline at end of file diff --git a/plugins/warp/ui/shared/search.cpp b/plugins/warp/ui/shared/search.cpp index 1431e9b6..a852605e 100644 --- a/plugins/warp/ui/shared/search.cpp +++ b/plugins/warp/ui/shared/search.cpp @@ -2,300 +2,312 @@ #include "misc.h" #include "../../../../../ui/mainwindow.h" -QVariant WarpSearchModel::data(const QModelIndex &index, int role) const +QVariant WarpSearchModel::data(const QModelIndex& index, int role) const { - if (!index.isValid() || index.row() < 0 || index.row() >= m_items.size()) - return {}; - const auto &it = m_items[index.row()]; + if (!index.isValid() || index.row() < 0 || index.row() >= m_items.size()) + return {}; + const auto& it = m_items[index.row()]; - // Provide tokenized type for functions via UserRole for TokenDataDelegate - if (role == Qt::UserRole && index.column() == DisplayCol) - { - if (it && it->GetKind() == WARPContainerSearchItemKindFunction) - if (auto itemType = it->GetType(nullptr)) - return QVariant::fromValue(TokenData(*itemType, it->GetName())); - return {}; - } + // Provide tokenized type for functions via UserRole for TokenDataDelegate + if (role == Qt::UserRole && index.column() == DisplayCol) + { + if (it && it->GetKind() == WARPContainerSearchItemKindFunction) + if (auto itemType = it->GetType(nullptr)) + return QVariant::fromValue(TokenData(*itemType, it->GetName())); + return {}; + } - if (role == Qt::DisplayRole) - { - // TODO: We might want to run the demangler here on the name. - switch (index.column()) - { - case DisplayCol: return QString::fromStdString(it->GetName()); - case KindCol: - { - switch (it->GetKind()) - { - case WARPContainerSearchItemKindFunction: return "Function"; - case WARPContainerSearchItemKindType: return "Type"; - case WARPContainerSearchItemKindSource: return "Source"; - case WARPContainerSearchItemKindSymbol: return "Symbol"; - default: return {}; - } - } - case SourceCol: return QString::fromStdString(it->GetSource().ToString()); - default: return {}; - } - } - return {}; + if (role == Qt::DisplayRole) + { + // TODO: We might want to run the demangler here on the name. + switch (index.column()) + { + case DisplayCol: + return QString::fromStdString(it->GetName()); + case KindCol: + { + switch (it->GetKind()) + { + case WARPContainerSearchItemKindFunction: + return "Function"; + case WARPContainerSearchItemKindType: + return "Type"; + case WARPContainerSearchItemKindSource: + return "Source"; + case WARPContainerSearchItemKindSymbol: + return "Symbol"; + default: + return {}; + } + } + case SourceCol: + return QString::fromStdString(it->GetSource().ToString()); + default: + return {}; + } + } + return {}; } QVariant WarpSearchModel::headerData(int section, Qt::Orientation orientation, int role) const { - if (orientation == Qt::Horizontal && role == Qt::DisplayRole) - { - switch (section) - { - case DisplayCol: return "Item"; - case KindCol: return "Kind"; - case SourceCol: return "Source"; - default: return {}; - } - } - return {}; + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) + { + switch (section) + { + case DisplayCol: + return "Item"; + case KindCol: + return "Kind"; + case SourceCol: + return "Source"; + default: + return {}; + } + } + return {}; } -void WarpSearchDelegate::paint(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const +void WarpSearchDelegate::paint(QPainter* p, const QStyleOptionViewItem& option, const QModelIndex& index) const { - // TODO: We actually still want the function icon, I think? So this should not early return and instead replace the name. - // If model provided TokenData (e.g., for function types), render via TokenDataDelegate - if (index.data(Qt::UserRole).canConvert<TokenData>()) - { - TokenDataDelegate(parent()).paint(p, option, index); - return; - } + // TODO: We actually still want the function icon, I think? So this should not early return and instead replace the + // name. If model provided TokenData (e.g., for function types), render via TokenDataDelegate + if (index.data(Qt::UserRole).canConvert<TokenData>()) + { + TokenDataDelegate(parent()).paint(p, option, index); + return; + } - QStyleOptionViewItem opt(option); - initStyleOption(&opt, index); + QStyleOptionViewItem opt(option); + initStyleOption(&opt, index); - // We will draw the background/selection via style, and custom-draw icon (left) + text. - const QWidget *w = option.widget; - QStyle *style = w ? w->style() : QApplication::style(); + // We will draw the background/selection via style, and custom-draw icon (left) + text. + const QWidget* w = option.widget; + QStyle* style = w ? w->style() : QApplication::style(); - // Let style draw the item background/selection etc., but not the default text - QString originalText = opt.text; - opt.text.clear(); - style->drawControl(QStyle::CE_ItemViewItem, &opt, p, w); + // Let style draw the item background/selection etc., but not the default text + QString originalText = opt.text; + opt.text.clear(); + style->drawControl(QStyle::CE_ItemViewItem, &opt, p, w); - // Retrieve text for DisplayCol - const QString &text = originalText; + // Retrieve text for DisplayCol + const QString& text = originalText; - // Retrieve kind text from the hidden KindCol - const QModelIndex kindIndex = index.sibling(index.row(), WarpSearchModel::KindCol); - const QString kind = kindIndex.isValid() ? kindIndex.data(Qt::DisplayRole).toString() : QString(); + // Retrieve kind text from the hidden KindCol + const QModelIndex kindIndex = index.sibling(index.row(), WarpSearchModel::KindCol); + const QString kind = kindIndex.isValid() ? kindIndex.data(Qt::DisplayRole).toString() : QString(); - // Map kind -> icon (emoji for simplicity; replace with QIcon/QPixmap if desired) - const QString icon = kindToIcon(kind); + // Map kind -> icon (emoji for simplicity; replace with QIcon/QPixmap if desired) + const QString icon = kindToIcon(kind); - // Layout rects - const QRect r = opt.rect; - const int marginH = 8; - const int iconSide = 16; - const int spacing = 8; + // Layout rects + const QRect r = opt.rect; + const int marginH = 8; + const int iconSide = 16; + const int spacing = 8; - // Reserve space on the left for the icon if we have one - QRect iconRect = QRect(r.left() + marginH, r.center().y() - iconSide / 2, iconSide, iconSide); - QRect textRect = r.adjusted((icon.isEmpty() ? marginH : (marginH + iconSide + spacing)), 0, -marginH, 0); + // Reserve space on the left for the icon if we have one + QRect iconRect = QRect(r.left() + marginH, r.center().y() - iconSide / 2, iconSide, iconSide); + QRect textRect = r.adjusted((icon.isEmpty() ? marginH : (marginH + iconSide + spacing)), 0, -marginH, 0); - // Draw icon first (without clipping), then text with clipping - p->save(); - if (!icon.isEmpty()) - { - QFont iconFont = opt.font; - iconFont.setPointSizeF(iconFont.pointSizeF() + 2); - p->setFont(iconFont); - p->setPen( - opt.palette.color(opt.state & QStyle::State_Selected ? QPalette::HighlightedText : QPalette::Text)); - p->drawText(iconRect, Qt::AlignCenter, icon); - } - p->restore(); + // Draw icon first (without clipping), then text with clipping + p->save(); + if (!icon.isEmpty()) + { + QFont iconFont = opt.font; + iconFont.setPointSizeF(iconFont.pointSizeF() + 2); + p->setFont(iconFont); + p->setPen(opt.palette.color(opt.state & QStyle::State_Selected ? QPalette::HighlightedText : QPalette::Text)); + p->drawText(iconRect, Qt::AlignCenter, icon); + } + p->restore(); - // Draw elided text, vertically centered (with clipping so long strings don't overflow) - p->save(); - p->setClipRect(textRect); - const QFontMetrics fm(opt.font); - const QString elided = fm.elidedText(text, Qt::ElideRight, textRect.width()); - p->setPen(opt.palette.color(opt.state & QStyle::State_Selected ? QPalette::HighlightedText : QPalette::Text)); - p->drawText(textRect, Qt::AlignVCenter | Qt::AlignLeft, elided); - p->restore(); + // Draw elided text, vertically centered (with clipping so long strings don't overflow) + p->save(); + p->setClipRect(textRect); + const QFontMetrics fm(opt.font); + const QString elided = fm.elidedText(text, Qt::ElideRight, textRect.width()); + p->setPen(opt.palette.color(opt.state & QStyle::State_Selected ? QPalette::HighlightedText : QPalette::Text)); + p->drawText(textRect, Qt::AlignVCenter | Qt::AlignLeft, elided); + p->restore(); } void WarpSearchRunner::fetchPage(std::size_t offset, std::size_t limit) { - const auto session = currentSession(); - auto container = m_container; - auto q = m_queryText; - auto src = m_source; + const auto session = currentSession(); + auto container = m_container; + auto q = m_queryText; + auto src = m_source; - // Runs search on Qt threadpool then moves the result to the main thread to update the UI. - using TaskResult = std::pair<std::optional<Warp::ContainerSearchResponse>, QString>; - QFutureWatcher<TaskResult> *watcher = new QFutureWatcher<TaskResult>(this); - const auto future = QtConcurrent::run([container, q, src, offset, limit]() -> TaskResult { - const Warp::ContainerSearchQuery query(q.toStdString(), offset, limit, src); - auto respOpt = container->Search(query); - // TODO: We may want to provide better errors in the future, e.g. BNWARPGetError - if (!respOpt.has_value()) - return {std::nullopt, QStringLiteral("No response from container")}; - return {std::move(respOpt), QString()}; - }); - connect(watcher, &QFutureWatcher<TaskResult>::finished, this, [this, watcher, session]() { - const TaskResult result = watcher->result(); - watcher->deleteLater(); - if (const auto &err = result.second; !err.isEmpty()) - emit pageError(session, err); - else if (const auto &resp = result.first; resp.has_value()) - emit pageReady(session, *resp); - }); - watcher->setFuture(future); + // Runs search on Qt threadpool then moves the result to the main thread to update the UI. + using TaskResult = std::pair<std::optional<Warp::ContainerSearchResponse>, QString>; + QFutureWatcher<TaskResult>* watcher = new QFutureWatcher<TaskResult>(this); + const auto future = QtConcurrent::run([container, q, src, offset, limit]() -> TaskResult { + const Warp::ContainerSearchQuery query(q.toStdString(), offset, limit, src); + auto respOpt = container->Search(query); + // TODO: We may want to provide better errors in the future, e.g. BNWARPGetError + if (!respOpt.has_value()) + return {std::nullopt, QStringLiteral("No response from container")}; + return {std::move(respOpt), QString()}; + }); + connect(watcher, &QFutureWatcher<TaskResult>::finished, this, [this, watcher, session]() { + const TaskResult result = watcher->result(); + watcher->deleteLater(); + if (const auto& err = result.second; !err.isEmpty()) + emit pageError(session, err); + else if (const auto& resp = result.first; resp.has_value()) + emit pageReady(session, *resp); + }); + watcher->setFuture(future); } -WarpSearchWidget::WarpSearchWidget(Warp::Ref<Warp::Container> container, QWidget *parent) : QWidget(parent) +WarpSearchWidget::WarpSearchWidget(Warp::Ref<Warp::Container> container, QWidget* parent) : QWidget(parent) { - m_model = new WarpSearchModel(this); - m_runner = new WarpSearchRunner(container, this); - auto *layout = new QVBoxLayout(this); - m_query = new QLineEdit(this); - m_status = new QLabel(this); - m_view = new QTableView(this); + m_model = new WarpSearchModel(this); + m_runner = new WarpSearchRunner(container, this); + auto* layout = new QVBoxLayout(this); + m_query = new QLineEdit(this); + m_status = new QLabel(this); + m_view = new QTableView(this); - // TODO: I am not necessarily a fan with how this looks. - m_query->setPlaceholderText("Search… (Optionally filter with source:<uuid>)"); + // TODO: I am not necessarily a fan with how this looks. + m_query->setPlaceholderText("Search… (Optionally filter with source:<uuid>)"); - // This is where we make the table not look like a table but instead a list. - m_view->setShowGrid(false); - m_view->verticalHeader()->setVisible(false); - m_view->horizontalHeader()->setVisible(false); - m_view->setSelectionBehavior(QAbstractItemView::SelectRows); - m_view->setSelectionMode(QAbstractItemView::SingleSelection); - m_view->setEditTriggers(QAbstractItemView::NoEditTriggers); - m_view->setFocusPolicy(Qt::NoFocus); + // This is where we make the table not look like a table but instead a list. + m_view->setShowGrid(false); + m_view->verticalHeader()->setVisible(false); + m_view->horizontalHeader()->setVisible(false); + m_view->setSelectionBehavior(QAbstractItemView::SelectRows); + m_view->setSelectionMode(QAbstractItemView::SingleSelection); + m_view->setEditTriggers(QAbstractItemView::NoEditTriggers); + m_view->setFocusPolicy(Qt::NoFocus); - layout->addWidget(m_query); - layout->addWidget(m_status); - layout->addWidget(m_view); + layout->addWidget(m_query); + layout->addWidget(m_status); + layout->addWidget(m_view); - m_view->setModel(m_model); + m_view->setModel(m_model); - // Use a delegate to draw the DisplayCol text with an icon (optionally) aligned to the left. - m_view->setItemDelegateForColumn(WarpSearchModel::DisplayCol, new WarpSearchDelegate(m_view)); + // Use a delegate to draw the DisplayCol text with an icon (optionally) aligned to the left. + m_view->setItemDelegateForColumn(WarpSearchModel::DisplayCol, new WarpSearchDelegate(m_view)); - // Hide extra columns: only show the combined "Item" column; do not display source GUID. - m_view->setColumnHidden(WarpSearchModel::KindCol, true); - m_view->setColumnHidden(WarpSearchModel::SourceCol, true); - m_view->horizontalHeader()->setSectionResizeMode(WarpSearchModel::DisplayCol, QHeaderView::Stretch); + // Hide extra columns: only show the combined "Item" column; do not display source GUID. + m_view->setColumnHidden(WarpSearchModel::KindCol, true); + m_view->setColumnHidden(WarpSearchModel::SourceCol, true); + m_view->horizontalHeader()->setSectionResizeMode(WarpSearchModel::DisplayCol, QHeaderView::Stretch); - // Debounce user input - m_debounce.setSingleShot(true); - m_debounce.setInterval(SEARCH_DEBOUNCE_MS); - connect(m_query, &QLineEdit::textChanged, this, [this](const QString &) { m_debounce.start(); }); + // Debounce user input + m_debounce.setSingleShot(true); + m_debounce.setInterval(SEARCH_DEBOUNCE_MS); + connect(m_query, &QLineEdit::textChanged, this, [this](const QString&) { m_debounce.start(); }); - connect(&m_debounce, &QTimer::timeout, this, [this]() { - const QString raw = m_query->text(); + connect(&m_debounce, &QTimer::timeout, this, [this]() { + const QString raw = m_query->text(); - // Parse generic qualifiers and clean the free-text query - const auto parsed = ParsedQuery(raw); + // Parse generic qualifiers and clean the free-text query + const auto parsed = ParsedQuery(raw); - std::optional<Warp::Source> inlineSource = std::nullopt; - if (const auto val = parsed.GetValue("source"); val.has_value()) - inlineSource = Warp::Source::FromString(val.value().toStdString()); - const auto effectiveSource = inlineSource.has_value() ? inlineSource : m_source; - // TODO: Filter for tags and function guid. + std::optional<Warp::Source> inlineSource = std::nullopt; + if (const auto val = parsed.GetValue("source"); val.has_value()) + inlineSource = Warp::Source::FromString(val.value().toStdString()); + const auto effectiveSource = inlineSource.has_value() ? inlineSource : m_source; + // TODO: Filter for tags and function guid. - m_runner->startSession(parsed.query, effectiveSource); - m_model->beginNewSearch(SEARCH_PAGE_SIZE); - }); + m_runner->startSession(parsed.query, effectiveSource); + m_model->beginNewSearch(SEARCH_PAGE_SIZE); + }); - // Infinite scroll trigger - connect(m_model, &WarpSearchModel::fetchMoreRequested, this, [this](std::size_t offset, std::size_t limit) { - m_status->setText(QStringLiteral("Loading… (%1/%2)").arg(m_model->currentCount()).arg(m_model->total())); - m_runner->fetchPage(offset, limit); - }); + // Infinite scroll trigger + connect(m_model, &WarpSearchModel::fetchMoreRequested, this, [this](std::size_t offset, std::size_t limit) { + m_status->setText(QStringLiteral("Loading… (%1/%2)").arg(m_model->currentCount()).arg(m_model->total())); + m_runner->fetchPage(offset, limit); + }); - // Append results if session still valid - connect(m_runner, &WarpSearchRunner::pageReady, this, - [this](std::uint64_t, const Warp::ContainerSearchResponse &resp) { - m_model->appendResponse(resp); - m_status->setText( - QStringLiteral("Showing %1 of %2").arg(m_model->currentCount()).arg(m_model->total())); - }); + // Append results if session still valid + connect( + m_runner, &WarpSearchRunner::pageReady, this, [this](std::uint64_t, const Warp::ContainerSearchResponse& resp) { + m_model->appendResponse(resp); + m_status->setText(QStringLiteral("Showing %1 of %2").arg(m_model->currentCount()).arg(m_model->total())); + }); - // In cases like if the network container server goes down. - connect(m_runner, &WarpSearchRunner::pageError, this, - [this](std::uint64_t, const QString &msg) { - m_status->setText(QStringLiteral("Error: %1").arg(msg)); - }); + // In cases like if the network container server goes down. + connect(m_runner, &WarpSearchRunner::pageError, this, [this](std::uint64_t, const QString& msg) { + m_status->setText(QStringLiteral("Error: %1").arg(msg)); + }); - // Add a context menu so that we can actually do actionable things with what we find. - m_view->setContextMenuPolicy(Qt::CustomContextMenu); - connect(m_view, &QTableView::customContextMenuRequested, this, [this](const QPoint &pos) { - const QModelIndex idx = m_view->indexAt(pos); - if (!idx.isValid()) - return; + // Add a context menu so that we can actually do actionable things with what we find. + m_view->setContextMenuPolicy(Qt::CustomContextMenu); + connect(m_view, &QTableView::customContextMenuRequested, this, [this](const QPoint& pos) { + const QModelIndex idx = m_view->indexAt(pos); + if (!idx.isValid()) + return; - // TODO: Getting the current view here is really awful, but i dont care right now. - auto ctx = MainWindow::activeContext(); - auto view = ctx->getCurrentView(); - auto binaryView = view->getData(); - auto viewFrame = ctx->getCurrentViewFrame(); - auto viewLocation = viewFrame->getViewLocation(); - auto func = viewLocation.getFunction(); + // TODO: Getting the current view here is really awful, but i dont care right now. + auto ctx = MainWindow::activeContext(); + auto view = ctx->getCurrentView(); + auto binaryView = view->getData(); + auto viewFrame = ctx->getCurrentViewFrame(); + auto viewLocation = viewFrame->getViewLocation(); + auto func = viewLocation.getFunction(); - // Retrieve the current architecture from the current function or try the current view. - auto arch = binaryView->GetDefaultArchitecture(); - if (func) - arch = func->GetArchitecture(); + // Retrieve the current architecture from the current function or try the current view. + auto arch = binaryView->GetDefaultArchitecture(); + if (func) + arch = func->GetArchitecture(); - const int row = idx.row(); - const auto item = m_model->itemAt(row); - const auto itemType = item->GetType(arch); - const auto itemFunc = item->GetFunction(); + const int row = idx.row(); + const auto item = m_model->itemAt(row); + const auto itemType = item->GetType(arch); + const auto itemFunc = item->GetFunction(); - QMenu menu(this); - QAction *copySourceId = menu.addAction(tr("Copy Source")); - QAction *searchSource = menu.addAction(tr("Search Source")); - QAction *applyType = menu.addAction(tr("Apply Type")); - QAction *applyFunction = menu.addAction(tr("Apply to Current Function")); + QMenu menu(this); + QAction* copySourceId = menu.addAction(tr("Copy Source")); + QAction* searchSource = menu.addAction(tr("Search Source")); + QAction* applyType = menu.addAction(tr("Apply Type")); + QAction* applyFunction = menu.addAction(tr("Apply to Current Function")); - // We let users apply the type for types and functions (assuming the function has one) - // if the user applies a function, we actually will set the user type for the current view location function. - // For types, we will just throw it in the user types. - applyType->setEnabled(itemType != nullptr); - applyType->setVisible(applyType->isEnabled()); + // We let users apply the type for types and functions (assuming the function has one) + // if the user applies a function, we actually will set the user type for the current view location function. + // For types, we will just throw it in the user types. + applyType->setEnabled(itemType != nullptr); + applyType->setVisible(applyType->isEnabled()); - applyFunction->setEnabled(func != nullptr && itemFunc); - applyFunction->setVisible(applyFunction->isEnabled()); + applyFunction->setEnabled(func != nullptr && itemFunc); + applyFunction->setVisible(applyFunction->isEnabled()); - QAction *chosen = menu.exec(m_view->viewport()->mapToGlobal(pos)); - if (!chosen) - return; - if (chosen == copySourceId) - QApplication::clipboard()->setText(QString::fromStdString(item->GetSource().ToString())); - else if (chosen == searchSource) - { - const QString sourceId = QString::fromStdString(item->GetSource().ToString()); - // TODO: This does not preserve the query that existed prior, doing so may result in duplicate source qualifiers. - m_query->setText(QStringLiteral("source:\"%1\"").arg(sourceId)); - } else if (chosen == applyType) - { - if (func && item->GetKind() == WARPContainerSearchItemKindFunction) - { - func->SetUserType(itemType); - binaryView->UpdateAnalysis(); - } - else - binaryView->DefineUserType(item->GetName(), itemType); - } else if (chosen == applyFunction) - { - itemFunc->Apply(*func); - binaryView->UpdateAnalysis(); - } - }); + QAction* chosen = menu.exec(m_view->viewport()->mapToGlobal(pos)); + if (!chosen) + return; + if (chosen == copySourceId) + QApplication::clipboard()->setText(QString::fromStdString(item->GetSource().ToString())); + else if (chosen == searchSource) + { + const QString sourceId = QString::fromStdString(item->GetSource().ToString()); + // TODO: This does not preserve the query that existed prior, doing so may result in duplicate source + // qualifiers. + m_query->setText(QStringLiteral("source:\"%1\"").arg(sourceId)); + } + else if (chosen == applyType) + { + if (func && item->GetKind() == WARPContainerSearchItemKindFunction) + { + func->SetUserType(itemType); + binaryView->UpdateAnalysis(); + } + else + binaryView->DefineUserType(item->GetName(), itemType); + } + else if (chosen == applyFunction) + { + itemFunc->Apply(*func); + binaryView->UpdateAnalysis(); + } + }); - // This will start searching with an empty query once the widget is constructed. This is a decent behavior considering - // we should be heavily caching queries such as an empty one. - m_debounce.start(); + // This will start searching with an empty query once the widget is constructed. This is a decent behavior + // considering we should be heavily caching queries such as an empty one. + m_debounce.start(); } diff --git a/plugins/warp/ui/shared/search.h b/plugins/warp/ui/shared/search.h index 86c90942..8510edcc 100644 --- a/plugins/warp/ui/shared/search.h +++ b/plugins/warp/ui/shared/search.h @@ -29,206 +29,201 @@ constexpr auto SEARCH_DEBOUNCE_MS = 350; // Table model showing a paginated list of SearchItem. class WarpSearchModel final : public QAbstractTableModel { - Q_OBJECT + Q_OBJECT public: - enum Columns : int - { - DisplayCol = 0, - KindCol, - SourceCol, - ColumnCount - }; + enum Columns : int + { + DisplayCol = 0, + KindCol, + SourceCol, + ColumnCount + }; - explicit WarpSearchModel(QObject *parent = nullptr) - : QAbstractTableModel(parent) - { - } + explicit WarpSearchModel(QObject* parent = nullptr) : QAbstractTableModel(parent) {} - int rowCount(const QModelIndex &parent) const override - { - if (parent.isValid()) return 0; - return m_items.size(); - } + int rowCount(const QModelIndex& parent) const override + { + if (parent.isValid()) + return 0; + return m_items.size(); + } - int columnCount(const QModelIndex &parent) const override - { - Q_UNUSED(parent); - return ColumnCount; - } + int columnCount(const QModelIndex& parent) const override + { + Q_UNUSED(parent); + return ColumnCount; + } - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; - QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; - // Begin a new search session: clears all current items and resets counters. - Q_INVOKABLE void beginNewSearch(std::size_t pageSize = SEARCH_PAGE_SIZE) - { - beginResetModel(); - m_items.clear(); - m_total = 0; - m_pageSize = pageSize; - endResetModel(); - emit cleared(); - // Immediately ask for the first page from offset 0 - emit fetchMoreRequested(0, m_pageSize); - } + // Begin a new search session: clears all current items and resets counters. + Q_INVOKABLE void beginNewSearch(std::size_t pageSize = SEARCH_PAGE_SIZE) + { + beginResetModel(); + m_items.clear(); + m_total = 0; + m_pageSize = pageSize; + endResetModel(); + emit cleared(); + // Immediately ask for the first page from offset 0 + emit fetchMoreRequested(0, m_pageSize); + } - // Append results from a Warp::ContainerSearchResponse (infinite scroll, no replacement). - void appendResponse(const Warp::ContainerSearchResponse &resp) - { - // Update total first (so canFetchMore reflects new value) - m_total = resp.total; - if (resp.items.empty()) - { - emit responseUpdated(currentCount(), m_total); - return; - } - const int begin = m_items.size(); - const int end = begin + static_cast<int>(resp.items.size()) - 1; - beginInsertRows(QModelIndex(), begin, end); - for (const auto &refItem: resp.items) - m_items.push_back(refItem); - endInsertRows(); - emit responseUpdated(currentCount(), m_total); - } + // Append results from a Warp::ContainerSearchResponse (infinite scroll, no replacement). + void appendResponse(const Warp::ContainerSearchResponse& resp) + { + // Update total first (so canFetchMore reflects new value) + m_total = resp.total; + if (resp.items.empty()) + { + emit responseUpdated(currentCount(), m_total); + return; + } + const int begin = m_items.size(); + const int end = begin + static_cast<int>(resp.items.size()) - 1; + beginInsertRows(QModelIndex(), begin, end); + for (const auto& refItem : resp.items) + m_items.push_back(refItem); + endInsertRows(); + emit responseUpdated(currentCount(), m_total); + } - // Qt's infinite scroll hooks: the view will call these when near the end. - bool canFetchMore(const QModelIndex &parent) const override - { - Q_UNUSED(parent); - return static_cast<std::size_t>(m_items.size()) < m_total; - } + // Qt's infinite scroll hooks: the view will call these when near the end. + bool canFetchMore(const QModelIndex& parent) const override + { + Q_UNUSED(parent); + return static_cast<std::size_t>(m_items.size()) < m_total; + } - void fetchMore(const QModelIndex &parent) override - { - Q_UNUSED(parent); - if (!canFetchMore({})) - return; - const std::size_t nextOffset = m_items.size(); - emit fetchMoreRequested(nextOffset, m_pageSize); - } + void fetchMore(const QModelIndex& parent) override + { + Q_UNUSED(parent); + if (!canFetchMore({})) + return; + const std::size_t nextOffset = m_items.size(); + emit fetchMoreRequested(nextOffset, m_pageSize); + } - // Convenience accessors. - std::size_t total() const { return m_total; } - int currentCount() const { return m_items.size(); } - void setPageSize(std::size_t pageSize) { m_pageSize = pageSize; } + // Convenience accessors. + std::size_t total() const { return m_total; } + int currentCount() const { return m_items.size(); } + void setPageSize(std::size_t pageSize) { m_pageSize = pageSize; } - Q_INVOKABLE Warp::Ref<Warp::ContainerSearchItem> itemAt(int row) const - { - if (row < 0 || row >= m_items.size()) - return {}; - return m_items[row]; - } + Q_INVOKABLE Warp::Ref<Warp::ContainerSearchItem> itemAt(int row) const + { + if (row < 0 || row >= m_items.size()) + return {}; + return m_items[row]; + } signals: - // Emitted when model is cleared for a new query. - void cleared(); + // Emitted when model is cleared for a new query. + void cleared(); - // Emitted after items are appended or totals updated. - void responseUpdated(int currentCount, std::size_t total); + // Emitted after items are appended or totals updated. + void responseUpdated(int currentCount, std::size_t total); - // Request the next page; connect this to your async search runner. - void fetchMoreRequested(std::size_t offset, std::size_t limit); + // Request the next page; connect this to your async search runner. + void fetchMoreRequested(std::size_t offset, std::size_t limit); private: - QVector<Warp::Ref<Warp::ContainerSearchItem> > m_items; - std::size_t m_total = 0; - std::size_t m_pageSize = SEARCH_PAGE_SIZE; + QVector<Warp::Ref<Warp::ContainerSearchItem>> m_items; + std::size_t m_total = 0; + std::size_t m_pageSize = SEARCH_PAGE_SIZE; }; // A delegate to render the DisplayCol text with an icon (mapped from KindCol) on the right. class WarpSearchDelegate final : public QStyledItemDelegate { - Q_OBJECT + Q_OBJECT public: - explicit WarpSearchDelegate(QObject *parent = nullptr) : QStyledItemDelegate(parent) - { - } + explicit WarpSearchDelegate(QObject* parent = nullptr) : QStyledItemDelegate(parent) {} - QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override - { - // Base text size + space for an icon on the right - const QSize base = QStyledItemDelegate::sizeHint(option, index); - const int iconSide = 16; - return QSize(base.width(), qMax(base.height(), iconSide + 4)); // keep row height >= icon - } + QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override + { + // Base text size + space for an icon on the right + const QSize base = QStyledItemDelegate::sizeHint(option, index); + const int iconSide = 16; + return QSize(base.width(), qMax(base.height(), iconSide + 4)); // keep row height >= icon + } - void paint(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + void paint(QPainter* p, const QStyleOptionViewItem& option, const QModelIndex& index) const override; private: - static QString kindToIcon(const QString &kind) - { - // Example mapping; extend as needed - if (kind.compare(QStringLiteral("Function"), Qt::CaseInsensitive) == 0) - return QStringLiteral("ƒ"); - if (kind.compare(QStringLiteral("Type"), Qt::CaseInsensitive) == 0) - return QStringLiteral("𝑇"); - if (kind.compare(QStringLiteral("Source"), Qt::CaseInsensitive) == 0) - return QStringLiteral("📦"); - // Default: no icon - return {}; - } + static QString kindToIcon(const QString& kind) + { + // Example mapping; extend as needed + if (kind.compare(QStringLiteral("Function"), Qt::CaseInsensitive) == 0) + return QStringLiteral("ƒ"); + if (kind.compare(QStringLiteral("Type"), Qt::CaseInsensitive) == 0) + return QStringLiteral("𝑇"); + if (kind.compare(QStringLiteral("Source"), Qt::CaseInsensitive) == 0) + return QStringLiteral("📦"); + // Default: no icon + return {}; + } }; class WarpSearchRunner : public QObject { - Q_OBJECT + Q_OBJECT public: - explicit WarpSearchRunner(Warp::Ref<Warp::Container> container, QObject *parent = nullptr) - : QObject(parent), m_container(std::move(container)) - { - } + explicit WarpSearchRunner(Warp::Ref<Warp::Container> container, QObject* parent = nullptr) : + QObject(parent), m_container(std::move(container)) + {} signals: - // Emitted on the UI thread after the worker finishes - void pageReady(std::uint64_t sessionId, Warp::ContainerSearchResponse resp); + // Emitted on the UI thread after the worker finishes + void pageReady(std::uint64_t sessionId, Warp::ContainerSearchResponse resp); - void pageError(std::uint64_t sessionId, QString message); + void pageError(std::uint64_t sessionId, QString message); public slots: - // Start a new logical search session (new query or new filters) - void startSession(QString queryText, const std::optional<Warp::Source> &source) - { - m_queryText = std::move(queryText); - m_source = source; - m_sessionId.fetchAndAddRelaxed(1); - } + // Start a new logical search session (new query or new filters) + void startSession(QString queryText, const std::optional<Warp::Source>& source) + { + m_queryText = std::move(queryText); + m_source = source; + m_sessionId.fetchAndAddRelaxed(1); + } - // Fetch a page for the current session. Safe to call multiple times (infinite scroll). - void fetchPage(std::size_t offset, std::size_t limit); + // Fetch a page for the current session. Safe to call multiple times (infinite scroll). + void fetchPage(std::size_t offset, std::size_t limit); private: - std::uint64_t currentSession() const { return m_sessionId.loadRelaxed(); } + std::uint64_t currentSession() const { return m_sessionId.loadRelaxed(); } - Warp::Ref<Warp::Container> m_container; - QAtomicInteger<quint64> m_sessionId{0}; - QString m_queryText; - std::optional<Warp::Source> m_source; + Warp::Ref<Warp::Container> m_container; + QAtomicInteger<quint64> m_sessionId {0}; + QString m_queryText; + std::optional<Warp::Source> m_source; }; class WarpSearchWidget : public QWidget { - Q_OBJECT + Q_OBJECT public: - explicit WarpSearchWidget(Warp::Ref<Warp::Container> container, QWidget *parent = nullptr); + explicit WarpSearchWidget(Warp::Ref<Warp::Container> container, QWidget* parent = nullptr); - void setSourceFilter(const std::optional<Warp::Source> &src) - { - m_source = src; - m_debounce.start(); - } + void setSourceFilter(const std::optional<Warp::Source>& src) + { + m_source = src; + m_debounce.start(); + } private: - WarpSearchModel *m_model; - WarpSearchRunner *m_runner; - QLineEdit *m_query; - QLabel *m_status; - QTableView *m_view; - QTimer m_debounce; - // Source to filter for if no source is provided in the search. - std::optional<Warp::Source> m_source; + WarpSearchModel* m_model; + WarpSearchRunner* m_runner; + QLineEdit* m_query; + QLabel* m_status; + QTableView* m_view; + QTimer m_debounce; + // Source to filter for if no source is provided in the search. + std::optional<Warp::Source> m_source; }; |
