1 #include "FlagsWidget.h"
2 #include "ui_FlagsWidget.h"
3 #include "core/MainWindow.h"
4 #include "common/Helpers.h"
5
6 #include <QComboBox>
7 #include <QMenu>
8 #include <QShortcut>
9 #include <QTreeWidget>
10 #include <QStandardItemModel>
11 #include <QInputDialog>
12
FlagsModel(QList<FlagDescription> * flags,QObject * parent)13 FlagsModel::FlagsModel(QList<FlagDescription> *flags, QObject *parent)
14 : AddressableItemModel<QAbstractListModel>(parent),
15 flags(flags)
16 {
17 }
18
rowCount(const QModelIndex &) const19 int FlagsModel::rowCount(const QModelIndex &) const
20 {
21 return flags->count();
22 }
23
columnCount(const QModelIndex &) const24 int FlagsModel::columnCount(const QModelIndex &) const
25 {
26 return Columns::COUNT;
27 }
28
data(const QModelIndex & index,int role) const29 QVariant FlagsModel::data(const QModelIndex &index, int role) const
30 {
31 if (index.row() >= flags->count())
32 return QVariant();
33
34 const FlagDescription &flag = flags->at(index.row());
35
36 switch (role) {
37 case Qt::DisplayRole:
38 switch (index.column()) {
39 case SIZE:
40 return RSizeString(flag.size);
41 case OFFSET:
42 return RAddressString(flag.offset);
43 case NAME:
44 return flag.name;
45 case REALNAME:
46 return flag.realname;
47 case COMMENT:
48 return Core()->getCommentAt(flag.offset);
49 default:
50 return QVariant();
51 }
52 case FlagDescriptionRole:
53 return QVariant::fromValue(flag);
54 default:
55 return QVariant();
56 }
57 }
58
headerData(int section,Qt::Orientation,int role) const59 QVariant FlagsModel::headerData(int section, Qt::Orientation, int role) const
60 {
61 switch (role) {
62 case Qt::DisplayRole:
63 switch (section) {
64 case SIZE:
65 return tr("Size");
66 case OFFSET:
67 return tr("Offset");
68 case NAME:
69 return tr("Name");
70 case REALNAME:
71 return tr("Real Name");
72 case COMMENT:
73 return tr("Comment");
74 default:
75 return QVariant();
76 }
77 default:
78 return QVariant();
79 }
80 }
81
address(const QModelIndex & index) const82 RVA FlagsModel::address(const QModelIndex &index) const
83 {
84 const FlagDescription &flag = flags->at(index.row());
85 return flag.offset;
86 }
87
name(const QModelIndex & index) const88 QString FlagsModel::name(const QModelIndex &index) const
89 {
90 const FlagDescription &flag = flags->at(index.row());
91 return flag.name;
92 }
93
description(QModelIndex index) const94 const FlagDescription *FlagsModel::description(QModelIndex index) const
95 {
96 if (index.row() < flags->size()) {
97 return &flags->at(index.row());
98 }
99 return nullptr;
100 }
101
FlagsSortFilterProxyModel(FlagsModel * source_model,QObject * parent)102 FlagsSortFilterProxyModel::FlagsSortFilterProxyModel(FlagsModel *source_model, QObject *parent)
103 : AddressableFilterProxyModel(source_model, parent)
104 {
105 }
106
filterAcceptsRow(int row,const QModelIndex & parent) const107 bool FlagsSortFilterProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const
108 {
109 QModelIndex index = sourceModel()->index(row, 0, parent);
110 FlagDescription flag = index.data(FlagsModel::FlagDescriptionRole).value<FlagDescription>();
111 return flag.name.contains(filterRegExp()) || flag.realname.contains(filterRegExp());
112 }
113
lessThan(const QModelIndex & left,const QModelIndex & right) const114 bool FlagsSortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
115 {
116 auto source = static_cast<FlagsModel *>(sourceModel());
117 auto left_flag = source->description(left);
118 auto right_flag = source->description(right);
119
120 switch (left.column()) {
121 case FlagsModel::SIZE:
122 if (left_flag->size != right_flag->size)
123 return left_flag->size < right_flag->size;
124 // fallthrough
125 case FlagsModel::OFFSET:
126 if (left_flag->offset != right_flag->offset)
127 return left_flag->offset < right_flag->offset;
128 // fallthrough
129 case FlagsModel::NAME:
130 return left_flag->name < right_flag->name;
131
132 case FlagsModel::REALNAME:
133 return left_flag->realname < right_flag->realname;
134
135 case FlagsModel::COMMENT:
136 return Core()->getCommentAt(left_flag->offset) < Core()->getCommentAt(right_flag->offset);
137
138 default:
139 break;
140 }
141
142 // fallback
143 return left_flag->offset < right_flag->offset;
144 }
145
146
FlagsWidget(MainWindow * main)147 FlagsWidget::FlagsWidget(MainWindow *main) :
148 CutterDockWidget(main),
149 ui(new Ui::FlagsWidget),
150 main(main),
151 tree(new CutterTreeWidget(this))
152 {
153 ui->setupUi(this);
154
155 // Add Status Bar footer
156 tree->addStatusBar(ui->verticalLayout);
157
158 flags_model = new FlagsModel(&flags, this);
159 flags_proxy_model = new FlagsSortFilterProxyModel(flags_model, this);
160 connect(ui->filterLineEdit, &QLineEdit::textChanged,
161 flags_proxy_model, &QSortFilterProxyModel::setFilterWildcard);
162 ui->flagsTreeView->setMainWindow(mainWindow);
163 ui->flagsTreeView->setModel(flags_proxy_model);
164 ui->flagsTreeView->sortByColumn(FlagsModel::OFFSET, Qt::AscendingOrder);
165
166 // Ctrl-F to move the focus to the Filter search box
167 QShortcut *searchShortcut = new QShortcut(QKeySequence::Find, this);
168 connect(searchShortcut, &QShortcut::activated, ui->filterLineEdit, [this]() { ui->filterLineEdit->setFocus(); });
169 searchShortcut->setContext(Qt::WidgetWithChildrenShortcut);
170
171 // Esc to clear the filter entry
172 QShortcut *clearShortcut = new QShortcut(QKeySequence(Qt::Key_Escape), this);
173 connect(clearShortcut, &QShortcut::activated, [this] {
174 if (ui->filterLineEdit->text().isEmpty()) {
175 ui->flagsTreeView->setFocus();
176 } else {
177 ui->filterLineEdit->setText("");
178 }
179 });
180 clearShortcut->setContext(Qt::WidgetWithChildrenShortcut);
181
182 connect(ui->filterLineEdit, &QLineEdit::textChanged, this, [this] {
183 tree->showItemsNumber(flags_proxy_model->rowCount());
184 });
185
186 setScrollMode();
187
188 connect(Core(), &CutterCore::flagsChanged, this, &FlagsWidget::flagsChanged);
189 connect(Core(), &CutterCore::codeRebased, this, &FlagsWidget::flagsChanged);
190 connect(Core(), &CutterCore::refreshAll, this, &FlagsWidget::refreshFlagspaces);
191 connect(Core(), &CutterCore::commentsChanged, this, [this]() {
192 qhelpers::emitColumnChanged(flags_model, FlagsModel::COMMENT);
193 });
194
195 auto menu = ui->flagsTreeView->getItemContextMenu();
196 menu->addSeparator();
197 menu->addAction(ui->actionRename);
198 menu->addAction(ui->actionDelete);
199 addAction(ui->actionRename);
200 addAction(ui->actionDelete);
201 }
202
~FlagsWidget()203 FlagsWidget::~FlagsWidget() {}
204
on_flagspaceCombo_currentTextChanged(const QString & arg1)205 void FlagsWidget::on_flagspaceCombo_currentTextChanged(const QString &arg1)
206 {
207 Q_UNUSED(arg1);
208
209 refreshFlags();
210 }
211
on_actionRename_triggered()212 void FlagsWidget::on_actionRename_triggered()
213 {
214 FlagDescription flag = ui->flagsTreeView->selectionModel()->currentIndex().data(
215 FlagsModel::FlagDescriptionRole).value<FlagDescription>();
216
217 bool ok;
218 QString newName = QInputDialog::getText(this, tr("Rename flag %1").arg(flag.name),
219 tr("Flag name:"), QLineEdit::Normal, flag.name, &ok);
220 if (ok && !newName.isEmpty()) {
221 Core()->renameFlag(flag.name, newName);
222 }
223 }
224
on_actionDelete_triggered()225 void FlagsWidget::on_actionDelete_triggered()
226 {
227 FlagDescription flag = ui->flagsTreeView->selectionModel()->currentIndex().data(
228 FlagsModel::FlagDescriptionRole).value<FlagDescription>();
229 Core()->delFlag(flag.name);
230 }
231
flagsChanged()232 void FlagsWidget::flagsChanged()
233 {
234 refreshFlagspaces();
235 }
236
refreshFlagspaces()237 void FlagsWidget::refreshFlagspaces()
238 {
239 int cur_idx = ui->flagspaceCombo->currentIndex();
240 if (cur_idx < 0)
241 cur_idx = 0;
242
243 disableFlagRefresh = true; // prevent duplicate flag refresh caused by flagspaceCombo modifications
244 ui->flagspaceCombo->clear();
245 ui->flagspaceCombo->addItem(tr("(all)"));
246
247 for (const FlagspaceDescription &i : Core()->getAllFlagspaces()) {
248 ui->flagspaceCombo->addItem(i.name, QVariant::fromValue(i));
249 }
250
251 if (cur_idx > 0)
252 ui->flagspaceCombo->setCurrentIndex(cur_idx);
253 disableFlagRefresh = false;
254
255 refreshFlags();
256 }
257
refreshFlags()258 void FlagsWidget::refreshFlags()
259 {
260 if (disableFlagRefresh) {
261 return;
262 }
263 QString flagspace;
264
265 QVariant flagspace_data = ui->flagspaceCombo->currentData();
266 if (flagspace_data.isValid())
267 flagspace = flagspace_data.value<FlagspaceDescription>().name;
268
269
270 flags_model->beginResetModel();
271 flags = Core()->getAllFlags(flagspace);
272 flags_model->endResetModel();
273
274 tree->showItemsNumber(flags_proxy_model->rowCount());
275
276 // TODO: this is not a very good place for the following:
277 QStringList flagNames;
278 for (const FlagDescription &i : flags)
279 flagNames.append(i.name);
280 main->refreshOmniBar(flagNames);
281 }
282
setScrollMode()283 void FlagsWidget::setScrollMode()
284 {
285 qhelpers::setVerticalScrollMode(ui->flagsTreeView);
286 }
287