1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
|
#pragma once
#include <QtWidgets/QListView>
#include <QtCore/QAbstractItemModel>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QFrame>
#include <QtCore/QPointer>
#include <QtCore/QThread>
#include <QtWidgets/QStyledItemDelegate>
#include <vector>
#include "action.h"
/*!
\defgroup commandpalette CommandPalette
\ingroup uiapi
*/
/*!
\ingroup commandpalette
*/
struct BINARYNINJAUIAPI CommandListItem
{
enum CommandListItemType
{
// Index is serialized so only to add to the end
Help,
UIAction,
OpenTab,
NavigationHistory,
RecentFile,
RecentProject,
ProjectFile,
Expression,
RecentExpression,
Function,
Symbol,
Type,
String,
DerivedString,
LastItemType
};
CommandListItemType type;
QString name;
QString secondary;
QString shortcut;
QString extraSearchableText;
QVariant action;
Qt::TextElideMode secondaryElide = Qt::ElideRight;
int score = 0;
bool addToRecents = true;
bool includeUnfiltered = true;
bool scoreFast = false;
};
/*!
\ingroup commandpalette
*/
struct BINARYNINJAUIAPI CommandListItemSearchInfo
{
std::unordered_set<CommandListItem::CommandListItemType> types;
QString name;
QString searchName;
std::string prefix;
QKeySequence shortcut;
static std::vector<CommandListItemSearchInfo> GetSearchTypes();
};
class CommandPalette;
class CommandListFilter;
class CommandListGenerateWorker : public QObject
{
Q_OBJECT
BinaryViewRef m_view;
bool m_aborted;
int m_request;
std::shared_ptr<std::atomic_int> m_latestRequest;
bool m_pending;
std::vector<CommandListItem> m_pendingItems;
std::mutex m_pendingItemsMutex;
public:
explicit CommandListGenerateWorker(QObject* parent, std::shared_ptr<std::atomic_int> request, const UIActionContext& context);
virtual ~CommandListGenerateWorker();
Q_SIGNALS:
void dataFetched(int request, const std::vector<CommandListItem>& items);
void noMoreDataToFetch(int request);
void workFinished(int request);
public Q_SLOTS:
void fetchMore();
void start();
void abort();
};
class CommandListScoreWorker : public QObject
{
public:
enum OrderStrategy
{
DefaultOrder,
ScoreOrder
};
private:
Q_OBJECT
bool m_aborted;
int m_request;
std::shared_ptr<std::atomic_int> m_latestRequest;
QString m_filter;
std::shared_ptr<std::vector<CommandListItem>> m_items;
std::vector<int> m_oldItemScores;
OrderStrategy m_strategy;
bool m_pending;
std::vector<std::pair<CommandListItem*, int>> m_pendingItems;
std::mutex m_pendingItemsMutex;
public:
explicit CommandListScoreWorker(
QObject* parent,
std::shared_ptr<std::atomic_int> request,
QString filter,
std::shared_ptr<std::vector<CommandListItem>> items,
OrderStrategy orderStrategy
);
virtual ~CommandListScoreWorker();
static int scoreItem(const CommandListItem* item, const QString& lowerFilter, const std::string& strippedFilter);
Q_SIGNALS:
void dataFetched(int request, const std::vector<std::pair<CommandListItem*, int>>& items);
void noMoreDataToFetch(int request);
void workFinished(int request);
public Q_SLOTS:
void fetchMore();
void start();
void abort();
};
/*!
\ingroup commandpalette
*/
class BINARYNINJAUIAPI CommandListDelegate : public QStyledItemDelegate
{
Q_OBJECT
QFont m_font;
int m_height;
public:
CommandListDelegate(QWidget* parent);
virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& idx) const override;
virtual QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override;
};
/*!
\ingroup commandpalette
*/
class BINARYNINJAUIAPI CommandListModel : public QAbstractItemModel
{
Q_OBJECT
UIActionHandler* m_handler;
UIActionContext m_context;
std::shared_ptr<std::vector<CommandListItem>> m_allItems;
std::vector<CommandListItem*> m_displayItems; // pointers into m_allItems
QString m_filterText;
bool m_updatesPaused;
std::vector<CommandListItem> m_recentItems;
QThread m_generateWorkerThread;
CommandListGenerateWorker* m_generateWorker;
std::shared_ptr<std::atomic_int> m_generateWorkerRequest;
QTimer m_generateFetchTimer;
bool m_generateMoreToFetch;
QTimer m_scoreTimer;
QMetaObject::Connection m_scoreTimerConnection;
QThread m_scoreWorkerThread;
CommandListScoreWorker* m_scoreWorker;
std::shared_ptr<std::atomic_int> m_scoreWorkerRequest;
QTimer m_scoreFetchTimer;
bool m_scoreMoreToFetch;
void loadRecentItems();
std::vector<CommandListItem> generateFastCommandList();
void scoreFastCommandList();
void sortCommandList(std::vector<CommandListItem>& list);
void mergeCommandList(std::vector<CommandListItem>& output, std::vector<CommandListItem>&& input);
void startScoreListThread();
public:
CommandListModel(QWidget* parent);
~CommandListModel();
virtual QModelIndex index(int row, int col, const QModelIndex& parent) const override;
virtual QModelIndex parent(const QModelIndex& i) const override;
virtual bool hasChildren(const QModelIndex& parent) const override;
virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override;
virtual int columnCount(const QModelIndex& parent) const override;
virtual QVariant data(const QModelIndex& i, int role) const override;
CommandListItem getItem(int row);
void setFilterText(const QString& text);
size_t getRecentPosition(const CommandListItem& item) const;
void addRecentItem(const CommandListItem& item);
void clearCommandList();
void generateCommandList(UIActionHandler* handler, const UIActionContext& context);
void cancelGenerateCommandList();
void scoreCommandList(CommandListScoreWorker::OrderStrategy strategy);
void cancelScoreCommandList();
void pauseCommandListUpdates();
void unpauseCommandListUpdates();
void updateDisplayedItems();
private Q_SLOTS:
void generateFetch();
void itemsGenerated(int request, const std::vector<CommandListItem>& items);
void itemsFinishedGenerating(int request);
void generateWorkFinished(int request);
void scoreFetch();
void itemsScored(int request, const std::vector<std::pair<CommandListItem*, int>>& items);
void itemsFinishedScoring(int request);
void scoreWorkFinished(int request);
};
/*!
\ingroup commandpalette
*/
class BINARYNINJAUIAPI CommandList : public QListView
{
Q_OBJECT
CommandPalette* m_palette;
CommandListModel* m_model;
CommandListFilter* m_filter;
public:
CommandList(CommandPalette* parent);
void setFilter(CommandListFilter* filter) { m_filter = filter; }
void setFilterText(const QString& text);
CommandListItem getItem(int row);
QModelIndex index(int row, int col, const QModelIndex& parent = QModelIndex()) const;
void addRecentItem(const CommandListItem& item);
void clearCommandList();
void generateCommandList(UIActionHandler* handler, const UIActionContext& context);
void cancelGenerateCommandList();
void pauseCommandListUpdates();
void unpauseCommandListUpdates();
protected:
virtual void keyPressEvent(QKeyEvent* event) override;
virtual void focusOutEvent(QFocusEvent* event) override;
};
/*!
\ingroup commandpalette
*/
class BINARYNINJAUIAPI CommandListFilter : public QLineEdit
{
Q_OBJECT
CommandPalette* m_palette;
CommandList* m_list;
//! Focus the next or previous results list item.
bool cycleSelection(bool forward = true);
public:
CommandListFilter(CommandPalette* parent, CommandList* list);
protected:
bool event(QEvent* event) override;
virtual void keyPressEvent(QKeyEvent* event) override;
virtual void focusInEvent(QFocusEvent* event) override;
virtual void focusOutEvent(QFocusEvent* event) override;
virtual void paintEvent(QPaintEvent* event) override;
};
/*!
\ingroup commandpalette
*/
class BINARYNINJAUIAPI CommandPalette : public QFrame
{
Q_OBJECT
UIActionHandler* m_handler;
UIActionContext m_context;
QPointer<QWidget> m_previousWidget;
CommandListFilter* m_filter;
CommandList* m_list;
std::optional<CommandListItem> m_savedTop;
bool m_executing;
void init();
public:
CommandPalette(QWidget* parent);
void openWithInput(const QString& text);
void focusInput();
void clearCommandList();
void generateCommandList(UIActionHandler* handler, const UIActionContext& context);
//! Activate the focused item, or topmost item if there is no selection.
void activateFocusedItem();
void selectFirstItem();
void close(bool restoreFocus = true);
void activateItem(const CommandListItem& item);
private Q_SLOTS:
void itemClicked(const QModelIndex& idx);
void filterChanged(const QString& text);
};
|