diff options
| author | Rusty Wagner <rusty.wagner@gmail.com> | 2021-11-18 13:52:25 -0500 |
|---|---|---|
| committer | Rusty Wagner <rusty.wagner@gmail.com> | 2022-01-10 15:25:18 -0700 |
| commit | 7f6e54fdc8ff4a06fdcd3c510a37add337f04bac (patch) | |
| tree | 60cce5955fcc05fc84f35f9662d49c7e0790dde1 | |
| parent | d9661f34eec6855d495a10eaafc2a8e2679756a7 (diff) | |
Refactor main area to use pane system
| -rw-r--r-- | binaryninjacore.h | 6 | ||||
| -rw-r--r-- | docs/getting-started.md | 6 | ||||
| -rw-r--r-- | python/examples/hellodockwidget.py | 107 | ||||
| -rw-r--r-- | python/examples/hellopane.py | 125 | ||||
| -rw-r--r-- | ui/clickablelabel.h | 6 | ||||
| -rw-r--r-- | ui/datatypelist.h | 26 | ||||
| -rw-r--r-- | ui/disassemblyview.h | 20 | ||||
| -rw-r--r-- | ui/featuremap.h | 6 | ||||
| -rw-r--r-- | ui/filecontext.h | 20 | ||||
| -rw-r--r-- | ui/flowgraphwidget.h | 20 | ||||
| -rw-r--r-- | ui/globalarea.h | 4 | ||||
| -rw-r--r-- | ui/hexeditor.h | 16 | ||||
| -rw-r--r-- | ui/ilchooser.h | 34 | ||||
| -rw-r--r-- | ui/linearview.h | 28 | ||||
| -rw-r--r-- | ui/minigraph.h | 20 | ||||
| -rw-r--r-- | ui/pane.h | 356 | ||||
| -rw-r--r-- | ui/reflectionview.h | 49 | ||||
| -rw-r--r-- | ui/searchresult.h | 19 | ||||
| -rw-r--r-- | ui/sidebar.h | 18 | ||||
| -rw-r--r-- | ui/syncgroup.h | 39 | ||||
| -rw-r--r-- | ui/uicontext.h | 27 | ||||
| -rw-r--r-- | ui/viewframe.h | 51 | ||||
| -rw-r--r-- | ui/viewlist.h | 31 | ||||
| -rw-r--r-- | ui/xreflist.h | 13 |
24 files changed, 825 insertions, 222 deletions
diff --git a/binaryninjacore.h b/binaryninjacore.h index 4b379263..1943d8a2 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -1672,7 +1672,11 @@ extern "C" SidebarActiveIconColor, SidebarHeaderBackgroundColor, SidebarHeaderTextColor, - SidebarWidgetBackgroundColor + SidebarWidgetBackgroundColor, + + // Pane colors + ActivePaneBackgroundColor, + InactivePaneBackgroundColor }; // The following edge styles map to Qt's Qt::PenStyle enumeration diff --git a/docs/getting-started.md b/docs/getting-started.md index 33f90395..fdafeeae 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -378,13 +378,13 @@ The first of the two drop down boxes allows the selection of incoming, outgoing, #### Cross-Reference Pinning -By default Binary Ninja's cross-reference pane is dynamic, allowing quick navigation to relevant references. Sometimes you might rather have the current references stick around so they can be used as a sort of work-list. This workflow is supported in three different ways. First and most obviously by clicking the `Pin` checkbox (which is only visible if the `Filter` drop-down is open). This prevents the list of cross-references from being updated even after the current selection is changed. Alternatively, `SHIFT+X` (or selecting `Focus Pinned Cross References` in the context menu or command palette) pops up a `Pinned Cross References` pane. This pane has a static address range which can only be updated through the `Pinned Cross References` action. The third way would be to select (or multi-select in table view) a set of cross-references then right-click `Tag Selected Rows`. The tag pane can then be used to navigate those references. Tags allow for persistent lists to be saved to analysis database whereas the other options only last for the current session. +By default Binary Ninja's cross-reference pane is dynamic, allowing quick navigation to relevant references. Sometimes you might rather have the current references stick around so they can be used as a sort of work-list. This workflow is supported in three different ways. First and most obviously by clicking the `Pin` checkbox (which is only visible if the `Filter` drop-down is open). This prevents the list of cross-references from being updated even after the current selection is changed. Alternatively, `SHIFT+X` (or selecting `Pin Cross References` in the context menu or command palette) pops up a `Pinned Cross References` pane. This pane has a static address range which can only be updated through the `Pin Cross References` action. The third way would be to select (or multi-select in table view) a set of cross-references then right-click `Tag Selected Rows`. The tag pane can then be used to navigate those references. Tags allow for persistent lists to be saved to analysis database whereas the other options only last for the current session. #### Cross-Reference Hotkeys * `x` - Focus the cross-references pane -* `[SHIFT] x` Focus the pinned cross-references pane +* `[SHIFT] x` Create a new pinned cross-references pane * `[OPTION/ALT] x` - Navigate to the next cross-reference * `[OPTION/ALT-SHIFT] x` - Navigate to the previous cross-reference @@ -491,7 +491,7 @@ Performing this action on both variables in the example results in the following The integrated script console is useful for small scripts that aren't worth writing as full plugins. -To trigger the console, either use `<CTRL>-<BACKTICK>`, or use the `View`/`Native Docks`/`Show Python Console` menu. +To trigger the console, either use `<CTRL>-<BACKTICK>`, or use the `View`/`Python Console` menu. Once loaded, the script console can be docked in different locations or popped out into a stand-alone window. Note that [at this time](https://github.com/Vector35/binaryninja-api/issues/226) window locations are not saved on restart. diff --git a/python/examples/hellodockwidget.py b/python/examples/hellodockwidget.py deleted file mode 100644 index af6a86ff..00000000 --- a/python/examples/hellodockwidget.py +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright (c) 2015-2022 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 dock widgets to Binary Ninja. -# Dock widgets are realized in Binary Ninja with the QDockWidget class provided by Qt. -# QDockWidgets are container objects that can be docked inside a main window or floated as -# a top-level window. A UI widget is placed inside a QDockWidget container. In Binary Ninja, -# QDockWidgets are created and managed internally. Binary Ninja presents two styles of dock widgets. -# Static: where one UI widget is placed in the container for the lifetime of the application. -# Dynamic: where a UI widget exists for each binary view type instance, and is dynamically -# swapped in/out of the container based on the current binary view selection. -# See .../api/ui/dockhandler.h for interface details. -# For Static dock widgets, the UI widget operates on multiple binary view instances (notified by notifyViewChanged) -# For Dynamic dock widgets, the UI widget operates on a single binary view instance - -from binaryninjaui import DockHandler, DockContextHandler, UIActionHandler -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): - def __init__(self, parent, name, data): - global instance_id - QWidget.__init__(self, parent) - DockContextHandler.__init__(self, 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 shouldBeVisible(self, view_frame): - if view_frame is None: - return False - else: - return True - - 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) - - @staticmethod - def create_widget(name, parent, data = None): - return HelloDockWidget(parent, name, data) - -def addStaticDockWidget(): - dock_handler = DockHandler.getActiveDockHandler() - parent = dock_handler.parent() - dock_widget = HelloDockWidget.create_widget("HelloDockWidget (Static Dock)", parent) - dock_handler.addDockWidget(dock_widget, Qt.BottomDockWidgetArea, Qt.Horizontal, True, False) - -def addDynamicDockWidget(): - dock_handler = DockHandler.getActiveDockHandler() - dock_handler.addDockWidget("HelloDockWidget (Dynamic Dock)", HelloDockWidget.create_widget, Qt.BottomDockWidgetArea, Qt.Horizontal, True) - -addStaticDockWidget() -addDynamicDockWidget() diff --git a/python/examples/hellopane.py b/python/examples/hellopane.py new file mode 100644 index 00000000..2b9fbe29 --- /dev/null +++ b/python/examples/hellopane.py @@ -0,0 +1,125 @@ +# Copyright (c) 2015-2022 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 panes to Binary Ninja. +# See .../api/ui/pane.h for interface details. + +from binaryninjaui import Pane, WidgetPane, UIActionHandler, UIActionHandler, UIAction, Menu, UIContext, UIContextNotification +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 + +# Class to handle UI context notifications. This will be used to listen for view and address +# changes and update the pane accordingly. +class HelloNotifications(UIContextNotification): + def __init__(self, widget): + UIContextNotification.__init__(self) + self.widget = widget + + # __del__ will not be called because the widget owns a reference to this object. Use + # QWidget.destroyed event to know when to stop listening for notifications. + self.widget.destroyed.connect(self.destroyed) + + UIContext.registerNotification(self) + + def destroyed(self): + UIContext.unregisterNotification(self) + + def OnViewChange(self, context, frame, type): + self.widget.updateState() + + def OnAddressChange(self, context, frame, view, location): + self.widget.updateState() + +# Pane widget itself. This can be any QWidget. +class HelloPaneWidget(QWidget, UIContextNotification): + def __init__(self, data): + global instance_id + QWidget.__init__(self) + 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("Hello Pane", 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 + + # Populate initial state + self.updateState() + + # Set up view and address change notifications + self.notifications = HelloNotifications(self) + + def updateState(self): + # Get the currently active view frame for this group of panes. There can be + # multiple view frames in a single window, or the pane could be popped out + # into its own window. UIContext.currentViewFrameForWidget will determine + # the best view frame to use for context. + frame = UIContext.currentViewFrameForWidget(self) + + # Update UI according to the active frame + if frame: + self.datatype.setText(frame.getCurrentView()) + view = frame.getCurrentViewInterface() + self.data = view.getData() + self.offset.setText(hex(view.getCurrentOffset())) + else: + self.datatype.setText("None") + self.data = None + + def contextMenuEvent(self, event): + self.m_contextMenuManager.show(self.m_menu, self.actionHandler) + + @staticmethod + def createPane(context): + if context.context and context.binaryView: + widget = HelloPaneWidget(context.binaryView) + pane = WidgetPane(widget, "Hello") + context.context.openPane(pane) + + @staticmethod + def canCreatePane(context): + return context.context and context.binaryView + +UIAction.registerAction("Hello Pane") +UIActionHandler.globalActions().bindAction("Hello Pane", UIAction(HelloPaneWidget.createPane, HelloPaneWidget.canCreatePane)) +Menu.mainMenu("Tools").addAction("Hello Pane", "Hello") diff --git a/ui/clickablelabel.h b/ui/clickablelabel.h index 606ec369..0b373d6b 100644 --- a/ui/clickablelabel.h +++ b/ui/clickablelabel.h @@ -29,6 +29,7 @@ Q_SIGNALS: void clicked(); protected: + void mousePressEvent(QMouseEvent*) override {} void mouseReleaseEvent(QMouseEvent* event) override { if (event->button() == Qt::LeftButton) Q_EMIT clicked(); } }; @@ -50,6 +51,8 @@ public: void setActive(bool state); bool active() const { return m_active; } + void setImage(const QImage& icon); + Q_SIGNALS: void clicked(); void toggle(bool newState); @@ -62,7 +65,8 @@ 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(); } + void mousePressEvent(QMouseEvent*) override {} + void mouseReleaseEvent(QMouseEvent* event) override; }; diff --git a/ui/datatypelist.h b/ui/datatypelist.h new file mode 100644 index 00000000..be0fc5e9 --- /dev/null +++ b/ui/datatypelist.h @@ -0,0 +1,26 @@ +#pragma once + +#include <QtWidgets/QLabel> +#include <string> +#include "viewframe.h" +#include "menus.h" + +class BINARYNINJAUIAPI DataTypeList: public MenuHelper +{ + Q_OBJECT + + FileContext* m_context = nullptr; + QString m_currentType; + std::string m_currentDataType; + +public: + DataTypeList(QWidget* parent); + + void setCurrentViewType(ViewFrame* view, const QString& type); + +protected: + virtual void showMenu(); + +Q_SIGNALS: + void viewChanged(QString type); +}; diff --git a/ui/disassemblyview.h b/ui/disassemblyview.h index 4f200c54..035eec37 100644 --- a/ui/disassemblyview.h +++ b/ui/disassemblyview.h @@ -14,6 +14,7 @@ #include "menus.h" #include "statusbarwidget.h" #include "flowgraphwidget.h" +#include "ilchooser.h" #define FUNCTION_UPDATE_CHECK_INTERVAL 100 @@ -45,12 +46,14 @@ public: virtual bool navigate(uint64_t pos) override; virtual bool navigateToFunction(FunctionRef func, uint64_t pos) override; - virtual bool navigateToViewLocation(const ViewLocation& viewLocation) override; + virtual bool navigateToViewLocation(const ViewLocation& viewLocation, bool center = false) override; virtual BinaryNinja::Ref<HistoryEntry> getHistoryEntry() override; virtual void navigateToHistoryEntry(BinaryNinja::Ref<HistoryEntry> entry) override; virtual StatusBarWidget* getStatusBarWidget() override; + virtual ViewPaneHeaderSubtypeWidget* getHeaderSubtypeWidget() override; + virtual QWidget* getHeaderOptionsWidget() override; virtual BNFunctionGraphType getILViewType() override { return m_ilViewType; }; virtual void setILViewType(BNFunctionGraphType ilViewType) override; @@ -80,6 +83,19 @@ private: DisassemblyView* m_view; }; + class DisassemblyViewOptionsIconWidget: public QWidget + { + public: + DisassemblyViewOptionsIconWidget(DisassemblyView* parent); + + private: + DisassemblyView* m_view; + ContextMenuManager* m_contextMenuManager; + Menu m_menu; + + void showMenu(); + }; + class DisassemblyViewStatusBarWidget: public StatusBarWidget { public: @@ -88,10 +104,12 @@ private: private: DisassemblyView* m_view; + ILChooserWidget* m_chooser; DisassemblyViewOptionsWidget* m_options; }; void bindActions(); + static void addOptionsMenuActions(Menu& menu); BNFunctionGraphType m_ilViewType; std::set<BNDisassemblyOption> m_options; diff --git a/ui/featuremap.h b/ui/featuremap.h index ec597038..68fcab0a 100644 --- a/ui/featuremap.h +++ b/ui/featuremap.h @@ -25,7 +25,7 @@ class ContextMenuManager; class Menu; class View; -class ViewFrame; +class SplitPaneWidget; class BINARYNINJAUIAPI FeatureMap: public QWidget, public BinaryNinja::BinaryDataNotification { @@ -35,7 +35,7 @@ class BINARYNINJAUIAPI FeatureMap: public QWidget, public BinaryNinja::BinaryDat QImage* m_staticImage = nullptr; std::vector<std::pair<uint64_t, uint64_t>> m_regions; - ViewFrame* m_frame = nullptr; + SplitPaneWidget* m_owner = nullptr; BinaryViewRef m_data; bool m_updatesPending = false; @@ -75,7 +75,7 @@ class BINARYNINJAUIAPI FeatureMap: public QWidget, public BinaryNinja::BinaryDat void updateCoordinates(); public: - FeatureMap(ViewFrame* frame, BinaryViewRef data, bool vertical = true); + FeatureMap(SplitPaneWidget* owner, BinaryViewRef data, bool vertical = true); virtual ~FeatureMap(); View* getBinaryDataNavigableView(bool preferGraphView = false); diff --git a/ui/filecontext.h b/ui/filecontext.h index b58413b6..54861591 100644 --- a/ui/filecontext.h +++ b/ui/filecontext.h @@ -7,8 +7,10 @@ #include "binaryninjaapi.h" #include "uicontext.h" +class View; class ViewFrame; class ViewType; +class SyncGroup; // This base class is required for building the Python bindings. The other base classes of FileContext are // ignored (as they have no functions that should be exported to Python), but this would leave the binding @@ -31,6 +33,10 @@ class BINARYNINJAUIAPI FileContext: public FileContextBase, public BinaryNinja:: ViewFrame* m_currentViewFrame; std::set<QObject*> m_refs; + std::vector<SyncGroup*> m_syncGroups; + std::map<ViewFrame*, std::pair<View*, ViewLocation>> m_syncLastLocation; + bool m_suspendSync = false; + static std::set<FileContext*> m_openFiles; void createBinaryViews(); @@ -49,6 +55,8 @@ public: QString getFilename() const { return m_filename; } void setFilename(QString newName) {m_filename = newName;} ViewFrame* getCurrentViewFrame() const { return m_currentViewFrame; } + QString getTabName(QWidget* widget); + QString getShortFileName(QWidget* widget); bool isValidSaveFilename() const { return m_isValidSaveFilename; } void markAsSaved(const QString& filename); @@ -74,6 +82,18 @@ public: void updateAnalysis(); + SyncGroup* newSyncGroup(); + SyncGroup* syncGroupById(int id); + void deleteSyncGroup(SyncGroup* syncGroup); + SyncGroup* syncGroupForFrame(ViewFrame* frame); + void removeFrame(ViewFrame* frame); + const std::vector<SyncGroup*>& allSyncGroups() const { return m_syncGroups; } + + void forceLocationSyncForFrame(ViewFrame* frame); + bool syncLocation(ViewFrame* frame, View* view, const ViewLocation& location); + void suspendSync() { m_suspendSync = true; } + void resumeSync() { m_suspendSync = false; } + static FileContext* newFile(); static FileContext* openFilename(const QString& path); static const std::set<FileContext*>& getOpenFileContexts(); diff --git a/ui/flowgraphwidget.h b/ui/flowgraphwidget.h index 367e0174..2f294320 100644 --- a/ui/flowgraphwidget.h +++ b/ui/flowgraphwidget.h @@ -94,7 +94,6 @@ class BINARYNINJAUIAPI FlowGraphWidget: public QAbstractScrollArea, public View, int m_renderXOfs, m_renderYOfs, m_renderWidth, m_renderHeight; float m_scale; QRect m_renderRect; - QRect m_miniRenderRect; bool m_scrollMode; int m_scrollBaseX, m_scrollBaseY; @@ -216,14 +215,14 @@ public: virtual void setSelectionOffsets(BNAddressRange range) override; virtual bool navigate(uint64_t pos) override; virtual bool navigateToFunction(FunctionRef func, uint64_t pos) override; - virtual bool navigateToViewLocation(const ViewLocation& viewLocation) override; + virtual bool navigateToViewLocation(const ViewLocation& viewLocation, bool center = false) override; bool navigateWithHistoryEntry(uint64_t addr, BinaryNinja::Ref<FlowGraphHistoryEntry> entry); bool navigateWithHistoryEntry(FunctionRef func, uint64_t addr, BinaryNinja::Ref<FlowGraphHistoryEntry> entry); void setNavigationTarget(View* target) { m_navigationTarget = target; } - virtual void clearRelatedHighlights(); - virtual void setRelatedIndexHighlights(const std::set<size_t>& related); - virtual void setRelatedInstructionHighlights(const std::set<uint64_t>& related); + virtual void clearRelatedHighlights() override; + virtual void setRelatedIndexHighlights(FunctionRef func, const std::set<size_t>& related) override; + virtual void setRelatedInstructionHighlights(FunctionRef func, const std::set<uint64_t>& related) override; virtual void zoom(bool direction); virtual void zoomActual(); @@ -254,22 +253,21 @@ public: virtual HighLevelILFunctionRef getCurrentHighLevelILFunction() override; virtual size_t getCurrentILInstructionIndex() override; - void scrollToCursor(); + void scrollToCursor(bool center = false); bool isUpdating(); QFont getFont() override { return m_render.getFont(); } virtual HighlightTokenState getHighlightTokenState() override { return m_highlight; } - QRect getMiniRenderRect() const { return m_miniRenderRect; } - void paintMiniGraphAndViewport(QWidget* owner); - bool paintMiniGraph(QWidget* owner, QPainter& p); + void paintMiniGraphAndViewport(QWidget* owner, QRect& miniRenderRect); + bool paintMiniGraph(QWidget* owner, QPainter& p, QRect& miniRenderRect); void paintNode(QPainter& p, FlowGraphNodeRef& node, int minY, int maxY); void paintHighlight(QPainter& p, const std::vector<BinaryNinja::DisassemblyTextLine>& lines, int nodeX, int nodeWidth, int x, int y, size_t line, int tagIndent); void paintEdge(QPainter& p, const FlowGraphNodeRef& node, const BinaryNinja::FlowGraphEdge& edge); - void showAddress(uint64_t addr, bool select = false); - void showIndex(size_t index); + void showAddress(uint64_t addr, bool select = false, bool center = false); + void showIndex(size_t index, bool center = false); void showTopNode(); void showNode(FlowGraphNodeRef node); void showLineInNode(FlowGraphNodeRef node, size_t lineIndex); diff --git a/ui/globalarea.h b/ui/globalarea.h index 94b0b715..2dffe3b0 100644 --- a/ui/globalarea.h +++ b/ui/globalarea.h @@ -44,7 +44,7 @@ public: virtual DockableTabStyle* duplicate() override; }; -class BINARYNINJAUIAPI GlobalAreaHideButton: public QWidget +class BINARYNINJAUIAPI CloseButton: public QWidget { Q_OBJECT @@ -53,7 +53,7 @@ class BINARYNINJAUIAPI GlobalAreaHideButton: public QWidget QTimer* m_timer; public: - GlobalAreaHideButton(); + CloseButton(); virtual QSize sizeHint() const override; protected: diff --git a/ui/hexeditor.h b/ui/hexeditor.h index 08baf21d..0374f51d 100644 --- a/ui/hexeditor.h +++ b/ui/hexeditor.h @@ -41,6 +41,7 @@ public: virtual void updateFonts() override; virtual StatusBarWidget* getStatusBarWidget() override; + virtual QWidget* getHeaderOptionsWidget() override; virtual void followPointer(); @@ -67,6 +68,19 @@ private: HexEditor* m_editor; }; + class HexEditorOptionsIconWidget: public QWidget + { + public: + HexEditorOptionsIconWidget(HexEditor* parent); + + private: + HexEditor* m_view; + ContextMenuManager* m_contextMenuManager; + Menu m_menu; + + void showMenu(); + }; + class HexEditorStatusBarWidget: public StatusBarWidget { public: @@ -108,7 +122,9 @@ private: void setCursorPos(uint64_t pos); void repositionCaret(); void updateCaret(); + void bindActions(); + static void addOptionsMenuActions(Menu& menu); std::pair<uint64_t, uint64_t> getSelectionRange(); bool isSelectionActive(); diff --git a/ui/ilchooser.h b/ui/ilchooser.h new file mode 100644 index 00000000..acf1bd22 --- /dev/null +++ b/ui/ilchooser.h @@ -0,0 +1,34 @@ +#pragma once + +#include "pane.h" +#include "menus.h" + +class BINARYNINJAUIAPI ILChooserWidget: public MenuHelper +{ + Q_OBJECT + + UIActionHandler* m_handler; + bool m_longDescription; + +public: + ILChooserWidget(QWidget* parent, UIActionHandler* handler, bool longDescription); + void updateStatus(BNFunctionGraphType current); + + static QString shortNameForILType(BNFunctionGraphType type); + static QString longNameForILType(BNFunctionGraphType type); + +protected: + virtual void showMenu() override; +}; + +class BINARYNINJAUIAPI ViewPaneHeaderILChooserWidget: public ViewPaneHeaderSubtypeWidget +{ + Q_OBJECT + + View* m_view; + ILChooserWidget* m_widget; + +public: + ViewPaneHeaderILChooserWidget(View* view, UIActionHandler* handler); + virtual void updateStatus(); +}; diff --git a/ui/linearview.h b/ui/linearview.h index bca85e1b..6a319443 100644 --- a/ui/linearview.h +++ b/ui/linearview.h @@ -10,6 +10,7 @@ #include "statusbarwidget.h" #include "uicontext.h" #include "instructionedit.h" +#include "ilchooser.h" #include <assembledialog.h> #define LINEAR_VIEW_UPDATE_CHECK_INTERVAL 200 @@ -107,6 +108,19 @@ class BINARYNINJAUIAPI LinearView: public QAbstractScrollArea, public View, publ LinearView* m_view; }; + class LinearViewOptionsIconWidget: public QWidget + { + public: + LinearViewOptionsIconWidget(LinearView* parent); + + private: + LinearView* m_view; + ContextMenuManager* m_contextMenuManager; + Menu m_menu; + + void showMenu(); + }; + class LinearViewStatusBarWidget: public StatusBarWidget { public: @@ -116,6 +130,7 @@ class BINARYNINJAUIAPI LinearView: public QAbstractScrollArea, public View, publ private: LinearView* m_view; LinearViewOptionsWidget* m_options; + ILChooserWidget* m_chooser; }; BinaryViewRef m_data; @@ -138,6 +153,9 @@ class BINARYNINJAUIAPI LinearView: public QAbstractScrollArea, public View, publ uint64_t m_navByRefTarget; bool m_navByRef = false; bool m_doubleClickLatch = false; + FunctionRef m_relatedHighlightFunction; + std::set<size_t> m_relatedIndexHighlights; + std::set<uint64_t> m_relatedInstructionHighlights; SettingsRef m_settings; DisassemblySettingsRef m_options; @@ -177,6 +195,8 @@ class BINARYNINJAUIAPI LinearView: public QAbstractScrollArea, public View, publ void scrollLines(int count); void bindActions(); + static void addOptionsMenuActions(Menu& menu); + void getHexDumpLineBytes(const BinaryNinja::LinearDisassemblyLine& line, size_t& skippedBytes, size_t& totalBytes, size_t& totalCols); @@ -330,7 +350,7 @@ public: virtual size_t getCurrentILInstructionIndex() override; virtual bool navigate(uint64_t offset) override; virtual bool navigateToFunction(FunctionRef func, uint64_t offset) override; - virtual bool navigateToViewLocation(const ViewLocation& viewLocation) override; + virtual bool navigateToViewLocation(const ViewLocation& viewLocation, bool center = false) override; virtual std::string getNavigationMode() override; virtual void setNavigationMode(std::string mode) override; @@ -356,6 +376,8 @@ public: virtual void updateFonts() override; virtual StatusBarWidget* getStatusBarWidget() override; + virtual ViewPaneHeaderSubtypeWidget* getHeaderSubtypeWidget() override; + virtual QWidget* getHeaderOptionsWidget() override; virtual void followPointer(); @@ -379,6 +401,10 @@ public: virtual bool goToReference(FunctionRef func, uint64_t source, uint64_t target) override; QFont getFont() override { return m_render.getFont(); } + virtual void clearRelatedHighlights() override; + virtual void setRelatedIndexHighlights(FunctionRef func, const std::set<size_t>& related) override; + virtual void setRelatedInstructionHighlights(FunctionRef func, const std::set<uint64_t>& related) override; + static void registerActions(); protected: diff --git a/ui/minigraph.h b/ui/minigraph.h index 90a0c318..69b59768 100644 --- a/ui/minigraph.h +++ b/ui/minigraph.h @@ -13,19 +13,31 @@ class FlowGraphWidget; class Menu; class ViewFrame; -class BINARYNINJAUIAPI MiniGraph: public SidebarWidget +class BINARYNINJAUIAPI MiniGraph: public SidebarWidget, public UIContextNotification { Q_OBJECT - ViewFrame* m_frame; + ViewFrame* m_frame = nullptr; FlowGraphWidget* m_flowGraphWidget = nullptr; + QWidget* m_header = nullptr; + bool m_popout; + QRect m_miniRenderRect; public: - MiniGraph(ViewFrame* frame); + MiniGraph(bool popout = false); ~MiniGraph(); + // Called when used in sidebar virtual void notifyViewChanged(ViewFrame* frame) override; + // Called when popped out of sidebar + virtual void OnViewChange(UIContext* context, ViewFrame* frame, const QString& type) override; + + virtual QWidget* headerWidget() override { return m_header; } + virtual QSize sizeHint() const override { return QSize(200, 200); } + + void setSource(ViewFrame* frame, FlowGraphWidget* graphView); + protected: virtual void contextMenuEvent(QContextMenuEvent* event) override; virtual void mouseMoveEvent(QMouseEvent* event) override; @@ -35,6 +47,8 @@ protected: public Q_SLOTS: void notifyUpdate(); + void graphDestroyed(); + void newPane(); }; diff --git a/ui/pane.h b/ui/pane.h new file mode 100644 index 00000000..dfda17a4 --- /dev/null +++ b/ui/pane.h @@ -0,0 +1,356 @@ +#pragma once + +#include <QtWidgets/QWidget> +#include <QtWidgets/QSplitter> +#include <QtWidgets/QStackedWidget> +#include <QtWidgets/QVBoxLayout> +#include <QtWidgets/QRubberBand> +#include "uicontext.h" +#include "clickablelabel.h" + +class ViewFrame; +class FeatureMap; +class PaneHeader; +class CloseButton; +class TabDragIndicator; + +class BINARYNINJAUIAPI Pane: public QWidget +{ + Q_OBJECT + + QWidget* m_widget; + QWidget* m_headerContainer = nullptr; + PaneHeader* m_header = nullptr; + CloseButton* m_closeButton = nullptr; + bool m_active = false; + +public: + Pane(QWidget* widget); + + QWidget* widget() const { return m_widget; } + virtual bool canSplitPane() const { return false; } + virtual Pane* createSplitPane() { return nullptr; } + virtual void updateStatus(); + virtual void focus(); + virtual QString title() = 0; + void closePane(); + void splitPane(Qt::Orientation orientation); + + virtual void setIsSinglePane(bool isSinglePane); + virtual void setIsActivePane(bool active); + virtual Qt::Orientation defaultSplitDirection() const { return Qt::Horizontal; } + virtual void setDefaultSplitDirection(Qt::Orientation orientation); + +protected: + void init(PaneHeader* header); + +Q_SIGNALS: + void paneCloseRequested(); + void paneSplitRequested(Pane* newPane, Qt::Edge edge); + void movePane(Pane* target, Qt::Edge edge); + void newWindowForPane(QScreen* screen, QPoint pos); + void notifyViewChanged(ViewFrame* frame); + +public Q_SLOTS: + void splitButtonClicked(Qt::Orientation orientation); + void closeButtonClicked(); + void headerClicked(); + void movePaneRequested(Pane* target, Qt::Edge edge); + void newWindowForPaneRequested(QScreen* screen, QPoint pos); +}; + +class BINARYNINJAUIAPI SplitButton: public ClickableIcon +{ + Q_OBJECT + + Qt::Orientation m_defaultOrientation; + bool m_mouseInside = false; + bool m_inverted = false; + + void setIconForOrientation(Qt::Orientation orientation); + +public: + SplitButton(); + + void setDefaultOrientation(Qt::Orientation orientation); + Qt::Orientation orientation() const; + Qt::Orientation defaultOrientation() const { return m_defaultOrientation; } + +protected: + virtual void enterEvent(QEnterEvent* event) override; + virtual void leaveEvent(QEvent* event) override; + virtual bool eventFilter(QObject* obj, QEvent* event) override; +}; + +class BINARYNINJAUIAPI PaneHeader: public QWidget +{ + Q_OBJECT + + Pane* m_owner = nullptr; + std::optional<QPoint> m_dragStart; + TabDragIndicator* m_dragIndicator = nullptr; + bool m_dragNewWindow = false; + Pane* m_dropTarget = nullptr; + Qt::Edge m_dropEdge = Qt::RightEdge; + QRubberBand* m_dropIndicator = nullptr; + +public: + PaneHeader(); + + void setOwner(Pane* pane) { m_owner = pane; } + +protected: + virtual void mousePressEvent(QMouseEvent* event) override; + virtual void mouseMoveEvent(QMouseEvent* event) override; + virtual void mouseReleaseEvent(QMouseEvent* event) override; + +Q_SIGNALS: + void paneCloseRequested(); + void paneSplitRequested(Qt::Orientation orientation); + void movePane(Pane* target, Qt::Edge edge); + void newWindowForPane(QScreen* screen, QPoint pos); + void headerClicked(); +}; + +class ViewFrame; +class ViewPaneHeader; + +class BINARYNINJAUIAPI ViewPane: public Pane +{ + Q_OBJECT + + ViewFrame* m_frame; + UIActionHandler m_actionHandler; + ViewPaneHeader* m_header; + +public: + ViewPane(ViewFrame* frame); + + ViewFrame* viewFrame() const { return m_frame; } + virtual bool canSplitPane() const override { return true; } + virtual Pane* createSplitPane() override; + virtual void updateStatus() override; + virtual Qt::Orientation defaultSplitDirection() const override; + virtual void setDefaultSplitDirection(Qt::Orientation orientation) override; + virtual void focus() override; + virtual QString title() override; + +private Q_SLOTS: + void viewChanged(ViewFrame* frame); + void viewChangeRequested(QString type); +}; + +class DataTypeList; +class ViewList; +class SyncGroupWidget; + +class BINARYNINJAUIAPI ViewPaneHeaderSubtypeWidget: public QWidget +{ + Q_OBJECT + +public: + ViewPaneHeaderSubtypeWidget() {} + virtual void updateStatus() = 0; +}; + +class BINARYNINJAUIAPI ViewPaneHeader: public PaneHeader +{ + Q_OBJECT + + ViewPane* m_owner; + DataTypeList* m_dataTypeList; + ViewList* m_viewList; + SyncGroupWidget* m_syncGroup; + + View* m_subtypeView = nullptr; + ViewPaneHeaderSubtypeWidget* m_subtypeWidget = nullptr; + QWidget* m_optionsWidget = nullptr; + QStackedWidget* m_subtypeWidgetContainer; + QStackedWidget* m_optionsWidgetContainer; + + SplitButton* m_splitButton; + +public: + ViewPaneHeader(ViewPane* owner, UIActionHandler* handler); + + void updateStatus(); + Qt::Orientation defaultSplitDirection() const; + void setDefaultSplitDirection(Qt::Orientation orientation); + +Q_SIGNALS: + void viewChanged(QString type); + +private Q_SLOTS: + void splitButtonClicked(); + void viewChangeRequested(QString type); + void updateViewType(ViewFrame* frame); +}; + +class BINARYNINJAUIAPI WidgetPane: public Pane +{ + Q_OBJECT + + QString m_title; + +public: + WidgetPane(QWidget* widget, QString title); + virtual QString title() override { return m_title; } + virtual void updateStatus() override; + +Q_SIGNALS: + void updateWidgetStatus(); +}; + +class BINARYNINJAUIAPI WidgetPaneHeader: public PaneHeader +{ + Q_OBJECT + + QString m_title; + +public: + WidgetPaneHeader(const QString& title); + +protected: + virtual void paintEvent(QPaintEvent* event) override; +}; + +class SplitPaneWidget; + +class BINARYNINJAUIAPI SplitPaneContainer: public QWidget +{ + Q_OBJECT + + Pane* m_pane = nullptr; + QSplitter* m_splitter = nullptr; + SplitPaneContainer* m_parent = nullptr; + std::vector<SplitPaneContainer*> m_children; + Pane* m_currentChild = nullptr; + ViewPane* m_currentViewPane = nullptr; + QVBoxLayout* m_layout; + FileContext* m_fileContext = nullptr; + + SplitPaneContainer(const std::vector<SplitPaneContainer*>& children, Qt::Orientation orientation); + + void removeChild(SplitPaneContainer* child); + void promoteChild(SplitPaneContainer* child); + void updateDefaultSplitDirectionForColumnCount(uint64_t count); + void removePaneForRelocation(); + void emitNewWindowForPane(SplitPaneWidget* paneWidget, QRect rect); + void openForColumnCount(Pane* pane, Qt::Orientation primaryDirection, uint64_t count); + void deactivateIfCurrent(Pane* pane); + void notifyCurrentChanged(Pane* pane); + +public: + SplitPaneContainer(Pane* initial); + Pane* currentPane() const { return m_currentChild; } + ViewPane* currentViewPane() const { return m_currentViewPane; } + void notifyFocused(); + void updateStatus(); + + void enumeratePanes(const std::function<void(Pane*)>& func); + void enumerateViewPanes(const std::function<void(ViewPane*)>& func); + + bool isSinglePane(); + bool canSplitCurrentPane(); + void closeCurrentPane(); + void splitCurrentPane(Qt::Orientation orientation); + Qt::Orientation defaultSplitDirection() const; + void nextPane(); + void prevPane(); + void focusPaneForEdge(Qt::Edge edge); + + SplitPaneContainer* root(); + static SplitPaneContainer* containerForWidget(QWidget* widget); + + FileContext* fileContext() const { return m_fileContext; } + void setFileContext(FileContext* fileContext) { m_fileContext = fileContext; } + + void open(Pane* pane, Qt::Orientation primaryDirection = Qt::Vertical); + +Q_SIGNALS: + void paneClosed(Pane* pane); + void currentChanged(Pane* pane); + void layoutChanged(); + void notifyViewChanged(ViewFrame* frame); + void lastPaneClosed(); + void newWindowForPane(SplitPaneWidget* paneWidget, QRect rect); + +private Q_SLOTS: + void paneCloseRequested(); + void paneSplitRequested(Pane* newPane, Qt::Edge edge); + void movePane(Pane* target, Qt::Edge edge); + void newWindowForPaneRequested(QScreen* screen, QPoint pos); + void paneViewChanged(ViewFrame* frame); + void childPaneClosed(Pane* pane); + void childLayoutChanged(); + void childViewChanged(ViewFrame* frame); +}; + +class BINARYNINJAUIAPI SplitPaneWidget: public QWidget +{ + Q_OBJECT + + SplitPaneContainer* m_container; + + QStackedWidget* m_featureMapContainer; + std::map<BinaryViewRef, FeatureMap*> m_featureMaps; + QSplitter* m_featureMapSplitter; + bool m_rightSideFeatureMap = true; + + UIActionHandler m_actionHandler; + + void bindActions(); + +public: + SplitPaneWidget(Pane* initial, FileContext* fileContext); + Pane* currentPane() const; + ViewPane* currentViewPane() const; + ViewFrame* currentViewFrame() const; + SplitPaneContainer* container() const { return m_container; } + FileContext* fileContext() const { return m_container->fileContext(); } + + void enumeratePanes(const std::function<void(Pane*)>& func); + void enumerateViewPanes(const std::function<void(ViewPane*)>& func); + + void createFeatureMap(); + void recreateFeatureMaps(); + void refreshFeatureMap(); + void updateFeatureMapLocation(const ViewLocation& location); + BinaryViewRef getCurrentBinaryView(); + + void updateStatus(); + + bool isSinglePane(); + bool canSplitCurrentPane(); + void closeCurrentPane(); + void splitCurrentPane(Qt::Orientation orientation); + Qt::Orientation defaultSplitDirection() const; + void nextPane(); + void prevPane(); + void focusPaneForEdge(Qt::Edge edge); + + QString getTabName(); + + void open(Pane* pane, Qt::Orientation primaryDirection = Qt::Vertical); + + bool closeRequest(); + void closing(); + + static void registerActions(); + +Q_SIGNALS: + void paneClosed(Pane* pane); + void currentChanged(Pane* pane); + void layoutChanged(); + void notifyViewChanged(ViewFrame* frame); + void newWindowForPane(SplitPaneWidget* paneWidget, QRect rect); + +private Q_SLOTS: + void containerPaneClosed(Pane* pane); + void containerCurrentChanged(Pane* pane); + void containerLayoutChanged(); + void containerNotifyViewChanged(ViewFrame* frame); + void containerLastPaneClosed(); + void containerNewWindowForPane(SplitPaneWidget* paneWidget, QRect rect); + void featureMapSplitterMoved(int pos, int idx); +}; diff --git a/ui/reflectionview.h b/ui/reflectionview.h deleted file mode 100644 index 1be8cc50..00000000 --- a/ui/reflectionview.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include <QtGui/QMouseEvent> -#include <QtGui/QPaintEvent> -#include <QtWidgets/QWidget> - -#include "binaryninjaapi.h" -#include "dockhandler.h" -#include "uitypes.h" - -class ContextMenuManager; -class DisassemblyContainer; -class FlowGraphWidget; -class Menu; -class ViewFrame; - -class BINARYNINJAUIAPI ReflectionView: public QWidget, public DockContextHandler -{ - Q_OBJECT - Q_INTERFACES(DockContextHandler) - - ViewFrame* m_frame; - BinaryViewRef m_data; - DisassemblyContainer* m_disassemblyContainer; - std::map<BNFunctionGraphType, BNFunctionGraphType> m_ilMap; - bool m_ilSync; - bool m_locationSync; - - BNFunctionGraphType m_lastSrcILViewType = NormalFunctionGraph; - BNFunctionGraphType m_lastTgtILViewType = NormalFunctionGraph; - bool m_ilSyncOverride = false; - -public: - ReflectionView(ViewFrame* frame, BinaryViewRef data); - ~ReflectionView(); - - FlowGraphWidget* getFlowGraphWidget(); - - void toggleILSync(); - void toggleLocationSync(); - - virtual void notifyFontChanged() override; - virtual void notifyViewLocationChanged(View* view, const ViewLocation& viewLocation) override; - virtual void notifyVisibilityChanged(bool visible) override; - virtual bool shouldBeVisible(ViewFrame* frame) override; - -protected: - virtual void contextMenuEvent(QContextMenuEvent* event) override; -}; diff --git a/ui/searchresult.h b/ui/searchresult.h index 5905ae0d..fe2d075e 100644 --- a/ui/searchresult.h +++ b/ui/searchresult.h @@ -13,7 +13,6 @@ #include <QtWidgets/QToolButton> #include "binaryninjaapi.h" #include "dockhandler.h" -#include "viewframe.h" #include "filter.h" #include "expandablegroup.h" @@ -72,7 +71,6 @@ class BINARYNINJAUIAPI SearchResultModel: public QAbstractTableModel protected: QWidget* m_owner; BinaryViewRef m_data; - ViewFrame* m_view; BinaryNinja::FindParameters m_params; std::vector<SearchResultItem> m_refs; mutable size_t m_columnWidths[4]; @@ -93,7 +91,7 @@ public: EndOfColumn = 4 }; - SearchResultModel(QWidget* parent, BinaryViewRef data, ViewFrame* view); + SearchResultModel(QWidget* parent, BinaryViewRef data); virtual ~SearchResultModel(); virtual QModelIndex index(int row, int col, const QModelIndex& parent = QModelIndex()) const override; @@ -150,7 +148,6 @@ class BINARYNINJAUIAPI SearchResultTable: public QTableView { Q_OBJECT - ViewFrame* m_view; SearchResultModel* m_table; SearchResultFilterProxyModel* m_model; SearchResultItemDelegate* m_itemDelegate; @@ -164,7 +161,7 @@ class BINARYNINJAUIAPI SearchResultTable: public QTableView bool m_cacheThreadShouldExit; public: - SearchResultTable(SearchResultWidget* parent, ViewFrame* view, BinaryViewRef data); + SearchResultTable(SearchResultWidget* parent, BinaryViewRef data); virtual ~SearchResultTable(); void addSearchResult(const SearchResultItem& addr); @@ -203,12 +200,14 @@ Q_SIGNALS: }; class SearchProgressBar; -class BINARYNINJAUIAPI SearchResultWidget: public QWidget, public DockContextHandler +class BINARYNINJAUIAPI SearchResultWidget: public QWidget { Q_OBJECT - Q_INTERFACES(DockContextHandler) - ViewFrame* m_view; + UIActionHandler m_actionHandler; + ContextMenuManager* m_contextMenuManager = nullptr; + Menu* m_menu = nullptr; + BinaryViewRef m_data; SearchResultTable* m_table; QLabel* m_label; @@ -221,10 +220,10 @@ class BINARYNINJAUIAPI SearchResultWidget: public QWidget, public DockContextHan virtual void contextMenuEvent(QContextMenuEvent* event) override; public: - SearchResultWidget(ViewFrame* frame, BinaryViewRef data); + SearchResultWidget(BinaryViewRef data); ~SearchResultWidget(); - virtual void notifyFontChanged() override; + void notifyFontChanged(); void startNewFind(const BinaryNinja::FindParameters& params); virtual QString getHeaderText(); diff --git a/ui/sidebar.h b/ui/sidebar.h index 7d8d1b02..52ea38de 100644 --- a/ui/sidebar.h +++ b/ui/sidebar.h @@ -11,6 +11,7 @@ class SidebarEntry; class MainWindow; class ContextMenuManager; +class SplitPaneWidget; struct SidebarIcon { @@ -125,6 +126,7 @@ class BINARYNINJAUIAPI SidebarWidgetContainer: public QWidget QSplitter* m_contentSplitter; QStackedWidget* m_contentStackedWidget; QStackedWidget* m_referenceStackedWidget; + SplitPaneWidget* m_panes; ViewFrame* m_frame; QString m_dataType; BinaryViewRef m_data; @@ -133,9 +135,9 @@ class BINARYNINJAUIAPI SidebarWidgetContainer: public QWidget 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::map<SplitPaneWidget*, std::map<QString, std::pair<SidebarWidgetType*, SidebarWidgetType*>>> m_priorWidgets; + std::map<SplitPaneWidget*, std::map<QString, QList<int>>> m_priorContentSplitterSizes; + std::map<SplitPaneWidget*, 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; @@ -150,8 +152,9 @@ public: SidebarWidgetContainer(); void setSplitter(QSplitter* splitter); - void setActiveContext(ViewFrame* frame, const QString& dataType, BinaryViewRef data); + void setActiveContext(SplitPaneWidget* panes, ViewFrame* frame, const QString& dataType, BinaryViewRef data); void destroyContext(ViewFrame* frame); + void destroyContext(SplitPaneWidget* panes); bool isContentActive() const { return m_contentActive != nullptr; } bool isActive(SidebarWidgetType* type) const { return m_contentActive == type || m_referenceActive == type; } @@ -178,7 +181,8 @@ public: void toggleSidebar(); - void moveContextToContainer(ViewFrame* frame, SidebarWidgetContainer* target); + void moveContextToContainer(SplitPaneWidget* panes, const std::vector<ViewFrame*>& frames, + SidebarWidgetContainer* target); Q_SIGNALS: void showContents(); @@ -208,13 +212,15 @@ public: SidebarWidgetContainer* container() const { return m_currentContainer; } void setContainer(SidebarWidgetContainer* container); - void setActiveContext(ViewFrame* frame, const QString& dataType, BinaryViewRef data); + void setActiveContext(SplitPaneWidget* panes, 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 deactivate(SidebarWidgetType* type); + void deactivate(const QString& name); void updateTheme(); void updateFonts(); diff --git a/ui/syncgroup.h b/ui/syncgroup.h new file mode 100644 index 00000000..5a9828f9 --- /dev/null +++ b/ui/syncgroup.h @@ -0,0 +1,39 @@ +#pragma once + +#include "viewframe.h" +#include "clickablelabel.h" + +class BINARYNINJAUIAPI SyncGroup +{ + std::set<ViewFrame*> m_members; + int m_id; + +public: + SyncGroup(int id); + + void addMember(ViewFrame* frame); + void removeMember(ViewFrame* frame); + bool isEmpty() const; + int identifier() const { return m_id; } + bool contains(ViewFrame* frame) const; + const std::set<ViewFrame*>& members() const { return m_members; } + + void syncLocation(ViewFrame* frame, View* view, const ViewLocation& location); + + static void syncToTarget(View* srcView, ViewFrame* targetFrame, const ViewLocation& location); +}; + +class BINARYNINJAUIAPI SyncGroupWidget: public ClickableIcon +{ + Q_OBJECT + + ViewFrame* m_frame; + +public: + SyncGroupWidget(ViewFrame* frame); + + void updateStatus(); + +private Q_SLOTS: + void handleClick(); +}; diff --git a/ui/uicontext.h b/ui/uicontext.h index de58a74b..4c2a5853 100644 --- a/ui/uicontext.h +++ b/ui/uicontext.h @@ -21,6 +21,7 @@ class ViewLocation; class Sidebar; class SidebarWidgetContainer; class GlobalArea; +class Pane; struct SelectionInfoForXref; /*! @@ -214,6 +215,12 @@ public: */ virtual void createTabForWidget(const QString& name, QWidget* widget) = 0; /*! + Open a new pane in the active tab + \param pane Pane widget to open + \param primaryDirection Primary axis for content in pane (determines default split direction) + */ + virtual void openPane(Pane* pane, Qt::Orientation primaryDirection = Qt::Vertical) = 0; + /*! Get a list of all tabs as QWidgets \return All tabs */ @@ -244,8 +251,9 @@ public: /*! Close the tab with the given QWidget \param tab QWidget which is in a tab + \param closeWindowIfLast If false, displays the new tab page if the widget was the last tab */ - virtual void closeTab(QWidget* tab) = 0; + virtual void closeTab(QWidget* tab, bool closeWindowIfLast = false) = 0; /*! Get the QWidget in the currently open tab \return QWidget for current tab. Qt claims "this value is never 0 (but if you try hard enough, it can be)" @@ -259,11 +267,18 @@ public: */ virtual View* getViewForTab(QWidget* tab) = 0; /*! - Get the ViewFrame associated with the given QWidget, if it exists + Get the active ViewFrame associated with the given QWidget, if it exists \param tab QWidget which could be a ViewFrame \return ViewFrame for the QWidget (which is likely itself), or nullptr if the QWidget is not a ViewFrame */ - virtual ViewFrame* getViewFrameForTab(QWidget* tab) = 0; + virtual ViewFrame* getViewFrameForTab(QWidget* tab) const = 0; + + /*! + Get all ViewFrame instances associated with the given QWidget, if they exist + \param tab QWidget which could contain a ViewFrame + \return List of ViewFrame objects for the QWidget + */ + virtual std::vector<ViewFrame*> getAllViewFramesForTab(QWidget* tab) const = 0; virtual bool openFilename(const QString& path, bool openOptions = false); virtual ViewFrame* openFileContext(FileContext* file, const QString& forcedView = "", bool addTab = true); @@ -314,6 +329,12 @@ public: static UIContext* contextForWidget(QWidget* widget); static UIContext* activeContext(); static std::set<UIContext*> allContexts(); + + static QWidget* topLevelAt(const QPoint& pt, QWidget* ignoreWidget = nullptr); + + static QRect placeNewTopLevelWindow(QScreen* screen, const QPoint& pos, QWidget* existingWidget); + + static ViewFrame* currentViewFrameForWidget(QWidget* widget); }; Q_DECLARE_METATYPE(UIContext*) diff --git a/ui/viewframe.h b/ui/viewframe.h index 6681582f..80abb3d5 100644 --- a/ui/viewframe.h +++ b/ui/viewframe.h @@ -94,6 +94,7 @@ class FeatureMap; class StatusBarWidget; class ViewNavigationMode; class TransformParameterDialog; +class ViewPaneHeaderSubtypeWidget; // struct BinaryNinjaCore::LinearDisassemblyLine; @@ -159,7 +160,10 @@ public: virtual bool navigate(uint64_t offset) = 0; virtual bool navigateToFunction(FunctionRef func, uint64_t offset); virtual bool goToReference(FunctionRef func, uint64_t source, uint64_t target); - virtual bool navigateToViewLocation(const ViewLocation& viewLocation) { return false; } + virtual bool navigateToViewLocation(const ViewLocation& viewLocation, bool center = false); + + bool navigateOnOtherPane(uint64_t offset); + bool navigateToFunctionOnOtherPane(FunctionRef func, uint64_t offset); bool isBinaryDataNavigable() { return m_binaryDataNavigable; } void setBinaryDataNavigable(bool navigable) { m_binaryDataNavigable = navigable; } @@ -197,6 +201,8 @@ public: virtual void navigateToHistoryEntry(BinaryNinja::Ref<HistoryEntry> entry); virtual StatusBarWidget* getStatusBarWidget() { return nullptr; } + virtual ViewPaneHeaderSubtypeWidget* getHeaderSubtypeWidget() { return nullptr; } + virtual QWidget* getHeaderOptionsWidget() { return nullptr; } static View* getViewFromWidget(QWidget* widget); @@ -224,6 +230,11 @@ public: QString viewType(); void updateCrossReferenceSelection(ViewFrame* frame = nullptr); + void forceSyncFromView(ViewFrame* frame = nullptr); + + virtual void clearRelatedHighlights() {} + virtual void setRelatedIndexHighlights(FunctionRef func, const std::set<size_t>& related) { (void)func; (void)related; } + virtual void setRelatedInstructionHighlights(FunctionRef func, const std::set<uint64_t>& related) { (void)func; (void)related; } static void registerActions(); }; @@ -297,6 +308,7 @@ public: }; class SymbolsView; +class ViewPane; class BINARYNINJAUIAPI ViewFrame : public QWidget { @@ -305,6 +317,7 @@ class BINARYNINJAUIAPI ViewFrame : public QWidget private: QWidget* createView(const QString& typeName, ViewType* type, BinaryViewRef data, bool createDynamicWidgets = true); BinaryNinja::Ref<HistoryEntry> getHistoryEntry(); + ViewFrame* searchForOtherPane(const std::function<void(const std::function<void(ViewPane*)>&)>& enumerator); FileContext* m_context; bool m_fileContentsLock = true; // file contents protection from accidental modification in the UI @@ -318,10 +331,7 @@ private: std::list<BinaryNinja::Ref<HistoryEntry>> m_back, m_forward; bool m_graphViewPreferred = false; std::vector<QString> m_viewTypePriority; - QStackedWidget* m_featureMapContainer; - std::map<BinaryViewRef, FeatureMap*> m_featureMaps; - QSplitter* m_featureMapSplitter; - bool m_rightSideFeatureMap = true; + int m_preferredSyncGroup = 1; UIActionHandler m_actionHandler; @@ -371,10 +381,6 @@ public: bool isGraphViewPreferred() { return m_graphViewPreferred; } void setGraphViewPreferred(bool graphViewPreferred) { m_graphViewPreferred = graphViewPreferred; } void focus(); - void createFeatureMap(); - void recreateFeatureMaps(); - void refreshFeatureMap(); - void updateFeatureMapLocation(const ViewLocation& location); QWidget* getExtendedView(const QString& name, bool create = false); @@ -398,7 +404,7 @@ public: bool navigateToFunction(FunctionRef func, uint64_t offset, bool updateInfo = true, bool addHistoryEntry = true); bool goToReference(BinaryViewRef data, FunctionRef func, uint64_t source, uint64_t target, bool addHistoryEntry = true); bool navigateToViewLocation(BinaryViewRef data, const ViewLocation& viewLocation, - bool addHistoryEntry = true); + bool addHistoryEntry = true, bool center = false); bool navigateToHistoryEntry(BinaryNinja::Ref<HistoryEntry> entry); QString getTypeForView(QWidget* view) const; QString getDataTypeForView(const QString& type) const; @@ -436,7 +442,6 @@ public: void setCurrentFunction(FunctionRef func); void updateCrossReferences(); void updateCrossReferenceSelection(); - void showPinnedCrossReferences(); void nextCrossReference(); void prevCrossReference(); @@ -448,13 +453,6 @@ public: void nextTag(); void prevTag(); - void startNewFind(const BinaryNinja::FindParameters& params); - bool updateSearchProgress(uint64_t cur, uint64_t total); - void notifySearchCompleted(); - void addSearchResult(uint64_t addr, const BinaryNinja::DataBuffer& match); - void addSearchResult(uint64_t addr, const BinaryNinja::DataBuffer& match, - const BinaryNinja::LinearDisassemblyLine& line); - virtual UIActionContext actionContext(); void bindActions(); static void registerActions(); @@ -463,13 +461,24 @@ public: static bool lineHasInstructionToken(const BinaryNinja::DisassemblyTextLine& line); static QString getDisassemblyText(const std::vector<BinaryNinja::DisassemblyTextLine>& lines); + int preferredSyncGroup() const { return m_preferredSyncGroup; } + void setPreferredSyncGroup(int syncGroup) { m_preferredSyncGroup = syncGroup; } + void disableSync(); + void enableSync(); + void enableSync(int id); + void newSyncGroup(); + void toggleSync(); + SyncGroup* syncGroup(); + + void syncToOtherViews(); + void forceSyncFromView(); + + ViewFrame* getOtherPane(); + public Q_SLOTS: virtual void assemble(); virtual void compile(); -private Q_SLOTS: - void splitterMoved(int pos, int idx); - Q_SIGNALS: void notifyCloseFeatureMap(bool recreate); void notifyViewChanged(ViewFrame* frame); diff --git a/ui/viewlist.h b/ui/viewlist.h new file mode 100644 index 00000000..c1926106 --- /dev/null +++ b/ui/viewlist.h @@ -0,0 +1,31 @@ +#pragma once + +#include <QtWidgets/QLabel> +#include <string> +#include "viewframe.h" +#include "menus.h" +#include "action.h" + +class BINARYNINJAUIAPI ViewList: public MenuHelper +{ + Q_OBJECT + + FileContext* m_context = nullptr; + QString m_currentType; + std::string m_currentDataType; + + UIActionHandler* m_handler; + +public: + ViewList(QWidget* parent); + + void bindActions(UIActionHandler* handler); + void addMenuActions(Menu* menu, const QString& group); + void setCurrentViewType(ViewFrame* view, const QString& type); + +protected: + virtual void showMenu(); + +Q_SIGNALS: + void viewChanged(QString type); +}; diff --git a/ui/xreflist.h b/ui/xreflist.h index 403178f2..73ae48ef 100644 --- a/ui/xreflist.h +++ b/ui/xreflist.h @@ -236,6 +236,7 @@ class BINARYNINJAUIAPI CrossReferenceTreeModel : public QAbstractItemModel ViewFrame* m_view; std::vector<XrefItem> m_refs; size_t m_maxUIItems; + std::optional<BNFunctionGraphType> m_graphType; public: CrossReferenceTreeModel(QWidget* parent, BinaryViewRef data, ViewFrame* view); @@ -257,6 +258,7 @@ public: ViewFrame* getView() const { return m_view; } virtual void updateMaxUIItems(size_t value) { m_maxUIItems = value; } size_t getMaxUIItems() const { return m_maxUIItems; } + void setGraphType(BNFunctionGraphType type) { m_graphType = type; } }; @@ -269,6 +271,7 @@ class BINARYNINJAUIAPI CrossReferenceTableModel : public QAbstractTableModel ViewFrame* m_view; std::vector<XrefItem> m_refs; size_t m_maxUIItems; + std::optional<BNFunctionGraphType> m_graphType; public: enum ColumnHeaders @@ -295,6 +298,7 @@ public: ViewFrame* getView() const { return m_view; } virtual void updateMaxUIItems(size_t value) { m_maxUIItems = value; } size_t getMaxUIItems() const { return m_maxUIItems; } + void setGraphType(BNFunctionGraphType type) { m_graphType = type; } }; @@ -409,6 +413,7 @@ public: virtual int filteredCount() const override; void updateTextFilter(const QString& filterText); virtual void updateMaxUIItems(size_t count) override; + void setGraphType(BNFunctionGraphType type) { m_tree->setGraphType(type); } Q_SIGNALS: void newSelection(); @@ -443,6 +448,7 @@ public: virtual int leafCount() const override; virtual int filteredCount() const override; virtual void updateMaxUIItems(size_t count) override; + void setGraphType(BNFunctionGraphType type) { m_table->setGraphType(type); } public Q_SLOTS: void updateTextFilter(const QString& filterText); @@ -466,6 +472,7 @@ class BINARYNINJAUIAPI CrossReferenceWidget: public SidebarWidget, public UICont CrossReferenceTree* m_tree; CrossReferenceContainer* m_container; bool m_useTableView; + std::optional<BNFunctionGraphType> m_graphType; QTimer* m_hoverTimer; QPoint m_hoverPos; @@ -482,6 +489,8 @@ class BINARYNINJAUIAPI CrossReferenceWidget: public SidebarWidget, public UICont bool m_pinned; bool m_uiMaxItemsExceeded = false; + QWidget* m_header = nullptr; + virtual void contextMenuEvent(QContextMenuEvent* event) override; virtual void wheelEvent(QWheelEvent* e) override; @@ -509,14 +518,18 @@ public: bool tableView() const { return m_useTableView; } bool uiMaxItemsExceeded() const { return m_uiMaxItemsExceeded; } void setUIMaxItemsExceeded(bool value) { m_uiMaxItemsExceeded = value; } + void setGraphType(BNFunctionGraphType type); virtual void focus() override; virtual void OnNewSelectionForXref(UIContext* context, ViewFrame* frame, View* view, const SelectionInfoForXref& selection) override; + virtual QWidget* headerWidget() override { return m_header; } + private Q_SLOTS: void hoverTimerEvent(); + void newPinnedPane(); public Q_SLOTS: void referenceActivated(const QModelIndex& idx); |
