summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGlenn Smith <glenn@vector35.com>2023-11-06 15:28:59 -0500
committerGlenn Smith <glenn@vector35.com>2023-11-06 16:10:47 -0500
commit6436615567547349434351468012512505109552 (patch)
treee798973c25805377731c4a199f40379263bc128c
parent4108964609171327618627477163339656037111 (diff)
Type Browser
-rw-r--r--binaryninjaapi.h10
-rw-r--r--binaryninjacore.h24
-rw-r--r--binaryview.cpp102
-rw-r--r--python/binaryview.py74
-rw-r--r--python/types.py5
-rw-r--r--suite/api_test.py2
-rw-r--r--type.cpp4
-rw-r--r--ui/action.h1
-rw-r--r--ui/render.h5
-rw-r--r--ui/theme.h1
-rw-r--r--ui/tokenizedtextwidget.h254
-rw-r--r--ui/typebrowser.h524
-rw-r--r--ui/typeeditor.h168
-rw-r--r--ui/util.h3
-rw-r--r--ui/viewframe.h1
15 files changed, 1173 insertions, 5 deletions
diff --git a/binaryninjaapi.h b/binaryninjaapi.h
index d32af5ce..021069e5 100644
--- a/binaryninjaapi.h
+++ b/binaryninjaapi.h
@@ -30,6 +30,7 @@
#include <vector>
#include <map>
#include <unordered_map>
+#include <unordered_set>
#include <exception>
#include <functional>
#include <set>
@@ -3169,7 +3170,7 @@ namespace BinaryNinja {
{
BNTypeDefinitionLineType lineType;
std::vector<InstructionTextToken> tokens;
- Ref<Type> type, rootType;
+ Ref<Type> type, parentType, rootType;
std::string rootTypeName;
Ref<NamedTypeReference> baseType;
uint64_t baseOffset;
@@ -4505,6 +4506,13 @@ namespace BinaryNinja {
*/
std::vector<Confidence<Ref<Type>>> GetTypesReferenced(const QualifiedName& type, uint64_t offset);
+ std::unordered_set<QualifiedName> GetOutgoingDirectTypeReferences(const QualifiedName& type);
+ std::unordered_set<QualifiedName> GetOutgoingRecursiveTypeReferences(const QualifiedName& type);
+ std::unordered_set<QualifiedName> GetOutgoingRecursiveTypeReferences(const std::unordered_set<QualifiedName>& types);
+ std::unordered_set<QualifiedName> GetIncomingDirectTypeReferences(const QualifiedName& type);
+ std::unordered_set<QualifiedName> GetIncomingRecursiveTypeReferences(const QualifiedName& type);
+ std::unordered_set<QualifiedName> GetIncomingRecursiveTypeReferences(const std::unordered_set<QualifiedName>& types);
+
Ref<Structure> CreateStructureBasedOnFieldAccesses(const QualifiedName& type); // Unimplemented!
/*! Returns a list of virtual addresses called by the call site in the ReferenceSource
diff --git a/binaryninjacore.h b/binaryninjacore.h
index 6f46326a..995f2f4f 100644
--- a/binaryninjacore.h
+++ b/binaryninjacore.h
@@ -1630,6 +1630,7 @@ extern "C"
PaddingLineType,
UndefinedXrefLineType,
CollapsedPaddingLineType,
+ EmptyLineType,
} BNTypeDefinitionLineType;
typedef struct BNTypeDefinitionLine
@@ -1638,6 +1639,7 @@ extern "C"
BNInstructionTextToken* tokens;
size_t count;
BNType* type;
+ BNType* parentType;
BNType* rootType;
char* rootTypeName;
BNNamedTypeReference* baseType;
@@ -1868,7 +1870,22 @@ extern "C"
BraceOption3Color,
BraceOption4Color,
BraceOption5Color,
- BraceOption6Color
+ BraceOption6Color,
+
+ // Type class colors
+ VoidTypeColor,
+ StructureTypeColor,
+ EnumerationTypeColor,
+ FunctionTypeColor,
+ BoolTypeColor,
+ IntegerTypeColor,
+ FloatTypeColor,
+ PointerTypeColor,
+ ArrayTypeColor,
+ VarArgsTypeColor,
+ ValueTypeColor,
+ NamedTypeReferenceColor,
+ WideCharTypeColor,
} BNThemeColor;
// The following edge styles map to Qt's Qt::PenStyle enumeration
@@ -4127,6 +4144,11 @@ extern "C"
BINARYNINJACOREAPI BNTypeWithConfidence* BNGetTypesReferenced(
BNBinaryView* view, BNQualifiedName* type, uint64_t offset, size_t* count);
+ BINARYNINJACOREAPI BNQualifiedName* BNGetOutgoingDirectTypeReferences(BNBinaryView* view, BNQualifiedName* type, size_t* count);
+ BINARYNINJACOREAPI BNQualifiedName* BNGetOutgoingRecursiveTypeReferences(BNBinaryView* view, BNQualifiedName* types, size_t typeCount, size_t* count);
+ BINARYNINJACOREAPI BNQualifiedName* BNGetIncomingDirectTypeReferences(BNBinaryView* view, BNQualifiedName* type, size_t* count);
+ BINARYNINJACOREAPI BNQualifiedName* BNGetIncomingRecursiveTypeReferences(BNBinaryView* view, BNQualifiedName* types, size_t typeCount, size_t* count);
+
BINARYNINJACOREAPI void BNRegisterGlobalFunctionRecognizer(BNFunctionRecognizer* rec);
BINARYNINJACOREAPI bool BNGetStringAtAddress(BNBinaryView* view, uint64_t addr, BNStringReference* strRef);
diff --git a/binaryview.cpp b/binaryview.cpp
index 4bbd3aa3..077f2b4a 100644
--- a/binaryview.cpp
+++ b/binaryview.cpp
@@ -2491,6 +2491,108 @@ std::vector<Confidence<Ref<Type>>> BinaryView::GetTypesReferenced(const Qualifie
}
+unordered_set<QualifiedName> BinaryView::GetOutgoingDirectTypeReferences(const QualifiedName& type)
+{
+ size_t count;
+ BNQualifiedName apiType = type.GetAPIObject();
+ BNQualifiedName* apiResult = BNGetOutgoingDirectTypeReferences(m_object, &apiType, &count);
+ QualifiedName::FreeAPIObject(&apiType);
+ if (!apiResult)
+ return {};
+
+ unordered_set<QualifiedName> result;
+ for (size_t i = 0; i < count; i ++)
+ {
+ result.insert(QualifiedName::FromAPIObject(&apiResult[i]));
+ }
+ BNFreeTypeNameList(apiResult, count);
+ return result;
+}
+
+
+unordered_set<QualifiedName> BinaryView::GetOutgoingRecursiveTypeReferences(const QualifiedName& type)
+{
+ return GetOutgoingRecursiveTypeReferences(unordered_set<QualifiedName>{type});
+}
+
+
+unordered_set<QualifiedName> BinaryView::GetOutgoingRecursiveTypeReferences(const unordered_set<QualifiedName>& types)
+{
+ size_t count;
+ vector<BNQualifiedName> apiTypes;
+ for (auto& type: types)
+ {
+ apiTypes.push_back(type.GetAPIObject());
+ }
+ BNQualifiedName* apiResult = BNGetOutgoingRecursiveTypeReferences(m_object, apiTypes.data(), apiTypes.size(), &count);
+ for (auto& type: apiTypes)
+ {
+ QualifiedName::FreeAPIObject(&type);
+ }
+ if (!apiResult)
+ return {};
+
+ unordered_set<QualifiedName> result;
+ for (size_t i = 0; i < count; i ++)
+ {
+ result.insert(QualifiedName::FromAPIObject(&apiResult[i]));
+ }
+ BNFreeTypeNameList(apiResult, count);
+ return result;
+}
+
+
+unordered_set<QualifiedName> BinaryView::GetIncomingDirectTypeReferences(const QualifiedName& type)
+{
+ size_t count;
+ BNQualifiedName apiType = type.GetAPIObject();
+ BNQualifiedName* apiResult = BNGetIncomingDirectTypeReferences(m_object, &apiType, &count);
+ QualifiedName::FreeAPIObject(&apiType);
+ if (!apiResult)
+ return {};
+
+ unordered_set<QualifiedName> result;
+ for (size_t i = 0; i < count; i ++)
+ {
+ result.insert(QualifiedName::FromAPIObject(&apiResult[i]));
+ }
+ BNFreeTypeNameList(apiResult, count);
+ return result;
+}
+
+
+unordered_set<QualifiedName> BinaryView::GetIncomingRecursiveTypeReferences(const QualifiedName& type)
+{
+ return GetIncomingRecursiveTypeReferences(unordered_set<QualifiedName>{type});
+}
+
+
+unordered_set<QualifiedName> BinaryView::GetIncomingRecursiveTypeReferences(const unordered_set<QualifiedName>& types)
+{
+ size_t count;
+ vector<BNQualifiedName> apiTypes;
+ for (auto& type: types)
+ {
+ apiTypes.push_back(type.GetAPIObject());
+ }
+ BNQualifiedName* apiResult = BNGetIncomingRecursiveTypeReferences(m_object, apiTypes.data(), apiTypes.size(), &count);
+ for (auto& type: apiTypes)
+ {
+ QualifiedName::FreeAPIObject(&type);
+ }
+ if (!apiResult)
+ return {};
+
+ unordered_set<QualifiedName> result;
+ for (size_t i = 0; i < count; i ++)
+ {
+ result.insert(QualifiedName::FromAPIObject(&apiResult[i]));
+ }
+ BNFreeTypeNameList(apiResult, count);
+ return result;
+}
+
+
vector<uint64_t> BinaryView::GetCallees(ReferenceSource callSite)
{
size_t count;
diff --git a/python/binaryview.py b/python/binaryview.py
index d7d2a2a8..de434074 100644
--- a/python/binaryview.py
+++ b/python/binaryview.py
@@ -4979,6 +4979,80 @@ class BinaryView:
finally:
core.BNFreeTypeFieldReferenceTypes(refs, count.value)
+ def get_outgoing_direct_type_references(self, name: '_types.QualifiedNameType') -> List['_types.QualifiedName']:
+ qname = _types.QualifiedName(name)
+ _qname = qname._to_core_struct()
+ count = ctypes.c_ulonglong(0)
+ _result = core.BNGetOutgoingDirectTypeReferences(self.handle, _qname, count)
+ assert _result is not None, "core.BNGetOutgoingDirectTypeReferences returned None"
+ try:
+ result = []
+ for i in range(0, count.value):
+ result_name = _types.QualifiedName._from_core_struct(_result[i])
+ result.append(result_name)
+ return result
+ finally:
+ core.BNFreeTypeNameList(_result, count.value)
+
+ def get_outgoing_recursive_type_references(self, names: Union['_types.QualifiedNameType', List['_types.QualifiedNameType']]) -> List['_types.QualifiedName']:
+ qnames = []
+ if isinstance(names, list):
+ for name in names:
+ qnames.append(_types.QualifiedName(name))
+ else:
+ qnames.append(_types.QualifiedName(names))
+ _qnames = (core.BNQualifiedName * len(qnames))()
+ for i, qname in enumerate(qnames):
+ _qnames[i] = qname._to_core_struct()
+ count = ctypes.c_ulonglong(0)
+ _result = core.BNGetOutgoingRecursiveTypeReferences(self.handle, _qnames, len(qnames), count)
+ assert _result is not None, "core.BNGetOutgoingRecursiveTypeReferences returned None"
+ try:
+ result = []
+ for i in range(0, count.value):
+ result_name = _types.QualifiedName._from_core_struct(_result[i])
+ result.append(result_name)
+ return result
+ finally:
+ core.BNFreeTypeNameList(_result, count.value)
+
+ def get_incoming_direct_type_references(self, name: '_types.QualifiedNameType') -> List['_types.QualifiedName']:
+ qname = _types.QualifiedName(name)
+ _qname = qname._to_core_struct()
+ count = ctypes.c_ulonglong(0)
+ _result = core.BNGetIncomingDirectTypeReferences(self.handle, _qname, count)
+ assert _result is not None, "core.BNGetIncomingDirectTypeReferences returned None"
+ try:
+ result = []
+ for i in range(0, count.value):
+ result_name = _types.QualifiedName._from_core_struct(_result[i])
+ result.append(result_name)
+ return result
+ finally:
+ core.BNFreeTypeNameList(_result, count.value)
+
+ def get_incoming_recursive_type_references(self, names: Union['_types.QualifiedNameType', List['_types.QualifiedNameType']]) -> List['_types.QualifiedName']:
+ qnames = []
+ if isinstance(names, list):
+ for name in names:
+ qnames.append(_types.QualifiedName(name))
+ else:
+ qnames.append(_types.QualifiedName(names))
+ _qnames = (core.BNQualifiedName * len(qnames))()
+ for i, qname in enumerate(qnames):
+ _qnames[i] = qname._to_core_struct()
+ count = ctypes.c_ulonglong(0)
+ _result = core.BNGetIncomingRecursiveTypeReferences(self.handle, _qnames, len(qnames), count)
+ assert _result is not None, "core.BNGetIncomingRecursiveTypeReferences returned None"
+ try:
+ result = []
+ for i in range(0, count.value):
+ result_name = _types.QualifiedName._from_core_struct(_result[i])
+ result.append(result_name)
+ return result
+ finally:
+ core.BNFreeTypeNameList(_result, count.value)
+
def create_structure_from_offset_access(self, name: '_types.QualifiedName') -> '_types.StructureType':
newMemberAdded = ctypes.c_bool(False)
_name = _types.QualifiedName(name)._to_core_struct()
diff --git a/python/types.py b/python/types.py
index 9fbe7a93..68858ac8 100644
--- a/python/types.py
+++ b/python/types.py
@@ -237,6 +237,7 @@ class TypeDefinitionLine:
line_type: TypeDefinitionLineType
tokens: List['_function.InstructionTextToken']
type: 'Type'
+ parent_type: 'Type'
root_type: 'Type'
root_type_name: str
base_type: Optional['NamedTypeReferenceType']
@@ -254,6 +255,7 @@ class TypeDefinitionLine:
def _from_core_struct(struct: core.BNTypeDefinitionLine, platform: Optional[_platform.Platform] = None):
tokens = _function.InstructionTextToken._from_core_struct(struct.tokens, struct.count)
type_ = Type.create(handle=core.BNNewTypeReference(struct.type), platform=platform)
+ parent_type = Type.create(handle=core.BNNewTypeReference(struct.parentType), platform=platform)
root_type = Type.create(handle=core.BNNewTypeReference(struct.rootType), platform=platform)
root_type_name = core.pyNativeStr(struct.rootTypeName)
if struct.baseType:
@@ -263,7 +265,7 @@ class TypeDefinitionLine:
base_type = NamedTypeReferenceType(handle, platform)
else:
base_type = None
- return TypeDefinitionLine(struct.lineType, tokens, type_, root_type, root_type_name, base_type,
+ return TypeDefinitionLine(struct.lineType, tokens, type_, parent_type, root_type, root_type_name, base_type,
struct.baseOffset, struct.offset, struct.fieldIndex)
def _to_core_struct(self):
@@ -272,6 +274,7 @@ class TypeDefinitionLine:
struct.tokens = _function.InstructionTextToken._get_core_struct(self.tokens)
struct.count = len(self.tokens)
struct.type = core.BNNewTypeReference(self.type.handle)
+ struct.parentType = core.BNNewTypeReference(self.parent_type.handle)
struct.rootType = core.BNNewTypeReference(self.root_type.handle)
struct.rootTypeName = self.root_type_name
if self.base_type is None:
diff --git a/suite/api_test.py b/suite/api_test.py
index 7366adad..dfba75ae 100644
--- a/suite/api_test.py
+++ b/suite/api_test.py
@@ -1028,7 +1028,7 @@ class TestTypePrinter(unittest.TestCase):
InstructionTextToken(InstructionTextTokenType.TextToken, "the type is: ", 0),
InstructionTextToken(InstructionTextTokenType.TypeNameToken, str(name), 0),
InstructionTextToken(InstructionTextTokenType.TextToken, " bottom text", 0)
- ], type, type, '', 0, 1)
+ ], type, type, type, '', 0, 1),
]
MyTypePrinter().register()
diff --git a/type.cpp b/type.cpp
index f581770c..383ef3e8 100644
--- a/type.cpp
+++ b/type.cpp
@@ -467,6 +467,7 @@ TypeDefinitionLine TypeDefinitionLine::FromAPIObject(BNTypeDefinitionLine* line)
result.lineType = line->lineType;
result.tokens = InstructionTextToken::ConvertInstructionTextTokenList(line->tokens, line->count);
result.type = new Type(BNNewTypeReference(line->type));
+ result.parentType = new Type(BNNewTypeReference(line->parentType));
result.rootType = new Type(BNNewTypeReference(line->rootType));
result.rootTypeName = line->rootTypeName;
result.baseType = line->baseType ? new NamedTypeReference(BNNewNamedTypeReference(line->baseType)) : nullptr;
@@ -487,6 +488,7 @@ BNTypeDefinitionLine* TypeDefinitionLine::CreateTypeDefinitionLineList(
result[i].tokens = InstructionTextToken::CreateInstructionTextTokenList(lines[i].tokens);
result[i].count = lines[i].tokens.size();
result[i].type = BNNewTypeReference(lines[i].type->GetObject());
+ result[i].parentType = BNNewTypeReference(lines[i].parentType->GetObject());
result[i].rootType = BNNewTypeReference(lines[i].rootType->GetObject());
result[i].rootTypeName = BNAllocString(lines[i].rootTypeName.c_str());
result[i].baseType = lines[i].baseType ? BNNewNamedTypeReference(lines[i].baseType->GetObject()) : nullptr;
@@ -504,6 +506,7 @@ void TypeDefinitionLine::FreeTypeDefinitionLineList(BNTypeDefinitionLine* lines,
{
InstructionTextToken::FreeInstructionTextTokenList(lines[i].tokens, lines[i].count);
BNFreeType(lines[i].type);
+ BNFreeType(lines[i].parentType);
BNFreeType(lines[i].rootType);
BNFreeNamedTypeReference(lines[i].baseType);
BNFreeString(lines[i].rootTypeName);
@@ -1213,6 +1216,7 @@ std::vector<TypeDefinitionLine> Type::GetLines(const TypeContainer& types, const
line.lineType = list[i].lineType;
line.tokens = InstructionTextToken::ConvertInstructionTextTokenList(list[i].tokens, list[i].count);
line.type = new Type(BNNewTypeReference(list[i].type));
+ line.parentType = list[i].parentType ? new Type(BNNewTypeReference(list[i].parentType)) : nullptr;
line.rootType = list[i].rootType ? new Type(BNNewTypeReference(list[i].rootType)) : nullptr;
line.rootTypeName = list[i].rootTypeName;
line.baseType = list[i].baseType ? new NamedTypeReference(BNNewNamedTypeReference(list[i].baseType)) : nullptr;
diff --git a/ui/action.h b/ui/action.h
index 38ca296a..9deb3a66 100644
--- a/ui/action.h
+++ b/ui/action.h
@@ -33,6 +33,7 @@ struct LinearViewCursorPosition;
struct BINARYNINJAUIAPI HighlightTokenState
{
bool valid;
+ bool focused;
bool secondaryHighlight;
BNInstructionTextTokenType type;
BinaryNinja::InstructionTextToken token;
diff --git a/ui/render.h b/ui/render.h
index 02b0a66c..8dc0d158 100644
--- a/ui/render.h
+++ b/ui/render.h
@@ -69,7 +69,9 @@ class BINARYNINJAUIAPI FontParameters
FontParameters(QWidget* parent, float fontScale = 1.0f);
void update();
+ const QFont& getFont() const { return m_font; }
QFont& getFont() { return m_font; }
+ const QFont& getEmojiFont() const { return m_emojiFont; }
QFont& getEmojiFont() { return m_emojiFont; }
void setFont(const QFont& font);
void setEmojiFont(const QFont& emojiFont);
@@ -94,6 +96,7 @@ class BINARYNINJAUIAPI RenderContext
void update();
FontParameters& getFontParamters() { return m_fontParams; }
+ const FontParameters& getFontParameters() const { return m_fontParams; }
int getFontWidth() const { return m_fontParams.getWidth(); }
int getFontHeight() const { return m_fontParams.getHeight(); }
@@ -113,7 +116,7 @@ class BINARYNINJAUIAPI RenderContext
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);
+ void drawInstructionHighlight(QPainter& p, const QRect& rect, bool focused = true);
void drawLinearDisassemblyLineBackground(
QPainter& p, BNLinearDisassemblyLineType type, const QRect& rect, const QRect& dirtyRect, int gutterWidth);
diff --git a/ui/theme.h b/ui/theme.h
index 55f93c22..1109ec9f 100644
--- a/ui/theme.h
+++ b/ui/theme.h
@@ -50,6 +50,7 @@ void BINARYNINJAUIAPI addJsonTheme(const char* json);
QColor BINARYNINJAUIAPI getThemeColor(BNThemeColor color);
QColor BINARYNINJAUIAPI getTokenColor(QWidget* widget, BNInstructionTextTokenType token);
+QColor BINARYNINJAUIAPI getTypeClassColor(const QWidget* widget, BNTypeClass typeClass);
QColor BINARYNINJAUIAPI avgColor(QColor a, QColor b);
QColor BINARYNINJAUIAPI mixColor(QColor a, QColor b, uint8_t mix);
diff --git a/ui/tokenizedtextwidget.h b/ui/tokenizedtextwidget.h
new file mode 100644
index 00000000..acbb2af5
--- /dev/null
+++ b/ui/tokenizedtextwidget.h
@@ -0,0 +1,254 @@
+#pragma once
+
+#include <QtWidgets/QAbstractScrollArea>
+#include <QtCore/QTimer>
+#include <optional>
+#include "binaryninjaapi.h"
+#include "viewframe.h"
+#include "render.h"
+#include "commentdialog.h"
+#include "menus.h"
+#include "uicontext.h"
+
+/*!
+
+ \defgroup tokenizedtextwidget TokenizedTextWidget
+ \ingroup uiapi
+*/
+
+
+/*!
+
+ \ingroup tokenizedtextwidget
+*/
+enum BINARYNINJAUIAPI TokenizedTextWidgetSelectionStyle
+{
+ NoSelection = 1 << 0,
+ SelectLines = 1 << 1,
+ SelectOneToken = 1 << 2,
+ SelectTokens = 1 << 3,
+ SelectCharacters = 1 << 4,
+
+ AllStyles = NoSelection | SelectLines | SelectOneToken | SelectTokens | SelectCharacters,
+};
+
+
+/*!
+
+ \ingroup tokenizedtextwidget
+*/
+struct BINARYNINJAUIAPI TokenizedTextWidgetCursorPosition
+{
+ /// Index of line in widget
+ size_t lineIndex = BN_INVALID_OPERAND;
+ /// Index of token in current line
+ size_t tokenIndex = BN_INVALID_OPERAND;
+ /// Index of character in current token
+ size_t characterIndex = BN_INVALID_OPERAND;
+
+ // Directly from QMouseEvent, not used in comparator
+ int cursorX;
+ int cursorY;
+
+ bool isValid() const { return lineIndex != BN_INVALID_OPERAND; }
+ bool operator==(const TokenizedTextWidgetCursorPosition& other) const;
+ bool operator!=(const TokenizedTextWidgetCursorPosition& other) const { return !(*this == other); }
+ bool operator<(const TokenizedTextWidgetCursorPosition& other) const;
+};
+
+/*!
+ QWidget that displays lines of InstructionTextTokens with the ability to make selections
+
+ \ingroup tokenizedtextwidget
+*/
+class BINARYNINJAUIAPI TokenizedTextWidget :
+ public QAbstractScrollArea
+{
+protected:
+ struct LineMetadata
+ {
+ size_t charWidth;
+ int copyStyles;
+
+ LineMetadata():
+ charWidth(0),
+ copyStyles(TokenizedTextWidgetSelectionStyle::AllStyles)
+ {}
+ };
+ struct TokenMetadata
+ {
+ size_t charOffset;
+ int copyStyles;
+ bool selectLineTarget;
+
+ TokenMetadata():
+ charOffset(0),
+ copyStyles(TokenizedTextWidgetSelectionStyle::AllStyles),
+ selectLineTarget(false)
+ {}
+ };
+
+private:
+ Q_OBJECT
+
+ UIActionHandler m_actionHandler;
+ ContextMenuManager* m_contextMenuManager;
+ Menu m_contextMenu;
+
+ RenderContext m_render;
+ int m_cols, m_rows;
+ int m_contentsCols, m_contentsRows;
+ int m_verticalWheelDelta, m_horizontalWheelDelta;
+ bool m_updatingScrollBar;
+ bool m_autoScrollHorizontal, m_autoScrollVertical;
+
+ TokenizedTextWidgetCursorPosition m_cursorPos, m_selectionStartPos, m_hoverPos;
+ TokenizedTextWidgetSelectionStyle m_selectionMode;
+ size_t m_hoverLine;
+ bool m_selectionStartedPastWidth;
+ bool m_selectionStartedPastHeight;
+ bool m_cursorKeys;
+ bool m_forceLineSelect;
+
+ std::vector<BinaryNinja::LinearDisassemblyLine> m_lines;
+ std::vector<LineMetadata> m_lineMetadata;
+ std::vector<std::vector<TokenMetadata>> m_tokenMetadata;
+ DisassemblySettingsRef m_settings;
+
+ void adjustSize(int width, int height);
+ void clampCursorPosition(TokenizedTextWidgetCursorPosition& pos);
+ void clampSelectionToValid();
+
+ protected:
+ virtual void updateMetadata(const std::vector<BinaryNinja::LinearDisassemblyLine>& lines, int& width, int& height);
+
+ private Q_SLOTS:
+ void verticalScrollBarMoved(int value);
+ void verticalScrollBarAction(int action);
+ void horizontalScrollBarMoved(int value);
+ void horizontalScrollBarAction(int action);
+
+ public:
+ explicit TokenizedTextWidget(QWidget* parent,
+ const std::vector<BinaryNinja::LinearDisassemblyLine>& lines =
+ std::vector<BinaryNinja::LinearDisassemblyLine>());
+ virtual ~TokenizedTextWidget();
+
+ void bindActions();
+
+ QFont font() const;
+ void setFont(const QFont& font);
+
+ DisassemblySettingsRef settings() { return m_settings; }
+ const DisassemblySettingsRef& settings() const { return m_settings; }
+
+ int topLineIndex() const;
+ int leftmostCharIndex() const;
+ int visibleColumnCount() const { return m_cols; }
+ int visibleRowCount() const { return m_rows; }
+ int contentsColumnCount() const { return m_contentsCols; }
+ int contentsRowCount() const { return m_contentsRows; }
+
+ bool hasSelection() const;
+ // Lines vs Tokens vs Characters vs NoSelection
+ TokenizedTextWidgetSelectionStyle selectionStyle() const;
+ // Lower bound of selection
+ TokenizedTextWidgetCursorPosition selectionBegin() const;
+ // Upper bound of selection
+ TokenizedTextWidgetCursorPosition selectionEnd() const;
+ // Originally highlighted selection base
+ TokenizedTextWidgetCursorPosition selectionBase() const;
+ // Position of cursor for movement operations
+ TokenizedTextWidgetCursorPosition cursorPosition() const;
+ bool forceLineSelect() const { return m_forceLineSelect; }
+ void setForceLineSelect(bool value) { m_forceLineSelect = value; }
+
+ void setSelection(TokenizedTextWidgetCursorPosition base, TokenizedTextWidgetCursorPosition cursor, TokenizedTextWidgetSelectionStyle mode);
+ void setCursorPosition(TokenizedTextWidgetCursorPosition newPosition, bool selecting, bool cursorKeys, bool evenIfNoChange);
+ void moveCursorHorizontal(int count, bool allTheWay, bool selecting, bool cursorKeys);
+ void moveCursorVertical(int count, bool allTheWay, bool selecting, bool cursorKeys);
+
+ bool autoScrollHorizontal() const { return m_autoScrollHorizontal; }
+ void setAutoScrollHorizontal(bool value) { m_autoScrollHorizontal = value; }
+ bool autoScrollVertical() const { return m_autoScrollVertical; }
+ void setAutoScrollVertical(bool value) { m_autoScrollVertical = value; }
+
+ HighlightTokenState highlightTokenState();
+ UIActionHandler* actionHandler() { return &m_actionHandler; }
+
+ virtual UIActionContext actionContext();
+ Menu& contextMenu() { return m_contextMenu; }
+ void showContextMenu();
+
+ void left(size_t count, bool selecting);
+ void right(size_t count, bool selecting);
+ void leftToWord(bool selecting);
+ void rightToWord(bool selecting);
+ void up(bool selecting);
+ void down(bool selecting);
+ void pageUp(bool selecting);
+ void pageDown(bool selecting);
+ void moveToStartOfLine(bool selecting);
+ void moveToEndOfLine(bool selecting);
+ void moveToStartOfView(bool selecting);
+ void moveToEndOfView(bool selecting);
+ void selectAll();
+ void selectNone();
+
+ void scrollLines(int count);
+ void scrollLineToVisible(int lineIndex);
+ void scrollLineToTop(int lineIndex);
+
+ void scrollChars(int count);
+ void scrollCharToVisible(int charIndex);
+ void scrollCharToLeftmost(int charIndex);
+
+ void copy() const;
+ std::string selectedText() const;
+
+ const std::vector<BinaryNinja::LinearDisassemblyLine>& lines() const { return m_lines; }
+ std::optional<std::reference_wrapper<const BinaryNinja::LinearDisassemblyLine>> lineAtPosition(const TokenizedTextWidgetCursorPosition& position) const;
+ std::optional<std::reference_wrapper<const BinaryNinja::InstructionTextToken>> tokenAtPosition(const TokenizedTextWidgetCursorPosition& position) const;
+ std::optional<char> charAtPosition(const TokenizedTextWidgetCursorPosition& position) const;
+
+ void clearLines();
+ void setLines(const std::vector<BinaryNinja::LinearDisassemblyLine>& lines, bool resetScroll = true);
+ void setLines(const std::vector<BinaryNinja::DisassemblyTextLine>& lines, bool resetScroll = true);
+ void setLines(const std::vector<BinaryNinja::TypeDefinitionLine>& lines, bool resetScroll = true);
+
+ int lineCopyStyles(size_t lineIndex) const;
+ void setLineCopyStyles(size_t lineIndex, int styles);
+
+ int tokenCopyStyles(size_t lineIndex, size_t tokenIndex) const;
+ void setTokenCopyStyles(size_t lineIndex, size_t tokenIndex, int styles);
+ bool tokenSelectLineTarget(size_t lineIndex, size_t tokenIndex) const;
+ void setTokenSelectLineTarget(size_t lineIndex, size_t tokenIndex, bool selectLineTarget);
+
+ Q_SIGNALS:
+ void sizeChanged(int cols, int rows);
+ void visibleChanged(int leftCol, int topRow);
+ void linesChanged();
+ void selectionChanged(const TokenizedTextWidgetCursorPosition& begin, const TokenizedTextWidgetCursorPosition& end);
+ void tokenLeftClicked(const TokenizedTextWidgetCursorPosition& position);
+ void tokenRightClicked(const TokenizedTextWidgetCursorPosition& position);
+ void tokenDoubleClicked(const TokenizedTextWidgetCursorPosition& position);
+ void tokenOtherClicked(const TokenizedTextWidgetCursorPosition& position, Qt::MouseButton button);
+ void tokenHovered(const TokenizedTextWidgetCursorPosition& position);
+ void lineLeftClicked(size_t lineIndex);
+ void lineRightClicked(size_t lineIndex);
+ void lineDoubleClicked(size_t lineIndex);
+ void lineOtherClicked(size_t lineIndex, Qt::MouseButton button);
+ void lineHovered(size_t lineIndex);
+
+ protected:
+ virtual void resizeEvent(QResizeEvent* event) override;
+ virtual void paintEvent(QPaintEvent* event) override;
+ virtual void wheelEvent(QWheelEvent* event) override;
+ virtual void mousePressEvent(QMouseEvent* event) override;
+ virtual void mouseMoveEvent(QMouseEvent* event) override;
+ virtual void mouseDoubleClickEvent(QMouseEvent* event) override;
+ virtual void leaveEvent(QEvent* event) override;
+ virtual void focusInEvent(QFocusEvent* event) override;
+ virtual void focusOutEvent(QFocusEvent* event) override;
+ virtual void contextMenuEvent(QContextMenuEvent* event) override;
+};
diff --git a/ui/typebrowser.h b/ui/typebrowser.h
new file mode 100644
index 00000000..d74fe341
--- /dev/null
+++ b/ui/typebrowser.h
@@ -0,0 +1,524 @@
+#pragma once
+
+#include <QtWidgets/QTreeView>
+#include <QtCore/QSortFilterProxyModel>
+#include <QtGui/QStandardItemModel>
+#include <QtWidgets/QItemDelegate>
+#include <QtWidgets/QTextEdit>
+#include <memory>
+#include "sidebar.h"
+#include "viewframe.h"
+#include "filter.h"
+#include "progresstask.h"
+#include "typeeditor.h"
+
+
+enum BINARYNINJAUIAPI TypeBrowserFilterMode
+{
+ NamesOnly = 0,
+ NamesAndMembers = 1,
+ FullDefinitions = 2
+};
+
+
+class BINARYNINJAUIAPI TypeBrowserTreeNode : public std::enable_shared_from_this<TypeBrowserTreeNode>
+{
+public:
+ struct UpdateData
+ {
+ enum UpdateType
+ {
+ NodeInserted,
+ NodeUpdated,
+ NodeRemoved,
+ UpdatesFinished,
+ };
+
+ UpdateType type;
+ std::shared_ptr<TypeBrowserTreeNode> parent;
+ std::shared_ptr<TypeBrowserTreeNode> node;
+ std::function<void(const UpdateData&)> commit;
+ };
+
+ typedef std::function<void(UpdateData)> UpdateNodeCallback;
+
+protected:
+ class TypeBrowserModel* m_model;
+ std::optional<std::weak_ptr<TypeBrowserTreeNode>> m_parent;
+ std::vector<std::shared_ptr<TypeBrowserTreeNode>> m_children;
+ std::map<const TypeBrowserTreeNode*, size_t> m_childIndices;
+ bool m_hasGeneratedChildren;
+
+ TypeBrowserTreeNode(class TypeBrowserModel* model, std::optional<std::weak_ptr<TypeBrowserTreeNode>> parent);
+ virtual ~TypeBrowserTreeNode() = default;
+ virtual void generateChildren() = 0;
+ void updateChildIndices();
+
+ void removeChild(std::shared_ptr<TypeBrowserTreeNode> child);
+ void addChild(std::shared_ptr<TypeBrowserTreeNode> child);
+
+public:
+ class TypeBrowserModel* model() const { return m_model; }
+ std::optional<std::shared_ptr<TypeBrowserTreeNode>> parent() const;
+ const std::vector<std::shared_ptr<TypeBrowserTreeNode>>& children();
+ int indexOfChild(std::shared_ptr<const TypeBrowserTreeNode> child) const;
+
+ virtual std::string text(int column) const = 0;
+ virtual bool lessThan(const TypeBrowserTreeNode& other, int column) const = 0;
+ virtual bool filter(const std::string& filter, TypeBrowserFilterMode mode) const = 0;
+ virtual void updateChildren(bool recursive, UpdateNodeCallback update);
+};
+
+
+class BINARYNINJAUIAPI EmptyTreeNode : public TypeBrowserTreeNode
+{
+public:
+ EmptyTreeNode(class TypeBrowserModel* model, std::optional<std::weak_ptr<TypeBrowserTreeNode>> parent);
+ virtual ~EmptyTreeNode() = default;
+
+ virtual std::string text(int column) const override;
+ virtual bool lessThan(const TypeBrowserTreeNode& other, int column) const override;
+ virtual bool filter(const std::string& filter, TypeBrowserFilterMode mode) const override;
+
+protected:
+ virtual void generateChildren() override;
+ virtual void updateChildren(bool recursive, UpdateNodeCallback update) override;
+};
+
+
+class BINARYNINJAUIAPI RootTreeNode : public TypeBrowserTreeNode
+{
+ std::map<std::string, std::shared_ptr<class TypeContainerTreeNode>> m_containerNodes;
+
+public:
+ RootTreeNode(class TypeBrowserModel* model, std::optional<std::weak_ptr<TypeBrowserTreeNode>> parent);
+ virtual ~RootTreeNode() = default;
+
+ virtual std::string text(int column) const override;
+ virtual bool lessThan(const TypeBrowserTreeNode& other, int column) const override;
+ virtual bool filter(const std::string& filter, TypeBrowserFilterMode mode) const override;
+
+protected:
+ virtual void generateChildren() override;
+ virtual void updateChildren(bool recursive, UpdateNodeCallback update) override;
+};
+
+
+class BINARYNINJAUIAPI TypeTreeNode : public TypeBrowserTreeNode
+{
+public:
+ enum SourceType
+ {
+ None,
+ TypeLibrary,
+ DebugInfo,
+ Platform,
+ Other
+ };
+
+private:
+ std::string m_id;
+ BinaryNinja::QualifiedName m_name;
+ TypeRef m_type;
+ std::string m_sortName;
+
+ SourceType m_sourceType;
+ std::optional<TypeLibraryRef> m_sourceLibrary;
+ std::optional<std::string> m_sourceDebugInfoParser;
+ std::optional<PlatformRef> m_sourcePlatform;
+ std::optional<std::string> m_sourceOtherName;
+ std::optional<BinaryNinja::QualifiedName> m_sourceOriginalName;
+
+public:
+ TypeTreeNode(class TypeBrowserModel* model, std::optional<std::weak_ptr<TypeBrowserTreeNode>> parent, const std::string& id, BinaryNinja::QualifiedName name, TypeRef type);
+ virtual ~TypeTreeNode() = default;
+
+ const std::string& id() const { return m_id; }
+ const BinaryNinja::QualifiedName& name() const { return m_name; }
+ const TypeRef& type() const { return m_type; }
+ void setType(const std::string& id, const BinaryNinja::QualifiedName& name, const TypeRef& type);
+
+ const SourceType& sourceType() const { return m_sourceType; }
+ std::optional<BinaryNinja::TypeContainer> typeContainer() const;
+ std::optional<BinaryNinja::TypeContainer> sourceTypeContainer() const;
+ PlatformRef sourcePlatform() const;
+
+ virtual std::string text(int column) const override;
+ virtual bool lessThan(const TypeBrowserTreeNode& other, int column) const override;
+ virtual bool filter(const std::string& filter, TypeBrowserFilterMode mode) const override;
+
+protected:
+ virtual void generateChildren() override;
+};
+
+
+class BINARYNINJAUIAPI TypeContainerTreeNode : public TypeBrowserTreeNode
+{
+ std::string m_containerId;
+ // TODO: Gross
+ std::map<std::string, std::pair<std::pair<BinaryNinja::QualifiedName, TypeRef>, std::shared_ptr<TypeTreeNode>>> m_typeNodes;
+
+public:
+ TypeContainerTreeNode(class TypeBrowserModel* model, std::optional<std::weak_ptr<TypeBrowserTreeNode>> parent, const std::string& m_containerId);
+ virtual ~TypeContainerTreeNode();
+
+ virtual std::string text(int column) const override;
+ virtual bool filter(const std::string& filter, TypeBrowserFilterMode mode) const override;
+ virtual bool lessThan(const TypeBrowserTreeNode& other, int column) const override;
+
+ const std::string& containerId() const { return m_containerId; }
+ std::optional<PlatformRef> platform() const;
+ std::optional<BinaryNinja::TypeContainer> typeContainer() const;
+ std::optional<BNTypeContainerType> containerType() const;
+ virtual void updateChildren(bool recursive, UpdateNodeCallback update) override;
+
+protected:
+ virtual void generateChildren() override;
+};
+
+//-----------------------------------------------------------------------------
+
+
+class BINARYNINJAUIAPI TypeBrowserModel : public QAbstractItemModel, public BinaryNinja::BinaryDataNotification
+{
+ Q_OBJECT
+ BinaryViewRef m_data;
+ std::shared_ptr<TypeBrowserTreeNode> m_rootNode;
+ mutable std::recursive_mutex m_rootNodeMutex;
+ bool m_needsUpdate;
+ bool m_updating;
+
+ std::recursive_mutex m_updateMutex;
+ std::vector<std::function<void()>> m_updateCallbacks;
+
+ std::vector<std::string> m_containerIds;
+ std::map<std::string, std::string> m_containerNames;
+ std::map<std::string, BNTypeContainerType> m_containerTypes;
+ std::map<std::string, BinaryNinja::TypeContainer> m_containers;
+
+ std::map<std::string, BinaryViewRef> m_containerViews;
+ std::map<std::string, TypeLibraryRef> m_containerLibraries;
+ std::map<std::string, DebugInfoRef> m_containerDebugInfos;
+ std::map<std::string, PlatformRef> m_containerPlatforms;
+
+ void updateContainerList();
+ void callUpdateCallbacks();
+ void commitUpdates(std::vector<TypeBrowserTreeNode::UpdateData>& updates);
+
+public:
+ TypeBrowserModel(BinaryViewRef data);
+ virtual ~TypeBrowserModel();
+ BinaryViewRef getData() { return m_data; }
+ std::shared_ptr<TypeBrowserTreeNode> getRootNode() { return m_rootNode; }
+
+ std::vector<std::string> containerIds() const;
+
+ std::string nameForContainerId(const std::string& id) const;
+ std::optional<std::reference_wrapper<BinaryNinja::TypeContainer>> containerForContainerId(const std::string& id);
+ std::optional<std::reference_wrapper<const BinaryNinja::TypeContainer>> containerForContainerId(const std::string& id) const;
+ std::optional<BinaryViewRef> viewForContainerId(const std::string& id) const;
+ std::optional<TypeLibraryRef> libraryForContainerId(const std::string& id) const;
+ std::optional<DebugInfoRef> debugInfoForContainerId(const std::string& id) const;
+ std::optional<PlatformRef> platformForContainerId(const std::string& id) const;
+
+ void updateFonts();
+ void runAfterUpdate(std::function<void()> callback);
+
+ int columnCount(const QModelIndex& parent = QModelIndex()) const override;
+ int rowCount(const QModelIndex& parent = QModelIndex()) const override;
+ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
+ QModelIndex parent(const QModelIndex& child) const override;
+ QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
+
+ std::shared_ptr<TypeBrowserTreeNode> nodeForIndex(const QModelIndex& index) const;
+ QModelIndex indexForNode(std::shared_ptr<TypeBrowserTreeNode> node, int column = 0) const;
+
+ std::vector<std::shared_ptr<TypeContainerTreeNode>> containerNodes() const;
+
+ bool filter(const QModelIndex& index, const std::string& filter, TypeBrowserFilterMode mode) const;
+ bool lessThan(const QModelIndex& left, const QModelIndex& right) const;
+
+ void OnTypeDefined(BinaryNinja::BinaryView* data, const BinaryNinja::QualifiedName& name, BinaryNinja::Type* type) override;
+ void OnTypeUndefined(BinaryNinja::BinaryView* data, const BinaryNinja::QualifiedName& name, BinaryNinja::Type* type) override;
+ void OnTypeReferenceChanged(BinaryNinja::BinaryView* data, const BinaryNinja::QualifiedName& name, BinaryNinja::Type* type) override;
+ void OnTypeFieldReferenceChanged(BinaryNinja::BinaryView* data, const BinaryNinja::QualifiedName& name, uint64_t offset) override;
+
+Q_SIGNALS:
+ void updatesAboutToHappen();
+ void updateComplete();
+
+public Q_SLOTS:
+ void markDirty();
+ void notifyRefresh();
+};
+
+
+class BINARYNINJAUIAPI TypeBrowserFilterModel : public QSortFilterProxyModel
+{
+ Q_OBJECT
+ BinaryViewRef m_data;
+ TypeBrowserModel* m_model;
+ std::string m_filter;
+ TypeBrowserFilterMode m_filterMode;
+
+protected:
+ bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override;
+ bool lessThan(const QModelIndex& source_left, const QModelIndex& source_right) const override;
+
+public:
+ TypeBrowserFilterModel(BinaryViewRef data, TypeBrowserModel* model);
+
+ void setFilter(const std::string& filter);
+ TypeBrowserFilterMode filterMode() const { return m_filterMode; }
+ void setFilterMode(TypeBrowserFilterMode newMode) { m_filterMode = newMode; }
+
+Q_SIGNALS:
+ void filterAboutToBeChanged();
+ void filterChanged();
+};
+
+
+class BINARYNINJAUIAPI TypeBrowserItemDelegate : public QItemDelegate
+{
+ QFont m_font;
+ QFont m_monospaceFont;
+ float m_charWidth, m_charHeight, m_charOffset;
+ float m_baseline;
+ class TypeBrowserView* m_view;
+
+ void initFont();
+public:
+ TypeBrowserItemDelegate(class TypeBrowserView* view);
+ void updateFonts();
+ virtual QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override;
+ virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
+};
+
+
+class BINARYNINJAUIAPI TypeBrowserTreeView : public QTreeView
+{
+ Q_OBJECT
+ UIActionHandler m_actionHandler;
+
+public:
+ explicit TypeBrowserTreeView(class TypeBrowserView* parent);
+};
+
+
+struct BINARYNINJAUIAPI TypeReference
+{
+ std::string containerId;
+ BinaryNinja::QualifiedName typeName;
+
+ TypeReference() = default;
+ TypeReference(std::string containerId, BinaryNinja::QualifiedName typeName);
+};
+
+
+class BINARYNINJAUIAPI TypeBrowserView : public QFrame, public View, public FilterTarget
+{
+ Q_OBJECT
+ ViewFrame* m_frame;
+ BinaryViewRef m_data;
+ class TypeBrowserContainer* m_container;
+ ContextMenuManager* m_contextMenuManager;
+
+ QSplitter* m_splitter;
+
+ TypeBrowserModel* m_model;
+ TypeBrowserFilterModel* m_filterModel;
+ QStandardItemModel* m_loadingModel;
+ QTreeView* m_tree;
+ TypeBrowserItemDelegate* m_delegate;
+ bool m_updatedWidths;
+
+ bool m_navigateToNextInsert;
+ QModelIndex m_lastPosition;
+ QModelIndex m_lastInsert;
+ TypeEditor::SavedCursorPosition m_editorPosition;
+
+ TypeEditor* m_typeEditor;
+ QTextEdit* m_debugText;
+
+public:
+ TypeBrowserView(ViewFrame* frame, BinaryViewRef data, TypeBrowserContainer* container);
+
+ TypeBrowserContainer* getContainer() { return m_container; }
+ TypeBrowserModel* getModel() { return m_model; }
+ TypeBrowserFilterModel* getFilterModel() { return m_filterModel; }
+ QTreeView* getTreeView() { return m_tree; }
+ TypeEditor* getTypeEditor() { return m_typeEditor; }
+
+ virtual BinaryViewRef getData() override { return m_data; }
+ virtual uint64_t getCurrentOffset() override;
+ virtual void setSelectionOffsets(BNAddressRange range) override;
+ virtual bool navigate(uint64_t offset) override;
+ virtual SelectionInfoForXref getSelectionForXref() override;
+ virtual QFont getFont() override;
+ virtual void updateFonts() override;
+
+ virtual void showEvent(QShowEvent* event) override;
+ virtual void hideEvent(QHideEvent* event) override;
+ virtual void resizeEvent(QResizeEvent* event) override;
+
+ virtual StatusBarWidget* getStatusBarWidget() override;
+ virtual QWidget* getHeaderOptionsWidget() override;
+
+ virtual void setFilter(const std::string& filter) override;
+ virtual void scrollToFirstItem() override;
+ virtual void scrollToCurrentItem() override;
+ virtual void selectFirstItem() override;
+ virtual void activateFirstItem() override;
+
+ virtual void notifyRefresh() override;
+
+ void showSelectedTypes();
+ void showTypes(const std::vector<TypeReference>& types);
+ void selectTypeByName(const std::string& name, bool newSelection);
+
+ bool navigateToType(const std::string& typeName, uint64_t offset);
+ void scrollToIndexWithContext(const QModelIndex& index, int context = 1);
+
+ // Selection helpers
+
+ // All nodes
+ std::vector<std::shared_ptr<TypeBrowserTreeNode>> selectedNodes() const;
+ // BV selected or BV relevant to selected types, only if JUST bv stuff is selected
+ std::optional<BinaryViewRef> selectedBV() const;
+ // If selectedBV exists, names of selected types
+ std::optional<std::unordered_set<BinaryNinja::QualifiedName>> selectedBVTypeNames() const;
+
+ std::optional<std::pair<BinaryNinja::TypeContainer, BinaryNinja::QualifiedName>> selectedTypeNameAndContainer() const;
+ // All selected type names, grouped by type container
+ std::vector<std::pair<BinaryNinja::TypeContainer, std::vector<BinaryNinja::QualifiedName>>> selectedTypeNamesByContainers() const;
+ // Selected type reference
+ std::optional<TypeReference> selectedType() const;
+ // Selected type references
+ std::vector<TypeReference> selectedTypes() const;
+ // Selected type container, or container of selected type
+ // makeSureItHasPlatform: if the type container is a BV with no platform (raw), ask for one and return nullopt if rejected
+ // preferView: if the type container is a BV and the user/auto-only container, switch to the whole-view container for that BV instead
+ std::optional<BinaryNinja::TypeContainer> selectedTypeContainer(bool makeSureItHasPlatform = true, bool preferView = false) const;
+
+ // Names -> Ids, if any don't exist then nullopt
+ static std::optional<std::unordered_set<std::string>> typeIdsFromNames(BinaryViewRef view, const std::unordered_set<BinaryNinja::QualifiedName>& names);
+
+ std::optional<std::reference_wrapper<BinaryNinja::TypeContainer>> containerForId(const std::string& containerId, bool makeSureItHasPlatform = false, bool preferView = false);
+
+ // Menu actions
+ static void registerActions();
+ void bindActions();
+ void showContextMenu();
+
+ bool canCreateNewTypes();
+ void createNewTypes();
+ bool canCreateNewStructure();
+ void createNewStructure();
+ bool canCreateNewEnumeration();
+ void createNewEnumeration();
+ bool canCreateNewUnion();
+ void createNewUnion();
+ bool canRenameTypes();
+ void renameTypes();
+ bool canDeleteTypes();
+ void deleteTypes();
+ bool canChangeTypes();
+ void changeTypes();
+ bool canImportType();
+ void importType();
+ bool canAddTypeLibrary();
+ void addTypeLibrary();
+ bool canExpandAll();
+ void expandAll();
+ bool canCollapseAll();
+ void collapseAll();
+
+Q_SIGNALS:
+ void typeNameNavigated(const std::string& typeName, bool newSelection);
+
+protected:
+ void itemSelected(const QItemSelection& selected, const QItemSelection& deselected);
+ void itemDoubleClicked(const QModelIndex& index);
+ virtual void contextMenuEvent(QContextMenuEvent* event) override;
+};
+
+class BINARYNINJAUIAPI TypeBrowserOptionsIconWidget : public QWidget
+{
+public:
+ TypeBrowserOptionsIconWidget(TypeBrowserView* parent);
+
+private:
+ TypeBrowserView* m_view;
+
+ void showMenu();
+};
+
+class BINARYNINJAUIAPI TypeBrowserContainer : public QWidget, public ViewContainer
+{
+ Q_OBJECT
+
+ ViewFrame* m_frame;
+ BinaryViewRef m_data;
+ TypeBrowserView* m_view;
+ FilteredView* m_filter;
+ FilterEdit* m_separateEdit;
+ class TypeBrowserSidebarWidget* m_sidebarWidget;
+ UIActionHandler m_actionHandler;
+
+public:
+ TypeBrowserContainer(ViewFrame* frame, BinaryViewRef data, class TypeBrowserSidebarWidget* parent);
+ virtual View* getView() override { return m_view; }
+
+ ViewFrame* getViewFrame() { return m_frame; }
+ BinaryViewRef getData() { return m_data; }
+ TypeBrowserView* getTypeBrowserView() { return m_view; }
+ FilteredView* getFilter() { return m_filter; }
+ FilterEdit* getSeparateFilterEdit() { return m_separateEdit; }
+ class TypeBrowserSidebarWidget* getSidebarWidget() { return m_sidebarWidget; }
+ void showContextMenu();
+
+protected:
+ virtual void focusInEvent(QFocusEvent* event) override;
+};
+
+
+class BINARYNINJAUIAPI TypeBrowserViewType : public ViewType
+{
+ static TypeBrowserViewType* g_instance;
+
+public:
+ TypeBrowserViewType();
+ virtual int getPriority(BinaryViewRef data, const QString& filename) override;
+ virtual QWidget* create(BinaryViewRef data, ViewFrame* frame) override;
+ static void init();
+};
+
+
+class BINARYNINJAUIAPI TypeBrowserSidebarWidget : public SidebarWidget
+{
+ Q_OBJECT
+
+ QWidget* m_header;
+ TypeBrowserContainer* m_container;
+
+public:
+ TypeBrowserSidebarWidget(ViewFrame* frame, BinaryViewRef data);
+ TypeBrowserContainer* container() { return m_container; }
+ virtual QWidget* headerWidget() override { return m_header; }
+ virtual void focus() override;
+
+protected:
+ virtual void contextMenuEvent(QContextMenuEvent* event) override;
+
+private Q_SLOTS:
+ void showContextMenu();
+};
+
+
+class BINARYNINJAUIAPI TypeBrowserSidebarWidgetType : public SidebarWidgetType
+{
+public:
+ TypeBrowserSidebarWidgetType();
+ virtual SidebarWidget* createWidget(ViewFrame* frame, BinaryViewRef data) override;
+};
diff --git a/ui/typeeditor.h b/ui/typeeditor.h
new file mode 100644
index 00000000..4de99778
--- /dev/null
+++ b/ui/typeeditor.h
@@ -0,0 +1,168 @@
+
+#pragma once
+
+#include "tokenizedtextwidget.h"
+#include "uitypes.h"
+
+class BINARYNINJAUIAPI TypeEditor: public TokenizedTextWidget
+{
+public:
+ struct SavedCursorPosition
+ {
+ struct PositionData
+ {
+ BinaryNinja::QualifiedName typeName;
+ TokenizedTextWidgetCursorPosition position;
+ size_t structOffset;
+ std::pair<size_t, size_t> lineStart;
+ };
+ bool restoreCursor;
+ bool restoreTop;
+ TokenizedTextWidgetSelectionStyle style;
+ PositionData cursor;
+ PositionData base;
+ PositionData top;
+ };
+
+private:
+ Q_OBJECT
+
+ PlatformRef m_platform;
+ std::optional<BinaryNinja::TypeContainer> m_typeContainer;
+ std::optional<BinaryViewRef> m_binaryView;
+ // Empty view for bv-requiring operations
+ mutable std::optional<BinaryViewRef> m_emptyView;
+ std::vector<BinaryNinja::QualifiedName> m_typeNames;
+
+ // line index -> index of first line from wrapped line
+ std::vector<size_t> m_lineUnwrapIndex;
+ // line index -> type name
+ std::vector<BinaryNinja::QualifiedName> m_lineTypeRefs;
+ // type name -> index of first line
+ std::map<BinaryNinja::QualifiedName, size_t> m_lineTypeStarts;
+ // type name -> { offset -> index of first { line, token } at offset }
+ std::map<BinaryNinja::QualifiedName, std::map<size_t, std::pair<size_t, size_t>>> m_lineTypeOffsetStarts;
+ // type name -> { offset -> index of last { line, token } at offset }
+ std::map<BinaryNinja::QualifiedName, std::map<size_t, std::pair<size_t, size_t>>> m_lineTypeOffsetEnds;
+ // line index -> line
+ std::vector<BinaryNinja::TypeDefinitionLine> m_typeLines;
+
+ TokenizedTextWidgetCursorPosition m_originalBase;
+
+ bool m_wrapLines;
+ bool m_showInherited;
+
+public:
+ TypeEditor(QWidget* parent);
+
+ static void registerActions();
+ void bindActions();
+
+ PlatformRef platform() const { return m_platform; }
+ void setPlatform(PlatformRef platform) { m_platform = platform; }
+
+ std::optional<BinaryViewRef> binaryView() const { return m_binaryView; }
+ void setBinaryView(std::optional<BinaryViewRef> binaryView) { m_binaryView = binaryView; }
+
+ std::optional<std::reference_wrapper<const BinaryNinja::TypeContainer>> typeContainer() const;
+ void setTypeContainer(std::optional<BinaryNinja::TypeContainer> container);
+
+ std::vector<BinaryNinja::QualifiedName> typeNames() const { return m_typeNames; }
+ void setTypeNames(const std::vector<BinaryNinja::QualifiedName>& names);
+
+ int selectedLineCount() const;
+ std::unordered_set<size_t> selectedLineStarts() const;
+ int selectedRootTypeCount() const;
+ std::unordered_set<BinaryNinja::QualifiedName> selectedRootTypes() const;
+ std::optional<size_t> firstWrappedLineIndexForLineIndex(size_t lineIndex) const;
+ std::optional<size_t> lastWrappedLineIndexForLineIndex(size_t lineIndex) const;
+ std::optional<std::reference_wrapper<const BinaryNinja::TypeDefinitionLine>> typeLineAtIndex(size_t lineIndex) const;
+ std::optional<std::reference_wrapper<const BinaryNinja::TypeDefinitionLine>> typeLineAtPosition(const TokenizedTextWidgetCursorPosition& position) const;
+ std::optional<BinaryNinja::QualifiedName> rootTypeNameAtIndex(size_t lineIndex) const;
+ std::optional<BinaryNinja::QualifiedName> rootTypeNameAtPosition(const TokenizedTextWidgetCursorPosition& position) const;
+ std::optional<TypeRef> rootTypeAtIndex(size_t lineIndex) const;
+ std::optional<TypeRef> rootTypeAtPosition(const TokenizedTextWidgetCursorPosition& position) const;
+ std::optional<uint64_t> offsetAtIndex(size_t lineIndex) const;
+ std::optional<uint64_t> offsetAtPosition(const TokenizedTextWidgetCursorPosition& position) const;
+ std::optional<int64_t> relativeOffsetAtPosition(const TokenizedTextWidgetCursorPosition& position) const;
+ std::optional<TokenizedTextWidgetCursorPosition> firstPositionForOffset(const BinaryNinja::QualifiedName& name, uint64_t offset) const;
+ std::optional<TokenizedTextWidgetCursorPosition> lastPositionForOffset(const BinaryNinja::QualifiedName& name, uint64_t offset) const;
+ void selectOffsetRange(const BinaryNinja::QualifiedName& name, uint64_t start, uint64_t end);
+
+ SavedCursorPosition saveCursorPosition() const;
+ void restoreCursorPosition(const SavedCursorPosition& position);
+
+ bool canCreateAllMembersForStructure();
+ void createAllMembersForStructure();
+ bool canCreateCurrentMemberForStructure();
+ void createCurrentMemberForStructure();
+ bool canRename();
+ void rename();
+ void renameRoot();
+ void renameMember();
+ bool canUndefine();
+ void undefine();
+ void undefineRoots();
+ void undefineMembers();
+ bool canCreateArray();
+ void createArray();
+ bool canChangeType();
+ void changeType();
+ void changeTypeRoot();
+ void changeTypeAddMember();
+ void changeTypeMember();
+ bool canChangeTypeMembers();
+ void changeTypeMembers(TypeRef newType);
+ bool canSetStructureSize();
+ void setStructureSize();
+ bool canAddUserXref();
+ void addUserXref();
+ bool canMakePointer();
+ void makePointer();
+ bool canMakeCString();
+ void makeCString();
+ bool canMakeUTF16String();
+ void makeUTF16String();
+ bool canMakeUTF32String();
+ void makeUTF32String();
+ bool canCycleIntegerSize();
+ void cycleIntegerSize();
+ bool canCycleFloatSize();
+ void cycleFloatSize();
+ bool canInvertIntegerSize();
+ void invertIntegerSize();
+ bool canMakeInt8();
+ void makeInt8();
+ bool canMakeInt16();
+ void makeInt16();
+ bool canMakeInt32();
+ void makeInt32();
+ bool canMakeInt64();
+ void makeInt64();
+ bool canMakeFloat32();
+ void makeFloat32();
+ bool canMakeFloat64();
+ void makeFloat64();
+ bool canGoToAddress(bool selecting);
+ void goToAddress(bool selecting);
+ void toggleWrapLines();
+ void toggleShowInherited();
+
+ std::string getDebugText();
+
+Q_SIGNALS:
+ void typeNameNavigated(const std::string& typeName);
+ void currentTypeUpdated(const BinaryNinja::QualifiedName& typeName);
+ void currentTypeDeleted(const BinaryNinja::QualifiedName& typeName);
+ void currentTypeNameUpdated(const BinaryNinja::QualifiedName& typeName);
+
+private:
+ void updateLines();
+ BinaryViewRef binaryViewOrEmpty() const;
+ void updateInTransaction(std::function<void()> transaction);
+ void updateInTransaction(std::function<void(BinaryViewRef)> transaction);
+ std::string dumpType(TypeRef type);
+
+ void forEachMember(const TokenizedTextWidgetCursorPosition& begin, const TokenizedTextWidgetCursorPosition& end,
+ std::function<void(TypeRef /* type */, TypeRef /* parent */, size_t /* memberIndex */, size_t /* rootOffset */)> func, bool childrenFirst = false);
+};
diff --git a/ui/util.h b/ui/util.h
index bb2a9b50..c3e5ece5 100644
--- a/ui/util.h
+++ b/ui/util.h
@@ -31,6 +31,9 @@ void BINARYNINJAUIAPI showTextTooltip(QWidget* parent, const QPoint& previewPos,
bool BINARYNINJAUIAPI isBinaryNinjaDataBase(QFileInfo& info, QFileAccessor& accessor);
+PlatformRef BINARYNINJAUIAPI getOrAskForPlatform(QWidget* parent, BinaryViewRef data);
+PlatformRef BINARYNINJAUIAPI getOrAskForPlatform(QWidget* parent, PlatformRef defaultValue);
+
/*!
@}
*/
diff --git a/ui/viewframe.h b/ui/viewframe.h
index 59e45a75..7f4376e8 100644
--- a/ui/viewframe.h
+++ b/ui/viewframe.h
@@ -136,6 +136,7 @@ class BINARYNINJAUIAPI View
virtual ~View() {}
void enableRefreshTimer(QWidget* owner, int interval);
+ void setRefreshTimerRunning(bool running);
void setRefreshQuiesce(bool enable);
virtual void notifyRefresh() {};