diff options
| -rw-r--r-- | binaryninjacore.h | 10 | ||||
| -rw-r--r-- | examples/triage/byte.cpp | 2 | ||||
| -rw-r--r-- | examples/triage/view.cpp | 3 | ||||
| -rw-r--r-- | python/examples/hellodockwidget.py | 6 | ||||
| -rw-r--r-- | python/examples/hellosidebar.py | 107 | ||||
| -rw-r--r-- | ui/clickablelabel.h | 46 | ||||
| -rw-r--r-- | ui/filter.h | 7 | ||||
| -rw-r--r-- | ui/render.h | 1 | ||||
| -rw-r--r-- | ui/sidebar.h | 408 | ||||
| -rw-r--r-- | ui/stringsview.h | 23 | ||||
| -rw-r--r-- | ui/symbolsview.h | 17 | ||||
| -rw-r--r-- | ui/taglist.h | 17 | ||||
| -rw-r--r-- | ui/typeview.h | 59 | ||||
| -rw-r--r-- | ui/uicontext.h | 21 | ||||
| -rw-r--r-- | ui/viewframe.h | 23 | ||||
| -rw-r--r-- | ui/xreflist.h | 22 |
16 files changed, 738 insertions, 34 deletions
diff --git a/binaryninjacore.h b/binaryninjacore.h index 3f48736d..1037114d 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -1654,7 +1654,15 @@ extern "C" FeatureMapFunctionColor, FeatureMapImportColor, FeatureMapExternColor, - FeatureMapLibraryColor + FeatureMapLibraryColor, + + // Sidebar colors + SidebarBackgroundColor, + SidebarInactiveIconColor, + SidebarActiveIconColor, + SidebarHeaderBackgroundColor, + SidebarHeaderTextColor, + SidebarWidgetBackgroundColor }; // The following edge styles map to QT's Qt::PenStyle enumeration diff --git a/examples/triage/byte.cpp b/examples/triage/byte.cpp index c3a91372..be25368a 100644 --- a/examples/triage/byte.cpp +++ b/examples/triage/byte.cpp @@ -548,6 +548,7 @@ void ByteView::repositionCaret() m_cursorTimer->start(); updateCaret(); UIContext::updateStatus(); + updateCrossReferenceSelection(); } @@ -817,6 +818,7 @@ void ByteView::selectAll() m_cursorAddr = getEnd(); viewport()->update(); UIContext::updateStatus(); + updateCrossReferenceSelection(); } diff --git a/examples/triage/view.cpp b/examples/triage/view.cpp index 417006b2..e69fa0d9 100644 --- a/examples/triage/view.cpp +++ b/examples/triage/view.cpp @@ -135,7 +135,8 @@ void TriageView::setSelectionOffsets(BNAddressRange range) void TriageView::setCurrentOffset(uint64_t offset) { m_currentOffset = offset; - UIContext::updateStatus(true); + UIContext::updateStatus(); + updateCrossReferenceSelection(); } diff --git a/python/examples/hellodockwidget.py b/python/examples/hellodockwidget.py index 6f5f52ea..879b424f 100644 --- a/python/examples/hellodockwidget.py +++ b/python/examples/hellodockwidget.py @@ -31,9 +31,9 @@ # For Dynamic dock widgets, the UI widget operates on a single binary view instance from binaryninjaui import DockHandler, DockContextHandler, UIActionHandler -from PySide2 import QtCore -from PySide2.QtCore import Qt -from PySide2.QtWidgets import QApplication, QHBoxLayout, QVBoxLayout, QLabel, QWidget +from PySide6 import QtCore +from PySide6.QtCore import Qt +from PySide6.QtWidgets import QApplication, QHBoxLayout, QVBoxLayout, QLabel, QWidget instance_id = 0 class HelloDockWidget(QWidget, DockContextHandler): diff --git a/python/examples/hellosidebar.py b/python/examples/hellosidebar.py new file mode 100644 index 00000000..7c490dbb --- /dev/null +++ b/python/examples/hellosidebar.py @@ -0,0 +1,107 @@ +# Copyright (c) 2015-2021 Vector 35 Inc +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +# This is an example UI plugin which demonstrates how to add sidebar widgets to Binary Ninja. +# See .../api/ui/sidebar.h for interface details. + +from binaryninjaui import SidebarWidget, SidebarWidgetType, Sidebar, UIActionHandler +from PySide6 import QtCore +from PySide6.QtCore import Qt, QRectF +from PySide6.QtWidgets import QApplication, QHBoxLayout, QVBoxLayout, QLabel, QWidget +from PySide6.QtGui import QImage, QPixmap, QPainter, QFont, QColor + +instance_id = 0 + +# Sidebar widgets must derive from SidebarWidget, not QWidget. SidebarWidget is a QWidget but +# provides callbacks for sidebar events, and must be created with a title. +class HelloSidebarWidget(SidebarWidget): + def __init__(self, name, frame, data): + global instance_id + SidebarWidget.__init__(self, name) + self.actionHandler = UIActionHandler() + self.actionHandler.setupActionHandler(self) + offset_layout = QHBoxLayout() + offset_layout.addWidget(QLabel("Offset: ")) + self.offset = QLabel(hex(0)) + offset_layout.addWidget(self.offset) + offset_layout.setAlignment(QtCore.Qt.AlignCenter) + datatype_layout = QHBoxLayout() + datatype_layout.addWidget(QLabel("Data Type: ")) + self.datatype = QLabel("") + datatype_layout.addWidget(self.datatype) + datatype_layout.setAlignment(QtCore.Qt.AlignCenter) + layout = QVBoxLayout() + title = QLabel(name, self) + title.setAlignment(QtCore.Qt.AlignCenter) + instance = QLabel("Instance: " + str(instance_id), self) + instance.setAlignment(QtCore.Qt.AlignCenter) + layout.addStretch() + layout.addWidget(title) + layout.addWidget(instance) + layout.addLayout(datatype_layout) + layout.addLayout(offset_layout) + layout.addStretch() + self.setLayout(layout) + instance_id += 1 + self.data = data + + def notifyOffsetChanged(self, offset): + self.offset.setText(hex(offset)) + + def notifyViewChanged(self, view_frame): + if view_frame is None: + self.datatype.setText("None") + self.data = None + else: + self.datatype.setText(view_frame.getCurrentView()) + view = view_frame.getCurrentViewInterface() + self.data = view.getData() + + def contextMenuEvent(self, event): + self.m_contextMenuManager.show(self.m_menu, self.actionHandler) + +class HelloSidebarWidgetType(SidebarWidgetType): + def __init__(self): + # Sidebar icons are 28x28 points. Should be at least 56x56 pixels for + # HiDPI display compatibility. They will be automatically made theme + # aware, so you need only provide a grayscale image, where white is + # the color of the shape. + icon = QImage(56, 56, QImage.Format_RGB32) + icon.fill(0) + + # Render an "H" as the example icon + p = QPainter() + p.begin(icon) + p.setFont(QFont("Open Sans", 56)) + p.setPen(QColor(255, 255, 255, 255)) + p.drawText(QRectF(0, 0, 56, 56), Qt.AlignCenter, "H") + p.end() + + SidebarWidgetType.__init__(self, icon, "Hello") + + def createWidget(self, frame, data): + # This callback is called when a widget needs to be created for a given context. Different + # widgets are created for each unique BinaryView. They are created on demand when the sidebar + # widget is visible and the BinaryView becomes active. + return HelloSidebarWidget("Hello", frame, data) + +# Register the sidebar widget type with Binary Ninja. This will make it appear as an icon in the +# sidebar and the `createWidget` method will be called when a widget is required. +Sidebar.addSidebarWidgetType(HelloSidebarWidgetType()) diff --git a/ui/clickablelabel.h b/ui/clickablelabel.h index 4025d7c3..606ec369 100644 --- a/ui/clickablelabel.h +++ b/ui/clickablelabel.h @@ -1,8 +1,21 @@ #pragma once +#include <QtCore/QTimer> #include <QtGui/QColor> #include <QtGui/QPainter> +#include <QtGui/QPaintEvent> #include <QtWidgets/QLabel> +#include "uicontext.h" + + +struct BINARYNINJAUIAPI IconImage +{ + QImage original; + QImage active, activeHover; + QImage inactive, inactiveHover; + + static IconImage generate(const QImage& src); +}; class BINARYNINJAUIAPI ClickableLabel: public QLabel @@ -20,6 +33,39 @@ protected: }; +class BINARYNINJAUIAPI ClickableIcon: public QWidget +{ + Q_OBJECT + + IconImage m_image; + bool m_canToggle = false; + bool m_active = true; + bool m_hover = false; + QTimer* m_timer; + +public: + ClickableIcon(const QImage& icon, const QSize& desiredPointSize); + + void setAllowToggle(bool canToggle); + void setActive(bool state); + bool active() const { return m_active; } + +Q_SIGNALS: + void clicked(); + void toggle(bool newState); + +private Q_SLOTS: + void underMouseTimerEvent(); + void handleToggle(); + +protected: + void enterEvent(QEnterEvent* event) override; + void leaveEvent(QEvent* event) override; + void paintEvent(QPaintEvent* event) override; + void mouseReleaseEvent(QMouseEvent* event) override { if (event->button() == Qt::LeftButton) Q_EMIT clicked(); } +}; + + class BINARYNINJAUIAPI ClickableStateLabel: public ClickableLabel { Q_OBJECT diff --git a/ui/filter.h b/ui/filter.h index 5a43100f..72e2e930 100644 --- a/ui/filter.h +++ b/ui/filter.h @@ -24,7 +24,7 @@ class BINARYNINJAUIAPI FilterEdit: public QLineEdit FilterTarget* m_target; public: - FilterEdit(QWidget* parent, FilterTarget* target); + FilterEdit(FilterTarget* target); protected: virtual void keyPressEvent(QKeyEvent* event) override; @@ -37,9 +37,12 @@ class BINARYNINJAUIAPI FilteredView: public QWidget FilterTarget* m_target; QWidget* m_widget; FilterEdit* m_filter; + bool m_autoHide; public: - FilteredView(QWidget* parent, QWidget* filtered, FilterTarget* target); + FilteredView(QWidget* parent, QWidget* filtered, FilterTarget* target, + FilterEdit* edit = nullptr); + void setFilterPlaceholderText(const QString& text); void updateFonts(); void clearFilter(); void showFilter(const QString& initialText); diff --git a/ui/render.h b/ui/render.h index 5277f81f..5464f684 100644 --- a/ui/render.h +++ b/ui/render.h @@ -83,6 +83,7 @@ public: HighlightTokenState getHighlightTokenForTextToken(const BinaryNinja::InstructionTextToken& token); void drawText(QPainter& p, int x, int y, QColor color, const QString& text); + void drawUnderlinedText(QPainter& p, int x, int y, QColor color, const QString& text); void drawSeparatorLine(QPainter& p, QColor top, QColor bottom, QColor line, const QRect& rect); void drawInstructionHighlight(QPainter& p, const QRect& rect); diff --git a/ui/sidebar.h b/ui/sidebar.h new file mode 100644 index 00000000..0c3bc2c7 --- /dev/null +++ b/ui/sidebar.h @@ -0,0 +1,408 @@ +#pragma once + +#include <QtWidgets/QWidget> +#include <QtWidgets/QLabel> +#include <QtWidgets/QStackedWidget> +#include <QtWidgets/QSplitter> +#include <QtGui/QPicture> +#include <QtCore/QSettings> +#include "theme.h" + +class SidebarEntry; +class MainWindow; +class ContextMenuManager; + +struct SidebarIcon +{ + QImage original; + QImage active; + QImage inactive; + + static SidebarIcon generate(const QImage& src); +}; + +class BINARYNINJAUIAPI SidebarWidget: public QWidget +{ + Q_OBJECT + +protected: + QString m_title; + UIActionHandler m_actionHandler; + ContextMenuManager* m_contextMenuManager = nullptr; + Menu* m_menu = nullptr; + +public: + SidebarWidget(const QString& title); + + const QString& title() const { return m_title; } + + virtual void notifyFontChanged() { } + virtual void notifyOffsetChanged(uint64_t /*offset*/) { } + virtual void notifyThemeChanged(); + virtual void notifyViewChanged(ViewFrame* /*frame*/) { } + virtual void notifyViewLocationChanged(View* /*view*/, const ViewLocation& /*viewLocation*/) { } + virtual void focus(); + + virtual QWidget* headerWidget() { return nullptr; } +}; + +class BINARYNINJAUIAPI SidebarWidgetAndHeader: public QWidget +{ + Q_OBJECT + SidebarWidget* m_widget; + QWidget* m_header; + ViewFrame* m_frame; +public: + SidebarWidgetAndHeader(SidebarWidget* widget, ViewFrame* frame); + + SidebarWidget* widget() const { return m_widget; } + QWidget* header() const { return m_header; } + ViewFrame* viewFrame() const { return m_frame; } + + void updateTheme(); + void updateFonts(); +}; + +class BINARYNINJAUIAPI SidebarHeaderTitle: public QLabel +{ + Q_OBJECT +public: + SidebarHeaderTitle(const QString& name); +}; + +class BINARYNINJAUIAPI SidebarHeader: public QWidget +{ + Q_OBJECT +public: + SidebarHeader(const QString& name, QWidget* rightSide = nullptr); +}; + +class BINARYNINJAUIAPI SidebarInvalidContextWidget: public SidebarWidget +{ + Q_OBJECT +public: + SidebarInvalidContextWidget(const QString& title); + +private Q_SLOTS: + void openFile(); +}; + +class BINARYNINJAUIAPI SidebarWidgetType +{ + SidebarIcon m_icon; + QString m_name; + +public: + SidebarWidgetType(const QImage& icon, const QString& name); + virtual ~SidebarWidgetType() {} + + const SidebarIcon& icon() const { return m_icon; } + const QString& name() const { return m_name; } + + virtual bool isInReferenceArea() const { return false; } + virtual bool viewSensitive() const { return true; } + virtual SidebarWidget* createWidget(ViewFrame* frame, BinaryViewRef data) = 0; + virtual SidebarWidget* createInvalidContextWidget(); + + void updateTheme(); +}; + +class ViewFrame; + +struct SidebarWidgetContainerState +{ + SidebarWidgetType* contentWidgetType; + SidebarWidgetType* referenceWidgetType; + QList<int> contentSplitterSizes; + QList<int> parentSplitterSizes; +}; + +class BINARYNINJAUIAPI SidebarWidgetContainer: public QWidget +{ + Q_OBJECT + + QSplitter* m_parentSplitter = nullptr; + QSplitter* m_contentSplitter; + QStackedWidget* m_contentStackedWidget; + QStackedWidget* m_referenceStackedWidget; + ViewFrame* m_frame; + QString m_dataType; + BinaryViewRef m_data; + SidebarWidgetType* m_contentActive = nullptr; + SidebarWidgetType* m_lastContentActive = nullptr; + SidebarWidgetType* m_referenceActive = nullptr; + SidebarWidgetType* m_pendingReferenceType = nullptr; + std::map<ViewFrame*, std::map<QString, std::map<SidebarWidgetType*, SidebarWidgetAndHeader*>>> m_widgets; + std::map<ViewFrame*, std::map<QString, std::pair<SidebarWidgetType*, SidebarWidgetType*>>> m_priorWidgets; + std::map<ViewFrame*, std::map<QString, QList<int>>> m_priorContentSplitterSizes; + std::map<ViewFrame*, std::map<QString, QList<int>>> m_priorParentSplitterSizes; + std::optional<QList<int>> m_pendingContentSplitterSizes; + std::optional<QList<int>> m_pendingParentSplitterSizes; + std::map<ViewFrame*, std::map<QString, std::pair<View*, ViewLocation>>> m_currentViewLocation; + + void activateWidgetForType(SidebarWidgetType* type); + void deactivateWidgetForType(SidebarWidgetType* type); + + static QVariant sizesToVariant(const QList<int>& sizes); + static std::optional<QList<int>> variantToSizes(const QVariant& variant); + +public: + SidebarWidgetContainer(); + + void setSplitter(QSplitter* splitter); + void setActiveContext(ViewFrame* frame, const QString& dataType, BinaryViewRef data); + void destroyContext(ViewFrame* frame); + + bool isContentActive() const { return m_contentActive != nullptr; } + bool isActive(SidebarWidgetType* type) const { return m_contentActive == type || m_referenceActive == type; } + void activate(SidebarWidgetType* type); + void deactivate(SidebarWidgetType* type); + + SidebarWidget* widget(SidebarWidgetType* type); + SidebarWidget* widget(const QString& name); + + virtual QSize sizeHint() const override; + + void updateTheme(); + void updateFonts(); + + void setDefaultReferenceType(SidebarWidgetType* type); + + void saveSizes(const QSettings& settings, const QString& windowStateName); + void saveState(const QSettings& settings, const QString& windowStateName); + void restoreSizes(const QSettings& settings, const QString& windowStateName); + void restoreState(const QSettings& settings, const QString& windowStateName); + + void updateViewLocation(View* view, const ViewLocation& viewLocation); + void viewChanged(); + +Q_SIGNALS: + void showContents(); + void hideContents(); +}; + +class BINARYNINJAUIAPI Sidebar: public QWidget +{ + Q_OBJECT + + SidebarWidgetType* m_hoverItem = nullptr; + SidebarWidgetContainer* m_currentContainer = nullptr; + + static std::vector<SidebarWidgetType*> m_contentTypes; + static std::vector<SidebarWidgetType*> m_referenceTypes; + static SidebarWidgetType* m_defaultContentType; + static SidebarWidgetType* m_defaultReferenceType; + +protected: + virtual void paintEvent(QPaintEvent* event) override; + virtual void mouseMoveEvent(QMouseEvent* event) override; + virtual void mousePressEvent(QMouseEvent* event) override; + virtual void leaveEvent(QEvent* event) override; + +public: + Sidebar(); + + SidebarWidgetContainer* container() const { return m_currentContainer; } + void setContainer(SidebarWidgetContainer* container); + void setActiveContext(ViewFrame* frame, const QString& dataType, BinaryViewRef data); + + SidebarWidget* widget(SidebarWidgetType* type); + SidebarWidget* widget(const QString& name); + + void activate(SidebarWidgetType* type); + void activate(const QString& name); + + void updateTheme(); + void updateFonts(); + + static void addSidebarWidgetType(SidebarWidgetType* type); + static SidebarWidgetType* typeFromName(const QString& name); + static const std::vector<SidebarWidgetType*>& contentTypes() { return m_contentTypes; } + static const std::vector<SidebarWidgetType*>& referenceTypes() { return m_referenceTypes; } + + static SidebarWidgetType* defaultContentType() { return m_defaultContentType; } + static SidebarWidgetType* defaultReferenceType() { return m_defaultReferenceType; } + static void setDefaultContentType(SidebarWidgetType* type) { m_defaultContentType = type; } + static void setDefaultReferenceType(SidebarWidgetType* type) { m_defaultReferenceType = type; } + + static Sidebar* current() + { + UIContext* context = UIContext::activeContext(); + if (!context) + return nullptr; + return context->sidebar(); + } + + template<class T> + static T* widget(SidebarWidgetType* type) + { + Sidebar* sidebar = current(); + if (!type || !sidebar || !sidebar->container() || !sidebar->container()->isActive(type)) + return (T*)nullptr; + QWidget* widget = sidebar->widget(type); + if (!widget) + return (T*)nullptr; + return qobject_cast<T*>(widget); + } + + template<class T> + static T* widget(const QString& name) + { + return widget<T>(Sidebar::typeFromName(name)); + } + + template<class T> + static T* activateWidget(SidebarWidgetType* type) + { + Sidebar* sidebar = current(); + if (!type || !sidebar || !sidebar->container()) + return (T*)nullptr; + sidebar->activate(type); + QWidget* widget = sidebar->widget(type); + if (!widget) + return (T*)nullptr; + T* result = qobject_cast<T*>(widget); + if (!result) + return (T*)nullptr; + return result; + } + + template<class T> + static T* activateWidget(const QString& name) + { + return activateWidget<T>(Sidebar::typeFromName(name)); + } + + template<class T> + static UIAction globalSidebarAction(const QString& name, + const std::function<void(T* obj)>& activate) + { + return globalSidebarAction<T>(name, + [=](T* obj, const UIActionContext&) { activate(obj); }, + [=](T*, const UIActionContext&) { return true; }); + } + + template<class T> + static UIAction globalSidebarAction(const QString& name, + const std::function<void(T* obj, const UIActionContext& ctxt)>& activate) + { + return globalSidebarAction<T>(name, activate, + [](T*, const UIActionContext&) { return true; }); + } + + template<class T> + static UIAction globalSidebarAction(const QString& name, + const std::function<void(T* obj)>& activate, const std::function<bool(T* obj)>& isValid) + { + return globalSidebarAction<T>(name, + [=](T* obj, const UIActionContext&) { activate(obj); }, + [=](T* obj, const UIActionContext&) { return isValid(obj); }); + } + + template<class T> + static UIAction globalSidebarAction(const QString& name, + const std::function<void(T* obj, const UIActionContext& ctxt)>& activate, + const std::function<bool(T* obj, const UIActionContext& ctxt)>& isValid) + { + return globalSidebarAction<T>(Sidebar::typeFromName(name), activate, isValid); + } + + template<class T> + static UIAction globalSidebarAction(SidebarWidgetType* type, + const std::function<void(T* obj)>& activate) + { + return globalSidebarAction<T>(type, + [=](T* obj, const UIActionContext&) { activate(obj); }, + [=](T*, const UIActionContext&) { return true; }); + } + + template<class T> + static UIAction globalSidebarAction(SidebarWidgetType* type, + const std::function<void(T* obj, const UIActionContext& ctxt)>& activate) + { + return globalSidebarAction<T>(type, activate, + [](T*, const UIActionContext&) { return true; }); + } + + template<class T> + static UIAction globalSidebarAction(SidebarWidgetType* type, + const std::function<void(T* obj)>& activate, const std::function<bool(T* obj)>& isValid) + { + return globalSidebarAction<T>(type, + [=](T* obj, const UIActionContext&) { activate(obj); }, + [=](T* obj, const UIActionContext&) { return isValid(obj); }); + } + + template<class T> + static UIAction globalSidebarAction(SidebarWidgetType* type, + const std::function<void(T* obj, const UIActionContext& ctxt)>& activate, + const std::function<bool(T* obj, const UIActionContext& ctxt)>& isValid) + { + std::function<T*(const UIActionContext& ctxt)> lookup = [=](const UIActionContext& ctxt) { + if (!type || !ctxt.context) + return (T*)nullptr; + Sidebar* sidebar = ctxt.context->sidebar(); + if (!sidebar->container() || !sidebar->container()->isActive(type)) + return (T*)nullptr; + QWidget* widget = sidebar->widget(type); + if (!widget) + return (T*)nullptr; + return qobject_cast<T*>(widget); + }; + return UIAction( + [=](const UIActionContext& ctxt) { + T* obj = lookup(ctxt); + if (obj) + activate(obj, ctxt); + }, + [=](const UIActionContext& ctxt) { + T* obj = lookup(ctxt); + if (obj) + return isValid(obj, ctxt); + return false; + }); + } + + template<class T> + static std::function<bool(const UIActionContext&)> globalSidebarActionChecked(const QString& name, + const std::function<bool(T* obj)>& isChecked) + { + return globalSidebarActionChecked<T>(name, + [=](T* obj, const UIActionContext&) { return isChecked(obj); }); + } + + template<class T> + static std::function<bool(const UIActionContext&)> globalSidebarActionChecked(const QString& name, + const std::function<bool(T* obj, const UIActionContext& ctxt)>& isChecked) + { + return globalSidebarActionChecked<T>(Sidebar::typeFromName(name), isChecked); + } + + template<class T> + static std::function<bool(const UIActionContext&)> globalSidebarActionChecked(SidebarWidgetType* type, + const std::function<bool(T* obj)>& isChecked) + { + return globalSidebarActionChecked<T>(type, + [=](T* obj, const UIActionContext&) { return isChecked(obj); }); + } + + template<class T> + static std::function<bool(const UIActionContext&)> globalSidebarActionChecked(SidebarWidgetType* type, + const std::function<bool(T* obj, const UIActionContext& ctxt)>& isChecked) + { + return [=](const UIActionContext& ctxt) { + if (!type || !ctxt.context) + return false; + Sidebar* sidebar = ctxt.context->sidebar(); + if (!sidebar->container() || !sidebar->container()->isActive(type)) + return false; + QWidget* widget = sidebar->widget(type); + if (!widget) + return false; + T* obj = qobject_cast<T*>(widget); + if (obj) + return isChecked(obj, ctxt); + return false; + }; + } +}; diff --git a/ui/stringsview.h b/ui/stringsview.h index 5b5dbff5..9dc71fed 100644 --- a/ui/stringsview.h +++ b/ui/stringsview.h @@ -126,13 +126,15 @@ class BINARYNINJAUIAPI StringsContainer: public QWidget, public ViewContainer ViewFrame* m_view; StringsView* m_strings; FilteredView* m_filter; + FilterEdit* m_separateEdit = nullptr; public: - StringsContainer(BinaryViewRef data, ViewFrame* view); + StringsContainer(BinaryViewRef data, ViewFrame* view, bool separateEdit = false); virtual View* getView() override { return m_strings; } StringsView* getStringsView() { return m_strings; } FilteredView* getFilter() { return m_filter; } + FilterEdit* getSeparateFilterEdit() { return m_separateEdit; } protected: virtual void focusInEvent(QFocusEvent* event) override; @@ -148,3 +150,22 @@ public: virtual QWidget* create(BinaryViewRef data, ViewFrame* viewFrame); static void init(); }; + + +class BINARYNINJAUIAPI StringsViewSidebarWidget: public SidebarWidget +{ + Q_OBJECT + + QWidget* m_header; +public: + StringsViewSidebarWidget(BinaryViewRef data, ViewFrame* frame); + virtual QWidget* headerWidget() { return m_header; } +}; + + +class BINARYNINJAUIAPI StringsViewSidebarWidgetType: public SidebarWidgetType +{ +public: + StringsViewSidebarWidgetType(); + virtual SidebarWidget* createWidget(ViewFrame* frame, BinaryViewRef data) override; +}; diff --git a/ui/symbolsview.h b/ui/symbolsview.h index da857521..26a0a7d0 100644 --- a/ui/symbolsview.h +++ b/ui/symbolsview.h @@ -2,17 +2,16 @@ #include <QtCore/QTimer> #include "binaryninjaapi.h" -#include "dockhandler.h" +#include "sidebar.h" #include "filter.h" #include "symbollist.h" class ViewFrame; class SymbolList; -class BINARYNINJAUIAPI SymbolsView: public QWidget, public DockContextHandler, public BinaryNinja::BinaryDataNotification +class BINARYNINJAUIAPI SymbolsView: public SidebarWidget, public BinaryNinja::BinaryDataNotification { Q_OBJECT - Q_INTERFACES(DockContextHandler) friend class SymbolList; @@ -20,6 +19,7 @@ class BINARYNINJAUIAPI SymbolsView: public QWidget, public DockContextHandler, p SymbolList* m_funcList; FilteredView* m_funcFilter; + QWidget* m_header; bool m_updatesPending; QTimer* m_updateTimer; @@ -48,11 +48,20 @@ public: void toggleLocalFunctions() { m_funcList->toggleLocalFunctions(); } void toggleLocalDataVars() { m_funcList->toggleLocalDataVars(); } + virtual QWidget* headerWidget() override { return m_header; } + protected: virtual void contextMenuEvent(QContextMenuEvent* event) override; virtual void notifyFontChanged() override; - virtual bool shouldBeVisible(ViewFrame* frame) override; private Q_SLOTS: void updateTimerEvent(); + void showContextMenu(); +}; + +class BINARYNINJAUIAPI SymbolsViewSidebarWidgetType: public SidebarWidgetType +{ +public: + SymbolsViewSidebarWidgetType(); + virtual SidebarWidget* createWidget(ViewFrame* frame, BinaryViewRef data) override; }; diff --git a/ui/taglist.h b/ui/taglist.h index 924bc04d..fb7ecce8 100644 --- a/ui/taglist.h +++ b/ui/taglist.h @@ -7,7 +7,7 @@ #include <QtWidgets/QStyledItemDelegate> #include <QtWidgets/QDialog> #include "binaryninjaapi.h" -#include "dockhandler.h" +#include "sidebar.h" #include "viewframe.h" #include "filter.h" #include "tagtypelist.h" @@ -176,10 +176,9 @@ public: }; -class BINARYNINJAUIAPI TagListWidget: public QWidget, public DockContextHandler +class BINARYNINJAUIAPI TagListWidget: public SidebarWidget { Q_OBJECT - Q_INTERFACES(DockContextHandler) ViewFrame* m_view; QTabWidget* m_tabs; @@ -205,8 +204,10 @@ public: TagList* GetList(); void editTag(TagRef tag); - TagListWidget(QWidget* parent, ViewFrame* view, BinaryViewRef data); + TagListWidget(ViewFrame* view, BinaryViewRef data); virtual ~TagListWidget(); + + virtual void focus() override; }; @@ -236,3 +237,11 @@ private Q_SLOTS: void createTagAccept(TagTypeRef tt); void removeTag(); }; + + +class BINARYNINJAUIAPI TagListSidebarWidgetType: public SidebarWidgetType +{ +public: + TagListSidebarWidgetType(); + virtual SidebarWidget* createWidget(ViewFrame* frame, BinaryViewRef data) override; +}; diff --git a/ui/typeview.h b/ui/typeview.h index f31f2aae..5d0a59fc 100644 --- a/ui/typeview.h +++ b/ui/typeview.h @@ -17,6 +17,7 @@ #include "render.h" #include "menus.h" #include "xreflist.h" +#include "clickablelabel.h" #define TYPE_VIEW_UPDATE_CHECK_INTERVAL 200 @@ -102,7 +103,11 @@ class BINARYNINJAUIAPI TypeView: public QAbstractScrollArea, public View, public QWidget* m_lineNumberArea; int m_lineNumberAreaWidth = 0; int m_lineCount = 0; - int m_cols, m_rows, m_paddingCols; + size_t m_systemTypesHidden = 0; + std::optional<size_t> m_showSystemTypesLine; + size_t m_typesFiltered = 0; + std::optional<size_t> m_clearFilterLine; + int m_cols, m_rows, m_paddingCols, m_offsetPaddingWidth; uint64_t m_maxOffset; size_t m_offsetWidth; HighlightTokenState m_highlight; @@ -130,6 +135,8 @@ class BINARYNINJAUIAPI TypeView: public QAbstractScrollArea, public View, public QAction* m_actionCopy; QAction* m_actionSelectAll; + bool m_compact; + void adjustSize(int width, int height); void refreshAllTypes(); @@ -163,7 +170,7 @@ class BINARYNINJAUIAPI TypeView: public QAbstractScrollArea, public View, public static TypeDefinitionLine getTypeDefinitionHeaderLine(PlatformRef platform, const std::string& name, TypeRef type); public: - explicit TypeView(BinaryViewRef data, ViewFrame* view, TypesContainer* container); + explicit TypeView(BinaryViewRef data, ViewFrame* view, TypesContainer* container, bool compact = false); virtual ~TypeView(); virtual bool findNextData(uint64_t start, uint64_t end, const BinaryNinja::DataBuffer& data, uint64_t& addr, BNFindFlag flags, @@ -230,6 +237,8 @@ public: const std::string& varName, size_t index, TypeRef type, TypeRef parent, BinaryViewRef data, int paddingCols, bool collapsed = false); + void showContextMenu(Menu* source = nullptr); + protected: virtual void resizeEvent(QResizeEvent* event) override; virtual void paintEvent(QPaintEvent* event) override; @@ -295,8 +304,7 @@ class BINARYNINJAUIAPI TypeFilter: public QWidget Q_OBJECT TypesContainer* m_container; - ExpandableGroup* m_group; - QComboBox* m_showTypes; + ClickableIcon* m_showSystemTypes; QLineEdit* m_textFilter; bool MatchesAutoFilter(BinaryViewRef data, const BinaryNinja::QualifiedName& name); @@ -306,12 +314,15 @@ Q_SIGNALS: void filterChanged(); public: - TypeFilter(TypesContainer* container); + TypeFilter(TypesContainer* container = nullptr); + void setContainer(TypesContainer* container) { m_container = container; } - std::map<BinaryNinja::QualifiedName, std::vector<TypeDefinitionLine>> GetFilteredTypeLines(BinaryViewRef data, int padding); + std::map<BinaryNinja::QualifiedName, std::vector<TypeDefinitionLine>> GetFilteredTypeLines( + BinaryViewRef data, int padding, size_t& systemTypesHidden, size_t& typesFiltered); void showAndFocus(); - uint64_t getCurrentTypeFilter(); - void setTypeFilter(uint64_t typesToShow); + bool areAutoTypesVisible(); + void setShowAutoTypes(bool showAutoTypes); + void clearTextFilter(); }; @@ -324,7 +335,8 @@ class BINARYNINJAUIAPI TypesContainer: public QWidget, public ViewContainer UIActionHandler m_actionHandler; public: - TypesContainer(BinaryViewRef data, ViewFrame* view); + TypesContainer(BinaryViewRef data, ViewFrame* view, + TypeFilter* filter = nullptr, bool compact = false); virtual View* getView() override { return m_typeView; } TypeView* getTypesView() { return m_typeView; } @@ -335,3 +347,32 @@ public: protected: virtual void focusInEvent(QFocusEvent* event) override; }; + + +class BINARYNINJAUIAPI TypeViewSidebarWidget: public SidebarWidget +{ + Q_OBJECT + + TypesContainer* m_container; + QWidget* m_header; + Menu m_addMenu; + +public: + TypeViewSidebarWidget(BinaryViewRef data, ViewFrame* frame); + + TypesContainer* container() const { return m_container; } + virtual void focus() override; + + virtual QWidget* headerWidget() override { return m_header; } + +private Q_SLOTS: + void showAddMenu(); +}; + + +class BINARYNINJAUIAPI TypeViewSidebarWidgetType: public SidebarWidgetType +{ +public: + TypeViewSidebarWidgetType(); + virtual SidebarWidget* createWidget(ViewFrame* frame, BinaryViewRef data) override; +}; diff --git a/ui/uicontext.h b/ui/uicontext.h index c1cc2993..b0e2adde 100644 --- a/ui/uicontext.h +++ b/ui/uicontext.h @@ -18,6 +18,9 @@ class ViewFrame; class UIActionHandler; class FileContext; class ViewLocation; +class Sidebar; +class SidebarWidgetContainer; +struct SelectionInfoForXref; /*! Interface used to receive notifications related to files and contexts. Many notifications include the ability @@ -131,13 +134,22 @@ public: \return True if the value in name should be used */ virtual bool GetNameForPath(UIContext* context, const QString& path, QString& name) { (void)context; (void)path; (void)name; return false; } + + /*! + Callback when the ui changes selection and should update cross references + \param context Context changing selection + \param frame ViewFrame which changed selection + \param view View that changed selection + \param selection New selection + */ + virtual void OnNewSelectionForXref(UIContext* context, ViewFrame* frame, View* view, const SelectionInfoForXref& selection) { (void)context; (void)frame; (void)view; (void)selection; } }; class BINARYNINJAUIAPI UIContextHandler { public: virtual ~UIContextHandler(); - virtual void updateStatus(bool updateInfo) = 0; + virtual void updateStatus() = 0; virtual void notifyThemeChanged() = 0; virtual void registerFileOpenMode(const QString& buttonName, const QString& description, const QString& action); }; @@ -168,6 +180,7 @@ protected: void NotifyOnViewChange(ViewFrame* frame, const QString& type); void NotifyOnAddressChange(ViewFrame* frame, View* view, const ViewLocation& location); + void NotifyOnNewSelectionForXref(ViewFrame* frame, View* view, const SelectionInfoForXref& selection); public: UIContext(); @@ -257,6 +270,10 @@ public: UIActionHandler* globalActions() { return &m_globalActions; } virtual UIActionHandler* contentActionHandler() = 0; + virtual Sidebar* sidebar() = 0; + + void updateCrossReferences(ViewFrame* frame, View* view, const SelectionInfoForXref& selection); + /*! Register an object to receive notifications of UIContext events \param notification Object which will receive notifications @@ -284,7 +301,7 @@ public: static void setHandler(UIContextHandler* handler); static QSize getScaledWindowSize(int x, int y); - static void updateStatus(bool updateInfo = true); + static void updateStatus(); static void notifyThemeChanged(); static void showPreview(QWidget* parent, PreviewWidget* preview, QPoint localPos, bool anchorAtPoint = false); static void closePreview(); diff --git a/ui/viewframe.h b/ui/viewframe.h index 1a87e7af..016ccc20 100644 --- a/ui/viewframe.h +++ b/ui/viewframe.h @@ -19,6 +19,7 @@ #include "filecontext.h" #include "viewtype.h" #include "action.h" +#include "sidebar.h" // this struct is used to pass selection information for cross references struct SelectionInfoForXref @@ -221,6 +222,8 @@ public: QString viewType(); + void updateCrossReferenceSelection(ViewFrame* frame = nullptr); + static void registerActions(); }; @@ -307,7 +310,7 @@ private: ClickableStateLabel* m_fileContentsLockStatus; BinaryViewRef m_data; DockHandler* m_docks; - QWidget* m_view; + QWidget* m_view = nullptr; QWidget* m_viewContainer; QVBoxLayout* m_viewLayout; std::map<QString, std::map<QString, QPointer<QWidget>>> m_extViewCache; @@ -349,6 +352,7 @@ public: std::vector<QString> getAvailableTypes() const; QString getCurrentView() const; + BinaryViewRef getCurrentBinaryView() const; QString getCurrentDataType() const; uint64_t getCurrentOffset() const; BNAddressRange getSelectionOffsets() const; @@ -369,6 +373,20 @@ public: QWidget* getExtendedView(const QString& name, bool create = false); + Sidebar* getSidebar(); + + template<class T> + T* getSidebarWidget(const QString& name) + { + Sidebar* sidebar = getSidebar(); + if (!sidebar) + return (T*)nullptr; + QWidget* widget = sidebar->widget(name); + if (!widget) + return (T*)nullptr; + return qobject_cast<T*>(widget); + } + bool navigate(const QString& type, uint64_t offset, bool updateInfo = true, bool addHistoryEntry = true); bool navigate(const QString& type, const std::function<bool(View*)>& handler, bool updateInfo = true, bool addHistoryEntry = true); bool navigate(BinaryViewRef data, uint64_t offset, bool updateInfo = true, bool addHistoryEntry = true); @@ -412,12 +430,11 @@ public: void setCurrentFunction(FunctionRef func); void updateCrossReferences(); - void showCrossReferences(); + void updateCrossReferenceSelection(); void showPinnedCrossReferences(); void nextCrossReference(); void prevCrossReference(); - void showTags(); void editTag(TagRef tag); void nextTag(); void prevTag(); diff --git a/ui/xreflist.h b/ui/xreflist.h index d603c599..403178f2 100644 --- a/ui/xreflist.h +++ b/ui/xreflist.h @@ -20,7 +20,7 @@ #include <memory> #include "binaryninjaapi.h" -#include "dockhandler.h" +#include "sidebar.h" #include "viewframe.h" #include "fontsettings.h" #include "expandablegroup.h" @@ -452,10 +452,9 @@ Q_SIGNALS: class ExpandableGroup; class QCheckboxCombo; -class BINARYNINJAUIAPI CrossReferenceWidget: public QWidget, public DockContextHandler +class BINARYNINJAUIAPI CrossReferenceWidget: public SidebarWidget, public UIContextNotification { Q_OBJECT - Q_INTERFACES(DockContextHandler) ViewFrame* m_view; BinaryViewRef m_data; @@ -488,11 +487,12 @@ class BINARYNINJAUIAPI CrossReferenceWidget: public QWidget, public DockContextH public: CrossReferenceWidget(ViewFrame* view, BinaryViewRef data, bool pinned); + virtual ~CrossReferenceWidget(); virtual void notifyFontChanged() override; - virtual bool shouldBeVisible(ViewFrame* frame) override; virtual QString getHeaderText(SelectionInfoForXref selectionInfo); virtual void setCurrentSelection(SelectionInfoForXref selectionInfo); + virtual void updateCrossReferences(); virtual void setCurrentPinnedSelection(SelectionInfoForXref selectionInfo); void updatePinnedSelection(); virtual void navigateToNext(); @@ -510,6 +510,11 @@ public: bool uiMaxItemsExceeded() const { return m_uiMaxItemsExceeded; } void setUIMaxItemsExceeded(bool value) { m_uiMaxItemsExceeded = value; } + virtual void focus() override; + + virtual void OnNewSelectionForXref(UIContext* context, ViewFrame* frame, View* view, + const SelectionInfoForXref& selection) override; + private Q_SLOTS: void hoverTimerEvent(); @@ -522,6 +527,15 @@ public Q_SLOTS: }; +class BINARYNINJAUIAPI CrossReferenceSidebarWidgetType: public SidebarWidgetType +{ +public: + CrossReferenceSidebarWidgetType(); + virtual bool isInReferenceArea() const override { return true; } + virtual SidebarWidget* createWidget(ViewFrame* frame, BinaryViewRef data) override; +}; + + // https://github.com/CuriousCrow/QCheckboxCombo /* * QCheckboxCombo is a combobox widget that contains items with checkboxes |
