From 0aa91c328c25efcd72c2c9111357d30245c29fe5 Mon Sep 17 00:00:00 2001 From: karliss Date: Mon, 19 Aug 2019 16:35:25 +0300 Subject: [PATCH] Common behaviour for lists with items that have address (#1700) --- src/Cutter.pro | 20 +- src/common/AddressableItemModel.cpp | 30 +++ src/common/AddressableItemModel.h | 49 ++++ src/core/Cutter.cpp | 11 + src/core/Cutter.h | 7 + src/menus/AddressableItemContextMenu.cpp | 103 +++++++ src/menus/AddressableItemContextMenu.h | 43 +++ src/widgets/CommentsWidget.cpp | 168 +++++------- src/widgets/CommentsWidget.h | 54 ++-- src/widgets/CommentsWidget.ui | 87 ------ src/widgets/CutterTreeWidget.cpp | 5 + src/widgets/CutterTreeWidget.h | 1 + src/widgets/ExportsWidget.cpp | 72 ++--- src/widgets/ExportsWidget.h | 26 +- src/widgets/ExportsWidget.ui | 75 ------ src/widgets/FunctionsWidget.cpp | 255 +++++++----------- src/widgets/FunctionsWidget.h | 57 ++-- src/widgets/FunctionsWidget.ui | 146 ---------- src/widgets/HeadersWidget.cpp | 47 ++-- src/widgets/HeadersWidget.h | 27 +- src/widgets/HeadersWidget.ui | 66 ----- src/widgets/ImportsWidget.cpp | 73 ++--- src/widgets/ImportsWidget.h | 31 +-- src/widgets/ImportsWidget.ui | 75 ------ src/widgets/ListDockWidget.cpp | 118 ++++++++ src/widgets/ListDockWidget.h | 57 ++++ .../{RelocsWidget.ui => ListDockWidget.ui} | 19 +- src/widgets/RelocsWidget.cpp | 74 ++--- src/widgets/RelocsWidget.h | 29 +- src/widgets/SectionsWidget.cpp | 75 ++---- src/widgets/SectionsWidget.h | 19 +- src/widgets/SegmentsWidget.cpp | 78 ++---- src/widgets/SegmentsWidget.h | 29 +- src/widgets/SymbolsWidget.cpp | 77 ++---- src/widgets/SymbolsWidget.h | 30 +-- src/widgets/SymbolsWidget.ui | 62 ----- src/widgets/VTablesWidget.cpp | 1 + 37 files changed, 906 insertions(+), 1290 deletions(-) create mode 100644 src/common/AddressableItemModel.cpp create mode 100644 src/common/AddressableItemModel.h create mode 100644 src/menus/AddressableItemContextMenu.cpp create mode 100644 src/menus/AddressableItemContextMenu.h delete mode 100644 src/widgets/CommentsWidget.ui delete mode 100644 src/widgets/ExportsWidget.ui delete mode 100644 src/widgets/FunctionsWidget.ui delete mode 100644 src/widgets/HeadersWidget.ui delete mode 100644 src/widgets/ImportsWidget.ui create mode 100644 src/widgets/ListDockWidget.cpp create mode 100644 src/widgets/ListDockWidget.h rename src/widgets/{RelocsWidget.ui => ListDockWidget.ui} (81%) delete mode 100644 src/widgets/SymbolsWidget.ui diff --git a/src/Cutter.pro b/src/Cutter.pro index dea769c497a2a..2c349fedd9b0d 100644 --- a/src/Cutter.pro +++ b/src/Cutter.pro @@ -360,7 +360,10 @@ SOURCES += \ widgets/GraphGridLayout.cpp \ widgets/HexWidget.cpp \ common/SelectionHighlight.cpp \ - common/Decompiler.cpp + common/Decompiler.cpp \ + menus/AddressableItemContextMenu.cpp \ + common/AddressableItemModel.cpp \ + widgets/ListDockWidget.cpp GRAPHVIZ_SOURCES = \ widgets/GraphvizLayout.cpp @@ -490,7 +493,10 @@ HEADERS += \ widgets/GraphLayout.h \ widgets/HexWidget.h \ common/SelectionHighlight.h \ - common/Decompiler.h + common/Decompiler.h \ + menus/AddressableItemContextMenu.h \ + common/AddressableItemModel.h \ + widgets/ListDockWidget.h GRAPHVIZ_HEADERS = widgets/GraphGridLayout.h @@ -506,17 +512,11 @@ FORMS += \ dialogs/InitialOptionsDialog.ui \ dialogs/EditFunctionDialog.ui \ core/MainWindow.ui \ - widgets/CommentsWidget.ui \ widgets/ConsoleWidget.ui \ widgets/Dashboard.ui \ widgets/EntrypointWidget.ui \ widgets/FlagsWidget.ui \ - widgets/ExportsWidget.ui \ - widgets/FunctionsWidget.ui \ - widgets/ImportsWidget.ui \ - widgets/RelocsWidget.ui \ widgets/StringsWidget.ui \ - widgets/SymbolsWidget.ui \ widgets/HexdumpWidget.ui \ dialogs/SaveProjectDialog.ui \ dialogs/preferences/PreferencesDialog.ui \ @@ -527,7 +527,6 @@ FORMS += \ widgets/ClassesWidget.ui \ widgets/VTablesWidget.ui \ widgets/TypesWidget.ui \ - widgets/HeadersWidget.ui \ widgets/SearchWidget.ui \ dialogs/R2PluginsDialog.ui \ dialogs/VersionInfoDialog.ui \ @@ -554,7 +553,8 @@ FORMS += \ widgets/SdbWidget.ui \ dialogs/LinkTypeDialog.ui \ widgets/ColorPicker.ui \ - dialogs/preferences/ColorThemeEditDialog.ui + dialogs/preferences/ColorThemeEditDialog.ui \ + widgets/ListDockWidget.ui RESOURCES += \ resources.qrc \ diff --git a/src/common/AddressableItemModel.cpp b/src/common/AddressableItemModel.cpp new file mode 100644 index 0000000000000..554559989d647 --- /dev/null +++ b/src/common/AddressableItemModel.cpp @@ -0,0 +1,30 @@ +#include "AddressableItemModel.h" + +AddressableFilterProxyModel::AddressableFilterProxyModel(AddressableItemModelI *sourceModel, + QObject *parent) : + AddressableItemModel(parent) +{ + setSourceModel(sourceModel); + addressableSourceModel = sourceModel; +} + +RVA AddressableFilterProxyModel::address(const QModelIndex &index) const +{ + return addressableSourceModel->address(this->mapToSource(index)); +} + +QString AddressableFilterProxyModel::name(const QModelIndex &index) const +{ + return addressableSourceModel->name(this->mapToSource(index)); +} + +void AddressableFilterProxyModel::setSourceModel(QAbstractItemModel *) +{ + throw new std::runtime_error("Not supported"); +} + +void AddressableFilterProxyModel::setSourceModel(AddressableItemModelI *sourceModel) +{ + ParentClass::setSourceModel(sourceModel->asItemModel()); + addressableSourceModel = sourceModel; +} diff --git a/src/common/AddressableItemModel.h b/src/common/AddressableItemModel.h new file mode 100644 index 0000000000000..dc34ba00c7d89 --- /dev/null +++ b/src/common/AddressableItemModel.h @@ -0,0 +1,49 @@ + +#ifndef ADDRESSABLEITEMMODEL_H +#define ADDRESSABLEITEMMODEL_H + +#include +#include +#include + +#include + +class AddressableItemModelI +{ +public: + virtual RVA address(const QModelIndex &index) const = 0; + /** + * @brief Get name for item, optional. + * @param index item intex + * @return Item name or empty QString if item doesn't have short descriptive name. + */ + virtual QString name(const QModelIndex &index) const { Q_UNUSED(index) return QString(); } + virtual QAbstractItemModel *asItemModel() = 0; +}; + +template +class AddressableItemModel : public ParentModel, public AddressableItemModelI +{ + static_assert (std::is_base_of::value, + "ParentModel needs to inherit from QAbstractItemModel"); +public: + explicit AddressableItemModel(QObject *parent = nullptr) : ParentModel(parent) {} + virtual ~AddressableItemModel() {} + QAbstractItemModel *asItemModel() { return this; } +}; + +class AddressableFilterProxyModel : public AddressableItemModel +{ + using ParentClass = AddressableItemModel; +public: + AddressableFilterProxyModel(AddressableItemModelI *sourceModel, QObject *parent); + + RVA address(const QModelIndex &index) const override; + QString name(const QModelIndex &) const override; + void setSourceModel(AddressableItemModelI *sourceModel); +private: + void setSourceModel(QAbstractItemModel *sourceModel) override; // Don't use this directly + AddressableItemModelI *addressableSourceModel; +}; + +#endif // ADDRESSABLEITEMMODEL_H diff --git a/src/core/Cutter.cpp b/src/core/Cutter.cpp index 32484a44e38bd..f570e3cac7b92 100644 --- a/src/core/Cutter.cpp +++ b/src/core/Cutter.cpp @@ -2597,6 +2597,17 @@ void CutterCore::addFlag(RVA offset, QString name, RVA size) emit flagsChanged(); } +QString CutterCore::nearestFlag(RVA offset, RVA *flagOffsetOut) +{ + auto r = cmdj(QString("fdj @") + QString::number(offset)).object(); + QString name = r.value("name").toString(); + if (flagOffsetOut) { + int queryOffset = r.value("offset").toInt(0); + *flagOffsetOut = offset + static_cast(-queryOffset); + } + return name; +} + void CutterCore::handleREvent(int type, void *data) { switch (type) { diff --git a/src/core/Cutter.h b/src/core/Cutter.h index 26dbd699e4082..1a70e38768a73 100644 --- a/src/core/Cutter.h +++ b/src/core/Cutter.h @@ -91,6 +91,13 @@ class CutterCore: public QObject void delFlag(RVA addr); void delFlag(const QString &name); void addFlag(RVA offset, QString name, RVA size); + /** + * @brief Get nearest flag at or before offset. + * @param offset search position + * @param flagOffsetOut adress of returned flag + * @return flag name + */ + QString nearestFlag(RVA offset, RVA *flagOffsetOut); void triggerFlagsChanged(); /* Edition functions */ diff --git a/src/menus/AddressableItemContextMenu.cpp b/src/menus/AddressableItemContextMenu.cpp new file mode 100644 index 0000000000000..154f5b33d31e4 --- /dev/null +++ b/src/menus/AddressableItemContextMenu.cpp @@ -0,0 +1,103 @@ +#include "AddressableItemContextMenu.h" +#include "dialogs/XrefsDialog.h" +#include "MainWindow.h" +#include "dialogs/CommentsDialog.h" + +#include +#include +#include +#include +#include +#include + +AddressableItemContextMenu::AddressableItemContextMenu(QWidget *parent, MainWindow *mainWindow) + : QMenu(parent) + , mainWindow(mainWindow) + , actionShowInMenu(tr("Show in"), this) + , actionCopyAddress(tr("Copy address"), this) + , actionShowXrefs(tr("Show X-Refs"), this) + , actionAddcomment(tr("Add comment"), this) +{ + connect(&actionCopyAddress, &QAction::triggered, this, + &AddressableItemContextMenu::onActionCopyAddress); + actionCopyAddress.setShortcuts({Qt::CTRL + Qt::SHIFT + Qt::Key_C}); + actionCopyAddress.setShortcutContext(Qt::ShortcutContext::WidgetWithChildrenShortcut); + + connect(&actionShowXrefs, &QAction::triggered, this, + &AddressableItemContextMenu::onActionShowXrefs); + actionShowXrefs.setShortcut({Qt::Key_X}); + actionShowXrefs.setShortcutContext(Qt::ShortcutContext::WidgetWithChildrenShortcut); + + connect(&actionAddcomment, &QAction::triggered, this, + &AddressableItemContextMenu::onActionAddComment); + + + addAction(&actionShowInMenu); + addAction(&actionCopyAddress); + addAction(&actionShowXrefs); + addSeparator(); + addAction(&actionAddcomment); + + connect(this, &QMenu::aboutToShow, this, &AddressableItemContextMenu::aboutToShowSlot); +} + +AddressableItemContextMenu::~AddressableItemContextMenu() +{ +} + +void AddressableItemContextMenu::setWholeFunction(bool wholeFunciton) +{ + this->wholeFunction = wholeFunciton; +} + +void AddressableItemContextMenu::setOffset(RVA offset) +{ + setTarget(offset); +} + +void AddressableItemContextMenu::setTarget(RVA offset, QString name) +{ + this->offset = offset; + this->name = name; +} + +void AddressableItemContextMenu::onActionCopyAddress() +{ + auto clipboard = QApplication::clipboard(); + clipboard->setText(RAddressString(offset)); +} + +void AddressableItemContextMenu::onActionShowXrefs() +{ + XrefsDialog dialog(nullptr); + QString tmpName = name; + if (name.isEmpty()) { + name = RAddressString(offset); + } + dialog.fillRefsForAddress(offset, name, wholeFunction); + dialog.exec(); +} + +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); + } +} + +void AddressableItemContextMenu::aboutToShowSlot() +{ + if (actionShowInMenu.menu()) { + actionShowInMenu.menu()->deleteLater(); + } + actionShowInMenu.setMenu(mainWindow->createShowInMenu(this, offset)); +} + diff --git a/src/menus/AddressableItemContextMenu.h b/src/menus/AddressableItemContextMenu.h new file mode 100644 index 0000000000000..cf0bf17d38eb4 --- /dev/null +++ b/src/menus/AddressableItemContextMenu.h @@ -0,0 +1,43 @@ +#ifndef ADDRESSABLEITEMCONTEXTMENU_H +#define ADDRESSABLEITEMCONTEXTMENU_H + +#include "core/Cutter.h" +#include +#include + +class AddressableItemContextMenu : public QMenu +{ + Q_OBJECT + +public: + AddressableItemContextMenu(QWidget *parent, MainWindow *mainWindow); + ~AddressableItemContextMenu(); + + /** + * @brief Configure if addressable item refers to whole function or specific address + * @param wholeFunciton + */ + void setWholeFunction(bool wholeFunciton); +public slots: + void setOffset(RVA offset); + void setTarget(RVA offset, QString name = QString()); +private: + void onActionCopyAddress(); + void onActionShowXrefs(); + void onActionAddComment(); + + virtual void aboutToShowSlot(); + + MainWindow *mainWindow; + + RVA offset; +protected: + QAction actionShowInMenu; + QAction actionCopyAddress; + QAction actionShowXrefs; + QAction actionAddcomment; + + QString name; + bool wholeFunction = false; +}; +#endif // ADDRESSABLEITEMCONTEXTMENU_H diff --git a/src/widgets/CommentsWidget.cpp b/src/widgets/CommentsWidget.cpp index bf1063b97aa48..deaac07c66730 100644 --- a/src/widgets/CommentsWidget.cpp +++ b/src/widgets/CommentsWidget.cpp @@ -1,5 +1,5 @@ #include "CommentsWidget.h" -#include "ui_CommentsWidget.h" +#include "ui_ListDockWidget.h" #include "core/MainWindow.h" #include "common/Helpers.h" @@ -8,9 +8,9 @@ #include CommentsModel::CommentsModel(QList *comments, - QMap > *nestedComments, + QList *nestedComments, QObject *parent) - : QAbstractItemModel(parent), + : AddressableItemModel<>(parent), comments(comments), nestedComments(nestedComments), nested(false) @@ -28,6 +28,20 @@ void CommentsModel::setNested(bool nested) endResetModel(); } +RVA CommentsModel::address(const QModelIndex &index) const +{ + if (isNested()) { + if (index.internalId() != 0) { + auto &group = nestedComments->at(index.parent().row()); + return group.comments.at(index.row()).offset; + } else { + return nestedComments->at(index.row()).offset; + } + } else { + return comments->at(index.row()).offset; + } +} + QModelIndex CommentsModel::index(int row, int column, const QModelIndex &parent) const { if (!parent.isValid()) { @@ -37,7 +51,8 @@ QModelIndex CommentsModel::index(int row, int column, const QModelIndex &parent) return createIndex(row, column, (quintptr)(parent.row() + 1)); } -QModelIndex CommentsModel::parent(const QModelIndex &index) const { +QModelIndex CommentsModel::parent(const QModelIndex &index) const +{ /* Ignore invalid indexes and root nodes */ if (!index.isValid() || index.internalId() == 0) { return QModelIndex(); @@ -52,8 +67,7 @@ int CommentsModel::rowCount(const QModelIndex &parent) const return (isNested() ? nestedComments->size() : comments->count()); if (isNested() && parent.internalId() == 0) { - QString fcnName = nestedComments->keys().at(parent.row()); - return nestedComments->operator[](fcnName).size(); + return nestedComments->at(parent.row()).comments.size(); } return 0; @@ -83,12 +97,13 @@ QVariant CommentsModel::data(const QModelIndex &index, int role) const isSubnode = false; } - QString offset; + QString groupName; CommentDescription comment; if (isNested()) { - offset = nestedComments->keys().at(commentIndex); + auto &group = nestedComments->at(commentIndex); + groupName = group.name; if (isSubnode) { - comment = nestedComments->operator[](offset).at(index.row()); + comment = group.comments.at(index.row()); } } else { comment = comments->at(commentIndex); @@ -107,7 +122,7 @@ QVariant CommentsModel::data(const QModelIndex &index, int role) const break; } } else if (index.column() == OffsetNestedColumn) { - return offset; + return groupName; } } else { switch (index.column()) { @@ -164,9 +179,8 @@ QVariant CommentsModel::headerData(int section, Qt::Orientation, int role) const } CommentsProxyModel::CommentsProxyModel(CommentsModel *sourceModel, QObject *parent) - : QSortFilterProxyModel(parent) + : AddressableFilterProxyModel(sourceModel, parent) { - setSourceModel(sourceModel); setFilterCaseSensitivity(Qt::CaseInsensitive); setSortCaseSensitivity(Qt::CaseInsensitive); } @@ -217,45 +231,33 @@ bool CommentsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &ri } CommentsWidget::CommentsWidget(MainWindow *main, QAction *action) : - CutterDockWidget(main, action), - ui(new Ui::CommentsWidget), - main(main), - tree(new CutterTreeWidget(this)) + ListDockWidget(main, action), + actionHorizontal(tr("Horizontal"), this), + actionVertical(tr("Vertical"), this) { - ui->setupUi(this); - - // Add Status Bar footer - tree->addStatusBar(ui->verticalLayout); + setWindowTitle(tr("Comments")); + setObjectName("CommentsWidget"); commentsModel = new CommentsModel(&comments, &nestedComments, this); commentsProxyModel = new CommentsProxyModel(commentsModel, this); - ui->commentsTreeView->setModel(commentsProxyModel); - ui->commentsTreeView->sortByColumn(CommentsModel::CommentColumn, Qt::AscendingOrder); - - // Ctrl-F to show/hide the filter entry - QShortcut *searchShortcut = new QShortcut(QKeySequence::Find, this); - connect(searchShortcut, &QShortcut::activated, ui->quickFilterView, &QuickFilterView::showFilter); - searchShortcut->setContext(Qt::WidgetWithChildrenShortcut); - - // Esc to clear the filter entry - QShortcut *clearShortcut = new QShortcut(QKeySequence(Qt::Key_Escape), this); - connect(clearShortcut, &QShortcut::activated, ui->quickFilterView, &QuickFilterView::clearFilter); - clearShortcut->setContext(Qt::WidgetWithChildrenShortcut); - - connect(ui->quickFilterView, SIGNAL(filterTextChanged(const QString &)), - commentsProxyModel, SLOT(setFilterWildcard(const QString &))); - connect(ui->quickFilterView, SIGNAL(filterClosed()), ui->commentsTreeView, SLOT(setFocus())); - - connect(ui->quickFilterView, &QuickFilterView::filterTextChanged, this, [this] { - tree->showItemsNumber(commentsProxyModel->rowCount()); - }); - - setScrollMode(); - - ui->actionHorizontal->setChecked(true); + setModels(commentsProxyModel); + ui->treeView->sortByColumn(CommentsModel::CommentColumn, Qt::AscendingOrder); + + titleContextMenu = new QMenu(this); + auto viewTypeGroup = new QActionGroup(titleContextMenu); + actionHorizontal.setCheckable(true); + actionHorizontal.setActionGroup(viewTypeGroup); + connect(&actionHorizontal, &QAction::toggled, this, &CommentsWidget::onActionHorizontalToggled); + actionVertical.setCheckable(true); + actionVertical.setActionGroup(viewTypeGroup); + connect(&actionVertical, &QAction::toggled, this, &CommentsWidget::onActionVerticalToggled); + titleContextMenu->addActions(viewTypeGroup->actions()); + + + actionHorizontal.setChecked(true); this->setContextMenuPolicy(Qt::CustomContextMenu); - connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), - this, SLOT(showTitleContextMenu(const QPoint &))); + connect(this, &QWidget::customContextMenuRequested, + this, &CommentsWidget::showTitleContextMenu); connect(Core(), SIGNAL(commentsChanged()), this, SLOT(refreshTree())); connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshTree())); @@ -263,61 +265,36 @@ CommentsWidget::CommentsWidget(MainWindow *main, QAction *action) : CommentsWidget::~CommentsWidget() {} -void CommentsWidget::on_commentsTreeView_doubleClicked(const QModelIndex &index) +void CommentsWidget::onActionHorizontalToggled(bool checked) { - if (!index.isValid()) - return; - - if (commentsModel->isNested() && !index.parent().isValid()) - return; - - auto comment = index.data(CommentsModel::CommentDescriptionRole).value(); - Core()->seekAndShow(comment.offset); -} - -void CommentsWidget::on_actionHorizontal_triggered() -{ - commentsModel->setNested(false); - ui->commentsTreeView->setIndentation(8); + if (checked) { + commentsModel->setNested(false); + ui->treeView->setIndentation(8); + } } -void CommentsWidget::on_actionVertical_triggered() +void CommentsWidget::onActionVerticalToggled(bool checked) { - commentsModel->setNested(true); - ui->commentsTreeView->setIndentation(20); + if (checked) { + commentsModel->setNested(true); + ui->treeView->setIndentation(20); + } } void CommentsWidget::showTitleContextMenu(const QPoint &pt) { - // Set functions popup menu - QMenu *menu = new QMenu(this); - menu->clear(); - menu->addAction(ui->actionHorizontal); - menu->addAction(ui->actionVertical); - - if (!commentsModel->isNested()) { - ui->actionHorizontal->setChecked(true); - ui->actionVertical->setChecked(false); - } else { - ui->actionVertical->setChecked(true); - ui->actionHorizontal->setChecked(false); - } - - this->setContextMenuPolicy(Qt::CustomContextMenu); - - menu->exec(this->mapToGlobal(pt)); - delete menu; + titleContextMenu->exec(this->mapToGlobal(pt)); } void CommentsWidget::resizeEvent(QResizeEvent *event) { - if (main->responsive && isVisible()) { + if (mainWindow->responsive && isVisible()) { if (event->size().width() >= event->size().height()) { // Set horizontal view (list) - on_actionHorizontal_triggered(); + actionHorizontal.setChecked(true); } else { // Set vertical view (Tree) - on_actionVertical_triggered(); + actionVertical.setChecked(true); } } QDockWidget::resizeEvent(event); @@ -329,19 +306,22 @@ void CommentsWidget::refreshTree() comments = Core()->getAllComments("CCu"); nestedComments.clear(); + QMap nestedCommentMapping; for (const CommentDescription &comment : comments) { - QString fcnName = Core()->cmdFunctionAt(comment.offset); - nestedComments[fcnName].append(comment); + RVA offset = RVA_INVALID; + QString fcnName = Core()->nearestFlag(comment.offset, &offset); + auto nestedCommentIt = nestedCommentMapping.find(fcnName); + if (nestedCommentIt == nestedCommentMapping.end()) { + nestedCommentMapping.insert(fcnName, nestedComments.size()); + nestedComments.push_back({fcnName, offset, {comment}}); + } else { + auto &commentGroup = nestedComments[nestedCommentIt.value()]; + commentGroup.comments.append(comment); + } } commentsModel->endResetModel(); - qhelpers::adjustColumns(ui->commentsTreeView, 3, 0); - - tree->showItemsNumber(commentsProxyModel->rowCount()); + qhelpers::adjustColumns(ui->treeView, 3, 0); } -void CommentsWidget::setScrollMode() -{ - qhelpers::setVerticalScrollMode(ui->commentsTreeView); -} diff --git a/src/widgets/CommentsWidget.h b/src/widgets/CommentsWidget.h index 96c7c31753113..2645c8d798137 100644 --- a/src/widgets/CommentsWidget.h +++ b/src/widgets/CommentsWidget.h @@ -6,18 +6,23 @@ #include #include "core/Cutter.h" +#include "common/AddressableItemModel.h" #include "CutterDockWidget.h" #include "CutterTreeWidget.h" +#include "widgets/ListDockWidget.h" class MainWindow; class QTreeWidgetItem; class CommentsWidget; -namespace Ui { -class CommentsWidget; -} +struct CommentGroup +{ + QString name; + RVA offset; + QList comments; +}; -class CommentsModel : public QAbstractItemModel +class CommentsModel : public AddressableItemModel<> { Q_OBJECT @@ -25,7 +30,7 @@ class CommentsModel : public QAbstractItemModel private: QList *comments; - QMap> *nestedComments; + QList *nestedComments; bool nested; public: @@ -34,23 +39,26 @@ class CommentsModel : public QAbstractItemModel enum Role { CommentDescriptionRole = Qt::UserRole, FunctionRole }; CommentsModel(QList *comments, - QMap> *nestedComments, + QList *nestedComments, QObject *parent = nullptr); - QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; - QModelIndex parent(const QModelIndex &index) const; + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex &index) const override; - 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; bool isNested() const; void setNested(bool nested); + + RVA address(const QModelIndex &index) const override; }; -class CommentsProxyModel : public QSortFilterProxyModel +class CommentsProxyModel : public AddressableFilterProxyModel { Q_OBJECT @@ -62,39 +70,35 @@ class CommentsProxyModel : public QSortFilterProxyModel bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; }; -class CommentsWidget : public CutterDockWidget +class CommentsWidget : public ListDockWidget { Q_OBJECT public: explicit CommentsWidget(MainWindow *main, QAction *action = nullptr); - ~CommentsWidget(); + ~CommentsWidget() override; protected: void resizeEvent(QResizeEvent *event) override; private slots: - void on_commentsTreeView_doubleClicked(const QModelIndex &index); - - void on_actionHorizontal_triggered(); - void on_actionVertical_triggered(); + void onActionHorizontalToggled(bool checked); + void onActionVerticalToggled(bool checked); void showTitleContextMenu(const QPoint &pt); void refreshTree(); private: - std::unique_ptr ui; - MainWindow *main; - CommentsModel *commentsModel; CommentsProxyModel *commentsProxyModel; - CutterTreeWidget *tree; + QAction actionHorizontal; + QAction actionVertical; QList comments; - QMap> nestedComments; + QList nestedComments; - void setScrollMode(); + QMenu *titleContextMenu; }; #endif // COMMENTSWIDGET_H diff --git a/src/widgets/CommentsWidget.ui b/src/widgets/CommentsWidget.ui deleted file mode 100644 index f471916ef9b74..0000000000000 --- a/src/widgets/CommentsWidget.ui +++ /dev/null @@ -1,87 +0,0 @@ - - - CommentsWidget - - - - 0 - 0 - 645 - 250 - - - - Comments - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - true - - - true - - - - - - - - - - - true - - - Horizontal - - - Horizontal view - - - - - true - - - Vertical - - - Vertical view - - - - - - CutterTreeView - QTreeView -
widgets/CutterTreeView.h
- 1 -
- - QuickFilterView - QWidget -
widgets/QuickFilterView.h
- 1 -
-
- - -
diff --git a/src/widgets/CutterTreeWidget.cpp b/src/widgets/CutterTreeWidget.cpp index 30563bae97510..09009d04a8084 100644 --- a/src/widgets/CutterTreeWidget.cpp +++ b/src/widgets/CutterTreeWidget.cpp @@ -23,4 +23,9 @@ void CutterTreeWidget::showItemsNumber(int count) } } +void CutterTreeWidget::showStatusBar(bool show) +{ + bar->setVisible(show); +} + CutterTreeWidget::~CutterTreeWidget() {} diff --git a/src/widgets/CutterTreeWidget.h b/src/widgets/CutterTreeWidget.h index 1cdc10cc6c6f5..b077201fa9b76 100644 --- a/src/widgets/CutterTreeWidget.h +++ b/src/widgets/CutterTreeWidget.h @@ -16,6 +16,7 @@ class CutterTreeWidget : public QObject ~CutterTreeWidget(); void addStatusBar(QVBoxLayout *pos); void showItemsNumber(int count); + void showStatusBar(bool show); private: QStatusBar *bar; diff --git a/src/widgets/ExportsWidget.cpp b/src/widgets/ExportsWidget.cpp index 05061d3d05bdc..70ed650c7f265 100644 --- a/src/widgets/ExportsWidget.cpp +++ b/src/widgets/ExportsWidget.cpp @@ -1,5 +1,5 @@ #include "ExportsWidget.h" -#include "ui_ExportsWidget.h" +#include "ui_ListDockWidget.h" #include "core/MainWindow.h" #include "common/Helpers.h" #include "WidgetShortcuts.h" @@ -7,7 +7,7 @@ #include ExportsModel::ExportsModel(QList *exports, QObject *parent) - : QAbstractListModel(parent), + : AddressableItemModel(parent), exports(exports) { } @@ -71,10 +71,21 @@ QVariant ExportsModel::headerData(int section, Qt::Orientation, int role) const } } +RVA ExportsModel::address(const QModelIndex &index) const +{ + const ExportDescription &exp = exports->at(index.row()); + return exp.vaddr; +} + +QString ExportsModel::name(const QModelIndex &index) const +{ + const ExportDescription &exp = exports->at(index.row()); + return exp.name; +} + ExportsProxyModel::ExportsProxyModel(ExportsModel *source_model, QObject *parent) - : QSortFilterProxyModel(parent) + : AddressableFilterProxyModel(source_model, parent) { - setSourceModel(source_model); setFilterCaseSensitivity(Qt::CaseInsensitive); setSortCaseSensitivity(Qt::CaseInsensitive); } @@ -115,19 +126,15 @@ bool ExportsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &rig } ExportsWidget::ExportsWidget(MainWindow *main, QAction *action) : - CutterDockWidget(main, action), - ui(new Ui::ExportsWidget), - tree(new CutterTreeWidget(this)) + ListDockWidget(main, action) { - ui->setupUi(this); + setWindowTitle(tr("Exports")); + setObjectName("ExportsWidget"); - // Add Status Bar footer - tree->addStatusBar(ui->verticalLayout); - exportsModel = new ExportsModel(&exports, this); exportsProxyModel = new ExportsProxyModel(exportsModel, this); - ui->exportsTreeView->setModel(exportsProxyModel); - ui->exportsTreeView->sortByColumn(ExportsModel::OffsetColumn, Qt::AscendingOrder); + setModels(exportsProxyModel); + ui->treeView->sortByColumn(ExportsModel::OffsetColumn, Qt::AscendingOrder); QShortcut *toggle_shortcut = new QShortcut(widgetShortcuts["ExportsWidget"], main); connect(toggle_shortcut, &QShortcut::activated, this, [=] (){ @@ -135,26 +142,6 @@ ExportsWidget::ExportsWidget(MainWindow *main, QAction *action) : main->updateDockActionChecked(action); } ); - // Ctrl-F to show/hide the filter entry - QShortcut *searchShortcut = new QShortcut(QKeySequence::Find, this); - connect(searchShortcut, &QShortcut::activated, ui->quickFilterView, &QuickFilterView::showFilter); - searchShortcut->setContext(Qt::WidgetWithChildrenShortcut); - - // Esc to clear the filter entry - QShortcut *clearShortcut = new QShortcut(QKeySequence(Qt::Key_Escape), this); - connect(clearShortcut, &QShortcut::activated, ui->quickFilterView, &QuickFilterView::clearFilter); - clearShortcut->setContext(Qt::WidgetWithChildrenShortcut); - - connect(ui->quickFilterView, SIGNAL(filterTextChanged(const QString &)), - exportsProxyModel, SLOT(setFilterWildcard(const QString &))); - connect(ui->quickFilterView, SIGNAL(filterClosed()), ui->exportsTreeView, SLOT(setFocus())); - - connect(ui->quickFilterView, &QuickFilterView::filterTextChanged, this, [this] { - tree->showItemsNumber(exportsProxyModel->rowCount()); - }); - - setScrollMode(); - connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshExports())); } @@ -166,22 +153,5 @@ void ExportsWidget::refreshExports() exports = Core()->getAllExports(); exportsModel->endResetModel(); - qhelpers::adjustColumns(ui->exportsTreeView, 3, 0); - - tree->showItemsNumber(exportsProxyModel->rowCount()); -} - - -void ExportsWidget::setScrollMode() -{ - qhelpers::setVerticalScrollMode(ui->exportsTreeView); -} - -void ExportsWidget::on_exportsTreeView_doubleClicked(const QModelIndex &index) -{ - if (!index.isValid()) - return; - - ExportDescription exp = index.data(ExportsModel::ExportDescriptionRole).value(); - Core()->seekAndShow(exp.vaddr); + qhelpers::adjustColumns(ui->treeView, 3, 0); } diff --git a/src/widgets/ExportsWidget.h b/src/widgets/ExportsWidget.h index 16aa9484b18ca..b1836c03c50fd 100644 --- a/src/widgets/ExportsWidget.h +++ b/src/widgets/ExportsWidget.h @@ -5,7 +5,7 @@ #include "core/Cutter.h" #include "CutterDockWidget.h" -#include "CutterTreeWidget.h" +#include "widgets/ListDockWidget.h" #include #include @@ -18,7 +18,7 @@ namespace Ui { class ExportsWidget; } -class ExportsModel : public QAbstractListModel +class ExportsModel : public AddressableItemModel { Q_OBJECT @@ -33,14 +33,17 @@ class ExportsModel : public QAbstractListModel ExportsModel(QList *exports, 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 ExportsProxyModel : public QSortFilterProxyModel +class ExportsProxyModel : public AddressableFilterProxyModel { Q_OBJECT @@ -52,7 +55,7 @@ class ExportsProxyModel : public QSortFilterProxyModel bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; }; -class ExportsWidget : public CutterDockWidget +class ExportsWidget : public ListDockWidget { Q_OBJECT @@ -61,19 +64,12 @@ class ExportsWidget : public CutterDockWidget ~ExportsWidget(); private slots: - void on_exportsTreeView_doubleClicked(const QModelIndex &index); - void refreshExports(); private: - std::unique_ptr ui; - ExportsModel *exportsModel; ExportsProxyModel *exportsProxyModel; QList exports; - CutterTreeWidget *tree; - - void setScrollMode(); }; #endif // EXPORTSWIDGET_H diff --git a/src/widgets/ExportsWidget.ui b/src/widgets/ExportsWidget.ui deleted file mode 100644 index aca22bfb5dbb8..0000000000000 --- a/src/widgets/ExportsWidget.ui +++ /dev/null @@ -1,75 +0,0 @@ - - - ExportsWidget - - - - 0 - 0 - 400 - 300 - - - - Exports - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - CutterTreeView::item -{ - padding-top: 1px; - padding-bottom: 1px; -} - - - QFrame::NoFrame - - - 0 - - - 8 - - - true - - - - - - - - - - - - CutterTreeView - QTreeView -
widgets/CutterTreeView.h
- 1 -
- - QuickFilterView - QWidget -
widgets/QuickFilterView.h
- 1 -
-
- - -
diff --git a/src/widgets/FunctionsWidget.cpp b/src/widgets/FunctionsWidget.cpp index 5f0349b657ef6..b3ba344514175 100644 --- a/src/widgets/FunctionsWidget.cpp +++ b/src/widgets/FunctionsWidget.cpp @@ -1,5 +1,5 @@ #include "FunctionsWidget.h" -#include "ui_FunctionsWidget.h" +#include "ui_ListDockWidget.h" #include "core/MainWindow.h" #include "common/Helpers.h" @@ -8,6 +8,7 @@ #include "dialogs/XrefsDialog.h" #include "common/FunctionsTask.h" #include "common/TempConfig.h" +#include "menus/AddressableItemContextMenu.h" #include #include @@ -28,7 +29,7 @@ static const int kMaxTooltipHighlightsLines = 5; FunctionModel::FunctionModel(QList *functions, QSet *importAddresses, ut64 *mainAdress, bool nested, QFont default_font, QFont highlight_font, QObject *parent) - : QAbstractItemModel(parent), + : AddressableItemModel<>(parent), functions(functions), importAddresses(importAddresses), mainAdress(mainAdress), @@ -147,21 +148,21 @@ QVariant FunctionModel::data(const QModelIndex &index, int role) const case NameColumn: return function.name; case SizeColumn: - return function.size; + return QString::number(function.size); case OffsetColumn: return RAddressString(function.offset); case NargsColumn: - return function.nargs; + return QString::number(function.nargs); case NlocalsColumn: - return function.nlocals; + return QString::number(function.nlocals); case NbbsColumn: - return function.nbbs; + return QString::number(function.nbbs); case CalltypeColumn: return function.calltype; case EdgesColumn: - return function.edges; + return QString::number(function.edges); case FrameColumn: - return function.stackframe; + return QString::number(function.stackframe); default: return QVariant(); } @@ -187,7 +188,8 @@ QVariant FunctionModel::data(const QModelIndex &index, int role) const case Qt::ToolTipRole: { - QStringList disasmPreview = Core()->getDisassemblyPreview(function.offset, kMaxTooltipDisasmPreviewLines); + QStringList disasmPreview = Core()->getDisassemblyPreview(function.offset, + kMaxTooltipDisasmPreviewLines); const QStringList &summary = Core()->cmdList(QString("pdsf @ %1").arg(function.offset)); const QFont &fnt = Config()->getFont(); QFontMetrics fm{ fnt }; @@ -204,17 +206,20 @@ QVariant FunctionModel::data(const QModelIndex &index, int role) const if (disasmPreview.isEmpty() && highlights.isEmpty()) return QVariant(); - QString toolTipContent = QString("
") - .arg(fnt.family()) - .arg(qMax(6, fnt.pointSize() - 1)); // slightly decrease font size, to keep more text in the same box + QString toolTipContent = + QString("
") + .arg(fnt.family()) + .arg(qMax(6, fnt.pointSize() - + 1)); // slightly decrease font size, to keep more text in the same box if (!disasmPreview.isEmpty()) - toolTipContent += tr("
Disassembly preview:
%1
") + toolTipContent += + tr("
Disassembly preview:
%1
") .arg(disasmPreview.join("
")); if (!highlights.isEmpty()) { toolTipContent += tr("
Highlights:
%1
") - .arg(highlights.join(QLatin1Char('\n')).toHtmlEscaped().replace(QLatin1Char('\n'), "
")); + .arg(highlights.join(QLatin1Char('\n')).toHtmlEscaped().replace(QLatin1Char('\n'), "
")); } toolTipContent += "
"; return toolTipContent; @@ -282,6 +287,18 @@ void FunctionModel::setNested(bool nested) endResetModel(); } +RVA FunctionModel::address(const QModelIndex &index) const +{ + auto function = data(index, FunctionDescriptionRole).value(); + return function.offset; +} + +QString FunctionModel::name(const QModelIndex &index) const +{ + auto function = data(index, FunctionDescriptionRole).value(); + return function.name; +} + void FunctionModel::seekChanged(RVA) { int previousIndex = currentIndex; @@ -332,9 +349,8 @@ void FunctionModel::functionRenamed(const QString &prev_name, const QString &new FunctionSortFilterProxyModel::FunctionSortFilterProxyModel(FunctionModel *source_model, QObject *parent) - : QSortFilterProxyModel(parent) + : AddressableFilterProxyModel(source_model, parent) { - setSourceModel(source_model); setFilterCaseSensitivity(Qt::CaseInsensitive); setSortCaseSensitivity(Qt::CaseInsensitive); } @@ -411,64 +427,56 @@ bool FunctionSortFilterProxyModel::lessThan(const QModelIndex &left, const QMode } FunctionsWidget::FunctionsWidget(MainWindow *main, QAction *action) : - CutterDockWidget(main, action), - ui(new Ui::FunctionsWidget), - tree(new CutterTreeWidget(this)) + ListDockWidget(main, action), + actionRename(tr("Rename"), this), + actionUndefine(tr("Undefine"), this), + actionHorizontal(tr("Horizontal"), this), + actionVertical(tr("Vertical"), this) { - ui->setupUi(this); + setWindowTitle(tr("Functions")); + setObjectName("FunctionsWidget"); - // Add Status Bar footer - tree->addStatusBar(ui->verticalLayout); - - // Radare core found in: - this->main = main; setTooltipStylesheet(); connect(Config(), SIGNAL(colorsUpdated()), this, SLOT(setTooltipStylesheet())); - - // leave the filter visible by default so users know it exists - //ui->filterLineEdit->setVisible(false); - - // Ctrl-F to show/hide the filter entry - QShortcut *search_shortcut = new QShortcut(QKeySequence::Find, this); - connect(search_shortcut, &QShortcut::activated, ui->quickFilterView, &QuickFilterView::showFilter); - search_shortcut->setContext(Qt::WidgetWithChildrenShortcut); - - // Esc to clear the filter entry - QShortcut *clear_shortcut = new QShortcut(QKeySequence(Qt::Key_Escape), this); - connect(clear_shortcut, &QShortcut::activated, ui->quickFilterView, &QuickFilterView::clearFilter); - clear_shortcut->setContext(Qt::WidgetWithChildrenShortcut); - - QFontInfo font_info = ui->functionsTreeView->fontInfo(); + QFontInfo font_info = ui->treeView->fontInfo(); QFont default_font = QFont(font_info.family(), font_info.pointSize()); QFont highlight_font = QFont(font_info.family(), font_info.pointSize(), QFont::Bold); functionModel = new FunctionModel(&functions, &importAddresses, &mainAdress, false, default_font, highlight_font, this); functionProxyModel = new FunctionSortFilterProxyModel(functionModel, this); - ui->functionsTreeView->setModel(functionProxyModel); - ui->functionsTreeView->sortByColumn(FunctionModel::NameColumn, Qt::AscendingOrder); - - connect(ui->quickFilterView, SIGNAL(filterTextChanged(const QString &)), functionProxyModel, - SLOT(setFilterWildcard(const QString &))); - connect(ui->quickFilterView, SIGNAL(filterClosed()), ui->functionsTreeView, SLOT(setFocus())); - - connect(ui->quickFilterView, &QuickFilterView::filterTextChanged, this, [this] { - tree->showItemsNumber(functionProxyModel->rowCount()); - }); - - setScrollMode(); - - // Set Functions context menu - connect(ui->functionsTreeView, SIGNAL(customContextMenuRequested(const QPoint &)), - this, SLOT(showFunctionsContextMenu(const QPoint &))); - - connect(ui->functionsTreeView, SIGNAL(doubleClicked(const QModelIndex &)), this, - SLOT(onFunctionsDoubleClicked(const QModelIndex &))); + setModels(functionProxyModel); + ui->treeView->sortByColumn(FunctionModel::NameColumn, Qt::AscendingOrder); + + + titleContextMenu = new QMenu(this); + auto viewTypeGroup = new QActionGroup(titleContextMenu); + actionHorizontal.setCheckable(true); + actionHorizontal.setActionGroup(viewTypeGroup); + connect(&actionHorizontal, &QAction::toggled, this, &FunctionsWidget::onActionHorizontalToggled); + actionVertical.setCheckable(true); + actionVertical.setActionGroup(viewTypeGroup); + connect(&actionVertical, &QAction::toggled, this, &FunctionsWidget::onActionVerticalToggled); + titleContextMenu->addActions(viewTypeGroup->actions()); + + actionRename.setShortcut({Qt::Key_N}); + actionRename.setShortcutContext(Qt::ShortcutContext::WidgetWithChildrenShortcut); + connect(&actionRename, &QAction::triggered, this, + &FunctionsWidget::onActionFunctionsRenameTriggered); + connect(&actionUndefine, &QAction::triggered, this, + &FunctionsWidget::onActionFunctionsUndefineTriggered); + + auto itemConextMenu = getItemContextMenu(); + itemConextMenu->addSeparator(); + itemConextMenu->addAction(&actionRename); + itemConextMenu->addAction(&actionUndefine); + itemConextMenu->setWholeFunction(true); + + addActions(itemConextMenu->actions()); // Use a custom context menu on the dock title bar - //this->title_bar = this->titleBarWidget(); - ui->actionHorizontal->setChecked(true); + actionHorizontal.setChecked(true); this->setContextMenuPolicy(Qt::CustomContextMenu); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(showTitleContextMenu(const QPoint &))); @@ -503,9 +511,7 @@ void FunctionsWidget::refreshTree() functionModel->endResetModel(); // resize offset and size columns - qhelpers::adjustColumns(ui->functionsTreeView, 3, 0); - - tree->showItemsNumber(functionProxyModel->rowCount()); + qhelpers::adjustColumns(ui->treeView, 3, 0); }); Core()->getAsyncTaskManager()->start(task); } @@ -515,56 +521,10 @@ void FunctionsWidget::changeSizePolicy(QSizePolicy::Policy hor, QSizePolicy::Pol ui->dockWidgetContents->setSizePolicy(hor, ver); } -void FunctionsWidget::onFunctionsDoubleClicked(const QModelIndex &index) -{ - if (!index.isValid()) - return; - - FunctionDescription function = index.data( - FunctionModel::FunctionDescriptionRole).value(); - Core()->seekAndShow(function.offset); -} - -void FunctionsWidget::showFunctionsContextMenu(const QPoint &pt) -{ - // Set functions popup menu - QMenu *menu = new QMenu(ui->functionsTreeView); - menu->clear(); - menu->addAction(ui->actionDisasAdd_comment); - menu->addAction(ui->actionFunctionsRename); - menu->addAction(ui->actionFunctionsUndefine); - menu->addSeparator(); - menu->addAction(ui->action_References); - - menu->exec(ui->functionsTreeView->mapToGlobal(pt)); - - delete menu; -} - -void FunctionsWidget::on_actionDisasAdd_comment_triggered() +void FunctionsWidget::onActionFunctionsRenameTriggered() { // Get selected item in functions tree view - FunctionDescription function = ui->functionsTreeView->selectionModel()->currentIndex().data( - FunctionModel::FunctionDescriptionRole).value(); - - // Create dialog - CommentsDialog c(this); - - if (c.exec()) { - // Get new function name - QString comment = c.getComment(); - // Rename function in r2 core - Core()->setComment(function.offset, comment); - // Seek to new renamed function - Core()->seekAndShow(function.offset); - // TODO: Refresh functions tree widget - } -} - -void FunctionsWidget::on_actionFunctionsRename_triggered() -{ - // Get selected item in functions tree view - FunctionDescription function = ui->functionsTreeView->selectionModel()->currentIndex().data( + FunctionDescription function = ui->treeView->selectionModel()->currentIndex().data( FunctionModel::FunctionDescriptionRole).value(); // Create dialog @@ -585,79 +545,58 @@ void FunctionsWidget::on_actionFunctionsRename_triggered() } } -void FunctionsWidget::on_actionFunctionsUndefine_triggered() -{ - FunctionDescription function = ui->functionsTreeView->selectionModel()->currentIndex().data( - FunctionModel::FunctionDescriptionRole).value(); - Core()->delFunction(function.offset); -} -void FunctionsWidget::on_action_References_triggered() +void FunctionsWidget::onActionFunctionsUndefineTriggered() { - // Get selected item in functions tree view - FunctionDescription function = ui->functionsTreeView->selectionModel()->currentIndex().data( - FunctionModel::FunctionDescriptionRole).value(); - XrefsDialog x(nullptr); - x.fillRefsForAddress(function.offset, function.name, true); - x.exec(); + const auto selection = ui->treeView->selectionModel()->selection().indexes(); + std::vector offsets; + offsets.reserve(selection.size()); + for (const auto &index : selection) { + offsets.push_back(functionProxyModel->address(index)); + } + for (RVA offset : offsets) { + Core()->delFunction(offset); + } } void FunctionsWidget::showTitleContextMenu(const QPoint &pt) { - // Set functions popup menu - QMenu *menu = new QMenu(this); - menu->clear(); - menu->addAction(ui->actionHorizontal); - menu->addAction(ui->actionVertical); - - if (!functionModel->isNested()) { - ui->actionHorizontal->setChecked(true); - ui->actionVertical->setChecked(false); - } else { - ui->actionVertical->setChecked(true); - ui->actionHorizontal->setChecked(false); - } - - this->setContextMenuPolicy(Qt::CustomContextMenu); - - menu->exec(this->mapToGlobal(pt)); - delete menu; + titleContextMenu->exec(this->mapToGlobal(pt)); } -void FunctionsWidget::on_actionHorizontal_triggered() +void FunctionsWidget::onActionHorizontalToggled(bool enable) { - functionModel->setNested(false); - ui->functionsTreeView->setIndentation(8); + if (enable) { + functionModel->setNested(false); + ui->treeView->setIndentation(8); + } } -void FunctionsWidget::on_actionVertical_triggered() +void FunctionsWidget::onActionVerticalToggled(bool enable) { - functionModel->setNested(true); - ui->functionsTreeView->setIndentation(20); + if (enable) { + functionModel->setNested(true); + ui->treeView->setIndentation(20); + } } void FunctionsWidget::resizeEvent(QResizeEvent *event) { - if (main->responsive && isVisible()) { + if (mainWindow->responsive && isVisible()) { if (event->size().width() >= event->size().height()) { // Set horizontal view (list) - on_actionHorizontal_triggered(); + actionHorizontal.setChecked(true); } else { // Set vertical view (Tree) - on_actionVertical_triggered(); + actionVertical.setChecked(true); } } QDockWidget::resizeEvent(event); } -void FunctionsWidget::setScrollMode() -{ - qhelpers::setVerticalScrollMode(ui->functionsTreeView); -} - /** * @brief a SLOT to set the stylesheet for a tooltip - */ + */ void FunctionsWidget::setTooltipStylesheet() { setStyleSheet(QString("QToolTip { border-width: 1px; max-width: %1px;" \ diff --git a/src/widgets/FunctionsWidget.h b/src/widgets/FunctionsWidget.h index ac46998eefbe3..d553f97d541ba 100644 --- a/src/widgets/FunctionsWidget.h +++ b/src/widgets/FunctionsWidget.h @@ -3,24 +3,15 @@ #include -#include - #include "core/Cutter.h" #include "CutterDockWidget.h" -#include "CutterTreeWidget.h" -#include "CutterTreeView.h" +#include "widgets/ListDockWidget.h" class MainWindow; -class QTreeWidgetItem; class FunctionsTask; class FunctionsWidget; -namespace Ui { -class FunctionsWidget; -} - - -class FunctionModel : public QAbstractItemModel +class FunctionModel : public AddressableItemModel<> { Q_OBJECT @@ -53,14 +44,15 @@ class FunctionModel : public QAbstractItemModel FunctionModel(QList *functions, QSet *importAddresses, ut64 *mainAdress, bool nested, QFont defaultFont, QFont highlightFont, QObject *parent = nullptr); - QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; - QModelIndex parent(const QModelIndex &index) const; + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex &index) const override; - 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; /** * @return true if the index changed @@ -73,13 +65,15 @@ class FunctionModel : public QAbstractItemModel return nested; } + RVA address(const QModelIndex &index) const override; + QString name(const QModelIndex &index) const override; private slots: void seekChanged(RVA addr); void functionRenamed(const QString &prev_name, const QString &new_name); }; -class FunctionSortFilterProxyModel : public QSortFilterProxyModel +class FunctionSortFilterProxyModel : public AddressableFilterProxyModel { Q_OBJECT @@ -93,24 +87,20 @@ class FunctionSortFilterProxyModel : public QSortFilterProxyModel -class FunctionsWidget : public CutterDockWidget +class FunctionsWidget : public ListDockWidget { Q_OBJECT public: explicit FunctionsWidget(MainWindow *main, QAction *action = nullptr); - ~FunctionsWidget(); + ~FunctionsWidget() override; void changeSizePolicy(QSizePolicy::Policy hor, QSizePolicy::Policy ver); private slots: - void onFunctionsDoubleClicked(const QModelIndex &index); - void showFunctionsContextMenu(const QPoint &pt); - void on_actionDisasAdd_comment_triggered(); - void on_actionFunctionsRename_triggered(); - void on_action_References_triggered(); - void on_actionFunctionsUndefine_triggered(); - void on_actionHorizontal_triggered(); - void on_actionVertical_triggered(); + void onActionFunctionsRenameTriggered(); + void onActionFunctionsUndefineTriggered(); + void onActionHorizontalToggled(bool enable); + void onActionVerticalToggled(bool enable); void showTitleContextMenu(const QPoint &pt); void setTooltipStylesheet(); void refreshTree(); @@ -119,16 +109,19 @@ private slots: void resizeEvent(QResizeEvent *event) override; private: - std::unique_ptr ui; - MainWindow *main; QSharedPointer task; QList functions; QSet importAddresses; ut64 mainAdress; FunctionModel *functionModel; FunctionSortFilterProxyModel *functionProxyModel; - CutterTreeWidget *tree; - void setScrollMode(); + + QMenu *titleContextMenu; + + QAction actionRename; + QAction actionUndefine; + QAction actionHorizontal; + QAction actionVertical; }; #endif // FUNCTIONSWIDGET_H diff --git a/src/widgets/FunctionsWidget.ui b/src/widgets/FunctionsWidget.ui deleted file mode 100644 index 2967b8d6ddb1e..0000000000000 --- a/src/widgets/FunctionsWidget.ui +++ /dev/null @@ -1,146 +0,0 @@ - - - FunctionsWidget - - - - 0 - 0 - 289 - 359 - - - - - - - Functions - - - - - 1 - 0 - - - - - 200 - 0 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - Qt::CustomContextMenu - - - -CutterTreeView::item -{ - padding-top: 1px; - padding-bottom: 1px; -} - - - - QFrame::NoFrame - - - 0 - - - QAbstractScrollArea::AdjustToContents - - - 8 - - - true - - - - - - - - - - - Add comment - - - - - Rename - - - - - Undefine - - - - - X-Refs - - - Cross references - - - - - true - - - Horizontal - - - - - true - - - Vertical - - - - - - CutterTreeView - QTreeView -
widgets/CutterTreeView.h
- 1 -
- - QuickFilterView - QWidget -
widgets/QuickFilterView.h
- 1 -
-
- - -
diff --git a/src/widgets/HeadersWidget.cpp b/src/widgets/HeadersWidget.cpp index 403739cf9b796..2bbfec73ec17a 100644 --- a/src/widgets/HeadersWidget.cpp +++ b/src/widgets/HeadersWidget.cpp @@ -1,10 +1,10 @@ #include "HeadersWidget.h" -#include "ui_HeadersWidget.h" +#include "ui_ListDockWidget.h" #include "core/MainWindow.h" #include "common/Helpers.h" HeadersModel::HeadersModel(QList *headers, QObject *parent) - : QAbstractListModel(parent), + : AddressableItemModel(parent), headers(headers) { } @@ -64,10 +64,21 @@ QVariant HeadersModel::headerData(int section, Qt::Orientation, int role) const } } +RVA HeadersModel::address(const QModelIndex &index) const +{ + const HeaderDescription &header = headers->at(index.row()); + return header.vaddr; +} + +QString HeadersModel::name(const QModelIndex &index) const +{ + const HeaderDescription &header = headers->at(index.row()); + return header.name; +} + HeadersProxyModel::HeadersProxyModel(HeadersModel *sourceModel, QObject *parent) - : QSortFilterProxyModel(parent) + : AddressableFilterProxyModel(sourceModel, parent) { - setSourceModel(sourceModel); } bool HeadersProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const @@ -99,17 +110,18 @@ bool HeadersProxyModel::lessThan(const QModelIndex &left, const QModelIndex &rig } HeadersWidget::HeadersWidget(MainWindow *main, QAction *action) : - CutterDockWidget(main, action), - ui(new Ui::HeadersWidget) + ListDockWidget(main, action) { - ui->setupUi(this); + setWindowTitle(tr("Headers")); + setObjectName("HeadersWidget"); headersModel = new HeadersModel(&headers, this); headersProxyModel = new HeadersProxyModel(headersModel, this); - ui->headersTreeView->setModel(headersProxyModel); - ui->headersTreeView->sortByColumn(HeadersModel::OffsetColumn, Qt::AscendingOrder); + setModels(headersProxyModel); + ui->treeView->sortByColumn(HeadersModel::OffsetColumn, Qt::AscendingOrder); - setScrollMode(); + ui->quickFilterView->closeFilter(); + showCount(false); connect(Core(), &CutterCore::refreshAll, this, &HeadersWidget::refreshHeaders); } @@ -122,17 +134,6 @@ void HeadersWidget::refreshHeaders() headers = Core()->getAllHeaders(); headersModel->endResetModel(); - ui->headersTreeView->resizeColumnToContents(0); - ui->headersTreeView->resizeColumnToContents(1); -} - -void HeadersWidget::setScrollMode() -{ - qhelpers::setVerticalScrollMode(ui->headersTreeView); -} - -void HeadersWidget::on_headersTreeView_doubleClicked(const QModelIndex &index) -{ - HeaderDescription item = index.data(HeadersModel::HeaderDescriptionRole).value(); - Core()->seekAndShow(item.vaddr); + ui->treeView->resizeColumnToContents(0); + ui->treeView->resizeColumnToContents(1); } diff --git a/src/widgets/HeadersWidget.h b/src/widgets/HeadersWidget.h index 4126bf52d032a..4ac93f10423d4 100644 --- a/src/widgets/HeadersWidget.h +++ b/src/widgets/HeadersWidget.h @@ -4,7 +4,7 @@ #include #include "core/Cutter.h" -#include "CutterDockWidget.h" +#include "ListDockWidget.h" #include #include @@ -22,7 +22,7 @@ class QTreeWidgetItem; class HeadersWidget; -class HeadersModel: public QAbstractListModel +class HeadersModel: public AddressableItemModel { Q_OBJECT @@ -35,18 +35,21 @@ class HeadersModel: public QAbstractListModel enum Column { OffsetColumn = 0, NameColumn, ValueColumn, ColumnCount }; enum Role { HeaderDescriptionRole = Qt::UserRole }; - HeadersModel(QList *headers, QObject *parent = 0); + HeadersModel(QList *headers, 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 HeadersProxyModel : public QSortFilterProxyModel +class HeadersProxyModel : public AddressableFilterProxyModel { Q_OBJECT @@ -60,7 +63,7 @@ class HeadersProxyModel : public QSortFilterProxyModel -class HeadersWidget : public CutterDockWidget +class HeadersWidget : public ListDockWidget { Q_OBJECT @@ -69,18 +72,12 @@ class HeadersWidget : public CutterDockWidget ~HeadersWidget(); private slots: - void on_headersTreeView_doubleClicked(const QModelIndex &index); - void refreshHeaders(); private: - std::unique_ptr ui; - HeadersModel *headersModel; HeadersProxyModel *headersProxyModel; QList headers; - - void setScrollMode(); }; diff --git a/src/widgets/HeadersWidget.ui b/src/widgets/HeadersWidget.ui deleted file mode 100644 index 0b673f457356e..0000000000000 --- a/src/widgets/HeadersWidget.ui +++ /dev/null @@ -1,66 +0,0 @@ - - - HeadersWidget - - - - 0 - 0 - 400 - 300 - - - - Headers - - - - - 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/ImportsWidget.cpp b/src/widgets/ImportsWidget.cpp index 364764a35c176..dbaa3901a737e 100644 --- a/src/widgets/ImportsWidget.cpp +++ b/src/widgets/ImportsWidget.cpp @@ -1,5 +1,5 @@ #include "ImportsWidget.h" -#include "ui_ImportsWidget.h" +#include "ui_ListDockWidget.h" #include "WidgetShortcuts.h" #include "core/MainWindow.h" #include "common/Helpers.h" @@ -10,7 +10,7 @@ #include ImportsModel::ImportsModel(QList *imports, QObject *parent) : - QAbstractTableModel(parent), + AddressableItemModel(parent), imports(imports) {} @@ -81,10 +81,21 @@ QVariant ImportsModel::headerData(int section, Qt::Orientation, int role) const return QVariant(); } +RVA ImportsModel::address(const QModelIndex &index) const +{ + const ImportDescription &import = imports->at(index.row()); + return import.plt; +} + +QString ImportsModel::name(const QModelIndex &index) const +{ + const ImportDescription &import = imports->at(index.row()); + return import.name; +} + ImportsProxyModel::ImportsProxyModel(ImportsModel *sourceModel, QObject *parent) - : QSortFilterProxyModel(parent) + : AddressableFilterProxyModel(sourceModel, parent) { - setSourceModel(sourceModel); setFilterCaseSensitivity(Qt::CaseInsensitive); setSortCaseSensitivity(Qt::CaseInsensitive); } @@ -129,46 +140,21 @@ bool ImportsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &rig */ ImportsWidget::ImportsWidget(MainWindow *main, QAction *action) : - CutterDockWidget(main, action), - ui(new Ui::ImportsWidget), + ListDockWidget(main, action), importsModel(new ImportsModel(&imports, this)), - importsProxyModel(new ImportsProxyModel(importsModel, this)), - tree(new CutterTreeWidget(this)) + importsProxyModel(new ImportsProxyModel(importsModel, this)) { - ui->setupUi(this); - - // Add Status Bar footer - tree->addStatusBar(ui->verticalLayout); - - ui->importsTreeView->setModel(importsProxyModel); - ui->importsTreeView->sortByColumn(ImportsModel::NameColumn, Qt::AscendingOrder); + setWindowTitle(tr("Imports")); + setObjectName("ImportsWidget"); + setModels(importsProxyModel); + ui->treeView->sortByColumn(ImportsModel::NameColumn, Qt::AscendingOrder); QShortcut *toggle_shortcut = new QShortcut(widgetShortcuts["ImportsWidget"], main); connect(toggle_shortcut, &QShortcut::activated, this, [=] (){ toggleDockWidget(true); main->updateDockActionChecked(action); } ); - // Ctrl-F to show/hide the filter entry - QShortcut *searchShortcut = new QShortcut(QKeySequence::Find, this); - connect(searchShortcut, &QShortcut::activated, ui->quickFilterView, &QuickFilterView::showFilter); - searchShortcut->setContext(Qt::WidgetWithChildrenShortcut); - - // Esc to clear the filter entry - QShortcut *clearShortcut = new QShortcut(QKeySequence(Qt::Key_Escape), this); - connect(clearShortcut, &QShortcut::activated, ui->quickFilterView, &QuickFilterView::clearFilter); - clearShortcut->setContext(Qt::WidgetWithChildrenShortcut); - - connect(ui->quickFilterView, SIGNAL(filterTextChanged(const QString &)), - importsProxyModel, SLOT(setFilterWildcard(const QString &))); - connect(ui->quickFilterView, SIGNAL(filterClosed()), ui->importsTreeView, SLOT(setFocus())); - - connect(ui->quickFilterView, &QuickFilterView::filterTextChanged, this, [this] { - tree->showItemsNumber(importsProxyModel->rowCount()); - }); - - setScrollMode(); - connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshImports())); } @@ -179,20 +165,5 @@ void ImportsWidget::refreshImports() importsModel->beginResetModel(); imports = Core()->getAllImports(); importsModel->endResetModel(); - qhelpers::adjustColumns(ui->importsTreeView, 4, 0); - - tree->showItemsNumber(importsProxyModel->rowCount()); -} - -void ImportsWidget::setScrollMode() -{ - qhelpers::setVerticalScrollMode(ui->importsTreeView); -} - -void ImportsWidget::on_importsTreeView_doubleClicked(const QModelIndex &index) -{ - if (!index.isValid()) - return; - - Core()->seekAndShow(index.data(ImportsModel::AddressRole).toLongLong()); + qhelpers::adjustColumns(ui->treeView, 4, 0); } diff --git a/src/widgets/ImportsWidget.h b/src/widgets/ImportsWidget.h index c4abb2a84f95d..add0ff42d7756 100644 --- a/src/widgets/ImportsWidget.h +++ b/src/widgets/ImportsWidget.h @@ -11,17 +11,14 @@ #include "CutterDockWidget.h" #include "core/Cutter.h" -#include "CutterTreeWidget.h" +#include "widgets/ListDockWidget.h" +#include "common/AddressableItemModel.h" class MainWindow; class QTreeWidget; class ImportsWidget; -namespace Ui { -class ImportsWidget; -} - -class ImportsModel : public QAbstractTableModel +class ImportsModel : public AddressableItemModel { Q_OBJECT @@ -51,14 +48,17 @@ class ImportsModel : public QAbstractTableModel ImportsModel(QList *imports, QObject *parent = nullptr); - int rowCount(const QModelIndex &parent) const; - int columnCount(const QModelIndex &parent) const; + int rowCount(const QModelIndex &parent) const override; + int columnCount(const QModelIndex &parent) const override; + + QVariant data(const QModelIndex &index, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; - QVariant data(const QModelIndex &index, int role) const; - QVariant headerData(int section, Qt::Orientation orientation, int role) const; + RVA address(const QModelIndex &index) const override; + QString name(const QModelIndex &index) const override; }; -class ImportsProxyModel : public QSortFilterProxyModel +class ImportsProxyModel : public AddressableFilterProxyModel { Q_OBJECT @@ -70,7 +70,7 @@ class ImportsProxyModel : public QSortFilterProxyModel bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; }; -class ImportsWidget : public CutterDockWidget +class ImportsWidget : public ListDockWidget { Q_OBJECT @@ -79,20 +79,13 @@ class ImportsWidget : public CutterDockWidget ~ImportsWidget(); private slots: - void on_importsTreeView_doubleClicked(const QModelIndex &index); - void refreshImports(); - private: - std::unique_ptr ui; - ImportsModel *importsModel; ImportsProxyModel *importsProxyModel; QList imports; - CutterTreeWidget *tree; void highlightUnsafe(); - void setScrollMode(); }; #endif // IMPORTSWIDGET_H diff --git a/src/widgets/ImportsWidget.ui b/src/widgets/ImportsWidget.ui deleted file mode 100644 index b918f8d9e4655..0000000000000 --- a/src/widgets/ImportsWidget.ui +++ /dev/null @@ -1,75 +0,0 @@ - - - ImportsWidget - - - - 0 - 0 - 400 - 300 - - - - Imports - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - CutterTreeView::item -{ - padding-top: 1px; - padding-bottom: 1px; -} - - - QFrame::NoFrame - - - 0 - - - 8 - - - true - - - - - - - - - - - - CutterTreeView - QTreeView -
widgets/CutterTreeView.h
- 1 -
- - QuickFilterView - QWidget -
widgets/QuickFilterView.h
- 1 -
-
- - -
diff --git a/src/widgets/ListDockWidget.cpp b/src/widgets/ListDockWidget.cpp new file mode 100644 index 0000000000000..fe5c9813dac20 --- /dev/null +++ b/src/widgets/ListDockWidget.cpp @@ -0,0 +1,118 @@ +#include "ListDockWidget.h" +#include "ui_ListDockWidget.h" +#include "core/MainWindow.h" +#include "common/Helpers.h" +#include "menus/AddressableItemContextMenu.h" + +#include +#include +#include + +ListDockWidget::ListDockWidget(MainWindow *main, QAction *action, SearchBarPolicy searchBarPolicy) : + CutterDockWidget(main, action), + ui(new Ui::ListDockWidget), + tree(new CutterTreeWidget(this)), + searchBarPolicy(searchBarPolicy) +{ + ui->setupUi(this); + + // Add Status Bar footer + tree->addStatusBar(ui->verticalLayout); + + if (searchBarPolicy != SearchBarPolicy::Hide) { + // Ctrl-F to show/hide the filter entry + QShortcut *searchShortcut = new QShortcut(QKeySequence::Find, this); + connect(searchShortcut, &QShortcut::activated, ui->quickFilterView, &QuickFilterView::showFilter); + searchShortcut->setContext(Qt::WidgetWithChildrenShortcut); + + // Esc to clear the filter entry + QShortcut *clearShortcut = new QShortcut(QKeySequence(Qt::Key_Escape), this); + connect(clearShortcut, &QShortcut::activated, [this]() { + ui->quickFilterView->clearFilter(); + ui->treeView->setFocus(); + }); + 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); + + if (searchBarPolicy != SearchBarPolicy::ShowByDefault) { + ui->quickFilterView->closeFilter(); + } +} + +ListDockWidget::~ListDockWidget() {} + +void ListDockWidget::showCount(bool show) +{ + tree->showStatusBar(show); +} + +void ListDockWidget::setModels(AddressableFilterProxyModel *objectFilterProxyModel) +{ + this->objectFilterProxyModel = objectFilterProxyModel; + + ui->treeView->setModel(objectFilterProxyModel); + + + connect(ui->quickFilterView, &QuickFilterView::filterTextChanged, + objectFilterProxyModel, &QSortFilterProxyModel::setFilterWildcard); + connect(ui->quickFilterView, &QuickFilterView::filterClosed, ui->treeView, + static_cast(&QWidget::setFocus)); + + + 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 new file mode 100644 index 0000000000000..9b0f4d66b5bf0 --- /dev/null +++ b/src/widgets/ListDockWidget.h @@ -0,0 +1,57 @@ +#ifndef LISTDOCKWIDGET_H +#define LISTDOCKWIDGET_H + +#include +#include +#include +#include + +#include "core/Cutter.h" +#include "common/AddressableItemModel.h" +#include "CutterDockWidget.h" +#include "CutterTreeWidget.h" +#include "menus/AddressableItemContextMenu.h" + +class MainWindow; +class QTreeWidgetItem; +class CommentsWidget; + +namespace Ui { +class ListDockWidget; +} + + +class ListDockWidget : public CutterDockWidget +{ + Q_OBJECT + +public: + enum class SearchBarPolicy { + ShowByDefault, + HideByDefault, + Hide, + }; + + explicit ListDockWidget(MainWindow *main, QAction *action = nullptr, SearchBarPolicy searchBarPolicy = SearchBarPolicy::ShowByDefault); + ~ListDockWidget() override; + + void showCount(bool show); +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; +}; + +#endif // LISTDOCKWIDGET_H diff --git a/src/widgets/RelocsWidget.ui b/src/widgets/ListDockWidget.ui similarity index 81% rename from src/widgets/RelocsWidget.ui rename to src/widgets/ListDockWidget.ui index 29f9772a4df2d..d351183bda3a4 100644 --- a/src/widgets/RelocsWidget.ui +++ b/src/widgets/ListDockWidget.ui @@ -1,20 +1,20 @@ - RelocsWidget - + ListDockWidget + 0 0 - 400 - 300 + 645 + 250 - - Relocs - + + 0 + 0 @@ -28,7 +28,7 @@ 0 - + CutterTreeView::item { @@ -45,6 +45,9 @@ true + + true + diff --git a/src/widgets/RelocsWidget.cpp b/src/widgets/RelocsWidget.cpp index 6711879426184..9d6a7be9f7a37 100644 --- a/src/widgets/RelocsWidget.cpp +++ b/src/widgets/RelocsWidget.cpp @@ -1,5 +1,5 @@ #include "RelocsWidget.h" -#include "ui_RelocsWidget.h" +#include "ui_ListDockWidget.h" #include "core/MainWindow.h" #include "common/Helpers.h" @@ -7,7 +7,7 @@ #include RelocsModel::RelocsModel(QList *relocs, QObject *parent) : - QAbstractTableModel(parent), + AddressableItemModel(parent), relocs(relocs) {} @@ -61,10 +61,21 @@ QVariant RelocsModel::headerData(int section, Qt::Orientation, int role) const return QVariant(); } +RVA RelocsModel::address(const QModelIndex &index) const +{ + const RelocDescription &reloc = relocs->at(index.row()); + return reloc.vaddr; +} + +QString RelocsModel::name(const QModelIndex &index) const +{ + const RelocDescription &reloc = relocs->at(index.row()); + return reloc.name; +} + RelocsProxyModel::RelocsProxyModel(RelocsModel *sourceModel, QObject *parent) - : QSortFilterProxyModel(parent) + : AddressableFilterProxyModel(sourceModel, parent) { - setSourceModel(sourceModel); setFilterCaseSensitivity(Qt::CaseInsensitive); setSortCaseSensitivity(Qt::CaseInsensitive); } @@ -103,64 +114,25 @@ bool RelocsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &righ } RelocsWidget::RelocsWidget(MainWindow *main, QAction *action) : - CutterDockWidget(main, action), - ui(new Ui::RelocsWidget), + ListDockWidget(main, action), relocsModel(new RelocsModel(&relocs, this)), - relocsProxyModel(new RelocsProxyModel(relocsModel, this)), - tree(new CutterTreeWidget(this)) + relocsProxyModel(new RelocsProxyModel(relocsModel, this)) { - ui->setupUi(this); - - // Add Status Bar footer - tree->addStatusBar(ui->verticalLayout); - - ui->relocsTreeView->setModel(relocsProxyModel); - ui->relocsTreeView->sortByColumn(RelocsModel::NameColumn, Qt::AscendingOrder); - - // Ctrl-F to show/hide the filter entry - QShortcut *searchShortcut = new QShortcut(QKeySequence::Find, this); - connect(searchShortcut, &QShortcut::activated, ui->quickFilterView, &QuickFilterView::showFilter); - searchShortcut->setContext(Qt::WidgetWithChildrenShortcut); + setWindowTitle(tr("Relocs")); + setObjectName("RelocsWidget"); - // Esc to clear the filter entry - QShortcut *clearShortcut = new QShortcut(QKeySequence(Qt::Key_Escape), this); - connect(clearShortcut, &QShortcut::activated, ui->quickFilterView, &QuickFilterView::clearFilter); - clearShortcut->setContext(Qt::WidgetWithChildrenShortcut); + setModels(relocsProxyModel); + ui->treeView->sortByColumn(RelocsModel::NameColumn, Qt::AscendingOrder); - connect(ui->quickFilterView, SIGNAL(filterTextChanged(const QString &)), - relocsProxyModel, SLOT(setFilterWildcard(const QString &))); - connect(ui->quickFilterView, SIGNAL(filterClosed()), ui->relocsTreeView, SLOT(setFocus())); - - connect(ui->quickFilterView, &QuickFilterView::filterTextChanged, this, [this] { - tree->showItemsNumber(relocsProxyModel->rowCount()); - }); - - setScrollMode(); - - connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshRelocs())); + connect(Core(), &CutterCore::refreshAll, this, &RelocsWidget::refreshRelocs); } RelocsWidget::~RelocsWidget() {} -void RelocsWidget::on_relocsTreeView_doubleClicked(const QModelIndex &index) -{ - if (!index.isValid()) - return; - - Core()->seekAndShow(index.data(RelocsModel::AddressRole).toLongLong()); -} - void RelocsWidget::refreshRelocs() { relocsModel->beginResetModel(); relocs = Core()->getAllRelocs(); relocsModel->endResetModel(); - qhelpers::adjustColumns(ui->relocsTreeView, 3, 0); - - tree->showItemsNumber(relocsProxyModel->rowCount()); -} - -void RelocsWidget::setScrollMode() -{ - qhelpers::setVerticalScrollMode(ui->relocsTreeView); + qhelpers::adjustColumns(ui->treeView, 3, 0); } diff --git a/src/widgets/RelocsWidget.h b/src/widgets/RelocsWidget.h index dc90e60d665d5..663c8c2be200d 100644 --- a/src/widgets/RelocsWidget.h +++ b/src/widgets/RelocsWidget.h @@ -7,16 +7,12 @@ #include "CutterDockWidget.h" #include "core/Cutter.h" -#include "CutterTreeWidget.h" +#include "widgets/ListDockWidget.h" class MainWindow; class RelocsWidget; -namespace Ui { -class RelocsWidget; -} - -class RelocsModel : public QAbstractTableModel +class RelocsModel : public AddressableItemModel { Q_OBJECT @@ -31,14 +27,17 @@ class RelocsModel : public QAbstractTableModel RelocsModel(QList *relocs, QObject *parent = nullptr); - int rowCount(const QModelIndex &parent) const; - int columnCount(const QModelIndex &parent) const; + int rowCount(const QModelIndex &parent) const override; + int columnCount(const QModelIndex &parent) const override; + + QVariant data(const QModelIndex &index, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; - QVariant data(const QModelIndex &index, int role) const; - QVariant headerData(int section, Qt::Orientation orientation, int role) const; + RVA address(const QModelIndex &index) const override; + QString name(const QModelIndex &index) const override; }; -class RelocsProxyModel : public QSortFilterProxyModel +class RelocsProxyModel : public AddressableFilterProxyModel { Q_OBJECT @@ -50,7 +49,7 @@ class RelocsProxyModel : public QSortFilterProxyModel bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; }; -class RelocsWidget : public CutterDockWidget +class RelocsWidget : public ListDockWidget { Q_OBJECT @@ -59,18 +58,12 @@ class RelocsWidget : public CutterDockWidget ~RelocsWidget(); private slots: - void on_relocsTreeView_doubleClicked(const QModelIndex &index); void refreshRelocs(); private: - std::unique_ptr ui; - RelocsModel *relocsModel; RelocsProxyModel *relocsProxyModel; QList relocs; - CutterTreeWidget *tree; - - void setScrollMode(); }; #endif // RELOCSWIDGET_H diff --git a/src/widgets/SectionsWidget.cpp b/src/widgets/SectionsWidget.cpp index f825b7403a054..574511f0f0eb8 100644 --- a/src/widgets/SectionsWidget.cpp +++ b/src/widgets/SectionsWidget.cpp @@ -1,9 +1,9 @@ #include "SectionsWidget.h" -#include "CutterTreeView.h" #include "QuickFilterView.h" #include "core/MainWindow.h" #include "common/Helpers.h" #include "common/Configuration.h" +#include "ui_ListDockWidget.h" #include #include @@ -14,7 +14,7 @@ #include SectionsModel::SectionsModel(QList *sections, QObject *parent) - : QAbstractListModel(parent), + : AddressableItemModel(parent), sections(sections) { } @@ -105,10 +105,21 @@ QVariant SectionsModel::headerData(int section, Qt::Orientation, int role) const } } +RVA SectionsModel::address(const QModelIndex &index) const +{ + const SectionDescription §ion = sections->at(index.row()); + return section.vaddr; +} + +QString SectionsModel::name(const QModelIndex &index) const +{ + const SectionDescription §ion = sections->at(index.row()); + return section.name; +} + SectionsProxyModel::SectionsProxyModel(SectionsModel *sourceModel, QObject *parent) - : QSortFilterProxyModel(parent) + : AddressableFilterProxyModel(sourceModel, parent) { - setSourceModel(sourceModel); setFilterCaseSensitivity(Qt::CaseInsensitive); setSortCaseSensitivity(Qt::CaseInsensitive); } @@ -138,8 +149,7 @@ bool SectionsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &ri } SectionsWidget::SectionsWidget(MainWindow *main, QAction *action) : - CutterDockWidget(main, action), - main(main) + ListDockWidget(main, action) { setObjectName("SectionsWidget"); setWindowTitle(QStringLiteral("Sections")); @@ -158,42 +168,22 @@ SectionsWidget::~SectionsWidget() = default; void SectionsWidget::initSectionsTable() { - sectionsTable = new CutterTreeView; sectionsModel = new SectionsModel(§ions, this); proxyModel = new SectionsProxyModel(sectionsModel, this); + setModels(proxyModel); - sectionsTable->setModel(proxyModel); - sectionsTable->setIndentation(10); - sectionsTable->setSortingEnabled(true); - sectionsTable->sortByColumn(SectionsModel::NameColumn, Qt::AscendingOrder); + ui->treeView->sortByColumn(SectionsModel::NameColumn, Qt::AscendingOrder); } void SectionsWidget::initQuickFilter() { - quickFilterView = new QuickFilterView(this, false); - quickFilterView->setObjectName(QStringLiteral("quickFilterView")); - QSizePolicy sizePolicy1(QSizePolicy::Preferred, QSizePolicy::Maximum); - sizePolicy1.setHorizontalStretch(0); - sizePolicy1.setVerticalStretch(0); - sizePolicy1.setHeightForWidth(quickFilterView->sizePolicy().hasHeightForWidth()); - quickFilterView->setSizePolicy(sizePolicy1); - - QShortcut *search_shortcut = new QShortcut(QKeySequence::Find, this); - search_shortcut->setContext(Qt::WidgetWithChildrenShortcut); - connect(search_shortcut, &QShortcut::activated, quickFilterView, &QuickFilterView::showFilter); - - QShortcut *clear_shortcut = new QShortcut(QKeySequence(Qt::Key_Escape), this); - clear_shortcut->setContext(Qt::WidgetWithChildrenShortcut); - connect(clear_shortcut, &QShortcut::activated, quickFilterView, &QuickFilterView::clearFilter); + ui->quickFilterView->closeFilter(); } void SectionsWidget::initAddrMapDocks() { - dockWidgetContents = new QWidget(this); - QVBoxLayout *layout = new QVBoxLayout(); - - layout->addWidget(sectionsTable); - layout->addWidget(quickFilterView); + QVBoxLayout *layout = ui->verticalLayout; + showCount(false); rawAddrDock = new RawAddrDock(sectionsModel, this); virtualAddrDock = new VirtualAddrDock(sectionsModel, this); @@ -220,20 +210,11 @@ void SectionsWidget::initAddrMapDocks() toggleButton->setArrowType(Qt::NoArrow); toggleButton->hide(); layout->addWidget(toggleButton); - - layout->setMargin(0); - dockWidgetContents->setLayout(layout); - setWidget(dockWidgetContents); } void SectionsWidget::initConnects() { - connect(sectionsTable, SIGNAL(doubleClicked(const QModelIndex &)), - this, SLOT(onSectionsDoubleClicked(const QModelIndex &))); - connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshSections())); - connect(quickFilterView, SIGNAL(filterTextChanged(const QString &)), proxyModel, - SLOT(setFilterWildcard(const QString &))); - connect(quickFilterView, SIGNAL(filterClosed()), sectionsTable, SLOT(setFocus())); + connect(Core(), &CutterCore::refreshAll, this, &SectionsWidget::refreshSections); connect(this, &QDockWidget::visibilityChanged, this, [ = ](bool visibility) { if (visibility) { refreshSections(); @@ -261,7 +242,7 @@ void SectionsWidget::refreshSections() sectionsModel->beginResetModel(); sections = Core()->getAllSections(); sectionsModel->endResetModel(); - qhelpers::adjustColumns(sectionsTable, SectionsModel::ColumnCount, 0); + qhelpers::adjustColumns(ui->treeView, SectionsModel::ColumnCount, 0); refreshDocks(); } @@ -296,16 +277,6 @@ void SectionsWidget::drawIndicatorOnAddrDocks() } } -void SectionsWidget::onSectionsDoubleClicked(const QModelIndex &index) -{ - if (!index.isValid()) { - return; - } - - auto section = index.data(SectionsModel::SectionDescriptionRole).value(); - Core()->seekAndShow(section.vaddr); -} - void SectionsWidget::resizeEvent(QResizeEvent *event) { CutterDockWidget::resizeEvent(event); refreshDocks(); diff --git a/src/widgets/SectionsWidget.h b/src/widgets/SectionsWidget.h index 4ced8a00cf27d..9e055a9355238 100644 --- a/src/widgets/SectionsWidget.h +++ b/src/widgets/SectionsWidget.h @@ -13,10 +13,9 @@ #include "core/Cutter.h" #include "CutterDockWidget.h" +#include "widgets/ListDockWidget.h" -class CutterTreeView; class QAbstractItemView; -class MainWindow; class SectionsWidget; class AbstractAddrDock; class AddrDockScene; @@ -27,7 +26,7 @@ class QuickFilterView; class QGraphicsView; class QGraphicsRectItem; -class SectionsModel : public QAbstractListModel +class SectionsModel : public AddressableItemModel { Q_OBJECT @@ -47,9 +46,12 @@ class SectionsModel : public QAbstractListModel QVariant data(const QModelIndex &index, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + + RVA address(const QModelIndex &index) const override; + QString name(const QModelIndex &index) const override; }; -class SectionsProxyModel : public QSortFilterProxyModel +class SectionsProxyModel : public AddressableFilterProxyModel { Q_OBJECT @@ -60,7 +62,7 @@ class SectionsProxyModel : public QSortFilterProxyModel bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; }; -class SectionsWidget : public CutterDockWidget +class SectionsWidget : public ListDockWidget { Q_OBJECT @@ -71,9 +73,6 @@ class SectionsWidget : public CutterDockWidget private slots: void refreshSections(); void refreshDocks(); - - void onSectionsDoubleClicked(const QModelIndex &index); - protected: void resizeEvent(QResizeEvent *event) override; @@ -81,10 +80,6 @@ private slots: QList sections; SectionsModel *sectionsModel; SectionsProxyModel *proxyModel; - CutterTreeView *sectionsTable; - MainWindow *main; - QWidget *dockWidgetContents; - QuickFilterView *quickFilterView; QWidget *addrDockWidget; RawAddrDock *rawAddrDock; diff --git a/src/widgets/SegmentsWidget.cpp b/src/widgets/SegmentsWidget.cpp index 5f24316ad21f3..8516dbb318899 100644 --- a/src/widgets/SegmentsWidget.cpp +++ b/src/widgets/SegmentsWidget.cpp @@ -1,14 +1,13 @@ #include "SegmentsWidget.h" -#include "CutterTreeView.h" #include "core/MainWindow.h" -#include "QuickFilterView.h" #include "common/Helpers.h" +#include "ui_ListDockWidget.h" #include #include SegmentsModel::SegmentsModel(QList *segments, QObject *parent) - : QAbstractListModel(parent), + : AddressableItemModel(parent), segments(segments) { } @@ -50,7 +49,7 @@ QVariant SegmentsModel::data(const QModelIndex &index, int role) const case SegmentsModel::NameColumn: return segment.name; case SegmentsModel::SizeColumn: - return segment.size; + return QString::number(segment.size); case SegmentsModel::AddressColumn: return RAddressString(segment.vaddr); case SegmentsModel::EndAddressColumn: @@ -94,10 +93,21 @@ QVariant SegmentsModel::headerData(int segment, Qt::Orientation, int role) const } } +RVA SegmentsModel::address(const QModelIndex &index) const +{ + const SegmentDescription &segment = segments->at(index.row()); + return segment.vaddr; +} + +QString SegmentsModel::name(const QModelIndex &index) const +{ + const SegmentDescription &segment = segments->at(index.row()); + return segment.name; +} + SegmentsProxyModel::SegmentsProxyModel(SegmentsModel *sourceModel, QObject *parent) - : QSortFilterProxyModel(parent) + : AddressableFilterProxyModel(sourceModel, parent) { - setSourceModel(sourceModel); setFilterCaseSensitivity(Qt::CaseInsensitive); setSortCaseSensitivity(Qt::CaseInsensitive); } @@ -121,54 +131,21 @@ bool SegmentsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &ri } SegmentsWidget::SegmentsWidget(MainWindow *main, QAction *action) : - CutterDockWidget(main, action), - main(main) + ListDockWidget(main, action) { - setObjectName("SegmentsWidget"); setWindowTitle(QStringLiteral("Segments")); - segmentsTable = new CutterTreeView; segmentsModel = new SegmentsModel(&segments, this); auto proxyModel = new SegmentsProxyModel(segmentsModel, this); + setModels(proxyModel); - segmentsTable->setModel(proxyModel); - segmentsTable->setIndentation(10); - segmentsTable->setSortingEnabled(true); - segmentsTable->sortByColumn(SegmentsModel::NameColumn, Qt::AscendingOrder); + ui->treeView->sortByColumn(SegmentsModel::NameColumn, Qt::AscendingOrder); - connect(segmentsTable, SIGNAL(doubleClicked(const QModelIndex &)), - this, SLOT(onSegmentsDoubleClicked(const QModelIndex &))); - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); - connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshSegments())); + ui->quickFilterView->closeFilter(); + showCount(false); - quickFilterView = new QuickFilterView(this, false); - quickFilterView->setObjectName(QStringLiteral("quickFilterView")); - QSizePolicy sizePolicy1(QSizePolicy::Preferred, QSizePolicy::Maximum); - sizePolicy1.setHorizontalStretch(0); - sizePolicy1.setVerticalStretch(0); - sizePolicy1.setHeightForWidth(quickFilterView->sizePolicy().hasHeightForWidth()); - quickFilterView->setSizePolicy(sizePolicy1); - - QShortcut *search_shortcut = new QShortcut(QKeySequence::Find, this); - connect(search_shortcut, &QShortcut::activated, quickFilterView, &QuickFilterView::showFilter); - search_shortcut->setContext(Qt::WidgetWithChildrenShortcut); - - QShortcut *clear_shortcut = new QShortcut(QKeySequence(Qt::Key_Escape), this); - connect(clear_shortcut, &QShortcut::activated, quickFilterView, &QuickFilterView::clearFilter); - clear_shortcut->setContext(Qt::WidgetWithChildrenShortcut); - - connect(quickFilterView, SIGNAL(filterTextChanged(const QString &)), proxyModel, - SLOT(setFilterWildcard(const QString &))); - connect(quickFilterView, SIGNAL(filterClosed()), segmentsTable, SLOT(setFocus())); - - dockWidgetContents = new QWidget(this); - QVBoxLayout *layout = new QVBoxLayout(); - layout->addWidget(segmentsTable); - layout->addWidget(quickFilterView); - layout->setMargin(0); - dockWidgetContents->setLayout(layout); - setWidget(dockWidgetContents); + connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshSegments())); } SegmentsWidget::~SegmentsWidget() {} @@ -179,14 +156,5 @@ void SegmentsWidget::refreshSegments() segments = Core()->getAllSegments(); segmentsModel->endResetModel(); - qhelpers::adjustColumns(segmentsTable, SegmentsModel::ColumnCount, 0); -} - -void SegmentsWidget::onSegmentsDoubleClicked(const QModelIndex &index) -{ - if (!index.isValid()) - return; - - auto segment = index.data(SegmentsModel::SegmentDescriptionRole).value(); - Core()->seekAndShow(segment.vaddr); + qhelpers::adjustColumns(ui->treeView, SegmentsModel::ColumnCount, 0); } diff --git a/src/widgets/SegmentsWidget.h b/src/widgets/SegmentsWidget.h index 7238cb38fdbb0..27bdaa4508d8b 100644 --- a/src/widgets/SegmentsWidget.h +++ b/src/widgets/SegmentsWidget.h @@ -7,16 +7,12 @@ #include #include "core/Cutter.h" -#include "CutterTreeView.h" -#include "CutterDockWidget.h" +#include "widgets/ListDockWidget.h" -class CutterTreeView; class QAbstractItemView; -class MainWindow; class SegmentsWidget; -class QuickFilterView; -class SegmentsModel : public QAbstractListModel +class SegmentsModel : public AddressableItemModel { Q_OBJECT @@ -31,14 +27,17 @@ class SegmentsModel : public QAbstractListModel SegmentsModel(QList *segments, 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 segment, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + QVariant data(const QModelIndex &index, int role) const override; + QVariant headerData(int segment, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + + RVA address(const QModelIndex &index) const override; + QString name(const QModelIndex &index) const override; }; -class SegmentsProxyModel : public QSortFilterProxyModel +class SegmentsProxyModel : public AddressableFilterProxyModel { Q_OBJECT @@ -49,7 +48,7 @@ class SegmentsProxyModel : public QSortFilterProxyModel bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; }; -class SegmentsWidget : public CutterDockWidget +class SegmentsWidget : public ListDockWidget { Q_OBJECT @@ -59,15 +58,9 @@ class SegmentsWidget : public CutterDockWidget private slots: void refreshSegments(); - void onSegmentsDoubleClicked(const QModelIndex &index); - private: QList segments; SegmentsModel *segmentsModel; - CutterTreeView *segmentsTable; - MainWindow *main; - QWidget *dockWidgetContents; - QuickFilterView *quickFilterView; }; #endif // SEGMENTSWIDGET_H diff --git a/src/widgets/SymbolsWidget.cpp b/src/widgets/SymbolsWidget.cpp index 1d85c9402b700..eff511a1f748f 100644 --- a/src/widgets/SymbolsWidget.cpp +++ b/src/widgets/SymbolsWidget.cpp @@ -1,12 +1,12 @@ #include "SymbolsWidget.h" -#include "ui_SymbolsWidget.h" +#include "ui_ListDockWidget.h" #include "core/MainWindow.h" #include "common/Helpers.h" #include SymbolsModel::SymbolsModel(QList *symbols, QObject *parent) - : QAbstractListModel(parent), + : AddressableItemModel(parent), symbols(symbols) { } @@ -67,10 +67,21 @@ QVariant SymbolsModel::headerData(int section, Qt::Orientation, int role) const } } +RVA SymbolsModel::address(const QModelIndex &index) const +{ + const SymbolDescription &symbol = symbols->at(index.row()); + return symbol.vaddr; +} + +QString SymbolsModel::name(const QModelIndex &index) const +{ + const SymbolDescription &symbol = symbols->at(index.row()); + return symbol.name; +} + SymbolsProxyModel::SymbolsProxyModel(SymbolsModel *sourceModel, QObject *parent) - : QSortFilterProxyModel(parent) + : AddressableFilterProxyModel(sourceModel, parent) { - setSourceModel(sourceModel); setFilterCaseSensitivity(Qt::CaseInsensitive); setSortCaseSensitivity(Qt::CaseInsensitive); } @@ -103,68 +114,26 @@ bool SymbolsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &rig } SymbolsWidget::SymbolsWidget(MainWindow *main, QAction *action) : - CutterDockWidget(main, action), - ui(new Ui::SymbolsWidget), - tree(new CutterTreeWidget(this)) + ListDockWidget(main, action) { - ui->setupUi(this); - - // Add Status Bar footer - tree->addStatusBar(ui->verticalLayout); + setWindowTitle(tr("Symbols")); + setObjectName("SymbolsWidget"); symbolsModel = new SymbolsModel(&symbols, this); symbolsProxyModel = new SymbolsProxyModel(symbolsModel, this); - ui->symbolsTreeView->setModel(symbolsProxyModel); - ui->symbolsTreeView->sortByColumn(SymbolsModel::AddressColumn, Qt::AscendingOrder); - - // Ctrl-F to show/hide the filter entry - QShortcut *searchShortcut = new QShortcut(QKeySequence::Find, this); - connect(searchShortcut, &QShortcut::activated, ui->quickFilterView, &QuickFilterView::showFilter); - searchShortcut->setContext(Qt::WidgetWithChildrenShortcut); - - // Esc to clear the filter entry - QShortcut *clearShortcut = new QShortcut(QKeySequence(Qt::Key_Escape), this); - connect(clearShortcut, &QShortcut::activated, ui->quickFilterView, &QuickFilterView::clearFilter); - clearShortcut->setContext(Qt::WidgetWithChildrenShortcut); - - connect(ui->quickFilterView, SIGNAL(filterTextChanged(const QString &)), - symbolsProxyModel, SLOT(setFilterWildcard(const QString &))); - connect(ui->quickFilterView, SIGNAL(filterClosed()), ui->symbolsTreeView, SLOT(setFocus())); - - connect(ui->quickFilterView, &QuickFilterView::filterTextChanged, this, [this] { - tree->showItemsNumber(symbolsProxyModel->rowCount()); - }); - - setScrollMode(); - - connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshSymbols())); + setModels(symbolsProxyModel); + ui->treeView->sortByColumn(SymbolsModel::AddressColumn, Qt::AscendingOrder); + + connect(Core(), &CutterCore::refreshAll, this, &SymbolsWidget::refreshSymbols); } SymbolsWidget::~SymbolsWidget() {} - -void SymbolsWidget::on_symbolsTreeView_doubleClicked(const QModelIndex &index) -{ - if (!index.isValid()) { - return; - } - - auto symbol = index.data(SymbolsModel::SymbolDescriptionRole).value(); - Core()->seekAndShow(symbol.vaddr); -} - void SymbolsWidget::refreshSymbols() { symbolsModel->beginResetModel(); symbols = Core()->getAllSymbols(); symbolsModel->endResetModel(); - qhelpers::adjustColumns(ui->symbolsTreeView, SymbolsModel::ColumnCount, 0); - - tree->showItemsNumber(symbolsProxyModel->rowCount()); -} - -void SymbolsWidget::setScrollMode() -{ - qhelpers::setVerticalScrollMode(ui->symbolsTreeView); + qhelpers::adjustColumns(ui->treeView, SymbolsModel::ColumnCount, 0); } diff --git a/src/widgets/SymbolsWidget.h b/src/widgets/SymbolsWidget.h index 8efb8585617c0..1af35ebc15451 100644 --- a/src/widgets/SymbolsWidget.h +++ b/src/widgets/SymbolsWidget.h @@ -7,17 +7,15 @@ #include "core/Cutter.h" #include "CutterDockWidget.h" -#include "CutterTreeWidget.h" +#include "widgets/ListDockWidget.h" + class MainWindow; class QTreeWidgetItem; class SymbolsWidget; -namespace Ui { -class SymbolsWidget; -} -class SymbolsModel: public QAbstractListModel +class SymbolsModel: public AddressableItemModel { Q_OBJECT @@ -32,14 +30,17 @@ class SymbolsModel: public QAbstractListModel SymbolsModel(QList *exports, 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 override; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; - QVariant data(const QModelIndex &index, int role) const; - QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + RVA address(const QModelIndex &index) const override; + QString name(const QModelIndex &index) const override; }; -class SymbolsProxyModel : public QSortFilterProxyModel +class SymbolsProxyModel : public AddressableFilterProxyModel { Q_OBJECT @@ -52,7 +53,7 @@ class SymbolsProxyModel : public QSortFilterProxyModel }; -class SymbolsWidget : public CutterDockWidget +class SymbolsWidget : public ListDockWidget { Q_OBJECT @@ -61,19 +62,12 @@ class SymbolsWidget : public CutterDockWidget ~SymbolsWidget(); private slots: - void on_symbolsTreeView_doubleClicked(const QModelIndex &index); - void refreshSymbols(); private: - std::unique_ptr ui; - QList symbols; SymbolsModel *symbolsModel; SymbolsProxyModel *symbolsProxyModel; - CutterTreeWidget *tree; - - void setScrollMode(); }; #endif // SYMBOLSWIDGET_H diff --git a/src/widgets/SymbolsWidget.ui b/src/widgets/SymbolsWidget.ui deleted file mode 100644 index 1ee5cafe2d857..0000000000000 --- a/src/widgets/SymbolsWidget.ui +++ /dev/null @@ -1,62 +0,0 @@ - - - SymbolsWidget - - - - 0 - 0 - 400 - 300 - - - - Symbols - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - true - - - true - - - - - - - - - - - - CutterTreeView - QTreeView -
widgets/CutterTreeView.h
- 1 -
- - QuickFilterView - QWidget -
widgets/QuickFilterView.h
- 1 -
-
- - -
diff --git a/src/widgets/VTablesWidget.cpp b/src/widgets/VTablesWidget.cpp index 9cde2777078bd..4beb31879698a 100644 --- a/src/widgets/VTablesWidget.cpp +++ b/src/widgets/VTablesWidget.cpp @@ -146,6 +146,7 @@ VTablesWidget::VTablesWidget(MainWindow *main, QAction *action) : // Esc to clear the filter entry QShortcut *clear_shortcut = new QShortcut(QKeySequence(Qt::Key_Escape), this); connect(clear_shortcut, &QShortcut::activated, ui->quickFilterView, &QuickFilterView::clearFilter); + clear_shortcut->setContext(Qt::WidgetWithChildrenShortcut); // Ctrl-F to show/hide the filter entry QShortcut *search_shortcut = new QShortcut(QKeySequence::Find, this);