diff --git a/src/Cutter.pro b/src/Cutter.pro index 2c349fed..c5d532ff 100644 --- a/src/Cutter.pro +++ b/src/Cutter.pro @@ -496,7 +496,8 @@ HEADERS += \ common/Decompiler.h \ menus/AddressableItemContextMenu.h \ common/AddressableItemModel.h \ - widgets/ListDockWidget.h + widgets/ListDockWidget.h \ + widgets/AddressableItemList.h GRAPHVIZ_HEADERS = widgets/GraphGridLayout.h @@ -536,7 +537,6 @@ FORMS += \ widgets/RegistersWidget.ui \ widgets/BacktraceWidget.ui \ dialogs/OpenFileDialog.ui \ - widgets/MemoryMapWidget.ui \ dialogs/preferences/DebugOptionsWidget.ui \ widgets/BreakpointWidget.ui \ dialogs/BreakpointsDialog.ui \ diff --git a/src/common/Helpers.cpp b/src/common/Helpers.cpp index 10c1935b..8b377bdb 100644 --- a/src/common/Helpers.cpp +++ b/src/common/Helpers.cpp @@ -78,7 +78,7 @@ QTreeWidgetItem *appendRow(QTreeWidget *tw, const QString &str, const QString &s * @param tw - QTreeWidget instance * @return true - setCurrentItem was set, false - tree is empty */ -bool selectFirstItem(QTreeWidget* tw) +bool selectFirstItem(QTreeWidget *tw) { if (tw->topLevelItem(0)) { tw->setCurrentItem(tw->topLevelItem(0)); @@ -87,6 +87,19 @@ bool selectFirstItem(QTreeWidget* tw) return false; } + +bool selectFirstItem(QAbstractItemView *itemView) +{ + auto selectionModel = itemView->selectionModel(); + auto model = itemView->model(); + if (model->hasChildren()) { + selectionModel->setCurrentIndex(model->index(0, 0), QItemSelectionModel::SelectCurrent); + return true; + } else { + return false; + } +} + void setVerticalScrollMode(QAbstractItemView *tw) { tw->setVerticalScrollMode(scrollMode()); @@ -205,7 +218,8 @@ QByteArray applyColorToSvg(const QString &filename, QColor color) * @param setter functor which has to be called * for example we need to set an action icon, the functor can be just [](void* o, const QIcon &icon) { static_cast(o)->setIcon(icon); } */ -void setThemeIcons(QList> supportedIconsNames, std::function setter) +void setThemeIcons(QList> supportedIconsNames, + std::function setter) { if (supportedIconsNames.isEmpty() || !setter) { return; diff --git a/src/common/Helpers.h b/src/common/Helpers.h index 39c4acb5..e5c89c86 100644 --- a/src/common/Helpers.h +++ b/src/common/Helpers.h @@ -21,7 +21,8 @@ namespace qhelpers { QString formatBytecount(const long bytecount); void adjustColumns(QTreeView *tv, int columnCount, int padding); void adjustColumns(QTreeWidget *tw, int padding); -bool selectFirstItem(QTreeWidget* tw); +bool selectFirstItem(QTreeWidget *tw); +bool selectFirstItem(QAbstractItemView *itemView); QTreeWidgetItem *appendRow(QTreeWidget *tw, const QString &str, const QString &str2 = QString(), const QString &str3 = QString(), const QString &str4 = QString(), const QString &str5 = QString()); diff --git a/src/dialogs/CommentsDialog.cpp b/src/dialogs/CommentsDialog.cpp index 5a18cc0b..0f5a001a 100644 --- a/src/dialogs/CommentsDialog.cpp +++ b/src/dialogs/CommentsDialog.cpp @@ -1,6 +1,8 @@ #include "CommentsDialog.h" #include "ui_CommentsDialog.h" +#include "core/Cutter.h" + CommentsDialog::CommentsDialog(QWidget *parent) : QDialog(parent), ui(new Ui::CommentsDialog) @@ -34,6 +36,30 @@ void CommentsDialog::setComment(const QString &comment) ui->textEdit->document()->setPlainText(comment); } +void CommentsDialog::addOrEditComment(RVA offset, QWidget *parent) +{ + QString oldComment = Core()->cmd("CC." + RAddressString(offset)); + // Remove newline at the end added by cmd + oldComment.remove(oldComment.length() - 1, 1); + CommentsDialog c(parent); + + if (oldComment.isNull() || oldComment.isEmpty()) { + c.setWindowTitle(tr("Add Comment at %1").arg(RAddressString(offset))); + } else { + c.setWindowTitle(tr("Edit Comment at %1").arg(RAddressString(offset))); + } + + c.setComment(oldComment); + if (c.exec()) { + QString comment = c.getComment(); + if (comment.isEmpty()) { + Core()->delComment(offset); + } else { + Core()->setComment(offset, comment); + } + } +} + bool CommentsDialog::eventFilter(QObject */*obj*/, QEvent *event) { if (event -> type() == QEvent::KeyPress) { diff --git a/src/dialogs/CommentsDialog.h b/src/dialogs/CommentsDialog.h index 6e5c83e2..e5650e00 100644 --- a/src/dialogs/CommentsDialog.h +++ b/src/dialogs/CommentsDialog.h @@ -4,6 +4,8 @@ #include #include +#include "core/CutterCommon.h" + namespace Ui { class CommentsDialog; } @@ -19,6 +21,7 @@ public: QString getComment(); void setComment(const QString &comment); + static void addOrEditComment(RVA offset, QWidget *parent); private slots: void on_buttonBox_accepted(); diff --git a/src/dialogs/XrefsDialog.cpp b/src/dialogs/XrefsDialog.cpp index 45718dc1..89332e58 100644 --- a/src/dialogs/XrefsDialog.cpp +++ b/src/dialogs/XrefsDialog.cpp @@ -8,14 +8,22 @@ #include -XrefsDialog::XrefsDialog(QWidget *parent) : +XrefsDialog::XrefsDialog(MainWindow *main, QWidget *parent) : QDialog(parent), addr(0), + toModel(this), + fromModel(this), ui(new Ui::XrefsDialog) { ui->setupUi(this); setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint)); + ui->toTreeWidget->setMainWindow(main); + ui->fromTreeWidget->setMainWindow(main); + + ui->toTreeWidget->setModel(&toModel); + ui->fromTreeWidget->setModel(&fromModel); + // Modify the splitter's location to show more Disassembly instead of empty space. Not possible via Designer ui->splitter->setSizes(QList() << 100 << 200); @@ -30,66 +38,24 @@ XrefsDialog::XrefsDialog(QWidget *parent) : connect(ui->previewTextEdit, SIGNAL(cursorPositionChanged()), this, SLOT(highlightCurrentLine())); connect(Config(), SIGNAL(fontsUpdated()), this, SLOT(setupPreviewFont())); connect(Config(), SIGNAL(colorsUpdated()), this, SLOT(setupPreviewColors())); + + connect(ui->toTreeWidget->selectionModel(), &QItemSelectionModel::selectionChanged, + this, &XrefsDialog::onToTreeWidgetItemSelectionChanged); + connect(ui->fromTreeWidget->selectionModel(), &QItemSelectionModel::selectionChanged, + this, &XrefsDialog::onFromTreeWidgetItemSelectionChanged); + + // Don't create recursive xref dialogs + auto toContextMenu = ui->toTreeWidget->getItemContextMenu(); + connect(toContextMenu, &AddressableItemContextMenu::xrefsTriggered, this, &QWidget::close); + auto fromContextMenu = ui->fromTreeWidget->getItemContextMenu(); + connect(fromContextMenu, &AddressableItemContextMenu::xrefsTriggered, this, &QWidget::close); + + connect(ui->toTreeWidget, &QAbstractItemView::doubleClicked, this, &QWidget::close); + connect(ui->fromTreeWidget, &QAbstractItemView::doubleClicked, this, &QWidget::close); } XrefsDialog::~XrefsDialog() { } -void XrefsDialog::fillRefs(QList refs, QList xrefs) -{ - // Fill refs - ui->fromTreeWidget->clear(); - for (const auto &xref : refs) { - auto *tempItem = new QTreeWidgetItem(); - tempItem->setText(0, xref.to_str); - if (xref.type != "DATA") { - tempItem->setText(1, Core()->disassembleSingleInstruction(xref.to)); - } - tempItem->setText(2, xrefTypeString(xref.type)); - tempItem->setData(0, Qt::UserRole, QVariant::fromValue(xref)); - ui->fromTreeWidget->insertTopLevelItem(0, tempItem); - } - - // Adjust columns to content - qhelpers::adjustColumns(ui->fromTreeWidget, 0); - - // Fill Xrefs - ui->toTreeWidget->clear(); - for (const auto &xref : xrefs) { - auto *tempItem = new QTreeWidgetItem(); - tempItem->setText(0, xref.from_str); - tempItem->setText(1, Core()->disassembleSingleInstruction(xref.from)); - tempItem->setText(2, xrefTypeString(xref.type)); - tempItem->setData(0, Qt::UserRole, QVariant::fromValue(xref)); - ui->toTreeWidget->insertTopLevelItem(0, tempItem); - } - - // Adjust columns to content - qhelpers::adjustColumns(ui->toTreeWidget, 0); - - // try to select first item from refs or xrefs - if (!qhelpers::selectFirstItem(ui->toTreeWidget)) { - qhelpers::selectFirstItem(ui->fromTreeWidget); - } -} - -void XrefsDialog::on_fromTreeWidget_itemDoubleClicked(QTreeWidgetItem *item, int column) -{ - Q_UNUSED(column); - - XrefDescription xref = item->data(0, Qt::UserRole).value(); - Core()->seekAndShow(xref.to); - this->close(); -} - -void XrefsDialog::on_toTreeWidget_itemDoubleClicked(QTreeWidgetItem *item, int column) -{ - Q_UNUSED(column); - - XrefDescription xref = item->data(0, Qt::UserRole).value(); - Core()->seekAndShow(xref.from); - this->close(); -} - QString XrefsDialog::normalizeAddr(const QString &addr) const { QString ret = addr; @@ -129,26 +95,24 @@ void XrefsDialog::highlightCurrentLine() } } -void XrefsDialog::on_fromTreeWidget_itemSelectionChanged() +void XrefsDialog::onFromTreeWidgetItemSelectionChanged() { - if (ui->fromTreeWidget->selectedItems().isEmpty()) { + auto index = ui->fromTreeWidget->currentIndex(); + if (!ui->fromTreeWidget->selectionModel()->hasSelection() || !index.isValid()) { return; } ui->toTreeWidget->clearSelection(); - QTreeWidgetItem *item = ui->fromTreeWidget->currentItem(); - XrefDescription xref = item->data(0, Qt::UserRole).value(); - updatePreview(xref.to); + updatePreview(fromModel.address(index)); } -void XrefsDialog::on_toTreeWidget_itemSelectionChanged() +void XrefsDialog::onToTreeWidgetItemSelectionChanged() { - if (ui->toTreeWidget->selectedItems().isEmpty()) { + auto index = ui->toTreeWidget->currentIndex(); + if (!ui->toTreeWidget->selectionModel()->hasSelection() || !index.isValid()) { return; } ui->fromTreeWidget->clearSelection(); - QTreeWidgetItem *item = ui->toTreeWidget->currentItem(); - XrefDescription xref = item->data(0, Qt::UserRole).value(); - updatePreview(xref.from); + updatePreview(toModel.address(index)); } void XrefsDialog::updatePreview(RVA addr) @@ -180,24 +144,113 @@ void XrefsDialog::fillRefsForAddress(RVA addr, QString name, bool whole_function setWindowTitle(tr("X-Refs for %1").arg(name)); updateLabels(name); - // Get Refs and Xrefs - QList refs = Core()->getXRefs(addr, false, whole_function); - QList xrefs = Core()->getXRefs(addr, true, whole_function); + toModel.readForOffset(addr, true, whole_function); + fromModel.readForOffset(addr, false, whole_function); - fillRefs(refs, xrefs); + // Adjust columns to content + qhelpers::adjustColumns(ui->fromTreeWidget, fromModel.columnCount(), 0); + qhelpers::adjustColumns(ui->toTreeWidget, toModel.columnCount(), 0); + + // try to select first item from refs or xrefs + if (!qhelpers::selectFirstItem(ui->toTreeWidget)) { + qhelpers::selectFirstItem(ui->fromTreeWidget); + } } -QString XrefsDialog::xrefTypeString(const QString &type) +QString XrefModel::xrefTypeString(const QString &type) { if (type == "CODE") { - return QString("Code"); + return QStringLiteral("Code"); } else if (type == "CALL") { - return QString("Call"); + return QStringLiteral("Call"); } else if (type == "DATA") { - return QString("Data"); + return QStringLiteral("Data"); } else if (type == "STRING") { - return QString("String"); + return QStringLiteral("String"); } return type; } + +XrefModel::XrefModel(QObject *parent) + : AddressableItemModel(parent) +{ +} + +void XrefModel::readForOffset(RVA offset, bool to, bool whole_function) +{ + beginResetModel(); + this->to = to; + xrefs = Core()->getXRefs(offset, to, whole_function); + endResetModel(); +} + +int XrefModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + return xrefs.size(); +} + +int XrefModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + return Columns::COUNT; +} + +QVariant XrefModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() >= xrefs.count()) { + return QVariant(); + } + + const XrefDescription &xref = xrefs.at(index.row()); + + switch (role) { + case Qt::DisplayRole: + switch (index.column()) { + case OFFSET: + return to ? xref.from_str : xref.to_str; + case CODE: + if (to || xref.type != "DATA") { + return Core()->disassembleSingleInstruction(xref.from); + } else { + return QString(); + } + case TYPE: + return xrefTypeString(xref.type); + } + return QVariant(); + case FlagDescriptionRole: + return QVariant::fromValue(xref); + default: + return QVariant(); + } + return QVariant(); +} + +QVariant XrefModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + Q_UNUSED(orientation) + + switch (role) { + case Qt::DisplayRole: + switch (section) { + case OFFSET: + return tr("Address"); + case CODE: + return tr("Code"); + case TYPE: + return tr("Type"); + default: + return QVariant(); + } + default: + return QVariant(); + } +} + +RVA XrefModel::address(const QModelIndex &index) const +{ + const auto &xref = xrefs.at(index.row()); + return to ? xref.from : xref.to; +} diff --git a/src/dialogs/XrefsDialog.h b/src/dialogs/XrefsDialog.h index b50cc956..83aa1733 100644 --- a/src/dialogs/XrefsDialog.h +++ b/src/dialogs/XrefsDialog.h @@ -6,6 +6,32 @@ #include #include "common/Highlighter.h" #include "core/Cutter.h" +#include "common/AddressableItemModel.h" + +class XrefModel: public AddressableItemModel +{ +private: + QList xrefs; + bool to; +public: + enum Columns { OFFSET = 0, CODE, TYPE, COUNT }; + static const int FlagDescriptionRole = Qt::UserRole; + + XrefModel(QObject *parent = nullptr); + void readForOffset(RVA offset, bool to, bool whole_function); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + + QVariant data(const QModelIndex &index, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const override; + + RVA address(const QModelIndex &index) const override; + + static QString xrefTypeString(const QString &type); +}; + class MainWindow; @@ -18,37 +44,32 @@ class XrefsDialog : public QDialog Q_OBJECT public: - explicit XrefsDialog(QWidget *parent = nullptr); + explicit XrefsDialog(MainWindow *main, QWidget *parent); ~XrefsDialog(); void fillRefsForAddress(RVA addr, QString name, bool whole_function); private slots: - void on_fromTreeWidget_itemDoubleClicked(QTreeWidgetItem *item, int column); - void on_toTreeWidget_itemDoubleClicked(QTreeWidgetItem *item, int column); - QString normalizeAddr(const QString &addr) const; void setupPreviewFont(); void setupPreviewColors(); void highlightCurrentLine(); - void on_fromTreeWidget_itemSelectionChanged(); - void on_toTreeWidget_itemSelectionChanged(); + void onFromTreeWidgetItemSelectionChanged(); + void onToTreeWidgetItemSelectionChanged(); private: RVA addr; QString func_name; + XrefModel toModel; + XrefModel fromModel; std::unique_ptr ui; - void fillRefs(QList refs, QList xrefs); void updateLabels(QString name); void updatePreview(RVA addr); - - QString xrefTypeString(const QString &type); - }; #endif // XREFSDIALOG_H diff --git a/src/dialogs/XrefsDialog.ui b/src/dialogs/XrefsDialog.ui index fe48f8aa..badf262a 100644 --- a/src/dialogs/XrefsDialog.ui +++ b/src/dialogs/XrefsDialog.ui @@ -50,7 +50,7 @@ - + QFrame::Box @@ -60,24 +60,6 @@ 5 - - 3 - - - - Address - - - - - Code - - - - - Type - - @@ -88,7 +70,7 @@ - + QFrame::Box @@ -98,24 +80,6 @@ 5 - - 3 - - - - Address - - - - - Code - - - - - Type - - @@ -170,6 +134,13 @@ + + + AddressableItemList<> + QTreeView +
widgets/AddressableItemList.h
+
+
diff --git a/src/menus/AddressableItemContextMenu.cpp b/src/menus/AddressableItemContextMenu.cpp index 154f5b33..0019eb63 100644 --- a/src/menus/AddressableItemContextMenu.cpp +++ b/src/menus/AddressableItemContextMenu.cpp @@ -30,7 +30,8 @@ AddressableItemContextMenu::AddressableItemContextMenu(QWidget *parent, MainWind connect(&actionAddcomment, &QAction::triggered, this, &AddressableItemContextMenu::onActionAddComment); - + actionAddcomment.setShortcut({Qt::Key_Semicolon}); + actionAddcomment.setShortcutContext(Qt::ShortcutContext::WidgetWithChildrenShortcut); addAction(&actionShowInMenu); addAction(&actionCopyAddress); @@ -38,6 +39,7 @@ AddressableItemContextMenu::AddressableItemContextMenu(QWidget *parent, MainWind addSeparator(); addAction(&actionAddcomment); + setHasTarget(hasTarget); connect(this, &QMenu::aboutToShow, this, &AddressableItemContextMenu::aboutToShowSlot); } @@ -59,6 +61,12 @@ void AddressableItemContextMenu::setTarget(RVA offset, QString name) { this->offset = offset; this->name = name; + setHasTarget(true); +} + +void AddressableItemContextMenu::clearTarget() +{ + setHasTarget(false); } void AddressableItemContextMenu::onActionCopyAddress() @@ -69,7 +77,8 @@ void AddressableItemContextMenu::onActionCopyAddress() void AddressableItemContextMenu::onActionShowXrefs() { - XrefsDialog dialog(nullptr); + emit xrefsTriggered(); + XrefsDialog dialog(mainWindow, nullptr); QString tmpName = name; if (name.isEmpty()) { name = RAddressString(offset); @@ -80,17 +89,7 @@ void AddressableItemContextMenu::onActionShowXrefs() void AddressableItemContextMenu::onActionAddComment() { - // Create dialog - CommentsDialog c(this); - - if (c.exec()) { - // Get new function name - QString comment = c.getComment(); - // Rename function in r2 core - Core()->setComment(offset, comment); - // Seek to new renamed function - Core()->seekAndShow(offset); - } + CommentsDialog::addOrEditComment(offset, this); } void AddressableItemContextMenu::aboutToShowSlot() @@ -101,3 +100,11 @@ void AddressableItemContextMenu::aboutToShowSlot() actionShowInMenu.setMenu(mainWindow->createShowInMenu(this, offset)); } +void AddressableItemContextMenu::setHasTarget(bool hasTarget) +{ + this->hasTarget = hasTarget; + for (const auto &action : this->actions()) { + action->setEnabled(hasTarget); + } +} + diff --git a/src/menus/AddressableItemContextMenu.h b/src/menus/AddressableItemContextMenu.h index cf0bf17d..2c588979 100644 --- a/src/menus/AddressableItemContextMenu.h +++ b/src/menus/AddressableItemContextMenu.h @@ -21,6 +21,9 @@ public: public slots: void setOffset(RVA offset); void setTarget(RVA offset, QString name = QString()); + void clearTarget(); +signals: + void xrefsTriggered(); private: void onActionCopyAddress(); void onActionShowXrefs(); @@ -31,7 +34,9 @@ private: MainWindow *mainWindow; RVA offset; + bool hasTarget = false; protected: + void setHasTarget(bool hasTarget); QAction actionShowInMenu; QAction actionCopyAddress; QAction actionShowXrefs; diff --git a/src/menus/DisassemblyContextMenu.cpp b/src/menus/DisassemblyContextMenu.cpp index 02a842be..3f0db0a4 100644 --- a/src/menus/DisassemblyContextMenu.cpp +++ b/src/menus/DisassemblyContextMenu.cpp @@ -671,26 +671,7 @@ void DisassemblyContextMenu::on_actionSetPC_triggered() void DisassemblyContextMenu::on_actionAddComment_triggered() { - QString oldComment = Core()->cmd("CC." + RAddressString(offset)); - // Remove newline at the end added by cmd - oldComment.remove(oldComment.length() - 1, 1); - CommentsDialog c(this); - - if (oldComment.isNull() || oldComment.isEmpty()) { - c.setWindowTitle(tr("Add Comment at %1").arg(RAddressString(offset))); - } else { - c.setWindowTitle(tr("Edit Comment at %1").arg(RAddressString(offset))); - } - - c.setComment(oldComment); - if (c.exec()) { - QString comment = c.getComment(); - if (comment.isEmpty()) { - Core()->delComment(offset); - } else { - Core()->setComment(offset, comment); - } - } + CommentsDialog::addOrEditComment(offset, this); } void DisassemblyContextMenu::on_actionAnalyzeFunction_triggered() @@ -796,7 +777,7 @@ void DisassemblyContextMenu::on_actionSetFunctionVarTypes_triggered() void DisassemblyContextMenu::on_actionXRefs_triggered() { - XrefsDialog dialog(nullptr); + XrefsDialog dialog(mainWindow, nullptr); dialog.fillRefsForAddress(offset, RAddressString(offset), false); dialog.exec(); } diff --git a/src/widgets/AddressableItemList.h b/src/widgets/AddressableItemList.h new file mode 100644 index 00000000..c007dec0 --- /dev/null +++ b/src/widgets/AddressableItemList.h @@ -0,0 +1,103 @@ +#ifndef ADDRESSABLE_ITEM_LIST_H +#define ADDRESSABLE_ITEM_LIST_H + +#include +#include +#include +#include +#include + +#include "core/Cutter.h" +#include "common/AddressableItemModel.h" +#include "CutterDockWidget.h" +#include "CutterTreeWidget.h" +#include "menus/AddressableItemContextMenu.h" +#include "CutterTreeView.h" + +class MainWindow; + +template +class AddressableItemList : public BaseListWidget +{ + static_assert (std::is_base_of::value, + "ParentModel needs to inherit from QAbstractItemModel"); + +public: + explicit AddressableItemList(QWidget *parent = nullptr) : + BaseListWidget(parent) + { + this->connect(this, &QWidget::customContextMenuRequested, this, + &AddressableItemList::showItemContextMenu); + this->setContextMenuPolicy(Qt::CustomContextMenu); + this->connect(this, &QAbstractItemView::activated, this, + &AddressableItemList::onItemActivated); + } + + void setModel(AddressableItemModelI *addressableItemModel) + { + this->addressableModel = addressableItemModel; + + BaseListWidget::setModel(this->addressableModel->asItemModel()); + + this->connect(this->selectionModel(), &QItemSelectionModel::currentChanged, this, + &AddressableItemList::onSelectedItemChanged); + } + void setMainWindow(MainWindow *mainWindow) + { + this->mainWindow = mainWindow; + setItemContextMenu(new AddressableItemContextMenu(this, mainWindow)); + this->addActions(this->getItemContextMenu()->actions()); + } + + AddressableItemContextMenu *getItemContextMenu() + { + return itemContextMenu; + } + void setItemContextMenu(AddressableItemContextMenu *menu) + { + if (itemContextMenu != menu && itemContextMenu) { + itemContextMenu->deleteLater(); + } + itemContextMenu = menu; + } +protected: + virtual void showItemContextMenu(const QPoint &pt) + { + auto index = this->currentIndex(); + if (index.isValid() && itemContextMenu) { + auto offset = addressableModel->address(index); + auto name = addressableModel->name(index); + itemContextMenu->setTarget(offset, name); + itemContextMenu->exec(this->mapToGlobal(pt)); + } + } + + virtual void onItemActivated(const QModelIndex &index) + { + if (!index.isValid()) + return; + + auto offset = addressableModel->address(index); + Core()->seekAndShow(offset); + } + virtual void onSelectedItemChanged(const QModelIndex &index) + { + updateMenuFromItem(index); + } + void updateMenuFromItem(const QModelIndex &index) + { + if (index.isValid()) { + auto offset = addressableModel->address(index); + auto name = addressableModel->name(index); + itemContextMenu->setTarget(offset, name); + } else { + itemContextMenu->clearTarget(); + } + } +private: + AddressableItemModelI *addressableModel = nullptr; + AddressableItemContextMenu *itemContextMenu = nullptr; + MainWindow *mainWindow = nullptr; +}; + +#endif // ADDRESSABLE_ITEM_LIST_H diff --git a/src/widgets/FlagsWidget.cpp b/src/widgets/FlagsWidget.cpp index 528d5a36..73c0b5e1 100644 --- a/src/widgets/FlagsWidget.cpp +++ b/src/widgets/FlagsWidget.cpp @@ -2,7 +2,6 @@ #include "ui_FlagsWidget.h" #include "core/MainWindow.h" #include "dialogs/RenameDialog.h" -#include "dialogs/XrefsDialog.h" #include "common/Helpers.h" #include @@ -11,7 +10,7 @@ #include FlagsModel::FlagsModel(QList *flags, QObject *parent) - : QAbstractListModel(parent), + : AddressableItemModel(parent), flags(flags) { } @@ -71,14 +70,21 @@ QVariant FlagsModel::headerData(int section, Qt::Orientation, int role) const } } +RVA FlagsModel::address(const QModelIndex &index) const +{ + const FlagDescription &flag = flags->at(index.row()); + return flag.offset; +} - - +QString FlagsModel::name(const QModelIndex &index) const +{ + const FlagDescription &flag = flags->at(index.row()); + return flag.name; +} FlagsSortFilterProxyModel::FlagsSortFilterProxyModel(FlagsModel *source_model, QObject *parent) - : QSortFilterProxyModel(parent) + : AddressableFilterProxyModel(source_model, parent) { - setSourceModel(source_model); } bool FlagsSortFilterProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const @@ -128,6 +134,7 @@ FlagsWidget::FlagsWidget(MainWindow *main, QAction *action) : flags_proxy_model = new FlagsSortFilterProxyModel(flags_model, this); connect(ui->filterLineEdit, SIGNAL(textChanged(const QString &)), flags_proxy_model, SLOT(setFilterWildcard(const QString &))); + ui->flagsTreeView->setMainWindow(mainWindow); ui->flagsTreeView->setModel(flags_proxy_model); ui->flagsTreeView->sortByColumn(FlagsModel::OFFSET, Qt::AscendingOrder); @@ -151,32 +158,21 @@ FlagsWidget::FlagsWidget(MainWindow *main, QAction *action) : tree->showItemsNumber(flags_proxy_model->rowCount()); }); - auto xRefShortcut = new QShortcut(QKeySequence{Qt::CTRL + Qt::Key_X}, this); - xRefShortcut->setContext(Qt::WidgetWithChildrenShortcut); - ui->actionXrefs->setShortcut(Qt::CTRL + Qt::Key_X); - connect(xRefShortcut, SIGNAL(activated()), this, SLOT(on_actionXrefs_triggered())); - setScrollMode(); - ui->flagsTreeView->setContextMenuPolicy(Qt::CustomContextMenu); - connect(ui->flagsTreeView, SIGNAL(customContextMenuRequested(const QPoint &)), this, - SLOT(showContextMenu(const QPoint &))); - connect(Core(), SIGNAL(flagsChanged()), this, SLOT(flagsChanged())); connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshFlagspaces())); + + auto menu = ui->flagsTreeView->getItemContextMenu(); + menu->addSeparator(); + menu->addAction(ui->actionRename); + menu->addAction(ui->actionDelete); + addAction(ui->actionRename); + addAction(ui->actionDelete); } FlagsWidget::~FlagsWidget() {} -void FlagsWidget::on_flagsTreeView_doubleClicked(const QModelIndex &index) -{ - if (!index.isValid()) - return; - - FlagDescription flag = index.data(FlagsModel::FlagDescriptionRole).value(); - Core()->seekAndShow(flag.offset); -} - void FlagsWidget::on_flagspaceCombo_currentTextChanged(const QString &arg1) { Q_UNUSED(arg1); @@ -204,28 +200,6 @@ void FlagsWidget::on_actionDelete_triggered() Core()->delFlag(flag.name); } -void FlagsWidget::on_actionXrefs_triggered() -{ - FlagDescription flag = ui->flagsTreeView->selectionModel()->currentIndex().data( - FlagsModel::FlagDescriptionRole).value(); - - XrefsDialog xresfDialog(nullptr); - xresfDialog.fillRefsForAddress(flag.offset, RAddressString(flag.offset), false); - xresfDialog.exec(); -} - -void FlagsWidget::showContextMenu(const QPoint &pt) -{ - QMenu *menu = new QMenu(ui->flagsTreeView); - menu->addAction(ui->actionRename); - menu->addAction(ui->actionDelete); - menu->addSeparator(); - menu->addAction(ui->actionXrefs); - menu->exec(ui->flagsTreeView->mapToGlobal(pt)); - delete menu; -} - - void FlagsWidget::flagsChanged() { refreshFlagspaces(); diff --git a/src/widgets/FlagsWidget.h b/src/widgets/FlagsWidget.h index aef5c5d1..d0f51033 100644 --- a/src/widgets/FlagsWidget.h +++ b/src/widgets/FlagsWidget.h @@ -9,16 +9,16 @@ #include "core/Cutter.h" #include "CutterDockWidget.h" #include "CutterTreeWidget.h" +#include "AddressableItemList.h" +#include "AddressableItemModel.h" class MainWindow; class QTreeWidgetItem; class FlagsWidget; -class FlagsModel: public QAbstractListModel +class FlagsModel: public AddressableItemModel { - Q_OBJECT - friend FlagsWidget; private: @@ -30,16 +30,19 @@ public: FlagsModel(QList *flags, QObject *parent = nullptr); - int rowCount(const QModelIndex &parent = QModelIndex()) const; - int columnCount(const QModelIndex &parent = QModelIndex()) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; - QVariant data(const QModelIndex &index, int role) const; - QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + QVariant data(const QModelIndex &index, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + + RVA address(const QModelIndex &index) const override; + QString name(const QModelIndex &index) const override; }; -class FlagsSortFilterProxyModel : public QSortFilterProxyModel +class FlagsSortFilterProxyModel : public AddressableFilterProxyModel { Q_OBJECT @@ -66,14 +69,10 @@ public: ~FlagsWidget(); private slots: - void on_flagsTreeView_doubleClicked(const QModelIndex &index); void on_flagspaceCombo_currentTextChanged(const QString &arg1); void on_actionRename_triggered(); void on_actionDelete_triggered(); - void on_actionXrefs_triggered(); - - void showContextMenu(const QPoint &pt); void flagsChanged(); void refreshFlagspaces(); diff --git a/src/widgets/FlagsWidget.ui b/src/widgets/FlagsWidget.ui index 078b822f..a1ff6b22 100644 --- a/src/widgets/FlagsWidget.ui +++ b/src/widgets/FlagsWidget.ui @@ -31,7 +31,7 @@ 0 - + CutterTreeView::item { @@ -99,7 +99,7 @@ Rename - Ctrl+N + N @@ -110,23 +110,12 @@ Del - - - X-Refs - - - Show X-Refs of this flag - - - Ctrl+X - - - CutterTreeView + AddressableItemList<> QTreeView -
widgets/CutterTreeView.h
+
widgets/AddressableItemList.h
1
diff --git a/src/widgets/FunctionsWidget.cpp b/src/widgets/FunctionsWidget.cpp index b3ba3445..ce854bd3 100644 --- a/src/widgets/FunctionsWidget.cpp +++ b/src/widgets/FunctionsWidget.cpp @@ -3,9 +3,7 @@ #include "core/MainWindow.h" #include "common/Helpers.h" -#include "dialogs/CommentsDialog.h" #include "dialogs/RenameDialog.h" -#include "dialogs/XrefsDialog.h" #include "common/FunctionsTask.h" #include "common/TempConfig.h" #include "menus/AddressableItemContextMenu.h" @@ -18,6 +16,7 @@ #include #include #include +#include namespace { @@ -467,7 +466,7 @@ FunctionsWidget::FunctionsWidget(MainWindow *main, QAction *action) : connect(&actionUndefine, &QAction::triggered, this, &FunctionsWidget::onActionFunctionsUndefineTriggered); - auto itemConextMenu = getItemContextMenu(); + auto itemConextMenu = ui->treeView->getItemContextMenu(); itemConextMenu->addSeparator(); itemConextMenu->addAction(&actionRename); itemConextMenu->addAction(&actionUndefine); diff --git a/src/widgets/ListDockWidget.cpp b/src/widgets/ListDockWidget.cpp index fe5c9813..1b5ebdda 100644 --- a/src/widgets/ListDockWidget.cpp +++ b/src/widgets/ListDockWidget.cpp @@ -34,14 +34,9 @@ ListDockWidget::ListDockWidget(MainWindow *main, QAction *action, SearchBarPolic clearShortcut->setContext(Qt::WidgetWithChildrenShortcut); } - connect(ui->treeView, &QTreeView::activated, this, &ListDockWidget::onItemActivated); - qhelpers::setVerticalScrollMode(ui->treeView); - itemContextMenu = new AddressableItemContextMenu(ui->treeView, mainWindow); - ui->treeView->setContextMenuPolicy(Qt::CustomContextMenu); - connect(ui->treeView, &QWidget::customContextMenuRequested, - this, &ListDockWidget::showItemContextMenu); + ui->treeView->setMainWindow(mainWindow); if (searchBarPolicy != SearchBarPolicy::ShowByDefault) { ui->quickFilterView->closeFilter(); @@ -59,6 +54,7 @@ void ListDockWidget::setModels(AddressableFilterProxyModel *objectFilterProxyMod { this->objectFilterProxyModel = objectFilterProxyModel; + ui->treeView->setModel(objectFilterProxyModel); @@ -71,48 +67,4 @@ void ListDockWidget::setModels(AddressableFilterProxyModel *objectFilterProxyMod connect(ui->quickFilterView, &QuickFilterView::filterTextChanged, this, [this] { tree->showItemsNumber(this->objectFilterProxyModel->rowCount()); }); - - connect(ui->treeView->selectionModel(), &QItemSelectionModel::currentChanged, this, - &ListDockWidget::onSelectedItemChanged); - - addActions(this->getItemContextMenu()->actions()); -} - -AddressableItemContextMenu *ListDockWidget::getItemContextMenu() -{ - return itemContextMenu; -} - -void ListDockWidget::setItemContextMenu(AddressableItemContextMenu *menu) -{ - this->itemContextMenu = menu; -} - -void ListDockWidget::showItemContextMenu(const QPoint &pt) -{ - auto index = ui->treeView->currentIndex(); - if (index.isValid()) { - auto offset = objectFilterProxyModel->address(index); - auto name = objectFilterProxyModel->name(index); - itemContextMenu->setTarget(offset, name); - itemContextMenu->exec(ui->treeView->mapToGlobal(pt)); - } -} - -void ListDockWidget::onItemActivated(const QModelIndex &index) -{ - if (!index.isValid()) - return; - - auto offset = objectFilterProxyModel->address(index); - Core()->seekAndShow(offset); -} - -void ListDockWidget::onSelectedItemChanged(const QModelIndex &index) -{ - if (index.isValid()) { - auto offset = objectFilterProxyModel->address(index); - auto name = objectFilterProxyModel->name(index); - itemContextMenu->setTarget(offset, name); - } } diff --git a/src/widgets/ListDockWidget.h b/src/widgets/ListDockWidget.h index 9b0f4d66..5799cb2c 100644 --- a/src/widgets/ListDockWidget.h +++ b/src/widgets/ListDockWidget.h @@ -39,18 +39,10 @@ public: protected: void setModels(AddressableFilterProxyModel *objectFilterProxyModel); - AddressableItemContextMenu *getItemContextMenu(); - void setItemContextMenu(AddressableItemContextMenu *menu); - virtual void showItemContextMenu(const QPoint &pt); - - virtual void onItemActivated(const QModelIndex &index); - void onSelectedItemChanged(const QModelIndex &index); - std::unique_ptr ui; private: AddressableFilterProxyModel *objectFilterProxyModel; CutterTreeWidget *tree; - AddressableItemContextMenu *itemContextMenu; SearchBarPolicy searchBarPolicy; }; diff --git a/src/widgets/ListDockWidget.ui b/src/widgets/ListDockWidget.ui index d351183b..16e63e9c 100644 --- a/src/widgets/ListDockWidget.ui +++ b/src/widgets/ListDockWidget.ui @@ -28,7 +28,7 @@ 0 - + CutterTreeView::item { @@ -58,9 +58,9 @@ - CutterTreeView + AddressableItemList<> QTreeView -
widgets/CutterTreeView.h
+
widgets/AddressableItemList.h
1
diff --git a/src/widgets/MemoryMapWidget.cpp b/src/widgets/MemoryMapWidget.cpp index 3aaa70eb..389b4458 100644 --- a/src/widgets/MemoryMapWidget.cpp +++ b/src/widgets/MemoryMapWidget.cpp @@ -1,10 +1,10 @@ #include "MemoryMapWidget.h" -#include "ui_MemoryMapWidget.h" +#include "ui_ListDockWidget.h" #include "core/MainWindow.h" #include "common/Helpers.h" MemoryMapModel::MemoryMapModel(QList *memoryMaps, QObject *parent) - : QAbstractListModel(parent), + : AddressableItemModel(parent), memoryMaps(memoryMaps) { } @@ -68,10 +68,15 @@ QVariant MemoryMapModel::headerData(int section, Qt::Orientation, int role) cons } } -MemoryProxyModel::MemoryProxyModel(MemoryMapModel *sourceModel, QObject *parent) - : QSortFilterProxyModel(parent) +RVA MemoryMapModel::address(const QModelIndex &index) const +{ + const MemoryMapDescription &memoryMap = memoryMaps->at(index.row()); + return memoryMap.addrStart; +} + +MemoryProxyModel::MemoryProxyModel(MemoryMapModel *sourceModel, QObject *parent) + : AddressableFilterProxyModel(sourceModel, parent) { - setSourceModel(sourceModel); } bool MemoryProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const @@ -106,17 +111,15 @@ bool MemoryProxyModel::lessThan(const QModelIndex &left, const QModelIndex &righ } MemoryMapWidget::MemoryMapWidget(MainWindow *main, QAction *action) : - CutterDockWidget(main, action), - ui(new Ui::MemoryMapWidget) + ListDockWidget(main, action, ListDockWidget::SearchBarPolicy::HideByDefault) { - ui->setupUi(this); + setWindowTitle(tr("Memory Map")); + setObjectName("MemoryMapWidget"); memoryModel = new MemoryMapModel(&memoryMaps, this); memoryProxyModel = new MemoryProxyModel(memoryModel, this); - ui->memoryTreeView->setModel(memoryProxyModel); - ui->memoryTreeView->sortByColumn(MemoryMapModel::AddrStartColumn, Qt::AscendingOrder); - - setScrollMode(); + setModels(memoryProxyModel); + ui->treeView->sortByColumn(MemoryMapModel::AddrStartColumn, Qt::AscendingOrder); refreshDeferrer = createRefreshDeferrer([this]() { refreshMemoryMap(); @@ -124,6 +127,8 @@ MemoryMapWidget::MemoryMapWidget(MainWindow *main, QAction *action) : connect(Core(), &CutterCore::refreshAll, this, &MemoryMapWidget::refreshMemoryMap); connect(Core(), &CutterCore::registersChanged, this, &MemoryMapWidget::refreshMemoryMap); + + showCount(false); } MemoryMapWidget::~MemoryMapWidget() = default; @@ -138,19 +143,7 @@ void MemoryMapWidget::refreshMemoryMap() memoryMaps = Core()->getMemoryMap(); memoryModel->endResetModel(); - ui->memoryTreeView->resizeColumnToContents(0); - ui->memoryTreeView->resizeColumnToContents(1); - ui->memoryTreeView->resizeColumnToContents(2); -} - -void MemoryMapWidget::setScrollMode() -{ - qhelpers::setVerticalScrollMode(ui->memoryTreeView); -} - -void MemoryMapWidget::on_memoryTreeView_doubleClicked(const QModelIndex &index) -{ - MemoryMapDescription item = index.data( - MemoryMapModel::MemoryDescriptionRole).value(); - Core()->seekAndShow(item.addrStart); + ui->treeView->resizeColumnToContents(0); + ui->treeView->resizeColumnToContents(1); + ui->treeView->resizeColumnToContents(2); } diff --git a/src/widgets/MemoryMapWidget.h b/src/widgets/MemoryMapWidget.h index 5f45ad4a..a5859cde 100644 --- a/src/widgets/MemoryMapWidget.h +++ b/src/widgets/MemoryMapWidget.h @@ -4,6 +4,7 @@ #include "core/Cutter.h" #include "CutterDockWidget.h" +#include "ListDockWidget.h" #include #include @@ -21,7 +22,7 @@ class MainWindow; class QTreeWidgetItem; -class MemoryMapModel: public QAbstractListModel +class MemoryMapModel: public AddressableItemModel { Q_OBJECT @@ -34,18 +35,20 @@ public: enum Column { AddrStartColumn = 0, AddrEndColumn, NameColumn, PermColumn, ColumnCount }; enum Role { MemoryDescriptionRole = Qt::UserRole }; - MemoryMapModel(QList *memoryMaps, QObject *parent = 0); + MemoryMapModel(QList *memoryMaps, QObject *parent = nullptr); - int rowCount(const QModelIndex &parent = QModelIndex()) const; - int columnCount(const QModelIndex &parent = QModelIndex()) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; - QVariant data(const QModelIndex &index, int role) const; - QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + QVariant data(const QModelIndex &index, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + + RVA address(const QModelIndex &index) const override; }; -class MemoryProxyModel : public QSortFilterProxyModel +class MemoryProxyModel : public AddressableFilterProxyModel { Q_OBJECT @@ -59,7 +62,7 @@ protected: -class MemoryMapWidget : public CutterDockWidget +class MemoryMapWidget : public ListDockWidget { Q_OBJECT @@ -68,18 +71,13 @@ public: ~MemoryMapWidget(); private slots: - void on_memoryTreeView_doubleClicked(const QModelIndex &index); void refreshMemoryMap(); private: - std::unique_ptr ui; - MemoryMapModel *memoryModel; MemoryProxyModel *memoryProxyModel; QList memoryMaps; - void setScrollMode(); - RefreshDeferrer *refreshDeferrer; }; diff --git a/src/widgets/MemoryMapWidget.ui b/src/widgets/MemoryMapWidget.ui deleted file mode 100644 index 7bccee5e..00000000 --- a/src/widgets/MemoryMapWidget.ui +++ /dev/null @@ -1,66 +0,0 @@ - - - MemoryMapWidget - - - - 0 - 0 - 400 - 300 - - - - Memory Map - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - CutterTreeView::item -{ - padding-top: 1px; - padding-bottom: 1px; -} - - - QFrame::NoFrame - - - 0 - - - 8 - - - true - - - - - - - - - CutterTreeView - QTreeView -
widgets/CutterTreeView.h
- 1 -
-
- - -
diff --git a/src/widgets/ResourcesWidget.cpp b/src/widgets/ResourcesWidget.cpp index de6ecc85..026a3a26 100644 --- a/src/widgets/ResourcesWidget.cpp +++ b/src/widgets/ResourcesWidget.cpp @@ -4,7 +4,7 @@ #include ResourcesModel::ResourcesModel(QList *resources, QObject *parent) - : QAbstractListModel(parent), + : AddressableItemModel(parent), resources(resources) { } @@ -27,11 +27,11 @@ QVariant ResourcesModel::data(const QModelIndex &index, int role) const case Qt::DisplayRole: switch (index.column()) { case NAME: - return res.name; + return QString::number(res.name); case VADDR: return RAddressString(res.vaddr); case INDEX: - return res.index; + return QString::number(res.index); case TYPE: return res.type; case SIZE: @@ -73,25 +73,27 @@ QVariant ResourcesModel::headerData(int section, Qt::Orientation, int role) cons } } +RVA ResourcesModel::address(const QModelIndex &index) const +{ + const ResourcesDescription &res = resources->at(index.row()); + return res.vaddr; +} + ResourcesWidget::ResourcesWidget(MainWindow *main, QAction *action) : - CutterDockWidget(main, action) + ListDockWidget(main, action, ListDockWidget::SearchBarPolicy::HideByDefault) { setObjectName("ResourcesWidget"); model = new ResourcesModel(&resources, this); + filterModel = new AddressableFilterProxyModel(model, this); + setModels(filterModel); + + showCount(false); // Configure widget this->setWindowTitle(tr("Resources")); - // Add resources tree view - view = new CutterTreeView(this); - view->setModel(model); - view->show(); - this->setWidget(view); - connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshResources())); - connect(view, SIGNAL(doubleClicked(const QModelIndex &)), this, - SLOT(onDoubleClicked(const QModelIndex &))); } void ResourcesWidget::refreshResources() @@ -100,12 +102,3 @@ void ResourcesWidget::refreshResources() resources = Core()->getAllResources(); model->endResetModel(); } - -void ResourcesWidget::onDoubleClicked(const QModelIndex &index) -{ - if (!index.isValid()) - return; - - ResourcesDescription res = index.data(Qt::UserRole).value(); - Core()->seekAndShow(res.vaddr); -} diff --git a/src/widgets/ResourcesWidget.h b/src/widgets/ResourcesWidget.h index 66d25a71..a291b38e 100644 --- a/src/widgets/ResourcesWidget.h +++ b/src/widgets/ResourcesWidget.h @@ -4,13 +4,14 @@ #include "core/Cutter.h" #include "CutterDockWidget.h" #include "CutterTreeView.h" +#include "common/AddressableItemModel.h" +#include "widgets/ListDockWidget.h" -#include class MainWindow; class ResourcesWidget; -class ResourcesModel : public QAbstractListModel +class ResourcesModel : public AddressableItemModel { Q_OBJECT @@ -29,14 +30,17 @@ public: QVariant data(const QModelIndex &index, int role) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + + RVA address(const QModelIndex &index) const override; }; -class ResourcesWidget : public CutterDockWidget +class ResourcesWidget : public ListDockWidget { Q_OBJECT private: ResourcesModel *model; + AddressableFilterProxyModel *filterModel; CutterTreeView *view; QList resources; @@ -45,7 +49,6 @@ public: private slots: void refreshResources(); - void onDoubleClicked(const QModelIndex &); }; #endif // RESOURCESWIDGET_H diff --git a/src/widgets/SearchWidget.cpp b/src/widgets/SearchWidget.cpp index 4e4b6a3c..d05798fd 100644 --- a/src/widgets/SearchWidget.cpp +++ b/src/widgets/SearchWidget.cpp @@ -30,7 +30,7 @@ static const QMap kSearchBoundariesValues { }; SearchModel::SearchModel(QList *search, QObject *parent) - : QAbstractListModel(parent), + : AddressableItemModel(parent), search(search) { } @@ -119,11 +119,16 @@ QVariant SearchModel::headerData(int section, Qt::Orientation, int role) const } } +RVA SearchModel::address(const QModelIndex &index) const +{ + const SearchDescription &exp = search->at(index.row()); + return exp.offset; +} + SearchSortFilterProxyModel::SearchSortFilterProxyModel(SearchModel *source_model, QObject *parent) - : QSortFilterProxyModel(parent) + : AddressableFilterProxyModel(source_model, parent) { - setSourceModel(source_model); } bool SearchSortFilterProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const @@ -174,6 +179,7 @@ SearchWidget::SearchWidget(MainWindow *main, QAction *action) : search_model = new SearchModel(&search, this); search_proxy_model = new SearchSortFilterProxyModel(search_model, this); ui->searchTreeView->setModel(search_proxy_model); + ui->searchTreeView->setMainWindow(main); ui->searchTreeView->sortByColumn(SearchModel::OFFSET, Qt::AscendingOrder); setScrollMode(); @@ -199,16 +205,6 @@ SearchWidget::SearchWidget(MainWindow *main, QAction *action) : SearchWidget::~SearchWidget() {} -void SearchWidget::on_searchTreeView_doubleClicked(const QModelIndex &index) -{ - if (!index.isValid()) - return; - - SearchDescription search = index.data( - SearchModel::SearchDescriptionRole).value(); - Core()->seekAndShow(search.offset); -} - void SearchWidget::searchChanged() { refreshSearchspaces(); diff --git a/src/widgets/SearchWidget.h b/src/widgets/SearchWidget.h index 1dca4d07..20c9d76e 100644 --- a/src/widgets/SearchWidget.h +++ b/src/widgets/SearchWidget.h @@ -8,13 +8,14 @@ #include "core/Cutter.h" #include "CutterDockWidget.h" +#include "AddressableItemList.h" class MainWindow; class QTreeWidgetItem; class SearchWidget; -class SearchModel: public QAbstractListModel +class SearchModel: public AddressableItemModel { Q_OBJECT @@ -29,16 +30,18 @@ public: SearchModel(QList *search, QObject *parent = nullptr); - int rowCount(const QModelIndex &parent = QModelIndex()) const; - int columnCount(const QModelIndex &parent = QModelIndex()) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; - QVariant data(const QModelIndex &index, int role) const; - QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + QVariant data(const QModelIndex &index, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + + RVA address(const QModelIndex &index) const override; }; -class SearchSortFilterProxyModel : public QSortFilterProxyModel +class SearchSortFilterProxyModel : public AddressableFilterProxyModel { Q_OBJECT @@ -65,7 +68,6 @@ public: ~SearchWidget(); private slots: - void on_searchTreeView_doubleClicked(const QModelIndex &index); void on_searchInCombo_currentIndexChanged(int index); void searchChanged(); void refreshSearchspaces(); diff --git a/src/widgets/SearchWidget.ui b/src/widgets/SearchWidget.ui index 17d66538..2756367a 100644 --- a/src/widgets/SearchWidget.ui +++ b/src/widgets/SearchWidget.ui @@ -31,7 +31,7 @@ 0 - + CutterTreeView::item { @@ -118,9 +118,9 @@ - CutterTreeView + AddressableItemList<> QTreeView -
widgets/CutterTreeView.h
+
widgets/AddressableItemList.h
1
diff --git a/src/widgets/StringsWidget.cpp b/src/widgets/StringsWidget.cpp index 1564365b..ee7dae8b 100644 --- a/src/widgets/StringsWidget.cpp +++ b/src/widgets/StringsWidget.cpp @@ -2,7 +2,6 @@ #include "ui_StringsWidget.h" #include "core/MainWindow.h" #include "common/Helpers.h" -#include "dialogs/XrefsDialog.h" #include "WidgetShortcuts.h" #include @@ -11,7 +10,7 @@ #include StringsModel::StringsModel(QList *strings, QObject *parent) - : QAbstractListModel(parent), + : AddressableItemModel(parent), strings(strings) { } @@ -43,9 +42,9 @@ QVariant StringsModel::data(const QModelIndex &index, int role) const case StringsModel::TypeColumn: return str.type.toUpper(); case StringsModel::LengthColumn: - return str.length; + return QString::number(str.length); case StringsModel::SizeColumn: - return str.size; + return QString::number(str.size); case StringsModel::SectionColumn: return str.section; default: @@ -83,10 +82,15 @@ QVariant StringsModel::headerData(int section, Qt::Orientation, int role) const } } -StringsProxyModel::StringsProxyModel(StringsModel *sourceModel, QObject *parent) - : QSortFilterProxyModel(parent) +RVA StringsModel::address(const QModelIndex &index) const +{ + const StringDescription &str = strings->at(index.row()); + return str.vaddr; +} + +StringsProxyModel::StringsProxyModel(StringsModel *sourceModel, QObject *parent) + : AddressableFilterProxyModel(sourceModel, parent) { - setSourceModel(sourceModel); setFilterCaseSensitivity(Qt::CaseInsensitive); setSortCaseSensitivity(Qt::CaseInsensitive); } @@ -149,10 +153,6 @@ StringsWidget::StringsWidget(MainWindow *main, QAction *action) : } ); connect(ui->actionCopy_String, SIGNAL(triggered()), this, SLOT(on_actionCopy())); - connect(ui->actionCopy_Address, SIGNAL(triggered()), this, SLOT(on_actionCopy())); - - connect(ui->stringsTreeView, SIGNAL(customContextMenuRequested(const QPoint &)), - this, SLOT(showStringsContextMenu(const QPoint &))); ui->actionFilter->setShortcut(QKeySequence::Find); @@ -160,13 +160,13 @@ StringsWidget::StringsWidget(MainWindow *main, QAction *action) : model = new StringsModel(&strings, this); proxyModel = new StringsProxyModel(model, this); + ui->stringsTreeView->setMainWindow(main); ui->stringsTreeView->setModel(proxyModel); ui->stringsTreeView->sortByColumn(StringsModel::OffsetColumn, Qt::AscendingOrder); - auto xRefShortcut = new QShortcut(QKeySequence{Qt::CTRL + Qt::Key_X}, this); - xRefShortcut->setContext(Qt::WidgetWithChildrenShortcut); - ui->actionX_refs->setShortcut(Qt::CTRL + Qt::Key_X); - connect(xRefShortcut, SIGNAL(activated()), this, SLOT(on_actionX_refs_triggered())); + // + auto menu = ui->stringsTreeView->getItemContextMenu(); + menu->addAction(ui->actionCopy_String); connect(ui->quickFilterView, SIGNAL(filterTextChanged(const QString &)), proxyModel, SLOT(setFilterWildcard(const QString &))); @@ -180,7 +180,10 @@ StringsWidget::StringsWidget(MainWindow *main, QAction *action) : searchShortcut->setContext(Qt::WidgetWithChildrenShortcut); QShortcut *clearShortcut = new QShortcut(QKeySequence(Qt::Key_Escape), this); - connect(clearShortcut, &QShortcut::activated, ui->quickFilterView, &ComboQuickFilterView::clearFilter); + connect(clearShortcut, &QShortcut::activated, this, [this]() { + ui->quickFilterView->clearFilter(); + ui->stringsTreeView->setFocus(); + }); clearShortcut->setContext(Qt::WidgetWithChildrenShortcut); connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshStrings())); @@ -197,16 +200,6 @@ StringsWidget::StringsWidget(MainWindow *main, QAction *action) : StringsWidget::~StringsWidget() {} -void StringsWidget::on_stringsTreeView_doubleClicked(const QModelIndex &index) -{ - if (!index.isValid()) { - return; - } - - StringDescription str = index.data(StringsModel::StringDescriptionRole).value(); - Core()->seekAndShow(str.vaddr); -} - void StringsWidget::refreshStrings() { if (task) { @@ -250,32 +243,6 @@ void StringsWidget::stringSearchFinished(const QList &strings task = nullptr; } -void StringsWidget::showStringsContextMenu(const QPoint &pt) -{ - QMenu *menu = new QMenu(ui->stringsTreeView); - - menu->clear(); - menu->addAction(ui->actionCopy_String); - menu->addAction(ui->actionCopy_Address); - menu->addAction(ui->actionFilter); - menu->addSeparator(); - menu->addAction(ui->actionX_refs); - - menu->exec(ui->stringsTreeView->mapToGlobal(pt)); - - delete menu; -} - -void StringsWidget::on_actionX_refs_triggered() -{ - StringDescription str = ui->stringsTreeView->selectionModel()->currentIndex().data( - StringsModel::StringDescriptionRole).value(); - - XrefsDialog x(nullptr); - x.fillRefsForAddress(str.vaddr, RAddressString(str.vaddr), false); - x.exec(); -} - void StringsWidget::on_actionCopy() { QModelIndex current_item = ui->stringsTreeView->currentIndex(); @@ -283,11 +250,7 @@ void StringsWidget::on_actionCopy() QModelIndex index; - if (sender() == ui->actionCopy_String) { - index = ui->stringsTreeView->model()->index(row, 1); - } else if (sender() == ui->actionCopy_Address) { - index = ui->stringsTreeView->model()->index(row, 0); - } + index = ui->stringsTreeView->model()->index(row, 1); QClipboard *clipboard = QApplication::clipboard(); clipboard->setText(index.data().toString()); diff --git a/src/widgets/StringsWidget.h b/src/widgets/StringsWidget.h index 3bb3a5af..6bfd8358 100644 --- a/src/widgets/StringsWidget.h +++ b/src/widgets/StringsWidget.h @@ -7,6 +7,7 @@ #include "CutterDockWidget.h" #include "common/StringsTask.h" #include "CutterTreeWidget.h" +#include "AddressableItemModel.h" #include #include @@ -19,7 +20,7 @@ namespace Ui { class StringsWidget; } -class StringsModel: public QAbstractListModel +class StringsModel: public AddressableItemModel { Q_OBJECT @@ -34,16 +35,18 @@ public: StringsModel(QList *strings, QObject *parent = nullptr); - int rowCount(const QModelIndex &parent = QModelIndex()) const; - int columnCount(const QModelIndex &parent = QModelIndex()) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; - QVariant data(const QModelIndex &index, int role) const; - QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + QVariant data(const QModelIndex &index, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + + RVA address(const QModelIndex &index) const override; }; -class StringsProxyModel : public QSortFilterProxyModel +class StringsProxyModel : public AddressableFilterProxyModel { Q_OBJECT @@ -69,14 +72,10 @@ public: ~StringsWidget(); private slots: - void on_stringsTreeView_doubleClicked(const QModelIndex &index); - void refreshStrings(); void stringSearchFinished(const QList &strings); void refreshSectionCombo(); - void showStringsContextMenu(const QPoint &pt); - void on_actionX_refs_triggered(); void on_actionCopy(); private: diff --git a/src/widgets/StringsWidget.ui b/src/widgets/StringsWidget.ui index 2486df4d..cecdca91 100644 --- a/src/widgets/StringsWidget.ui +++ b/src/widgets/StringsWidget.ui @@ -31,7 +31,7 @@ 0 - + 0 @@ -71,21 +71,11 @@
- - - Copy Address - - Copy String - - - Xrefs - - Filter @@ -94,9 +84,9 @@
- CutterTreeView + AddressableItemList<> QTreeView -
widgets/CutterTreeView.h
+
widgets/AddressableItemList.h
1