diff options
| author | Josh Ferrell <josh@vector35.com> | 2026-01-26 14:23:12 -0500 |
|---|---|---|
| committer | Josh Ferrell <josh@vector35.com> | 2026-02-06 17:53:45 -0500 |
| commit | 70bbb18a9444824a8ec2b9a7ff57fc848e017b6e (patch) | |
| tree | fe87c0976b75a7557986d1ac6bcc2409a839624c | |
| parent | 9b7604d13e8bd98c6998facb9326926ed96281b5 (diff) | |
Add regex and case sensitivity options to FilterEdit
32 files changed, 252 insertions, 114 deletions
diff --git a/examples/triage/entry.cpp b/examples/triage/entry.cpp index addb5960..dfee4a2f 100644 --- a/examples/triage/entry.cpp +++ b/examples/triage/entry.cpp @@ -141,13 +141,14 @@ void GenericEntryModel::sort(int col, Qt::SortOrder order) } -void GenericEntryModel::setFilter(const std::string& filterText) +void GenericEntryModel::setFilter(const std::string& filterText, FilterOptions options) { beginResetModel(); m_entries.clear(); + bool caseSensitive = options.testFlag(FilterOption::CaseSensitiveOption); for (auto& entry : m_allEntries) { - if (FilteredView::match(entry->GetSymbol()->GetFullName(), filterText)) + if (FilteredView::match(entry->GetSymbol()->GetFullName(), filterText, caseSensitive)) m_entries.push_back(entry); } performSort(m_sortCol, m_sortOrder); @@ -250,9 +251,9 @@ void EntryTreeView::entryDoubleClicked(const QModelIndex& cur) } -void EntryTreeView::setFilter(const std::string& filterText) +void EntryTreeView::setFilter(const std::string& filterText, FilterOptions options) { - m_model->setFilter(filterText); + m_model->setFilter(filterText, options); } diff --git a/examples/triage/entry.h b/examples/triage/entry.h index a1396741..013e6793 100644 --- a/examples/triage/entry.h +++ b/examples/triage/entry.h @@ -24,7 +24,7 @@ class GenericEntryModel : public QAbstractItemModel virtual QModelIndex index(int row, int col, const QModelIndex& parent) const override; virtual QModelIndex parent(const QModelIndex& index) const override; virtual void sort(int col, Qt::SortOrder order) override; - void setFilter(const std::string& filterText); + void setFilter(const std::string& filterText, FilterOptions options); FunctionRef getEntry(const QModelIndex& index); }; @@ -46,7 +46,7 @@ class EntryTreeView : public QTreeView, public FilterTarget void copySelection(); bool canCopySelection() const; - virtual void setFilter(const std::string& filterText) override; + virtual void setFilter(const std::string& filterText, FilterOptions options) override; virtual void scrollToFirstItem() override; virtual void scrollToCurrentItem() override; virtual void ensureSelection() override; diff --git a/examples/triage/exports.cpp b/examples/triage/exports.cpp index 776044c5..976be45e 100644 --- a/examples/triage/exports.cpp +++ b/examples/triage/exports.cpp @@ -67,7 +67,7 @@ void GenericExportsModel::updateModel() } endResetModel(); - setFilter(m_filter); + setFilter(m_filter, m_filterOptions); } @@ -225,22 +225,26 @@ void GenericExportsModel::sort(int col, Qt::SortOrder order) } -void GenericExportsModel::setFilter(const std::string& filterText) +void GenericExportsModel::setFilter(const std::string& filterText, FilterOptions options) { m_filter = filterText; + m_filterOptions = options; beginResetModel(); m_entries.clear(); + + bool caseSensitive = options.testFlag(FilterOption::CaseSensitiveOption); for (auto& entry : m_allEntries) { - if (FilteredView::match(entry->GetFullName(), filterText)) + if (FilteredView::match(entry->GetFullName(), filterText, caseSensitive)) m_entries.push_back(entry); - else if (FilteredView::match(std::to_string(entry->GetOrdinal()), filterText)) + else if (FilteredView::match(std::to_string(entry->GetOrdinal()), filterText, caseSensitive)) m_entries.push_back(entry); } performSort(m_sortCol, m_sortOrder); endResetModel(); } + void GenericExportsModel::setNeedsUpdate(bool needed) { if (m_needsUpdate.exchange(needed) == needed) @@ -249,6 +253,7 @@ void GenericExportsModel::setNeedsUpdate(bool needed) updateTimer(needed); } + void GenericExportsModel::updateTimer(bool needsUpdate) { if (needsUpdate && !m_updateTimer->isActive()) @@ -427,9 +432,9 @@ void ExportsTreeView::exportDoubleClicked(const QModelIndex& cur) } -void ExportsTreeView::setFilter(const std::string& filterText) +void ExportsTreeView::setFilter(const std::string& filterText, FilterOptions options) { - m_model->setFilter(filterText); + m_model->setFilter(filterText, options); } diff --git a/examples/triage/exports.h b/examples/triage/exports.h index af0f4613..28c9d53a 100644 --- a/examples/triage/exports.h +++ b/examples/triage/exports.h @@ -13,6 +13,7 @@ class GenericExportsModel : public QAbstractItemModel, public BinaryNinja::Binar BinaryViewRef m_data; std::vector<SymbolRef> m_allEntries, m_entries; std::string m_filter; + FilterOptions m_filterOptions; QTimer* m_updateTimer; Qt::SortOrder m_sortOrder; int m_sortCol; @@ -46,7 +47,7 @@ signals: virtual QModelIndex index(int row, int col, const QModelIndex& parent) const override; virtual QModelIndex parent(const QModelIndex& index) const override; virtual void sort(int col, Qt::SortOrder order) override; - void setFilter(const std::string& filterText); + void setFilter(const std::string& filterText, FilterOptions options); void pauseUpdates(); void resumeUpdates(); @@ -79,7 +80,7 @@ class ExportsTreeView : public QTreeView, public FilterTarget void copySelection(); bool canCopySelection() const; - virtual void setFilter(const std::string& filterText) override; + virtual void setFilter(const std::string& filterText, FilterOptions options) override; virtual void scrollToFirstItem() override; virtual void scrollToCurrentItem() override; virtual void ensureSelection() override; diff --git a/examples/triage/imports.cpp b/examples/triage/imports.cpp index 19c6a62b..54b20bd2 100644 --- a/examples/triage/imports.cpp +++ b/examples/triage/imports.cpp @@ -205,19 +205,20 @@ void GenericImportsModel::sort(int col, Qt::SortOrder order) } -void GenericImportsModel::setFilter(const std::string& filterText) +void GenericImportsModel::setFilter(const std::string& filterText, FilterOptions options) { beginResetModel(); m_entries.clear(); + bool caseSensitive = options.testFlag(FilterOption::CaseSensitiveOption); for (auto& entry : m_allEntries) { - if (FilteredView::match(entry->GetFullName(), filterText)) + if (FilteredView::match(entry->GetFullName(), filterText, caseSensitive)) m_entries.push_back(entry); - else if (FilteredView::match(getNamespace(entry).toStdString(), filterText)) + else if (FilteredView::match(getNamespace(entry).toStdString(), filterText, caseSensitive)) m_entries.push_back(entry); - else if (FilteredView::match(std::to_string(entry->GetOrdinal()), filterText)) + else if (FilteredView::match(std::to_string(entry->GetOrdinal()), filterText, caseSensitive)) m_entries.push_back(entry); - else if (FilteredView::match(getLibrarySource(entry).toStdString(), filterText)) + else if (FilteredView::match(getLibrarySource(entry).toStdString(), filterText, caseSensitive)) m_entries.push_back(entry); } performSort(m_sortCol, m_sortOrder); @@ -325,9 +326,9 @@ void ImportsTreeView::importDoubleClicked(const QModelIndex& cur) } -void ImportsTreeView::setFilter(const std::string& filterText) +void ImportsTreeView::setFilter(const std::string& filterText, FilterOptions options) { - m_model->setFilter(filterText); + m_model->setFilter(filterText, options); } diff --git a/examples/triage/imports.h b/examples/triage/imports.h index 8a2441f7..dde9b06d 100644 --- a/examples/triage/imports.h +++ b/examples/triage/imports.h @@ -28,7 +28,7 @@ class GenericImportsModel : public QAbstractItemModel virtual QModelIndex index(int row, int col, const QModelIndex& parent) const override; virtual QModelIndex parent(const QModelIndex& index) const override; virtual void sort(int col, Qt::SortOrder order) override; - void setFilter(const std::string& filterText); + void setFilter(const std::string& filterText, FilterOptions options); SymbolRef getSymbol(const QModelIndex& index); @@ -55,7 +55,7 @@ class ImportsTreeView : public QTreeView, public FilterTarget void copySelection(); bool canCopySelection() const; - virtual void setFilter(const std::string& filterText) override; + virtual void setFilter(const std::string& filterText, FilterOptions options) override; virtual void scrollToFirstItem() override; virtual void scrollToCurrentItem() override; virtual void ensureSelection() override; diff --git a/examples/triage/strings.cpp b/examples/triage/strings.cpp index 32d9ff97..9ae176a0 100644 --- a/examples/triage/strings.cpp +++ b/examples/triage/strings.cpp @@ -115,16 +115,16 @@ QString GenericStringsModel::stringRefToQString(const BNStringReference& stringR BinaryNinja::DataBuffer stringBuffer = m_data->ReadBuffer(stringRef.start, stringRef.length); if (stringRef.type == BNStringType::Utf32String) - { + { char32_t* data = (char32_t*)stringBuffer.GetData(); qstr = QString::fromUcs4(data, stringRef.length / 4); - } + } else if (stringRef.type == BNStringType::Utf16String) { char16_t* data = (char16_t*)stringBuffer.GetData(); qstr = QString::fromUtf16(data, stringRef.length / 2); } - else + else { char* data = (char*)stringBuffer.GetData(); qstr = QString::fromUtf8(data, stringBuffer.GetLength()); @@ -159,7 +159,7 @@ void GenericStringsModel::performSort(int col, Qt::SortOrder order) return a.length > b.length; } else if (col == 2) - { + { QString s = stringRefToQString(a); QString s2 = stringRefToQString(b); @@ -188,18 +188,31 @@ void GenericStringsModel::applyFilter() m_entries.clear(); for (auto& entry : m_allEntries) { - auto s = stringRefToQString(entry).toStdString(); + auto s = stringRefToQString(entry); + + bool match; + if (m_filterOptions.testFlag(UseRegexOption)) + { + match = m_filterRegex.match(s).hasMatch(); + } + else + { + match = s.contains(m_filter, m_filterOptions.testFlag(CaseSensitiveOption) ? Qt::CaseSensitive : Qt::CaseInsensitive); + } - if (FilteredView::match(s, m_filter)) + if (match) m_entries.push_back(entry); } performSort(m_sortCol, m_sortOrder); } -void GenericStringsModel::setFilter(const std::string& filterText) +void GenericStringsModel::setFilter(const QString& filterText, FilterOptions options) { m_filter = filterText; + m_filterOptions = options; + bool caseSensitive = options.testFlag(CaseSensitiveOption); + m_filterRegex = QRegularExpression(filterText, caseSensitive ? QRegularExpression::NoPatternOption : QRegularExpression::CaseInsensitiveOption); beginResetModel(); applyFilter(); endResetModel(); @@ -412,9 +425,9 @@ void StringsTreeView::stringDoubleClicked(const QModelIndex& cur) } -void StringsTreeView::setFilter(const std::string& filterText) +void StringsTreeView::setFilter(const std::string& filterText, FilterOptions options) { - m_model->setFilter(filterText); + m_model->setFilter(QString::fromStdString(filterText), options); } diff --git a/examples/triage/strings.h b/examples/triage/strings.h index 31617f08..2a28c4f1 100644 --- a/examples/triage/strings.h +++ b/examples/triage/strings.h @@ -14,9 +14,12 @@ class GenericStringsModel : public QAbstractItemModel, public BinaryNinja::Binar std::vector<BNStringReference> m_allEntries, m_entries; int m_totalCols, m_sortCol; Qt::SortOrder m_sortOrder; - std::string m_filter; QTimer* m_updateTimer; + QString m_filter; + FilterOptions m_filterOptions; + QRegularExpression m_filterRegex; + // Read from arbitrary threads while processing notifications. std::atomic<bool> m_updatesPaused = false; // Read/written from arbitrary threads while processing notifications. @@ -46,7 +49,8 @@ signals: virtual QModelIndex index(int row, int col, const QModelIndex& parent) const override; virtual QModelIndex parent(const QModelIndex& index) const override; virtual void sort(int col, Qt::SortOrder order) override; - void setFilter(const std::string& filterText); + + void setFilter(const QString& filterText, FilterOptions options); void pauseUpdates(); void resumeUpdates(); @@ -81,7 +85,7 @@ class StringsTreeView : public QTreeView, public FilterTarget void copySelection(); bool canCopySelection() const; - virtual void setFilter(const std::string& filterText) override; + virtual void setFilter(const std::string& filterText, FilterOptions options) override; virtual void scrollToFirstItem() override; virtual void scrollToCurrentItem() override; virtual void ensureSelection() override; diff --git a/plugins/warp/ui/matches.cpp b/plugins/warp/ui/matches.cpp index 4b8f0495..289c1102 100644 --- a/plugins/warp/ui/matches.cpp +++ b/plugins/warp/ui/matches.cpp @@ -78,7 +78,7 @@ WarpCurrentFunctionWidget::WarpCurrentFunctionWidget() "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->setFilter(source->ToString(), NoFilterOption); }); connect(m_tableWidget->GetTableView(), &QTableView::clicked, this, [this](const QModelIndex& index) { diff --git a/plugins/warp/ui/shared/constraint.cpp b/plugins/warp/ui/shared/constraint.cpp index 39c9c7d3..fcebbfb0 100644 --- a/plugins/warp/ui/shared/constraint.cpp +++ b/plugins/warp/ui/shared/constraint.cpp @@ -124,8 +124,10 @@ void WarpConstraintTableWidget::SetMatchedConstraints(const std::vector<Warp::Co m_model->SetMatchedConstraints(analysisConstraints); } -void WarpConstraintTableWidget::setFilter(const std::string& filter) +void WarpConstraintTableWidget::setFilter(const std::string& filter, FilterOptions options) { m_proxyModel->setFilterFixedString(QString::fromStdString(filter)); + m_proxyModel->setFilterCaseSensitivity( + options.testFlag(CaseSensitiveOption) ? Qt::CaseSensitive : Qt::CaseInsensitive); m_filterView->showFilter(QString::fromStdString(filter)); } diff --git a/plugins/warp/ui/shared/constraint.h b/plugins/warp/ui/shared/constraint.h index 02702144..632c39ce 100644 --- a/plugins/warp/ui/shared/constraint.h +++ b/plugins/warp/ui/shared/constraint.h @@ -58,7 +58,7 @@ public: void SetMatchedConstraints(const std::vector<Warp::Constraint>& analysisConstraints); - void setFilter(const std::string&) override; + void setFilter(const std::string&, FilterOptions options) override; void scrollToFirstItem() override {} diff --git a/plugins/warp/ui/shared/function.cpp b/plugins/warp/ui/shared/function.cpp index 06d4cbcf..3b2451ab 100644 --- a/plugins/warp/ui/shared/function.cpp +++ b/plugins/warp/ui/shared/function.cpp @@ -299,9 +299,11 @@ void WarpFunctionTableWidget::RemoveFunction(uint64_t address) m_model->RemoveFunction(address); } -void WarpFunctionTableWidget::setFilter(const std::string& filter) +void WarpFunctionTableWidget::setFilter(const std::string& filter, FilterOptions options) { m_proxyModel->setFilterFixedString(QString::fromStdString(filter)); + m_proxyModel->setFilterCaseSensitivity( + options.testFlag(CaseSensitiveOption) ? Qt::CaseSensitive : Qt::CaseInsensitive); m_filterView->showFilter(QString::fromStdString(filter)); } diff --git a/plugins/warp/ui/shared/function.h b/plugins/warp/ui/shared/function.h index 23b2bd3b..c6e5ba63 100644 --- a/plugins/warp/ui/shared/function.h +++ b/plugins/warp/ui/shared/function.h @@ -123,7 +123,7 @@ public: void RemoveFunction(uint64_t address); - void setFilter(const std::string&) override; + void setFilter(const std::string&, FilterOptions options) override; void scrollToFirstItem() override {} diff --git a/ui/bndbimportdialog.h b/ui/bndbimportdialog.h index eb8f0dfd..4578b76c 100644 --- a/ui/bndbimportdialog.h +++ b/ui/bndbimportdialog.h @@ -117,7 +117,7 @@ public: BndbImportDialog(QWidget* parent, BinaryViewRef view); ~BndbImportDialog() = default; - void setFilter(const std::string& filter) override; + void setFilter(const std::string& filter, FilterOptions options) override; void scrollToFirstItem() override; void scrollToCurrentItem() override; void ensureSelection() override; diff --git a/ui/filter.h b/ui/filter.h index 98417f65..3178d4b8 100644 --- a/ui/filter.h +++ b/ui/filter.h @@ -14,12 +14,25 @@ \ingroup filter */ +enum BINARYNINJAUIAPI FilterOption { + NoFilterOption = 0, + CaseSensitiveOption = 1, + UseRegexOption = 2, +}; + +Q_DECLARE_FLAGS(FilterOptions, FilterOption) + + +/*! + + \ingroup filter +*/ class BINARYNINJAUIAPI FilterTarget { public: virtual ~FilterTarget() {} - virtual void setFilter(const std::string& filter) = 0; + virtual void setFilter(const std::string& filter, FilterOptions options) = 0; virtual void scrollToFirstItem() = 0; virtual void scrollToCurrentItem() = 0; @@ -44,10 +57,27 @@ class BINARYNINJAUIAPI FilterEdit : public QLineEdit Q_OBJECT FilterTarget* m_target; + FilterOptions m_filterOptions; + + QAction* m_clearAction; + QAction* m_regexAction; + QAction* m_regexWarningAction; + QAction* m_caseSensitivityAction; + + QIcon getActionIcon(const QString& iconName, bool enabled) const; public: FilterEdit(FilterTarget* target); + void showRegexToggle(bool enabled); + + void setRegexValidationError(const QString& error); + + FilterOptions getFilterOptions() const { return m_filterOptions; } + + Q_SIGNALS: + void optionsChanged(FilterOptions options); + protected: virtual void paintEvent(QPaintEvent* event) override; virtual void keyPressEvent(QKeyEvent* event) override; @@ -76,7 +106,7 @@ class BINARYNINJAUIAPI FilteredView : public QWidget void focusAndSelectFilter(); bool hasFilterText() const; - static bool match(const std::string& name, const std::string& filter); + static bool match(const std::string& name, const std::string& filter, bool caseSensitive = false); private Q_SLOTS: void timerStart(); diff --git a/ui/logview.h b/ui/logview.h index 0aaf9372..c2488ed5 100644 --- a/ui/logview.h +++ b/ui/logview.h @@ -268,7 +268,7 @@ class BINARYNINJAUIAPI LogView : public SidebarWidget, public FilterTarget void focus() override; LogListModel* model() { return m_listModel; } - void setFilter(const std::string& filter) override; + void setFilter(const std::string& filter, FilterOptions options) override; LoggingScope getScope() const { return m_model->getScope(); } void setScope(LoggingScope scope) { m_model->setScope(scope); } diff --git a/ui/metadatachoicedialog.h b/ui/metadatachoicedialog.h index 12de4a55..79cf662f 100644 --- a/ui/metadatachoicedialog.h +++ b/ui/metadatachoicedialog.h @@ -67,6 +67,7 @@ class BINARYNINJAUIAPI ManagedTableView : public QTableView, public FilterTarget ManagedTableModel* m_model; std::string m_filter; + FilterOptions m_filterOptions; ManagedTableDelegate* m_delegate; @@ -78,7 +79,7 @@ public: int getCurrentSelection(); void forceFilterUpdate() { - setFilter(m_filter); + setFilter(m_filter, m_filterOptions); }; void resetModel(); @@ -88,7 +89,7 @@ public: virtual void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) override; - virtual void setFilter(const std::string& filter) override; + virtual void setFilter(const std::string& filter, FilterOptions options) override; virtual void scrollToFirstItem() override; virtual void scrollToCurrentItem() override; virtual void ensureSelection() override; diff --git a/ui/projectbrowser.h b/ui/projectbrowser.h index 11e14058..348d5ff0 100644 --- a/ui/projectbrowser.h +++ b/ui/projectbrowser.h @@ -173,11 +173,14 @@ class BINARYNINJAUIAPI ProjectTree: public QTreeView, public FilterTarget QSet<QString> m_expandedIds; + FilterOptions m_filterOptions; + std::string m_filter; + virtual void scrollToFirstItem() override; virtual void scrollToCurrentItem() override; virtual void ensureSelection() override; virtual void activateSelection() override; - virtual void setFilter(const std::string& filter) override; + virtual void setFilter(const std::string& filter, FilterOptions options) override; protected: void mousePressEvent(QMouseEvent* event) override; @@ -188,7 +191,7 @@ public: ProjectTree(QWidget* parent = nullptr); Q_SIGNALS: - void filterChanged(const QString& filter); + void filterChanged(const QString& filter, FilterOptions options); }; @@ -196,11 +199,15 @@ class BINARYNINJAUIAPI RecentsList: public QListWidget, public FilterTarget { Q_OBJECT + QString m_filter; + FilterOptions m_filterOptions; + virtual void scrollToFirstItem() override; virtual void scrollToCurrentItem() override; virtual void ensureSelection() override; virtual void activateSelection() override; - virtual void setFilter(const std::string& filter) override; + void applyFilter(); + virtual void setFilter(const std::string& filter, FilterOptions options) override; public: @@ -333,7 +340,7 @@ private slots: void itemSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected); void itemChanged(QStandardItem* item); void handleItemsDropped(Qt::DropAction action, const QList<QString> fileIds, const QList<QString> folderIds, const QList<QUrl> newUrls, ProjectFolderRef newParentFolder); - void onTreeFilterChanged(const QString& filter); + void onTreeFilterChanged(const QString& filter, FilterOptions options); protected: void SelectItems(std::vector<ProjectFileRef> files, std::vector<ProjectFolderRef> folders); diff --git a/ui/searchresult.h b/ui/searchresult.h index 13fa77f9..42b79252 100644 --- a/ui/searchresult.h +++ b/ui/searchresult.h @@ -313,15 +313,14 @@ class BINARYNINJAUIAPI SearchResultHeaderWidget : public QWidget, public FilterT { Q_OBJECT - QLineEdit* m_search; + FilterEdit* m_search; QComboBox* m_mode; - ClickableIcon* m_caseSensitive; ClickableIcon* m_ignoreWhitespace; public: SearchResultHeaderWidget(); - virtual void setFilter(const std::string&) override {} + virtual void setFilter(const std::string&, FilterOptions) override {} virtual void scrollToFirstItem() override {} virtual void scrollToCurrentItem() override {} virtual void ensureSelection() override {} diff --git a/ui/stringsview.h b/ui/stringsview.h index 4be86d60..b3ced9ec 100644 --- a/ui/stringsview.h +++ b/ui/stringsview.h @@ -68,7 +68,10 @@ class BINARYNINJAUIAPI StringsListModel : public QAbstractItemModel, public Bina std::vector<StringsListItem> m_strings; std::map<uint64_t, uint64_t> m_refCounts; std::map<BinaryNinja::DerivedString, uint64_t> m_derivedRefCounts; - std::string m_filter; + + QString m_filter; + FilterOptions m_filterOptions; + QRegularExpression m_filterRegex; // This holds a compiled regex for m_filter size_t m_filteredByOptions; @@ -116,9 +119,9 @@ class BINARYNINJAUIAPI StringsListModel : public QAbstractItemModel, public Bina virtual void sort(int col, Qt::SortOrder order) override; - void setFilter(const std::string& filter); + void setFilter(const QString& filter, FilterOptions options); - void updateFilter() { setFilter(m_filter); }; + void updateFilter() { setFilter(m_filter, m_filterOptions); }; size_t getFilteredStringCount() const { return m_filteredByOptions; } size_t getStringCount() const { return m_strings.size(); } @@ -199,7 +202,7 @@ class BINARYNINJAUIAPI StringsView : public QTableView, public View, public Filt virtual void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) override; - virtual void setFilter(const std::string& filter) override; + virtual void setFilter(const std::string& filter, FilterOptions options) override; virtual void scrollToFirstItem() override; virtual void scrollToCurrentItem() override; virtual void ensureSelection() override; @@ -242,7 +245,7 @@ class BINARYNINJAUIAPI StringsContainer : public QWidget, public ViewContainer friend class StringsView; StringsView* m_strings; - FilteredView* m_filter; + FilteredView* m_filteredView; FilterEdit* m_separateEdit = nullptr; StringsViewSidebarWidget* m_widget; UIActionHandler m_actionHandler; @@ -252,7 +255,7 @@ class BINARYNINJAUIAPI StringsContainer : public QWidget, public ViewContainer virtual View* getView() override { return m_strings; } StringsView* getStringsView() { return m_strings; } - FilteredView* getFilter() { return m_filter; } + FilteredView* getFilter() { return m_filteredView; } FilterEdit* getSeparateFilterEdit() { return m_separateEdit; } protected: diff --git a/ui/symbollist.h b/ui/symbollist.h index 1bc4a6dd..6f196ddf 100644 --- a/ui/symbollist.h +++ b/ui/symbollist.h @@ -229,6 +229,7 @@ class BINARYNINJAUIAPI SymbolListModel : public QAbstractItemModel, public Binar std::unique_ptr<std::deque<NamedObject>> m_oldCurSyms; NamedObject m_currentSym; std::string m_filter; + FilterOptions m_filterOptions; int m_maxWidth = 32; std::mutex m_updateMutex; @@ -290,7 +291,7 @@ class BINARYNINJAUIAPI SymbolListModel : public QAbstractItemModel, public Binar bool hasSymbols() const; int getMaximumWidth() const { return m_maxWidth; } - void setFilter(const std::string& filter); + void setFilter(const std::string& filter, FilterOptions options); void showExportedDataVars(bool show) { m_showExportedDataVars = show; } void showExportedFunctions(bool show) { m_showExportedFunctions = show; } void showLocalFunctions(bool show) { m_showLocalFunctions = show; } @@ -348,6 +349,7 @@ class BINARYNINJAUIAPI SymbolList : public QListView, public FilterTarget bool m_showLocalDataVars; bool m_showImports; std::string m_filter; + FilterOptions m_filterOptions; SymbolListModel::NamedObject m_index; SymbolListModel::NamedObject m_topIndex; @@ -361,7 +363,7 @@ class BINARYNINJAUIAPI SymbolList : public QListView, public FilterTarget virtual void scrollToCurrentItem() override; virtual void ensureSelection() override; virtual void activateSelection() override; - virtual void setFilter(const std::string& filter) override; + virtual void setFilter(const std::string& filter, FilterOptions options) override; bool hasSymbols(); diff --git a/ui/taglist.h b/ui/taglist.h index 1f1e09cb..e19d899a 100644 --- a/ui/taglist.h +++ b/ui/taglist.h @@ -143,8 +143,9 @@ class BINARYNINJAUIAPI TagList : public QTreeView, public FilterTarget typedef std::function<bool(const BinaryNinja::TagReference&)> FilterFn; private: - bool m_hasFilter; - FilterFn m_filter; + std::optional<FilterFn> m_filterFn; + + FilterOptions m_searchFilterOptions; std::string m_searchFilter; QTimer* m_hoverTimer; @@ -161,7 +162,7 @@ class BINARYNINJAUIAPI TagList : public QTreeView, public FilterTarget virtual void resizeEvent(QResizeEvent* event) override; void goToReference(const QModelIndex& idx); - void setFilter(const std::string& filter) override; + void setFilter(const std::string& filter, FilterOptions options) override; private Q_SLOTS: void hoverTimerEvent(); @@ -186,7 +187,7 @@ class BINARYNINJAUIAPI TagList : public QTreeView, public FilterTarget void filterTagReferences(std::vector<BinaryNinja::TagReference>& refs); void clearFilter(); - void setFilter(FilterFn filter); + void setFilterFunction(FilterFn filter); void setFilterView(FilteredView* filterView) { m_filterView = filterView; } bool hasSelection(); @@ -255,6 +256,7 @@ class BINARYNINJAUIAPI TagListDialog : public QDialog private: BinaryViewRef m_data; TagList* m_list; + FilterEdit* m_filterEdit; FilteredView* m_filter; AddFn m_addFn; @@ -263,7 +265,7 @@ class BINARYNINJAUIAPI TagListDialog : public QDialog public: TagListDialog(QWidget* parent, ViewFrame* frame, BinaryViewRef data, AddFn addFn); - void setFilter(TagList::FilterFn filter); + void setFilterFunction(TagList::FilterFn filter); private Q_SLOTS: void updateActive(const QItemSelection&, const QItemSelection&); diff --git a/ui/typebrowser.h b/ui/typebrowser.h index 60eb0c30..6cd2ce89 100644 --- a/ui/typebrowser.h +++ b/ui/typebrowser.h @@ -65,7 +65,7 @@ public: virtual std::string text(int column) const = 0; virtual bool lessThan(const TypeBrowserTreeNode& other, int column) const = 0; - virtual bool filter(const std::string& filter, TypeBrowserFilterMode mode) const = 0; + virtual bool filter(const std::string& filter, TypeBrowserFilterMode mode, bool caseSensitive) const = 0; virtual void updateChildren(bool recursive, UpdateNodeCallback update); }; @@ -78,7 +78,7 @@ public: virtual std::string text(int column) const override; virtual bool lessThan(const TypeBrowserTreeNode& other, int column) const override; - virtual bool filter(const std::string& filter, TypeBrowserFilterMode mode) const override; + virtual bool filter(const std::string& filter, TypeBrowserFilterMode mode, bool caseSensitive) const override; protected: virtual void generateChildren() override; @@ -96,7 +96,7 @@ public: virtual std::string text(int column) const override; virtual bool lessThan(const TypeBrowserTreeNode& other, int column) const override; - virtual bool filter(const std::string& filter, TypeBrowserFilterMode mode) const override; + virtual bool filter(const std::string& filter, TypeBrowserFilterMode mode, bool caseSensitive) const override; protected: virtual void generateChildren() override; @@ -148,7 +148,7 @@ public: virtual std::string text(int column) const override; virtual bool lessThan(const TypeBrowserTreeNode& other, int column) const override; - virtual bool filter(const std::string& filter, TypeBrowserFilterMode mode) const override; + virtual bool filter(const std::string& filter, TypeBrowserFilterMode mode, bool caseSensitive) const override; protected: virtual void generateChildren() override; @@ -166,7 +166,7 @@ public: virtual ~TypeContainerTreeNode(); virtual std::string text(int column) const override; - virtual bool filter(const std::string& filter, TypeBrowserFilterMode mode) const override; + virtual bool filter(const std::string& filter, TypeBrowserFilterMode mode, bool caseSensitive) const override; virtual bool lessThan(const TypeBrowserTreeNode& other, int column) const override; const std::string& containerId() const { return m_containerId; } @@ -293,7 +293,7 @@ public: std::shared_ptr<TypeBrowserTreeNode> nodeForIndex(const QModelIndex& index) const; QModelIndex indexForNode(std::shared_ptr<TypeBrowserTreeNode> node, int column = 0) const; - bool filter(const QModelIndex& index, const std::string& filter, TypeBrowserFilterMode mode) const; + bool filter(const QModelIndex& index, const std::string& filter, TypeBrowserFilterMode mode, bool caseSensitive) const; bool lessThan(const QModelIndex& left, const QModelIndex& right) const; void OnTypeDefined(BinaryNinja::BinaryView* data, const BinaryNinja::QualifiedName& name, BinaryNinja::Type* type) override; @@ -326,7 +326,9 @@ class BINARYNINJAUIAPI TypeBrowserFilterModel : public QSortFilterProxyModel Q_OBJECT BinaryViewRef m_data; TypeBrowserModel* m_model; + std::string m_originalFilter; std::string m_filter; + bool m_filterCaseSensitive = false; TypeBrowserFilterMode m_filterMode; bool m_exactOnTop; @@ -337,7 +339,7 @@ protected: public: TypeBrowserFilterModel(BinaryViewRef data, TypeBrowserModel* model, QObject* parent); - void setFilter(const std::string& filter); + void setFilter(const std::string& filter, FilterOptions options); TypeBrowserFilterMode filterMode() const { return m_filterMode; } void setFilterMode(TypeBrowserFilterMode newMode) { m_filterMode = newMode; } @@ -435,7 +437,7 @@ public: virtual StatusBarWidget* getStatusBarWidget() override; virtual QWidget* getHeaderOptionsWidget() override; - virtual void setFilter(const std::string& filter) override; + virtual void setFilter(const std::string& filter, FilterOptions options) override; virtual void scrollToFirstItem() override; virtual void scrollToCurrentItem() override; virtual void ensureSelection() override; diff --git a/ui/variablelist.h b/ui/variablelist.h index 43e6b65c..923919ff 100644 --- a/ui/variablelist.h +++ b/ui/variablelist.h @@ -254,7 +254,7 @@ class BINARYNINJAUIAPI VariableList : public SidebarWidget, public FilterTarget //! Copy selected variables to clipboard void copy(); - virtual void setFilter(const std::string& filter) override; + virtual void setFilter(const std::string& filter, FilterOptions options) override; virtual void scrollToFirstItem() override; virtual void scrollToCurrentItem() override; virtual void ensureSelection() override; diff --git a/view/kernelcache/ui/kctriage.cpp b/view/kernelcache/ui/kctriage.cpp index 6dd13067..e13ebdcf 100644 --- a/view/kernelcache/ui/kctriage.cpp +++ b/view/kernelcache/ui/kctriage.cpp @@ -256,8 +256,8 @@ QWidget* KCTriageView::initImageTable() auto loadImageFilterEdit = new FilterEdit(m_imageTable); loadImageFilterEdit->setPlaceholderText("Filter images"); - connect(loadImageFilterEdit, &FilterEdit::textChanged, [this](const QString& filter) { - m_imageTable->setFilter(filter.toStdString()); + connect(loadImageFilterEdit, &FilterEdit::textChanged, [this, loadImageFilterEdit](const QString& filter) { + m_imageTable->setFilter(filter.toStdString(), loadImageFilterEdit->getFilterOptions()); }); connect(m_imageTable, &FilterableTableView::activated, this, [=, this](const QModelIndex& index) { @@ -310,8 +310,8 @@ void KCTriageView::initSymbolTable() auto symbolFilterEdit = new FilterEdit(m_symbolTable); symbolFilterEdit->setPlaceholderText("Filter symbols"); - connect(symbolFilterEdit, &FilterEdit::textChanged, [this](const QString& filter) { - m_symbolTable->setFilter(filter.toStdString()); + connect(symbolFilterEdit, &FilterEdit::textChanged, [this, symbolFilterEdit](const QString& filter) { + m_symbolTable->setFilter(filter.toStdString(), symbolFilterEdit->getFilterOptions()); }); auto loadSymbolImageButton = new QPushButton(); diff --git a/view/kernelcache/ui/kctriage.h b/view/kernelcache/ui/kctriage.h index c69c6a0a..f73fd264 100644 --- a/view/kernelcache/ui/kctriage.h +++ b/view/kernelcache/ui/kctriage.h @@ -102,7 +102,9 @@ public: class FilterableTableView : public QTableView, public FilterTarget { Q_OBJECT + std::string m_filter; bool m_filterByHiding; + FilterOptions m_filterOptions; public: explicit FilterableTableView(QWidget* parent = nullptr, bool filterByHiding = true) @@ -113,19 +115,24 @@ public: ~FilterableTableView() override = default; - void setFilter(const std::string& filter) override { + void setFilter(const std::string& filter, FilterOptions options) override { + m_filter = filter; + m_filterOptions = options; + QString qFilter = QString::fromStdString(m_filter); + if (!m_filterByHiding) { - emit filterTextChanged(QString::fromStdString(filter)); + emit filterTextChanged(qFilter); return; } - QString qFilter = QString::fromStdString(filter); + + bool caseSensitive = options.testFlag(CaseSensitiveOption); for (int row = 0; row < model()->rowCount(); ++row) { bool match = false; for (int col = 0; col < model()->columnCount(); ++col) { QModelIndex index = model()->index(row, col); QString data = model()->data(index).toString(); - if (data.contains(qFilter, Qt::CaseInsensitive)) { + if (data.contains(qFilter, caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive)) { match = true; break; } diff --git a/view/kernelcache/ui/symboltable.cpp b/view/kernelcache/ui/symboltable.cpp index 90d0d18b..c3c45b8a 100644 --- a/view/kernelcache/ui/symboltable.cpp +++ b/view/kernelcache/ui/symboltable.cpp @@ -128,7 +128,7 @@ void SymbolTableModel::sort(int column, Qt::SortOrder order) void SymbolTableModel::updateSymbols(std::vector<CacheSymbol>&& symbols) { m_preparedSymbols = symbols; - setFilter(m_filter); + setFilter(m_filter, m_filterOptions); } @@ -138,20 +138,42 @@ const CacheSymbol& SymbolTableModel::symbolAt(int row) const } -void SymbolTableModel::setFilter(std::string text) +void SymbolTableModel::setFilter(const std::string& text, FilterOptions options) { - beginResetModel(); - m_filter = text; + m_filterOptions = options; m_modelSymbols = {}; + bool caseSensitive = options.testFlag(CaseSensitiveOption); + beginResetModel(); // Skip filtering if no filter applied. if (!m_filter.empty()) { m_modelSymbols.reserve(m_preparedSymbols.size()); for (const auto& symbol : m_preparedSymbols) - if (((std::string_view)symbol.name).find(m_filter) != std::string::npos) + { + const std::string& symbolName = symbol.name; + bool match; + if (caseSensitive) + { + match = (symbolName.find(m_filter) != std::string::npos); + } + else + { + auto it = std::search( + symbolName.begin(), symbolName.end(), + m_filter.begin(), m_filter.end(), + [](char c1, char c2) { return std::tolower(c1) == std::tolower(c2); } + ); + + match = (it != symbolName.end()); + } + + if (match) + { m_modelSymbols.push_back(symbol); + } + } m_modelSymbols.shrink_to_fit(); } else @@ -163,6 +185,8 @@ void SymbolTableModel::setFilter(std::string text) } + + SymbolTableView::SymbolTableView(QWidget* parent) : QTableView(parent), m_model(new SymbolTableModel(this)) { @@ -215,6 +239,8 @@ void SymbolTableView::populateSymbols(BinaryView &view) } } -void SymbolTableView::setFilter(const std::string& filter) { - m_model->setFilter(filter); + +void SymbolTableView::setFilter(const std::string& filter, FilterOptions options) +{ + m_model->setFilter(filter, options); } diff --git a/view/kernelcache/ui/symboltable.h b/view/kernelcache/ui/symboltable.h index 28d07199..64a20010 100644 --- a/view/kernelcache/ui/symboltable.h +++ b/view/kernelcache/ui/symboltable.h @@ -19,6 +19,7 @@ Q_OBJECT SymbolTableView* m_parent; QFont m_font; std::string m_filter; + FilterOptions m_filterOptions; std::vector<KernelCacheAPI::CacheSymbol> m_preparedSymbols{}; // These are the symbols we actually use std::vector<KernelCacheAPI::CacheSymbol> m_modelSymbols{}; @@ -32,7 +33,7 @@ public: QVariant headerData(int section, Qt::Orientation orientation, int role) const override; void sort(int column, Qt::SortOrder order) override; void updateSymbols(std::vector<KernelCacheAPI::CacheSymbol>&& symbols); - void setFilter(std::string text); + void setFilter(const std::string& text, FilterOptions options); const KernelCacheAPI::CacheSymbol& symbolAt(int row) const; }; @@ -94,7 +95,7 @@ public: return m_model->symbolAt(row); } - void setFilter(const std::string& filter) override; + void setFilter(const std::string& filter, FilterOptions options) override; }; diff --git a/view/sharedcache/ui/dsctriage.cpp b/view/sharedcache/ui/dsctriage.cpp index 48ad6130..43302534 100644 --- a/view/sharedcache/ui/dsctriage.cpp +++ b/view/sharedcache/ui/dsctriage.cpp @@ -267,8 +267,8 @@ QWidget* DSCTriageView::initImageTable() auto loadImageFilterEdit = new FilterEdit(m_imageTable); loadImageFilterEdit->setPlaceholderText("Filter images"); - connect(loadImageFilterEdit, &FilterEdit::textChanged, [this](const QString& filter) { - m_imageTable->setFilter(filter.toStdString()); + connect(loadImageFilterEdit, &FilterEdit::textChanged, [this, loadImageFilterEdit](const QString& filter) { + m_imageTable->setFilter(filter.toStdString(), loadImageFilterEdit->getFilterOptions()); }); connect(m_imageTable, &FilterableTableView::activated, this, [=, this](const QModelIndex& index) { @@ -321,8 +321,8 @@ void DSCTriageView::initSymbolTable() auto symbolFilterEdit = new FilterEdit(m_symbolTable); symbolFilterEdit->setPlaceholderText("Filter symbols"); - connect(symbolFilterEdit, &FilterEdit::textChanged, [this](const QString& filter) { - m_symbolTable->setFilter(filter.toStdString()); + connect(symbolFilterEdit, &FilterEdit::textChanged, [this, symbolFilterEdit](const QString& filter) { + m_symbolTable->setFilter(filter.toStdString(), symbolFilterEdit->getFilterOptions()); }); auto loadSymbolImageButton = new QPushButton(); @@ -454,8 +454,8 @@ void DSCTriageView::initCacheInfoTables() auto mappingLabel = new QLabel("Mappings"); auto mappingFilterEdit = new FilterEdit(m_mappingTable); mappingFilterEdit->setPlaceholderText("Filter mappings"); - connect(mappingFilterEdit, &FilterEdit::textChanged, [this](const QString& filter) { - m_mappingTable->setFilter(filter.toStdString()); + connect(mappingFilterEdit, &FilterEdit::textChanged, [this, mappingFilterEdit](const QString& filter) { + m_mappingTable->setFilter(filter.toStdString(), mappingFilterEdit->getFilterOptions()); }); auto mappingHeaderLayout = new QHBoxLayout; @@ -467,8 +467,8 @@ void DSCTriageView::initCacheInfoTables() auto regionLabel = new QLabel("Regions"); auto regionFilterEdit = new FilterEdit(m_regionTable); regionFilterEdit->setPlaceholderText("Filter regions"); - connect(regionFilterEdit, &FilterEdit::textChanged, [this](const QString& filter) { - m_regionTable->setFilter(filter.toStdString()); + connect(regionFilterEdit, &FilterEdit::textChanged, [this, regionFilterEdit](const QString& filter) { + m_regionTable->setFilter(filter.toStdString(), regionFilterEdit->getFilterOptions()); }); auto regionHeaderLayout = new QHBoxLayout; diff --git a/view/sharedcache/ui/dsctriage.h b/view/sharedcache/ui/dsctriage.h index 02a41eab..03eea6be 100644 --- a/view/sharedcache/ui/dsctriage.h +++ b/view/sharedcache/ui/dsctriage.h @@ -102,6 +102,8 @@ public: class FilterableTableView : public QTableView, public FilterTarget { Q_OBJECT + std::string m_filter; + FilterOptions m_filterOptions; bool m_filterByHiding; public: @@ -113,19 +115,22 @@ public: ~FilterableTableView() override = default; - void setFilter(const std::string& filter) override { + void setFilter(const std::string& filter, FilterOptions options) override { + m_filter = filter; + m_filterOptions = options; + QString qFilter = QString::fromStdString(m_filter); if (!m_filterByHiding) { - emit filterTextChanged(QString::fromStdString(filter)); + emit filterTextChanged(qFilter); return; } - QString qFilter = QString::fromStdString(filter); + bool caseSensitive = options.testFlag(CaseSensitiveOption); for (int row = 0; row < model()->rowCount(); ++row) { bool match = false; for (int col = 0; col < model()->columnCount(); ++col) { QModelIndex index = model()->index(row, col); QString data = model()->data(index).toString(); - if (data.contains(qFilter, Qt::CaseInsensitive)) { + if (data.contains(qFilter, caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive)) { match = true; break; } diff --git a/view/sharedcache/ui/symboltable.cpp b/view/sharedcache/ui/symboltable.cpp index a876bf61..2a6cb467 100644 --- a/view/sharedcache/ui/symboltable.cpp +++ b/view/sharedcache/ui/symboltable.cpp @@ -127,7 +127,7 @@ void SymbolTableModel::sort(int column, Qt::SortOrder order) void SymbolTableModel::updateSymbols(std::vector<CacheSymbol> symbols) { m_symbols = std::move(symbols); - setFilter(m_filter); + setFilter(m_filter, m_filterOptions); } @@ -137,12 +137,12 @@ const CacheSymbol& SymbolTableModel::symbolAt(int row) const } -void SymbolTableModel::setFilter(std::string text) +void SymbolTableModel::setFilter(const std::string& text, FilterOptions options) { + m_filter = text; + m_filterOptions = options; + bool caseSensitive = options.testFlag(CaseSensitiveOption); beginResetModel(); - - m_filter = std::move(text); - // Skip filtering if no filter applied. if (m_filter.empty()) { @@ -156,8 +156,27 @@ void SymbolTableModel::setFilter(std::string text) for (const auto& symbol : m_symbols) { - if (symbol.name.find(m_filter) != std::string::npos) + const std::string& symbolName = symbol.name; + bool match; + if (caseSensitive) + { + match = (symbolName.find(m_filter) != std::string::npos); + } + else + { + auto it = std::search( + symbolName.begin(), symbolName.end(), + m_filter.begin(), m_filter.end(), + [](char c1, char c2) { return std::tolower(c1) == std::tolower(c2); } + ); + + match = (it != symbolName.end()); + } + + if (match) + { m_filteredSymbols.push_back(symbol); + } } // If the filtered vector is using less than 25% of its capacity, @@ -172,6 +191,8 @@ void SymbolTableModel::setFilter(std::string text) } + + SymbolTableView::SymbolTableView(QWidget* parent) : QTableView(parent), m_model(new SymbolTableModel(this)) { @@ -223,6 +244,8 @@ void SymbolTableView::populateSymbols(BinaryView &view) } } -void SymbolTableView::setFilter(const std::string& filter) { - m_model->setFilter(filter); + +void SymbolTableView::setFilter(const std::string& filter, FilterOptions options) +{ + m_model->setFilter(filter, options); } diff --git a/view/sharedcache/ui/symboltable.h b/view/sharedcache/ui/symboltable.h index ab0174f9..bcaef294 100644 --- a/view/sharedcache/ui/symboltable.h +++ b/view/sharedcache/ui/symboltable.h @@ -19,6 +19,7 @@ Q_OBJECT SymbolTableView* m_parent; QFont m_font; std::string m_filter; + FilterOptions m_filterOptions; std::vector<SharedCacheAPI::CacheSymbol> m_symbols; std::vector<SharedCacheAPI::CacheSymbol> m_filteredSymbols; @@ -36,7 +37,7 @@ public: void sort(int column, Qt::SortOrder order) override; void updateSymbols(std::vector<SharedCacheAPI::CacheSymbol> symbols); - void setFilter(std::string text); + void setFilter(const std::string& text, FilterOptions options); const SharedCacheAPI::CacheSymbol& symbolAt(int row) const; }; @@ -98,7 +99,7 @@ public: return m_model->symbolAt(row); } - void setFilter(const std::string& filter) override; + void setFilter(const std::string& filter, FilterOptions options) override; }; |
