Common behaviour for lists with items that have address (#1700)

This commit is contained in:
karliss 2019-08-19 16:35:25 +03:00 committed by xarkes
parent cd2dbc4a29
commit 0aa91c328c
37 changed files with 906 additions and 1290 deletions

View File

@ -360,7 +360,10 @@ SOURCES += \
widgets/GraphGridLayout.cpp \ widgets/GraphGridLayout.cpp \
widgets/HexWidget.cpp \ widgets/HexWidget.cpp \
common/SelectionHighlight.cpp \ common/SelectionHighlight.cpp \
common/Decompiler.cpp common/Decompiler.cpp \
menus/AddressableItemContextMenu.cpp \
common/AddressableItemModel.cpp \
widgets/ListDockWidget.cpp
GRAPHVIZ_SOURCES = \ GRAPHVIZ_SOURCES = \
widgets/GraphvizLayout.cpp widgets/GraphvizLayout.cpp
@ -490,7 +493,10 @@ HEADERS += \
widgets/GraphLayout.h \ widgets/GraphLayout.h \
widgets/HexWidget.h \ widgets/HexWidget.h \
common/SelectionHighlight.h \ common/SelectionHighlight.h \
common/Decompiler.h common/Decompiler.h \
menus/AddressableItemContextMenu.h \
common/AddressableItemModel.h \
widgets/ListDockWidget.h
GRAPHVIZ_HEADERS = widgets/GraphGridLayout.h GRAPHVIZ_HEADERS = widgets/GraphGridLayout.h
@ -506,17 +512,11 @@ FORMS += \
dialogs/InitialOptionsDialog.ui \ dialogs/InitialOptionsDialog.ui \
dialogs/EditFunctionDialog.ui \ dialogs/EditFunctionDialog.ui \
core/MainWindow.ui \ core/MainWindow.ui \
widgets/CommentsWidget.ui \
widgets/ConsoleWidget.ui \ widgets/ConsoleWidget.ui \
widgets/Dashboard.ui \ widgets/Dashboard.ui \
widgets/EntrypointWidget.ui \ widgets/EntrypointWidget.ui \
widgets/FlagsWidget.ui \ widgets/FlagsWidget.ui \
widgets/ExportsWidget.ui \
widgets/FunctionsWidget.ui \
widgets/ImportsWidget.ui \
widgets/RelocsWidget.ui \
widgets/StringsWidget.ui \ widgets/StringsWidget.ui \
widgets/SymbolsWidget.ui \
widgets/HexdumpWidget.ui \ widgets/HexdumpWidget.ui \
dialogs/SaveProjectDialog.ui \ dialogs/SaveProjectDialog.ui \
dialogs/preferences/PreferencesDialog.ui \ dialogs/preferences/PreferencesDialog.ui \
@ -527,7 +527,6 @@ FORMS += \
widgets/ClassesWidget.ui \ widgets/ClassesWidget.ui \
widgets/VTablesWidget.ui \ widgets/VTablesWidget.ui \
widgets/TypesWidget.ui \ widgets/TypesWidget.ui \
widgets/HeadersWidget.ui \
widgets/SearchWidget.ui \ widgets/SearchWidget.ui \
dialogs/R2PluginsDialog.ui \ dialogs/R2PluginsDialog.ui \
dialogs/VersionInfoDialog.ui \ dialogs/VersionInfoDialog.ui \
@ -554,7 +553,8 @@ FORMS += \
widgets/SdbWidget.ui \ widgets/SdbWidget.ui \
dialogs/LinkTypeDialog.ui \ dialogs/LinkTypeDialog.ui \
widgets/ColorPicker.ui \ widgets/ColorPicker.ui \
dialogs/preferences/ColorThemeEditDialog.ui dialogs/preferences/ColorThemeEditDialog.ui \
widgets/ListDockWidget.ui
RESOURCES += \ RESOURCES += \
resources.qrc \ resources.qrc \

View File

@ -0,0 +1,30 @@
#include "AddressableItemModel.h"
AddressableFilterProxyModel::AddressableFilterProxyModel(AddressableItemModelI *sourceModel,
QObject *parent) :
AddressableItemModel<QSortFilterProxyModel>(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;
}

View File

@ -0,0 +1,49 @@
#ifndef ADDRESSABLEITEMMODEL_H
#define ADDRESSABLEITEMMODEL_H
#include <QAbstractItemModel>
#include <QSortFilterProxyModel>
#include <QAbstractItemModel>
#include <core/CutterCommon.h>
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 ParentModel = QAbstractItemModel>
class AddressableItemModel : public ParentModel, public AddressableItemModelI
{
static_assert (std::is_base_of<QAbstractItemModel, ParentModel>::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<QSortFilterProxyModel>
{
using ParentClass = AddressableItemModel<QSortFilterProxyModel>;
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

View File

@ -2597,6 +2597,17 @@ void CutterCore::addFlag(RVA offset, QString name, RVA size)
emit flagsChanged(); 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<RVA>(-queryOffset);
}
return name;
}
void CutterCore::handleREvent(int type, void *data) void CutterCore::handleREvent(int type, void *data)
{ {
switch (type) { switch (type) {

View File

@ -91,6 +91,13 @@ public:
void delFlag(RVA addr); void delFlag(RVA addr);
void delFlag(const QString &name); void delFlag(const QString &name);
void addFlag(RVA offset, QString name, RVA size); 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(); void triggerFlagsChanged();
/* Edition functions */ /* Edition functions */

View File

@ -0,0 +1,103 @@
#include "AddressableItemContextMenu.h"
#include "dialogs/XrefsDialog.h"
#include "MainWindow.h"
#include "dialogs/CommentsDialog.h"
#include <QtCore>
#include <QShortcut>
#include <QJsonArray>
#include <QClipboard>
#include <QApplication>
#include <QPushButton>
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));
}

View File

@ -0,0 +1,43 @@
#ifndef ADDRESSABLEITEMCONTEXTMENU_H
#define ADDRESSABLEITEMCONTEXTMENU_H
#include "core/Cutter.h"
#include <QMenu>
#include <QKeySequence>
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

View File

@ -1,5 +1,5 @@
#include "CommentsWidget.h" #include "CommentsWidget.h"
#include "ui_CommentsWidget.h" #include "ui_ListDockWidget.h"
#include "core/MainWindow.h" #include "core/MainWindow.h"
#include "common/Helpers.h" #include "common/Helpers.h"
@ -8,9 +8,9 @@
#include <QShortcut> #include <QShortcut>
CommentsModel::CommentsModel(QList<CommentDescription> *comments, CommentsModel::CommentsModel(QList<CommentDescription> *comments,
QMap<QString, QList<CommentDescription> > *nestedComments, QList<CommentGroup> *nestedComments,
QObject *parent) QObject *parent)
: QAbstractItemModel(parent), : AddressableItemModel<>(parent),
comments(comments), comments(comments),
nestedComments(nestedComments), nestedComments(nestedComments),
nested(false) nested(false)
@ -28,6 +28,20 @@ void CommentsModel::setNested(bool nested)
endResetModel(); 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 QModelIndex CommentsModel::index(int row, int column, const QModelIndex &parent) const
{ {
if (!parent.isValid()) { 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)); 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 */ /* Ignore invalid indexes and root nodes */
if (!index.isValid() || index.internalId() == 0) { if (!index.isValid() || index.internalId() == 0) {
return QModelIndex(); return QModelIndex();
@ -52,8 +67,7 @@ int CommentsModel::rowCount(const QModelIndex &parent) const
return (isNested() ? nestedComments->size() : comments->count()); return (isNested() ? nestedComments->size() : comments->count());
if (isNested() && parent.internalId() == 0) { if (isNested() && parent.internalId() == 0) {
QString fcnName = nestedComments->keys().at(parent.row()); return nestedComments->at(parent.row()).comments.size();
return nestedComments->operator[](fcnName).size();
} }
return 0; return 0;
@ -83,12 +97,13 @@ QVariant CommentsModel::data(const QModelIndex &index, int role) const
isSubnode = false; isSubnode = false;
} }
QString offset; QString groupName;
CommentDescription comment; CommentDescription comment;
if (isNested()) { if (isNested()) {
offset = nestedComments->keys().at(commentIndex); auto &group = nestedComments->at(commentIndex);
groupName = group.name;
if (isSubnode) { if (isSubnode) {
comment = nestedComments->operator[](offset).at(index.row()); comment = group.comments.at(index.row());
} }
} else { } else {
comment = comments->at(commentIndex); comment = comments->at(commentIndex);
@ -107,7 +122,7 @@ QVariant CommentsModel::data(const QModelIndex &index, int role) const
break; break;
} }
} else if (index.column() == OffsetNestedColumn) { } else if (index.column() == OffsetNestedColumn) {
return offset; return groupName;
} }
} else { } else {
switch (index.column()) { switch (index.column()) {
@ -164,9 +179,8 @@ QVariant CommentsModel::headerData(int section, Qt::Orientation, int role) const
} }
CommentsProxyModel::CommentsProxyModel(CommentsModel *sourceModel, QObject *parent) CommentsProxyModel::CommentsProxyModel(CommentsModel *sourceModel, QObject *parent)
: QSortFilterProxyModel(parent) : AddressableFilterProxyModel(sourceModel, parent)
{ {
setSourceModel(sourceModel);
setFilterCaseSensitivity(Qt::CaseInsensitive); setFilterCaseSensitivity(Qt::CaseInsensitive);
setSortCaseSensitivity(Qt::CaseInsensitive); setSortCaseSensitivity(Qt::CaseInsensitive);
} }
@ -217,45 +231,33 @@ bool CommentsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &ri
} }
CommentsWidget::CommentsWidget(MainWindow *main, QAction *action) : CommentsWidget::CommentsWidget(MainWindow *main, QAction *action) :
CutterDockWidget(main, action), ListDockWidget(main, action),
ui(new Ui::CommentsWidget), actionHorizontal(tr("Horizontal"), this),
main(main), actionVertical(tr("Vertical"), this)
tree(new CutterTreeWidget(this))
{ {
ui->setupUi(this); setWindowTitle(tr("Comments"));
setObjectName("CommentsWidget");
// Add Status Bar footer
tree->addStatusBar(ui->verticalLayout);
commentsModel = new CommentsModel(&comments, &nestedComments, this); commentsModel = new CommentsModel(&comments, &nestedComments, this);
commentsProxyModel = new CommentsProxyModel(commentsModel, this); commentsProxyModel = new CommentsProxyModel(commentsModel, this);
ui->commentsTreeView->setModel(commentsProxyModel); setModels(commentsProxyModel);
ui->commentsTreeView->sortByColumn(CommentsModel::CommentColumn, Qt::AscendingOrder); ui->treeView->sortByColumn(CommentsModel::CommentColumn, Qt::AscendingOrder);
// Ctrl-F to show/hide the filter entry titleContextMenu = new QMenu(this);
QShortcut *searchShortcut = new QShortcut(QKeySequence::Find, this); auto viewTypeGroup = new QActionGroup(titleContextMenu);
connect(searchShortcut, &QShortcut::activated, ui->quickFilterView, &QuickFilterView::showFilter); actionHorizontal.setCheckable(true);
searchShortcut->setContext(Qt::WidgetWithChildrenShortcut); 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());
// 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 &)), actionHorizontal.setChecked(true);
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);
this->setContextMenuPolicy(Qt::CustomContextMenu); this->setContextMenuPolicy(Qt::CustomContextMenu);
connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), connect(this, &QWidget::customContextMenuRequested,
this, SLOT(showTitleContextMenu(const QPoint &))); this, &CommentsWidget::showTitleContextMenu);
connect(Core(), SIGNAL(commentsChanged()), this, SLOT(refreshTree())); connect(Core(), SIGNAL(commentsChanged()), this, SLOT(refreshTree()));
connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshTree())); connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshTree()));
@ -263,61 +265,36 @@ CommentsWidget::CommentsWidget(MainWindow *main, QAction *action) :
CommentsWidget::~CommentsWidget() {} CommentsWidget::~CommentsWidget() {}
void CommentsWidget::on_commentsTreeView_doubleClicked(const QModelIndex &index) void CommentsWidget::onActionHorizontalToggled(bool checked)
{ {
if (!index.isValid()) if (checked) {
return; commentsModel->setNested(false);
ui->treeView->setIndentation(8);
if (commentsModel->isNested() && !index.parent().isValid()) }
return;
auto comment = index.data(CommentsModel::CommentDescriptionRole).value<CommentDescription>();
Core()->seekAndShow(comment.offset);
} }
void CommentsWidget::on_actionHorizontal_triggered() void CommentsWidget::onActionVerticalToggled(bool checked)
{ {
commentsModel->setNested(false); if (checked) {
ui->commentsTreeView->setIndentation(8); commentsModel->setNested(true);
} ui->treeView->setIndentation(20);
}
void CommentsWidget::on_actionVertical_triggered()
{
commentsModel->setNested(true);
ui->commentsTreeView->setIndentation(20);
} }
void CommentsWidget::showTitleContextMenu(const QPoint &pt) void CommentsWidget::showTitleContextMenu(const QPoint &pt)
{ {
// Set functions popup menu titleContextMenu->exec(this->mapToGlobal(pt));
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;
} }
void CommentsWidget::resizeEvent(QResizeEvent *event) void CommentsWidget::resizeEvent(QResizeEvent *event)
{ {
if (main->responsive && isVisible()) { if (mainWindow->responsive && isVisible()) {
if (event->size().width() >= event->size().height()) { if (event->size().width() >= event->size().height()) {
// Set horizontal view (list) // Set horizontal view (list)
on_actionHorizontal_triggered(); actionHorizontal.setChecked(true);
} else { } else {
// Set vertical view (Tree) // Set vertical view (Tree)
on_actionVertical_triggered(); actionVertical.setChecked(true);
} }
} }
QDockWidget::resizeEvent(event); QDockWidget::resizeEvent(event);
@ -329,19 +306,22 @@ void CommentsWidget::refreshTree()
comments = Core()->getAllComments("CCu"); comments = Core()->getAllComments("CCu");
nestedComments.clear(); nestedComments.clear();
QMap<QString, size_t> nestedCommentMapping;
for (const CommentDescription &comment : comments) { for (const CommentDescription &comment : comments) {
QString fcnName = Core()->cmdFunctionAt(comment.offset); RVA offset = RVA_INVALID;
nestedComments[fcnName].append(comment); 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(); commentsModel->endResetModel();
qhelpers::adjustColumns(ui->commentsTreeView, 3, 0); qhelpers::adjustColumns(ui->treeView, 3, 0);
tree->showItemsNumber(commentsProxyModel->rowCount());
} }
void CommentsWidget::setScrollMode()
{
qhelpers::setVerticalScrollMode(ui->commentsTreeView);
}

View File

@ -6,18 +6,23 @@
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
#include "core/Cutter.h" #include "core/Cutter.h"
#include "common/AddressableItemModel.h"
#include "CutterDockWidget.h" #include "CutterDockWidget.h"
#include "CutterTreeWidget.h" #include "CutterTreeWidget.h"
#include "widgets/ListDockWidget.h"
class MainWindow; class MainWindow;
class QTreeWidgetItem; class QTreeWidgetItem;
class CommentsWidget; class CommentsWidget;
namespace Ui { struct CommentGroup
class CommentsWidget; {
} QString name;
RVA offset;
QList<CommentDescription> comments;
};
class CommentsModel : public QAbstractItemModel class CommentsModel : public AddressableItemModel<>
{ {
Q_OBJECT Q_OBJECT
@ -25,7 +30,7 @@ class CommentsModel : public QAbstractItemModel
private: private:
QList<CommentDescription> *comments; QList<CommentDescription> *comments;
QMap<QString, QList<CommentDescription>> *nestedComments; QList<CommentGroup> *nestedComments;
bool nested; bool nested;
public: public:
@ -34,23 +39,26 @@ public:
enum Role { CommentDescriptionRole = Qt::UserRole, FunctionRole }; enum Role { CommentDescriptionRole = Qt::UserRole, FunctionRole };
CommentsModel(QList<CommentDescription> *comments, CommentsModel(QList<CommentDescription> *comments,
QMap<QString, QList<CommentDescription>> *nestedComments, QList<CommentGroup> *nestedComments,
QObject *parent = nullptr); QObject *parent = nullptr);
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &index) const; QModelIndex parent(const QModelIndex &index) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const; int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const; QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
bool isNested() const; bool isNested() const;
void setNested(bool nested); void setNested(bool nested);
RVA address(const QModelIndex &index) const override;
}; };
class CommentsProxyModel : public QSortFilterProxyModel class CommentsProxyModel : public AddressableFilterProxyModel
{ {
Q_OBJECT Q_OBJECT
@ -62,39 +70,35 @@ protected:
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
}; };
class CommentsWidget : public CutterDockWidget class CommentsWidget : public ListDockWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit CommentsWidget(MainWindow *main, QAction *action = nullptr); explicit CommentsWidget(MainWindow *main, QAction *action = nullptr);
~CommentsWidget(); ~CommentsWidget() override;
protected: protected:
void resizeEvent(QResizeEvent *event) override; void resizeEvent(QResizeEvent *event) override;
private slots: private slots:
void on_commentsTreeView_doubleClicked(const QModelIndex &index); void onActionHorizontalToggled(bool checked);
void onActionVerticalToggled(bool checked);
void on_actionHorizontal_triggered();
void on_actionVertical_triggered();
void showTitleContextMenu(const QPoint &pt); void showTitleContextMenu(const QPoint &pt);
void refreshTree(); void refreshTree();
private: private:
std::unique_ptr<Ui::CommentsWidget> ui;
MainWindow *main;
CommentsModel *commentsModel; CommentsModel *commentsModel;
CommentsProxyModel *commentsProxyModel; CommentsProxyModel *commentsProxyModel;
CutterTreeWidget *tree; QAction actionHorizontal;
QAction actionVertical;
QList<CommentDescription> comments; QList<CommentDescription> comments;
QMap<QString, QList<CommentDescription>> nestedComments; QList<CommentGroup> nestedComments;
void setScrollMode(); QMenu *titleContextMenu;
}; };
#endif // COMMENTSWIDGET_H #endif // COMMENTSWIDGET_H

View File

@ -1,87 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CommentsWidget</class>
<widget class="QDockWidget" name="CommentsWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>645</width>
<height>250</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Comments</string>
</property>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="CutterTreeView" name="commentsTreeView">
<property name="sortingEnabled">
<bool>true</bool>
</property>
<attribute name="headerShowSortIndicator" stdset="0">
<bool>true</bool>
</attribute>
</widget>
</item>
<item>
<widget class="QuickFilterView" name="quickFilterView" native="true"/>
</item>
</layout>
</widget>
<action name="actionHorizontal">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Horizontal</string>
</property>
<property name="toolTip">
<string>Horizontal view</string>
</property>
</action>
<action name="actionVertical">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Vertical</string>
</property>
<property name="toolTip">
<string>Vertical view</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>CutterTreeView</class>
<extends>QTreeView</extends>
<header>widgets/CutterTreeView.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QuickFilterView</class>
<extends>QWidget</extends>
<header>widgets/QuickFilterView.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -23,4 +23,9 @@ void CutterTreeWidget::showItemsNumber(int count)
} }
} }
void CutterTreeWidget::showStatusBar(bool show)
{
bar->setVisible(show);
}
CutterTreeWidget::~CutterTreeWidget() {} CutterTreeWidget::~CutterTreeWidget() {}

View File

@ -16,6 +16,7 @@ public:
~CutterTreeWidget(); ~CutterTreeWidget();
void addStatusBar(QVBoxLayout *pos); void addStatusBar(QVBoxLayout *pos);
void showItemsNumber(int count); void showItemsNumber(int count);
void showStatusBar(bool show);
private: private:
QStatusBar *bar; QStatusBar *bar;

View File

@ -1,5 +1,5 @@
#include "ExportsWidget.h" #include "ExportsWidget.h"
#include "ui_ExportsWidget.h" #include "ui_ListDockWidget.h"
#include "core/MainWindow.h" #include "core/MainWindow.h"
#include "common/Helpers.h" #include "common/Helpers.h"
#include "WidgetShortcuts.h" #include "WidgetShortcuts.h"
@ -7,7 +7,7 @@
#include <QShortcut> #include <QShortcut>
ExportsModel::ExportsModel(QList<ExportDescription> *exports, QObject *parent) ExportsModel::ExportsModel(QList<ExportDescription> *exports, QObject *parent)
: QAbstractListModel(parent), : AddressableItemModel<QAbstractListModel>(parent),
exports(exports) exports(exports)
{ {
} }
@ -71,10 +71,21 @@ QVariant ExportsModel::headerData(int section, Qt::Orientation, int role) const
} }
} }
ExportsProxyModel::ExportsProxyModel(ExportsModel *source_model, QObject *parent) RVA ExportsModel::address(const QModelIndex &index) const
: QSortFilterProxyModel(parent) {
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)
: AddressableFilterProxyModel(source_model, parent)
{ {
setSourceModel(source_model);
setFilterCaseSensitivity(Qt::CaseInsensitive); setFilterCaseSensitivity(Qt::CaseInsensitive);
setSortCaseSensitivity(Qt::CaseInsensitive); setSortCaseSensitivity(Qt::CaseInsensitive);
} }
@ -115,19 +126,15 @@ bool ExportsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &rig
} }
ExportsWidget::ExportsWidget(MainWindow *main, QAction *action) : ExportsWidget::ExportsWidget(MainWindow *main, QAction *action) :
CutterDockWidget(main, action), ListDockWidget(main, action)
ui(new Ui::ExportsWidget),
tree(new CutterTreeWidget(this))
{ {
ui->setupUi(this); setWindowTitle(tr("Exports"));
setObjectName("ExportsWidget");
// Add Status Bar footer
tree->addStatusBar(ui->verticalLayout);
exportsModel = new ExportsModel(&exports, this); exportsModel = new ExportsModel(&exports, this);
exportsProxyModel = new ExportsProxyModel(exportsModel, this); exportsProxyModel = new ExportsProxyModel(exportsModel, this);
ui->exportsTreeView->setModel(exportsProxyModel); setModels(exportsProxyModel);
ui->exportsTreeView->sortByColumn(ExportsModel::OffsetColumn, Qt::AscendingOrder); ui->treeView->sortByColumn(ExportsModel::OffsetColumn, Qt::AscendingOrder);
QShortcut *toggle_shortcut = new QShortcut(widgetShortcuts["ExportsWidget"], main); QShortcut *toggle_shortcut = new QShortcut(widgetShortcuts["ExportsWidget"], main);
connect(toggle_shortcut, &QShortcut::activated, this, [=] (){ connect(toggle_shortcut, &QShortcut::activated, this, [=] (){
@ -135,26 +142,6 @@ ExportsWidget::ExportsWidget(MainWindow *main, QAction *action) :
main->updateDockActionChecked(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())); connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshExports()));
} }
@ -166,22 +153,5 @@ void ExportsWidget::refreshExports()
exports = Core()->getAllExports(); exports = Core()->getAllExports();
exportsModel->endResetModel(); exportsModel->endResetModel();
qhelpers::adjustColumns(ui->exportsTreeView, 3, 0); qhelpers::adjustColumns(ui->treeView, 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<ExportDescription>();
Core()->seekAndShow(exp.vaddr);
} }

View File

@ -5,7 +5,7 @@
#include "core/Cutter.h" #include "core/Cutter.h"
#include "CutterDockWidget.h" #include "CutterDockWidget.h"
#include "CutterTreeWidget.h" #include "widgets/ListDockWidget.h"
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
@ -18,7 +18,7 @@ namespace Ui {
class ExportsWidget; class ExportsWidget;
} }
class ExportsModel : public QAbstractListModel class ExportsModel : public AddressableItemModel<QAbstractListModel>
{ {
Q_OBJECT Q_OBJECT
@ -33,14 +33,17 @@ public:
ExportsModel(QList<ExportDescription> *exports, QObject *parent = nullptr); ExportsModel(QList<ExportDescription> *exports, QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const; int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const; QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; 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 Q_OBJECT
@ -52,7 +55,7 @@ protected:
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
}; };
class ExportsWidget : public CutterDockWidget class ExportsWidget : public ListDockWidget
{ {
Q_OBJECT Q_OBJECT
@ -61,19 +64,12 @@ public:
~ExportsWidget(); ~ExportsWidget();
private slots: private slots:
void on_exportsTreeView_doubleClicked(const QModelIndex &index);
void refreshExports(); void refreshExports();
private: private:
std::unique_ptr<Ui::ExportsWidget> ui;
ExportsModel *exportsModel; ExportsModel *exportsModel;
ExportsProxyModel *exportsProxyModel; ExportsProxyModel *exportsProxyModel;
QList<ExportDescription> exports; QList<ExportDescription> exports;
CutterTreeWidget *tree;
void setScrollMode();
}; };
#endif // EXPORTSWIDGET_H #endif // EXPORTSWIDGET_H

View File

@ -1,75 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ExportsWidget</class>
<widget class="QDockWidget" name="ExportsWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Exports</string>
</property>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="CutterTreeView" name="exportsTreeView">
<property name="styleSheet">
<string notr="true">CutterTreeView::item
{
padding-top: 1px;
padding-bottom: 1px;
}</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="indentation">
<number>8</number>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QuickFilterView" name="quickFilterView" native="true"/>
</item>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>CutterTreeView</class>
<extends>QTreeView</extends>
<header>widgets/CutterTreeView.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QuickFilterView</class>
<extends>QWidget</extends>
<header>widgets/QuickFilterView.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -1,5 +1,5 @@
#include "FunctionsWidget.h" #include "FunctionsWidget.h"
#include "ui_FunctionsWidget.h" #include "ui_ListDockWidget.h"
#include "core/MainWindow.h" #include "core/MainWindow.h"
#include "common/Helpers.h" #include "common/Helpers.h"
@ -8,6 +8,7 @@
#include "dialogs/XrefsDialog.h" #include "dialogs/XrefsDialog.h"
#include "common/FunctionsTask.h" #include "common/FunctionsTask.h"
#include "common/TempConfig.h" #include "common/TempConfig.h"
#include "menus/AddressableItemContextMenu.h"
#include <algorithm> #include <algorithm>
#include <QMenu> #include <QMenu>
@ -28,7 +29,7 @@ static const int kMaxTooltipHighlightsLines = 5;
FunctionModel::FunctionModel(QList<FunctionDescription> *functions, QSet<RVA> *importAddresses, FunctionModel::FunctionModel(QList<FunctionDescription> *functions, QSet<RVA> *importAddresses,
ut64 *mainAdress, bool nested, QFont default_font, QFont highlight_font, QObject *parent) ut64 *mainAdress, bool nested, QFont default_font, QFont highlight_font, QObject *parent)
: QAbstractItemModel(parent), : AddressableItemModel<>(parent),
functions(functions), functions(functions),
importAddresses(importAddresses), importAddresses(importAddresses),
mainAdress(mainAdress), mainAdress(mainAdress),
@ -147,21 +148,21 @@ QVariant FunctionModel::data(const QModelIndex &index, int role) const
case NameColumn: case NameColumn:
return function.name; return function.name;
case SizeColumn: case SizeColumn:
return function.size; return QString::number(function.size);
case OffsetColumn: case OffsetColumn:
return RAddressString(function.offset); return RAddressString(function.offset);
case NargsColumn: case NargsColumn:
return function.nargs; return QString::number(function.nargs);
case NlocalsColumn: case NlocalsColumn:
return function.nlocals; return QString::number(function.nlocals);
case NbbsColumn: case NbbsColumn:
return function.nbbs; return QString::number(function.nbbs);
case CalltypeColumn: case CalltypeColumn:
return function.calltype; return function.calltype;
case EdgesColumn: case EdgesColumn:
return function.edges; return QString::number(function.edges);
case FrameColumn: case FrameColumn:
return function.stackframe; return QString::number(function.stackframe);
default: default:
return QVariant(); return QVariant();
} }
@ -187,7 +188,8 @@ QVariant FunctionModel::data(const QModelIndex &index, int role) const
case Qt::ToolTipRole: { 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 QStringList &summary = Core()->cmdList(QString("pdsf @ %1").arg(function.offset));
const QFont &fnt = Config()->getFont(); const QFont &fnt = Config()->getFont();
QFontMetrics fm{ fnt }; QFontMetrics fm{ fnt };
@ -204,17 +206,20 @@ QVariant FunctionModel::data(const QModelIndex &index, int role) const
if (disasmPreview.isEmpty() && highlights.isEmpty()) if (disasmPreview.isEmpty() && highlights.isEmpty())
return QVariant(); return QVariant();
QString toolTipContent = QString("<html><div style=\"font-family: %1; font-size: %2pt; white-space: nowrap;\">") QString toolTipContent =
.arg(fnt.family()) QString("<html><div style=\"font-family: %1; font-size: %2pt; white-space: nowrap;\">")
.arg(qMax(6, fnt.pointSize() - 1)); // slightly decrease font size, to keep more text in the same box .arg(fnt.family())
.arg(qMax(6, fnt.pointSize() -
1)); // slightly decrease font size, to keep more text in the same box
if (!disasmPreview.isEmpty()) if (!disasmPreview.isEmpty())
toolTipContent += tr("<div style=\"margin-bottom: 10px;\"><strong>Disassembly preview</strong>:<br>%1</div>") toolTipContent +=
tr("<div style=\"margin-bottom: 10px;\"><strong>Disassembly preview</strong>:<br>%1</div>")
.arg(disasmPreview.join("<br>")); .arg(disasmPreview.join("<br>"));
if (!highlights.isEmpty()) { if (!highlights.isEmpty()) {
toolTipContent += tr("<div><strong>Highlights</strong>:<br>%1</div>") toolTipContent += tr("<div><strong>Highlights</strong>:<br>%1</div>")
.arg(highlights.join(QLatin1Char('\n')).toHtmlEscaped().replace(QLatin1Char('\n'), "<br>")); .arg(highlights.join(QLatin1Char('\n')).toHtmlEscaped().replace(QLatin1Char('\n'), "<br>"));
} }
toolTipContent += "</div></html>"; toolTipContent += "</div></html>";
return toolTipContent; return toolTipContent;
@ -282,6 +287,18 @@ void FunctionModel::setNested(bool nested)
endResetModel(); endResetModel();
} }
RVA FunctionModel::address(const QModelIndex &index) const
{
auto function = data(index, FunctionDescriptionRole).value<FunctionDescription>();
return function.offset;
}
QString FunctionModel::name(const QModelIndex &index) const
{
auto function = data(index, FunctionDescriptionRole).value<FunctionDescription>();
return function.name;
}
void FunctionModel::seekChanged(RVA) void FunctionModel::seekChanged(RVA)
{ {
int previousIndex = currentIndex; int previousIndex = currentIndex;
@ -332,9 +349,8 @@ void FunctionModel::functionRenamed(const QString &prev_name, const QString &new
FunctionSortFilterProxyModel::FunctionSortFilterProxyModel(FunctionModel *source_model, FunctionSortFilterProxyModel::FunctionSortFilterProxyModel(FunctionModel *source_model,
QObject *parent) QObject *parent)
: QSortFilterProxyModel(parent) : AddressableFilterProxyModel(source_model, parent)
{ {
setSourceModel(source_model);
setFilterCaseSensitivity(Qt::CaseInsensitive); setFilterCaseSensitivity(Qt::CaseInsensitive);
setSortCaseSensitivity(Qt::CaseInsensitive); setSortCaseSensitivity(Qt::CaseInsensitive);
} }
@ -411,64 +427,56 @@ bool FunctionSortFilterProxyModel::lessThan(const QModelIndex &left, const QMode
} }
FunctionsWidget::FunctionsWidget(MainWindow *main, QAction *action) : FunctionsWidget::FunctionsWidget(MainWindow *main, QAction *action) :
CutterDockWidget(main, action), ListDockWidget(main, action),
ui(new Ui::FunctionsWidget), actionRename(tr("Rename"), this),
tree(new CutterTreeWidget(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(); setTooltipStylesheet();
connect(Config(), SIGNAL(colorsUpdated()), this, SLOT(setTooltipStylesheet())); connect(Config(), SIGNAL(colorsUpdated()), this, SLOT(setTooltipStylesheet()));
QFontInfo font_info = ui->treeView->fontInfo();
// 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();
QFont default_font = QFont(font_info.family(), font_info.pointSize()); QFont default_font = QFont(font_info.family(), font_info.pointSize());
QFont highlight_font = QFont(font_info.family(), font_info.pointSize(), QFont::Bold); QFont highlight_font = QFont(font_info.family(), font_info.pointSize(), QFont::Bold);
functionModel = new FunctionModel(&functions, &importAddresses, &mainAdress, false, default_font, functionModel = new FunctionModel(&functions, &importAddresses, &mainAdress, false, default_font,
highlight_font, this); highlight_font, this);
functionProxyModel = new FunctionSortFilterProxyModel(functionModel, this); functionProxyModel = new FunctionSortFilterProxyModel(functionModel, this);
ui->functionsTreeView->setModel(functionProxyModel); setModels(functionProxyModel);
ui->functionsTreeView->sortByColumn(FunctionModel::NameColumn, Qt::AscendingOrder); ui->treeView->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] { titleContextMenu = new QMenu(this);
tree->showItemsNumber(functionProxyModel->rowCount()); auto viewTypeGroup = new QActionGroup(titleContextMenu);
}); actionHorizontal.setCheckable(true);
actionHorizontal.setActionGroup(viewTypeGroup);
setScrollMode(); connect(&actionHorizontal, &QAction::toggled, this, &FunctionsWidget::onActionHorizontalToggled);
actionVertical.setCheckable(true);
actionVertical.setActionGroup(viewTypeGroup);
connect(&actionVertical, &QAction::toggled, this, &FunctionsWidget::onActionVerticalToggled);
titleContextMenu->addActions(viewTypeGroup->actions());
// Set Functions context menu actionRename.setShortcut({Qt::Key_N});
connect(ui->functionsTreeView, SIGNAL(customContextMenuRequested(const QPoint &)), actionRename.setShortcutContext(Qt::ShortcutContext::WidgetWithChildrenShortcut);
this, SLOT(showFunctionsContextMenu(const QPoint &))); connect(&actionRename, &QAction::triggered, this,
&FunctionsWidget::onActionFunctionsRenameTriggered);
connect(&actionUndefine, &QAction::triggered, this,
&FunctionsWidget::onActionFunctionsUndefineTriggered);
connect(ui->functionsTreeView, SIGNAL(doubleClicked(const QModelIndex &)), this, auto itemConextMenu = getItemContextMenu();
SLOT(onFunctionsDoubleClicked(const QModelIndex &))); itemConextMenu->addSeparator();
itemConextMenu->addAction(&actionRename);
itemConextMenu->addAction(&actionUndefine);
itemConextMenu->setWholeFunction(true);
addActions(itemConextMenu->actions());
// Use a custom context menu on the dock title bar // Use a custom context menu on the dock title bar
//this->title_bar = this->titleBarWidget(); actionHorizontal.setChecked(true);
ui->actionHorizontal->setChecked(true);
this->setContextMenuPolicy(Qt::CustomContextMenu); this->setContextMenuPolicy(Qt::CustomContextMenu);
connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
this, SLOT(showTitleContextMenu(const QPoint &))); this, SLOT(showTitleContextMenu(const QPoint &)));
@ -503,9 +511,7 @@ void FunctionsWidget::refreshTree()
functionModel->endResetModel(); functionModel->endResetModel();
// resize offset and size columns // resize offset and size columns
qhelpers::adjustColumns(ui->functionsTreeView, 3, 0); qhelpers::adjustColumns(ui->treeView, 3, 0);
tree->showItemsNumber(functionProxyModel->rowCount());
}); });
Core()->getAsyncTaskManager()->start(task); Core()->getAsyncTaskManager()->start(task);
} }
@ -515,56 +521,10 @@ void FunctionsWidget::changeSizePolicy(QSizePolicy::Policy hor, QSizePolicy::Pol
ui->dockWidgetContents->setSizePolicy(hor, ver); ui->dockWidgetContents->setSizePolicy(hor, ver);
} }
void FunctionsWidget::onFunctionsDoubleClicked(const QModelIndex &index) void FunctionsWidget::onActionFunctionsRenameTriggered()
{
if (!index.isValid())
return;
FunctionDescription function = index.data(
FunctionModel::FunctionDescriptionRole).value<FunctionDescription>();
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()
{ {
// Get selected item in functions tree view // Get selected item in functions tree view
FunctionDescription function = ui->functionsTreeView->selectionModel()->currentIndex().data( FunctionDescription function = ui->treeView->selectionModel()->currentIndex().data(
FunctionModel::FunctionDescriptionRole).value<FunctionDescription>();
// 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(
FunctionModel::FunctionDescriptionRole).value<FunctionDescription>(); FunctionModel::FunctionDescriptionRole).value<FunctionDescription>();
// Create dialog // 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<FunctionDescription>();
Core()->delFunction(function.offset);
}
void FunctionsWidget::on_action_References_triggered() void FunctionsWidget::onActionFunctionsUndefineTriggered()
{ {
// Get selected item in functions tree view const auto selection = ui->treeView->selectionModel()->selection().indexes();
FunctionDescription function = ui->functionsTreeView->selectionModel()->currentIndex().data( std::vector<RVA> offsets;
FunctionModel::FunctionDescriptionRole).value<FunctionDescription>(); offsets.reserve(selection.size());
XrefsDialog x(nullptr); for (const auto &index : selection) {
x.fillRefsForAddress(function.offset, function.name, true); offsets.push_back(functionProxyModel->address(index));
x.exec(); }
for (RVA offset : offsets) {
Core()->delFunction(offset);
}
} }
void FunctionsWidget::showTitleContextMenu(const QPoint &pt) void FunctionsWidget::showTitleContextMenu(const QPoint &pt)
{ {
// Set functions popup menu titleContextMenu->exec(this->mapToGlobal(pt));
QMenu *menu = new QMenu(this); }
menu->clear();
menu->addAction(ui->actionHorizontal);
menu->addAction(ui->actionVertical);
if (!functionModel->isNested()) { void FunctionsWidget::onActionHorizontalToggled(bool enable)
ui->actionHorizontal->setChecked(true); {
ui->actionVertical->setChecked(false); if (enable) {
} else { functionModel->setNested(false);
ui->actionVertical->setChecked(true); ui->treeView->setIndentation(8);
ui->actionHorizontal->setChecked(false);
} }
this->setContextMenuPolicy(Qt::CustomContextMenu);
menu->exec(this->mapToGlobal(pt));
delete menu;
} }
void FunctionsWidget::on_actionHorizontal_triggered() void FunctionsWidget::onActionVerticalToggled(bool enable)
{ {
functionModel->setNested(false); if (enable) {
ui->functionsTreeView->setIndentation(8); functionModel->setNested(true);
} ui->treeView->setIndentation(20);
}
void FunctionsWidget::on_actionVertical_triggered()
{
functionModel->setNested(true);
ui->functionsTreeView->setIndentation(20);
} }
void FunctionsWidget::resizeEvent(QResizeEvent *event) void FunctionsWidget::resizeEvent(QResizeEvent *event)
{ {
if (main->responsive && isVisible()) { if (mainWindow->responsive && isVisible()) {
if (event->size().width() >= event->size().height()) { if (event->size().width() >= event->size().height()) {
// Set horizontal view (list) // Set horizontal view (list)
on_actionHorizontal_triggered(); actionHorizontal.setChecked(true);
} else { } else {
// Set vertical view (Tree) // Set vertical view (Tree)
on_actionVertical_triggered(); actionVertical.setChecked(true);
} }
} }
QDockWidget::resizeEvent(event); QDockWidget::resizeEvent(event);
} }
void FunctionsWidget::setScrollMode()
{
qhelpers::setVerticalScrollMode(ui->functionsTreeView);
}
/** /**
* @brief a SLOT to set the stylesheet for a tooltip * @brief a SLOT to set the stylesheet for a tooltip
*/ */
void FunctionsWidget::setTooltipStylesheet() void FunctionsWidget::setTooltipStylesheet()
{ {
setStyleSheet(QString("QToolTip { border-width: 1px; max-width: %1px;" \ setStyleSheet(QString("QToolTip { border-width: 1px; max-width: %1px;" \

View File

@ -3,24 +3,15 @@
#include <memory> #include <memory>
#include <QSortFilterProxyModel>
#include "core/Cutter.h" #include "core/Cutter.h"
#include "CutterDockWidget.h" #include "CutterDockWidget.h"
#include "CutterTreeWidget.h" #include "widgets/ListDockWidget.h"
#include "CutterTreeView.h"
class MainWindow; class MainWindow;
class QTreeWidgetItem;
class FunctionsTask; class FunctionsTask;
class FunctionsWidget; class FunctionsWidget;
namespace Ui { class FunctionModel : public AddressableItemModel<>
class FunctionsWidget;
}
class FunctionModel : public QAbstractItemModel
{ {
Q_OBJECT Q_OBJECT
@ -53,14 +44,15 @@ public:
FunctionModel(QList<FunctionDescription> *functions, QSet<RVA> *importAddresses, ut64 *mainAdress, FunctionModel(QList<FunctionDescription> *functions, QSet<RVA> *importAddresses, ut64 *mainAdress,
bool nested, QFont defaultFont, QFont highlightFont, QObject *parent = nullptr); bool nested, QFont defaultFont, QFont highlightFont, QObject *parent = nullptr);
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &index) const; QModelIndex parent(const QModelIndex &index) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const; int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const; QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
/** /**
* @return true if the index changed * @return true if the index changed
@ -73,13 +65,15 @@ public:
return nested; return nested;
} }
RVA address(const QModelIndex &index) const override;
QString name(const QModelIndex &index) const override;
private slots: private slots:
void seekChanged(RVA addr); void seekChanged(RVA addr);
void functionRenamed(const QString &prev_name, const QString &new_name); void functionRenamed(const QString &prev_name, const QString &new_name);
}; };
class FunctionSortFilterProxyModel : public QSortFilterProxyModel class FunctionSortFilterProxyModel : public AddressableFilterProxyModel
{ {
Q_OBJECT Q_OBJECT
@ -93,24 +87,20 @@ protected:
class FunctionsWidget : public CutterDockWidget class FunctionsWidget : public ListDockWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit FunctionsWidget(MainWindow *main, QAction *action = nullptr); explicit FunctionsWidget(MainWindow *main, QAction *action = nullptr);
~FunctionsWidget(); ~FunctionsWidget() override;
void changeSizePolicy(QSizePolicy::Policy hor, QSizePolicy::Policy ver); void changeSizePolicy(QSizePolicy::Policy hor, QSizePolicy::Policy ver);
private slots: private slots:
void onFunctionsDoubleClicked(const QModelIndex &index); void onActionFunctionsRenameTriggered();
void showFunctionsContextMenu(const QPoint &pt); void onActionFunctionsUndefineTriggered();
void on_actionDisasAdd_comment_triggered(); void onActionHorizontalToggled(bool enable);
void on_actionFunctionsRename_triggered(); void onActionVerticalToggled(bool enable);
void on_action_References_triggered();
void on_actionFunctionsUndefine_triggered();
void on_actionHorizontal_triggered();
void on_actionVertical_triggered();
void showTitleContextMenu(const QPoint &pt); void showTitleContextMenu(const QPoint &pt);
void setTooltipStylesheet(); void setTooltipStylesheet();
void refreshTree(); void refreshTree();
@ -119,16 +109,19 @@ protected:
void resizeEvent(QResizeEvent *event) override; void resizeEvent(QResizeEvent *event) override;
private: private:
std::unique_ptr<Ui::FunctionsWidget> ui;
MainWindow *main;
QSharedPointer<FunctionsTask> task; QSharedPointer<FunctionsTask> task;
QList<FunctionDescription> functions; QList<FunctionDescription> functions;
QSet<RVA> importAddresses; QSet<RVA> importAddresses;
ut64 mainAdress; ut64 mainAdress;
FunctionModel *functionModel; FunctionModel *functionModel;
FunctionSortFilterProxyModel *functionProxyModel; FunctionSortFilterProxyModel *functionProxyModel;
CutterTreeWidget *tree;
void setScrollMode(); QMenu *titleContextMenu;
QAction actionRename;
QAction actionUndefine;
QAction actionHorizontal;
QAction actionVertical;
}; };
#endif // FUNCTIONSWIDGET_H #endif // FUNCTIONSWIDGET_H

View File

@ -1,146 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FunctionsWidget</class>
<widget class="QDockWidget" name="FunctionsWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>289</width>
<height>359</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="windowTitle">
<string notr="true">Functions</string>
</property>
<widget class="QWidget" name="dockWidgetContents">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="CutterTreeView" name="functionsTreeView">
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="styleSheet">
<string notr="true">
CutterTreeView::item
{
padding-top: 1px;
padding-bottom: 1px;
}
</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
<property name="indentation">
<number>8</number>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QuickFilterView" name="quickFilterView" native="true"/>
</item>
</layout>
</widget>
<action name="actionDisasAdd_comment">
<property name="text">
<string>Add comment</string>
</property>
</action>
<action name="actionFunctionsRename">
<property name="text">
<string>Rename</string>
</property>
</action>
<action name="actionFunctionsUndefine">
<property name="text">
<string>Undefine</string>
</property>
</action>
<action name="action_References">
<property name="text">
<string>X-Refs</string>
</property>
<property name="toolTip">
<string>Cross references</string>
</property>
</action>
<action name="actionHorizontal">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Horizontal</string>
</property>
</action>
<action name="actionVertical">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Vertical</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>CutterTreeView</class>
<extends>QTreeView</extends>
<header>widgets/CutterTreeView.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QuickFilterView</class>
<extends>QWidget</extends>
<header>widgets/QuickFilterView.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -1,10 +1,10 @@
#include "HeadersWidget.h" #include "HeadersWidget.h"
#include "ui_HeadersWidget.h" #include "ui_ListDockWidget.h"
#include "core/MainWindow.h" #include "core/MainWindow.h"
#include "common/Helpers.h" #include "common/Helpers.h"
HeadersModel::HeadersModel(QList<HeaderDescription> *headers, QObject *parent) HeadersModel::HeadersModel(QList<HeaderDescription> *headers, QObject *parent)
: QAbstractListModel(parent), : AddressableItemModel<QAbstractListModel>(parent),
headers(headers) headers(headers)
{ {
} }
@ -64,10 +64,21 @@ QVariant HeadersModel::headerData(int section, Qt::Orientation, int role) const
} }
} }
HeadersProxyModel::HeadersProxyModel(HeadersModel *sourceModel, QObject *parent) RVA HeadersModel::address(const QModelIndex &index) const
: QSortFilterProxyModel(parent) {
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)
: AddressableFilterProxyModel(sourceModel, parent)
{ {
setSourceModel(sourceModel);
} }
bool HeadersProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const 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) : HeadersWidget::HeadersWidget(MainWindow *main, QAction *action) :
CutterDockWidget(main, action), ListDockWidget(main, action)
ui(new Ui::HeadersWidget)
{ {
ui->setupUi(this); setWindowTitle(tr("Headers"));
setObjectName("HeadersWidget");
headersModel = new HeadersModel(&headers, this); headersModel = new HeadersModel(&headers, this);
headersProxyModel = new HeadersProxyModel(headersModel, this); headersProxyModel = new HeadersProxyModel(headersModel, this);
ui->headersTreeView->setModel(headersProxyModel); setModels(headersProxyModel);
ui->headersTreeView->sortByColumn(HeadersModel::OffsetColumn, Qt::AscendingOrder); ui->treeView->sortByColumn(HeadersModel::OffsetColumn, Qt::AscendingOrder);
setScrollMode(); ui->quickFilterView->closeFilter();
showCount(false);
connect(Core(), &CutterCore::refreshAll, this, &HeadersWidget::refreshHeaders); connect(Core(), &CutterCore::refreshAll, this, &HeadersWidget::refreshHeaders);
} }
@ -122,17 +134,6 @@ void HeadersWidget::refreshHeaders()
headers = Core()->getAllHeaders(); headers = Core()->getAllHeaders();
headersModel->endResetModel(); headersModel->endResetModel();
ui->headersTreeView->resizeColumnToContents(0); ui->treeView->resizeColumnToContents(0);
ui->headersTreeView->resizeColumnToContents(1); ui->treeView->resizeColumnToContents(1);
}
void HeadersWidget::setScrollMode()
{
qhelpers::setVerticalScrollMode(ui->headersTreeView);
}
void HeadersWidget::on_headersTreeView_doubleClicked(const QModelIndex &index)
{
HeaderDescription item = index.data(HeadersModel::HeaderDescriptionRole).value<HeaderDescription>();
Core()->seekAndShow(item.vaddr);
} }

View File

@ -4,7 +4,7 @@
#include <memory> #include <memory>
#include "core/Cutter.h" #include "core/Cutter.h"
#include "CutterDockWidget.h" #include "ListDockWidget.h"
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
@ -22,7 +22,7 @@ class QTreeWidgetItem;
class HeadersWidget; class HeadersWidget;
class HeadersModel: public QAbstractListModel class HeadersModel: public AddressableItemModel<QAbstractListModel>
{ {
Q_OBJECT Q_OBJECT
@ -35,18 +35,21 @@ public:
enum Column { OffsetColumn = 0, NameColumn, ValueColumn, ColumnCount }; enum Column { OffsetColumn = 0, NameColumn, ValueColumn, ColumnCount };
enum Role { HeaderDescriptionRole = Qt::UserRole }; enum Role { HeaderDescriptionRole = Qt::UserRole };
HeadersModel(QList<HeaderDescription> *headers, QObject *parent = 0); HeadersModel(QList<HeaderDescription> *headers, QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const; int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const; QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; 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 Q_OBJECT
@ -60,7 +63,7 @@ protected:
class HeadersWidget : public CutterDockWidget class HeadersWidget : public ListDockWidget
{ {
Q_OBJECT Q_OBJECT
@ -69,18 +72,12 @@ public:
~HeadersWidget(); ~HeadersWidget();
private slots: private slots:
void on_headersTreeView_doubleClicked(const QModelIndex &index);
void refreshHeaders(); void refreshHeaders();
private: private:
std::unique_ptr<Ui::HeadersWidget> ui;
HeadersModel *headersModel; HeadersModel *headersModel;
HeadersProxyModel *headersProxyModel; HeadersProxyModel *headersProxyModel;
QList<HeaderDescription> headers; QList<HeaderDescription> headers;
void setScrollMode();
}; };

View File

@ -1,66 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>HeadersWidget</class>
<widget class="QDockWidget" name="HeadersWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Headers</string>
</property>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="CutterTreeView" name="headersTreeView">
<property name="styleSheet">
<string notr="true">CutterTreeView::item
{
padding-top: 1px;
padding-bottom: 1px;
}</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="indentation">
<number>8</number>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>CutterTreeView</class>
<extends>QTreeView</extends>
<header>widgets/CutterTreeView.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -1,5 +1,5 @@
#include "ImportsWidget.h" #include "ImportsWidget.h"
#include "ui_ImportsWidget.h" #include "ui_ListDockWidget.h"
#include "WidgetShortcuts.h" #include "WidgetShortcuts.h"
#include "core/MainWindow.h" #include "core/MainWindow.h"
#include "common/Helpers.h" #include "common/Helpers.h"
@ -10,7 +10,7 @@
#include <QTreeWidget> #include <QTreeWidget>
ImportsModel::ImportsModel(QList<ImportDescription> *imports, QObject *parent) : ImportsModel::ImportsModel(QList<ImportDescription> *imports, QObject *parent) :
QAbstractTableModel(parent), AddressableItemModel(parent),
imports(imports) imports(imports)
{} {}
@ -81,10 +81,21 @@ QVariant ImportsModel::headerData(int section, Qt::Orientation, int role) const
return QVariant(); return QVariant();
} }
ImportsProxyModel::ImportsProxyModel(ImportsModel *sourceModel, QObject *parent) RVA ImportsModel::address(const QModelIndex &index) const
: QSortFilterProxyModel(parent) {
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)
: AddressableFilterProxyModel(sourceModel, parent)
{ {
setSourceModel(sourceModel);
setFilterCaseSensitivity(Qt::CaseInsensitive); setFilterCaseSensitivity(Qt::CaseInsensitive);
setSortCaseSensitivity(Qt::CaseInsensitive); setSortCaseSensitivity(Qt::CaseInsensitive);
} }
@ -129,46 +140,21 @@ bool ImportsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &rig
*/ */
ImportsWidget::ImportsWidget(MainWindow *main, QAction *action) : ImportsWidget::ImportsWidget(MainWindow *main, QAction *action) :
CutterDockWidget(main, action), ListDockWidget(main, action),
ui(new Ui::ImportsWidget),
importsModel(new ImportsModel(&imports, this)), importsModel(new ImportsModel(&imports, this)),
importsProxyModel(new ImportsProxyModel(importsModel, this)), importsProxyModel(new ImportsProxyModel(importsModel, this))
tree(new CutterTreeWidget(this))
{ {
ui->setupUi(this); setWindowTitle(tr("Imports"));
setObjectName("ImportsWidget");
// Add Status Bar footer
tree->addStatusBar(ui->verticalLayout);
ui->importsTreeView->setModel(importsProxyModel);
ui->importsTreeView->sortByColumn(ImportsModel::NameColumn, Qt::AscendingOrder);
setModels(importsProxyModel);
ui->treeView->sortByColumn(ImportsModel::NameColumn, Qt::AscendingOrder);
QShortcut *toggle_shortcut = new QShortcut(widgetShortcuts["ImportsWidget"], main); QShortcut *toggle_shortcut = new QShortcut(widgetShortcuts["ImportsWidget"], main);
connect(toggle_shortcut, &QShortcut::activated, this, [=] (){ connect(toggle_shortcut, &QShortcut::activated, this, [=] (){
toggleDockWidget(true); toggleDockWidget(true);
main->updateDockActionChecked(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 &)),
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())); connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshImports()));
} }
@ -179,20 +165,5 @@ void ImportsWidget::refreshImports()
importsModel->beginResetModel(); importsModel->beginResetModel();
imports = Core()->getAllImports(); imports = Core()->getAllImports();
importsModel->endResetModel(); importsModel->endResetModel();
qhelpers::adjustColumns(ui->importsTreeView, 4, 0); qhelpers::adjustColumns(ui->treeView, 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());
} }

View File

@ -11,17 +11,14 @@
#include "CutterDockWidget.h" #include "CutterDockWidget.h"
#include "core/Cutter.h" #include "core/Cutter.h"
#include "CutterTreeWidget.h" #include "widgets/ListDockWidget.h"
#include "common/AddressableItemModel.h"
class MainWindow; class MainWindow;
class QTreeWidget; class QTreeWidget;
class ImportsWidget; class ImportsWidget;
namespace Ui { class ImportsModel : public AddressableItemModel<QAbstractTableModel>
class ImportsWidget;
}
class ImportsModel : public QAbstractTableModel
{ {
Q_OBJECT Q_OBJECT
@ -51,14 +48,17 @@ public:
ImportsModel(QList<ImportDescription> *imports, QObject *parent = nullptr); ImportsModel(QList<ImportDescription> *imports, QObject *parent = nullptr);
int rowCount(const QModelIndex &parent) const; int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const; int columnCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const; QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
RVA address(const QModelIndex &index) const override;
QString name(const QModelIndex &index) const override;
}; };
class ImportsProxyModel : public QSortFilterProxyModel class ImportsProxyModel : public AddressableFilterProxyModel
{ {
Q_OBJECT Q_OBJECT
@ -70,7 +70,7 @@ protected:
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
}; };
class ImportsWidget : public CutterDockWidget class ImportsWidget : public ListDockWidget
{ {
Q_OBJECT Q_OBJECT
@ -79,20 +79,13 @@ public:
~ImportsWidget(); ~ImportsWidget();
private slots: private slots:
void on_importsTreeView_doubleClicked(const QModelIndex &index);
void refreshImports(); void refreshImports();
private: private:
std::unique_ptr<Ui::ImportsWidget> ui;
ImportsModel *importsModel; ImportsModel *importsModel;
ImportsProxyModel *importsProxyModel; ImportsProxyModel *importsProxyModel;
QList<ImportDescription> imports; QList<ImportDescription> imports;
CutterTreeWidget *tree;
void highlightUnsafe(); void highlightUnsafe();
void setScrollMode();
}; };
#endif // IMPORTSWIDGET_H #endif // IMPORTSWIDGET_H

View File

@ -1,75 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ImportsWidget</class>
<widget class="QDockWidget" name="ImportsWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Imports</string>
</property>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="CutterTreeView" name="importsTreeView">
<property name="styleSheet">
<string notr="true">CutterTreeView::item
{
padding-top: 1px;
padding-bottom: 1px;
}</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="indentation">
<number>8</number>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QuickFilterView" name="quickFilterView" native="true"/>
</item>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>CutterTreeView</class>
<extends>QTreeView</extends>
<header>widgets/CutterTreeView.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QuickFilterView</class>
<extends>QWidget</extends>
<header>widgets/QuickFilterView.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,118 @@
#include "ListDockWidget.h"
#include "ui_ListDockWidget.h"
#include "core/MainWindow.h"
#include "common/Helpers.h"
#include "menus/AddressableItemContextMenu.h"
#include <QMenu>
#include <QResizeEvent>
#include <QShortcut>
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<void(QWidget::*)()>(&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);
}
}

View File

@ -0,0 +1,57 @@
#ifndef LISTDOCKWIDGET_H
#define LISTDOCKWIDGET_H
#include <memory>
#include <QAbstractItemModel>
#include <QSortFilterProxyModel>
#include <QMenu>
#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::ListDockWidget> ui;
private:
AddressableFilterProxyModel *objectFilterProxyModel;
CutterTreeWidget *tree;
AddressableItemContextMenu *itemContextMenu;
SearchBarPolicy searchBarPolicy;
};
#endif // LISTDOCKWIDGET_H

View File

@ -1,20 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0"> <ui version="4.0">
<class>RelocsWidget</class> <class>ListDockWidget</class>
<widget class="QDockWidget" name="RelocsWidget"> <widget class="QDockWidget" name="ListDockWidget">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>400</width> <width>645</width>
<height>300</height> <height>250</height>
</rect> </rect>
</property> </property>
<property name="windowTitle">
<string notr="true">Relocs</string>
</property>
<widget class="QWidget" name="dockWidgetContents"> <widget class="QWidget" name="dockWidgetContents">
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin"> <property name="leftMargin">
<number>0</number> <number>0</number>
</property> </property>
@ -28,7 +28,7 @@
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
<widget class="CutterTreeView" name="relocsTreeView"> <widget class="CutterTreeView" name="treeView">
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">CutterTreeView::item <string notr="true">CutterTreeView::item
{ {
@ -45,6 +45,9 @@
<property name="sortingEnabled"> <property name="sortingEnabled">
<bool>true</bool> <bool>true</bool>
</property> </property>
<attribute name="headerShowSortIndicator" stdset="0">
<bool>true</bool>
</attribute>
</widget> </widget>
</item> </item>
<item> <item>

View File

@ -1,5 +1,5 @@
#include "RelocsWidget.h" #include "RelocsWidget.h"
#include "ui_RelocsWidget.h" #include "ui_ListDockWidget.h"
#include "core/MainWindow.h" #include "core/MainWindow.h"
#include "common/Helpers.h" #include "common/Helpers.h"
@ -7,7 +7,7 @@
#include <QTreeWidget> #include <QTreeWidget>
RelocsModel::RelocsModel(QList<RelocDescription> *relocs, QObject *parent) : RelocsModel::RelocsModel(QList<RelocDescription> *relocs, QObject *parent) :
QAbstractTableModel(parent), AddressableItemModel<QAbstractTableModel>(parent),
relocs(relocs) relocs(relocs)
{} {}
@ -61,10 +61,21 @@ QVariant RelocsModel::headerData(int section, Qt::Orientation, int role) const
return QVariant(); return QVariant();
} }
RelocsProxyModel::RelocsProxyModel(RelocsModel *sourceModel, QObject *parent) RVA RelocsModel::address(const QModelIndex &index) const
: QSortFilterProxyModel(parent) {
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)
: AddressableFilterProxyModel(sourceModel, parent)
{ {
setSourceModel(sourceModel);
setFilterCaseSensitivity(Qt::CaseInsensitive); setFilterCaseSensitivity(Qt::CaseInsensitive);
setSortCaseSensitivity(Qt::CaseInsensitive); setSortCaseSensitivity(Qt::CaseInsensitive);
} }
@ -103,64 +114,25 @@ bool RelocsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &righ
} }
RelocsWidget::RelocsWidget(MainWindow *main, QAction *action) : RelocsWidget::RelocsWidget(MainWindow *main, QAction *action) :
CutterDockWidget(main, action), ListDockWidget(main, action),
ui(new Ui::RelocsWidget),
relocsModel(new RelocsModel(&relocs, this)), relocsModel(new RelocsModel(&relocs, this)),
relocsProxyModel(new RelocsProxyModel(relocsModel, this)), relocsProxyModel(new RelocsProxyModel(relocsModel, this))
tree(new CutterTreeWidget(this))
{ {
ui->setupUi(this); setWindowTitle(tr("Relocs"));
setObjectName("RelocsWidget");
// Add Status Bar footer setModels(relocsProxyModel);
tree->addStatusBar(ui->verticalLayout); ui->treeView->sortByColumn(RelocsModel::NameColumn, Qt::AscendingOrder);
ui->relocsTreeView->setModel(relocsProxyModel); connect(Core(), &CutterCore::refreshAll, this, &RelocsWidget::refreshRelocs);
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);
// 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 &)),
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()));
} }
RelocsWidget::~RelocsWidget() {} RelocsWidget::~RelocsWidget() {}
void RelocsWidget::on_relocsTreeView_doubleClicked(const QModelIndex &index)
{
if (!index.isValid())
return;
Core()->seekAndShow(index.data(RelocsModel::AddressRole).toLongLong());
}
void RelocsWidget::refreshRelocs() void RelocsWidget::refreshRelocs()
{ {
relocsModel->beginResetModel(); relocsModel->beginResetModel();
relocs = Core()->getAllRelocs(); relocs = Core()->getAllRelocs();
relocsModel->endResetModel(); relocsModel->endResetModel();
qhelpers::adjustColumns(ui->relocsTreeView, 3, 0); qhelpers::adjustColumns(ui->treeView, 3, 0);
tree->showItemsNumber(relocsProxyModel->rowCount());
}
void RelocsWidget::setScrollMode()
{
qhelpers::setVerticalScrollMode(ui->relocsTreeView);
} }

View File

@ -7,16 +7,12 @@
#include "CutterDockWidget.h" #include "CutterDockWidget.h"
#include "core/Cutter.h" #include "core/Cutter.h"
#include "CutterTreeWidget.h" #include "widgets/ListDockWidget.h"
class MainWindow; class MainWindow;
class RelocsWidget; class RelocsWidget;
namespace Ui { class RelocsModel : public AddressableItemModel<QAbstractTableModel>
class RelocsWidget;
}
class RelocsModel : public QAbstractTableModel
{ {
Q_OBJECT Q_OBJECT
@ -31,14 +27,17 @@ public:
RelocsModel(QList<RelocDescription> *relocs, QObject *parent = nullptr); RelocsModel(QList<RelocDescription> *relocs, QObject *parent = nullptr);
int rowCount(const QModelIndex &parent) const; int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const; int columnCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const; QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
RVA address(const QModelIndex &index) const override;
QString name(const QModelIndex &index) const override;
}; };
class RelocsProxyModel : public QSortFilterProxyModel class RelocsProxyModel : public AddressableFilterProxyModel
{ {
Q_OBJECT Q_OBJECT
@ -50,7 +49,7 @@ protected:
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
}; };
class RelocsWidget : public CutterDockWidget class RelocsWidget : public ListDockWidget
{ {
Q_OBJECT Q_OBJECT
@ -59,18 +58,12 @@ public:
~RelocsWidget(); ~RelocsWidget();
private slots: private slots:
void on_relocsTreeView_doubleClicked(const QModelIndex &index);
void refreshRelocs(); void refreshRelocs();
private: private:
std::unique_ptr<Ui::RelocsWidget> ui;
RelocsModel *relocsModel; RelocsModel *relocsModel;
RelocsProxyModel *relocsProxyModel; RelocsProxyModel *relocsProxyModel;
QList<RelocDescription> relocs; QList<RelocDescription> relocs;
CutterTreeWidget *tree;
void setScrollMode();
}; };
#endif // RELOCSWIDGET_H #endif // RELOCSWIDGET_H

View File

@ -1,9 +1,9 @@
#include "SectionsWidget.h" #include "SectionsWidget.h"
#include "CutterTreeView.h"
#include "QuickFilterView.h" #include "QuickFilterView.h"
#include "core/MainWindow.h" #include "core/MainWindow.h"
#include "common/Helpers.h" #include "common/Helpers.h"
#include "common/Configuration.h" #include "common/Configuration.h"
#include "ui_ListDockWidget.h"
#include <QGraphicsSceneMouseEvent> #include <QGraphicsSceneMouseEvent>
#include <QGraphicsTextItem> #include <QGraphicsTextItem>
@ -14,7 +14,7 @@
#include <QToolTip> #include <QToolTip>
SectionsModel::SectionsModel(QList<SectionDescription> *sections, QObject *parent) SectionsModel::SectionsModel(QList<SectionDescription> *sections, QObject *parent)
: QAbstractListModel(parent), : AddressableItemModel<QAbstractListModel>(parent),
sections(sections) sections(sections)
{ {
} }
@ -105,10 +105,21 @@ QVariant SectionsModel::headerData(int section, Qt::Orientation, int role) const
} }
} }
SectionsProxyModel::SectionsProxyModel(SectionsModel *sourceModel, QObject *parent) RVA SectionsModel::address(const QModelIndex &index) const
: QSortFilterProxyModel(parent) {
const SectionDescription &section = sections->at(index.row());
return section.vaddr;
}
QString SectionsModel::name(const QModelIndex &index) const
{
const SectionDescription &section = sections->at(index.row());
return section.name;
}
SectionsProxyModel::SectionsProxyModel(SectionsModel *sourceModel, QObject *parent)
: AddressableFilterProxyModel(sourceModel, parent)
{ {
setSourceModel(sourceModel);
setFilterCaseSensitivity(Qt::CaseInsensitive); setFilterCaseSensitivity(Qt::CaseInsensitive);
setSortCaseSensitivity(Qt::CaseInsensitive); setSortCaseSensitivity(Qt::CaseInsensitive);
} }
@ -138,8 +149,7 @@ bool SectionsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &ri
} }
SectionsWidget::SectionsWidget(MainWindow *main, QAction *action) : SectionsWidget::SectionsWidget(MainWindow *main, QAction *action) :
CutterDockWidget(main, action), ListDockWidget(main, action)
main(main)
{ {
setObjectName("SectionsWidget"); setObjectName("SectionsWidget");
setWindowTitle(QStringLiteral("Sections")); setWindowTitle(QStringLiteral("Sections"));
@ -158,42 +168,22 @@ SectionsWidget::~SectionsWidget() = default;
void SectionsWidget::initSectionsTable() void SectionsWidget::initSectionsTable()
{ {
sectionsTable = new CutterTreeView;
sectionsModel = new SectionsModel(&sections, this); sectionsModel = new SectionsModel(&sections, this);
proxyModel = new SectionsProxyModel(sectionsModel, this); proxyModel = new SectionsProxyModel(sectionsModel, this);
setModels(proxyModel);
sectionsTable->setModel(proxyModel); ui->treeView->sortByColumn(SectionsModel::NameColumn, Qt::AscendingOrder);
sectionsTable->setIndentation(10);
sectionsTable->setSortingEnabled(true);
sectionsTable->sortByColumn(SectionsModel::NameColumn, Qt::AscendingOrder);
} }
void SectionsWidget::initQuickFilter() void SectionsWidget::initQuickFilter()
{ {
quickFilterView = new QuickFilterView(this, false); ui->quickFilterView->closeFilter();
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);
} }
void SectionsWidget::initAddrMapDocks() void SectionsWidget::initAddrMapDocks()
{ {
dockWidgetContents = new QWidget(this); QVBoxLayout *layout = ui->verticalLayout;
QVBoxLayout *layout = new QVBoxLayout(); showCount(false);
layout->addWidget(sectionsTable);
layout->addWidget(quickFilterView);
rawAddrDock = new RawAddrDock(sectionsModel, this); rawAddrDock = new RawAddrDock(sectionsModel, this);
virtualAddrDock = new VirtualAddrDock(sectionsModel, this); virtualAddrDock = new VirtualAddrDock(sectionsModel, this);
@ -220,20 +210,11 @@ void SectionsWidget::initAddrMapDocks()
toggleButton->setArrowType(Qt::NoArrow); toggleButton->setArrowType(Qt::NoArrow);
toggleButton->hide(); toggleButton->hide();
layout->addWidget(toggleButton); layout->addWidget(toggleButton);
layout->setMargin(0);
dockWidgetContents->setLayout(layout);
setWidget(dockWidgetContents);
} }
void SectionsWidget::initConnects() void SectionsWidget::initConnects()
{ {
connect(sectionsTable, SIGNAL(doubleClicked(const QModelIndex &)), connect(Core(), &CutterCore::refreshAll, this, &SectionsWidget::refreshSections);
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(this, &QDockWidget::visibilityChanged, this, [ = ](bool visibility) { connect(this, &QDockWidget::visibilityChanged, this, [ = ](bool visibility) {
if (visibility) { if (visibility) {
refreshSections(); refreshSections();
@ -261,7 +242,7 @@ void SectionsWidget::refreshSections()
sectionsModel->beginResetModel(); sectionsModel->beginResetModel();
sections = Core()->getAllSections(); sections = Core()->getAllSections();
sectionsModel->endResetModel(); sectionsModel->endResetModel();
qhelpers::adjustColumns(sectionsTable, SectionsModel::ColumnCount, 0); qhelpers::adjustColumns(ui->treeView, SectionsModel::ColumnCount, 0);
refreshDocks(); 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<SectionDescription>();
Core()->seekAndShow(section.vaddr);
}
void SectionsWidget::resizeEvent(QResizeEvent *event) { void SectionsWidget::resizeEvent(QResizeEvent *event) {
CutterDockWidget::resizeEvent(event); CutterDockWidget::resizeEvent(event);
refreshDocks(); refreshDocks();

View File

@ -13,10 +13,9 @@
#include "core/Cutter.h" #include "core/Cutter.h"
#include "CutterDockWidget.h" #include "CutterDockWidget.h"
#include "widgets/ListDockWidget.h"
class CutterTreeView;
class QAbstractItemView; class QAbstractItemView;
class MainWindow;
class SectionsWidget; class SectionsWidget;
class AbstractAddrDock; class AbstractAddrDock;
class AddrDockScene; class AddrDockScene;
@ -27,7 +26,7 @@ class QuickFilterView;
class QGraphicsView; class QGraphicsView;
class QGraphicsRectItem; class QGraphicsRectItem;
class SectionsModel : public QAbstractListModel class SectionsModel : public AddressableItemModel<QAbstractListModel>
{ {
Q_OBJECT Q_OBJECT
@ -47,9 +46,12 @@ public:
QVariant data(const QModelIndex &index, int role) const; QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) 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 Q_OBJECT
@ -60,7 +62,7 @@ protected:
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
}; };
class SectionsWidget : public CutterDockWidget class SectionsWidget : public ListDockWidget
{ {
Q_OBJECT Q_OBJECT
@ -71,9 +73,6 @@ public:
private slots: private slots:
void refreshSections(); void refreshSections();
void refreshDocks(); void refreshDocks();
void onSectionsDoubleClicked(const QModelIndex &index);
protected: protected:
void resizeEvent(QResizeEvent *event) override; void resizeEvent(QResizeEvent *event) override;
@ -81,10 +80,6 @@ private:
QList<SectionDescription> sections; QList<SectionDescription> sections;
SectionsModel *sectionsModel; SectionsModel *sectionsModel;
SectionsProxyModel *proxyModel; SectionsProxyModel *proxyModel;
CutterTreeView *sectionsTable;
MainWindow *main;
QWidget *dockWidgetContents;
QuickFilterView *quickFilterView;
QWidget *addrDockWidget; QWidget *addrDockWidget;
RawAddrDock *rawAddrDock; RawAddrDock *rawAddrDock;

View File

@ -1,14 +1,13 @@
#include "SegmentsWidget.h" #include "SegmentsWidget.h"
#include "CutterTreeView.h"
#include "core/MainWindow.h" #include "core/MainWindow.h"
#include "QuickFilterView.h"
#include "common/Helpers.h" #include "common/Helpers.h"
#include "ui_ListDockWidget.h"
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QShortcut> #include <QShortcut>
SegmentsModel::SegmentsModel(QList<SegmentDescription> *segments, QObject *parent) SegmentsModel::SegmentsModel(QList<SegmentDescription> *segments, QObject *parent)
: QAbstractListModel(parent), : AddressableItemModel<QAbstractListModel>(parent),
segments(segments) segments(segments)
{ {
} }
@ -50,7 +49,7 @@ QVariant SegmentsModel::data(const QModelIndex &index, int role) const
case SegmentsModel::NameColumn: case SegmentsModel::NameColumn:
return segment.name; return segment.name;
case SegmentsModel::SizeColumn: case SegmentsModel::SizeColumn:
return segment.size; return QString::number(segment.size);
case SegmentsModel::AddressColumn: case SegmentsModel::AddressColumn:
return RAddressString(segment.vaddr); return RAddressString(segment.vaddr);
case SegmentsModel::EndAddressColumn: case SegmentsModel::EndAddressColumn:
@ -94,10 +93,21 @@ QVariant SegmentsModel::headerData(int segment, Qt::Orientation, int role) const
} }
} }
SegmentsProxyModel::SegmentsProxyModel(SegmentsModel *sourceModel, QObject *parent) RVA SegmentsModel::address(const QModelIndex &index) const
: QSortFilterProxyModel(parent) {
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)
: AddressableFilterProxyModel(sourceModel, parent)
{ {
setSourceModel(sourceModel);
setFilterCaseSensitivity(Qt::CaseInsensitive); setFilterCaseSensitivity(Qt::CaseInsensitive);
setSortCaseSensitivity(Qt::CaseInsensitive); setSortCaseSensitivity(Qt::CaseInsensitive);
} }
@ -121,54 +131,21 @@ bool SegmentsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &ri
} }
SegmentsWidget::SegmentsWidget(MainWindow *main, QAction *action) : SegmentsWidget::SegmentsWidget(MainWindow *main, QAction *action) :
CutterDockWidget(main, action), ListDockWidget(main, action)
main(main)
{ {
setObjectName("SegmentsWidget"); setObjectName("SegmentsWidget");
setWindowTitle(QStringLiteral("Segments")); setWindowTitle(QStringLiteral("Segments"));
segmentsTable = new CutterTreeView;
segmentsModel = new SegmentsModel(&segments, this); segmentsModel = new SegmentsModel(&segments, this);
auto proxyModel = new SegmentsProxyModel(segmentsModel, this); auto proxyModel = new SegmentsProxyModel(segmentsModel, this);
setModels(proxyModel);
segmentsTable->setModel(proxyModel); ui->treeView->sortByColumn(SegmentsModel::NameColumn, Qt::AscendingOrder);
segmentsTable->setIndentation(10);
segmentsTable->setSortingEnabled(true); ui->quickFilterView->closeFilter();
segmentsTable->sortByColumn(SegmentsModel::NameColumn, Qt::AscendingOrder); showCount(false);
connect(segmentsTable, SIGNAL(doubleClicked(const QModelIndex &)),
this, SLOT(onSegmentsDoubleClicked(const QModelIndex &)));
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshSegments())); connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshSegments()));
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);
} }
SegmentsWidget::~SegmentsWidget() {} SegmentsWidget::~SegmentsWidget() {}
@ -179,14 +156,5 @@ void SegmentsWidget::refreshSegments()
segments = Core()->getAllSegments(); segments = Core()->getAllSegments();
segmentsModel->endResetModel(); segmentsModel->endResetModel();
qhelpers::adjustColumns(segmentsTable, SegmentsModel::ColumnCount, 0); qhelpers::adjustColumns(ui->treeView, SegmentsModel::ColumnCount, 0);
}
void SegmentsWidget::onSegmentsDoubleClicked(const QModelIndex &index)
{
if (!index.isValid())
return;
auto segment = index.data(SegmentsModel::SegmentDescriptionRole).value<SegmentDescription>();
Core()->seekAndShow(segment.vaddr);
} }

View File

@ -7,16 +7,12 @@
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
#include "core/Cutter.h" #include "core/Cutter.h"
#include "CutterTreeView.h" #include "widgets/ListDockWidget.h"
#include "CutterDockWidget.h"
class CutterTreeView;
class QAbstractItemView; class QAbstractItemView;
class MainWindow;
class SegmentsWidget; class SegmentsWidget;
class QuickFilterView;
class SegmentsModel : public QAbstractListModel class SegmentsModel : public AddressableItemModel<QAbstractListModel>
{ {
Q_OBJECT Q_OBJECT
@ -31,14 +27,17 @@ public:
SegmentsModel(QList<SegmentDescription> *segments, QObject *parent = nullptr); SegmentsModel(QList<SegmentDescription> *segments, QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const; int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const; QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int segment, Qt::Orientation orientation, int role = Qt::DisplayRole) const; 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 Q_OBJECT
@ -49,7 +48,7 @@ protected:
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
}; };
class SegmentsWidget : public CutterDockWidget class SegmentsWidget : public ListDockWidget
{ {
Q_OBJECT Q_OBJECT
@ -59,15 +58,9 @@ public:
private slots: private slots:
void refreshSegments(); void refreshSegments();
void onSegmentsDoubleClicked(const QModelIndex &index);
private: private:
QList<SegmentDescription> segments; QList<SegmentDescription> segments;
SegmentsModel *segmentsModel; SegmentsModel *segmentsModel;
CutterTreeView *segmentsTable;
MainWindow *main;
QWidget *dockWidgetContents;
QuickFilterView *quickFilterView;
}; };
#endif // SEGMENTSWIDGET_H #endif // SEGMENTSWIDGET_H

View File

@ -1,12 +1,12 @@
#include "SymbolsWidget.h" #include "SymbolsWidget.h"
#include "ui_SymbolsWidget.h" #include "ui_ListDockWidget.h"
#include "core/MainWindow.h" #include "core/MainWindow.h"
#include "common/Helpers.h" #include "common/Helpers.h"
#include <QShortcut> #include <QShortcut>
SymbolsModel::SymbolsModel(QList<SymbolDescription> *symbols, QObject *parent) SymbolsModel::SymbolsModel(QList<SymbolDescription> *symbols, QObject *parent)
: QAbstractListModel(parent), : AddressableItemModel<QAbstractListModel>(parent),
symbols(symbols) symbols(symbols)
{ {
} }
@ -67,10 +67,21 @@ QVariant SymbolsModel::headerData(int section, Qt::Orientation, int role) const
} }
} }
SymbolsProxyModel::SymbolsProxyModel(SymbolsModel *sourceModel, QObject *parent) RVA SymbolsModel::address(const QModelIndex &index) const
: QSortFilterProxyModel(parent) {
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)
: AddressableFilterProxyModel(sourceModel, parent)
{ {
setSourceModel(sourceModel);
setFilterCaseSensitivity(Qt::CaseInsensitive); setFilterCaseSensitivity(Qt::CaseInsensitive);
setSortCaseSensitivity(Qt::CaseInsensitive); setSortCaseSensitivity(Qt::CaseInsensitive);
} }
@ -103,68 +114,26 @@ bool SymbolsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &rig
} }
SymbolsWidget::SymbolsWidget(MainWindow *main, QAction *action) : SymbolsWidget::SymbolsWidget(MainWindow *main, QAction *action) :
CutterDockWidget(main, action), ListDockWidget(main, action)
ui(new Ui::SymbolsWidget),
tree(new CutterTreeWidget(this))
{ {
ui->setupUi(this); setWindowTitle(tr("Symbols"));
setObjectName("SymbolsWidget");
// Add Status Bar footer
tree->addStatusBar(ui->verticalLayout);
symbolsModel = new SymbolsModel(&symbols, this); symbolsModel = new SymbolsModel(&symbols, this);
symbolsProxyModel = new SymbolsProxyModel(symbolsModel, this); symbolsProxyModel = new SymbolsProxyModel(symbolsModel, this);
ui->symbolsTreeView->setModel(symbolsProxyModel); setModels(symbolsProxyModel);
ui->symbolsTreeView->sortByColumn(SymbolsModel::AddressColumn, Qt::AscendingOrder); ui->treeView->sortByColumn(SymbolsModel::AddressColumn, Qt::AscendingOrder);
// Ctrl-F to show/hide the filter entry connect(Core(), &CutterCore::refreshAll, this, &SymbolsWidget::refreshSymbols);
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()));
} }
SymbolsWidget::~SymbolsWidget() {} SymbolsWidget::~SymbolsWidget() {}
void SymbolsWidget::on_symbolsTreeView_doubleClicked(const QModelIndex &index)
{
if (!index.isValid()) {
return;
}
auto symbol = index.data(SymbolsModel::SymbolDescriptionRole).value<SymbolDescription>();
Core()->seekAndShow(symbol.vaddr);
}
void SymbolsWidget::refreshSymbols() void SymbolsWidget::refreshSymbols()
{ {
symbolsModel->beginResetModel(); symbolsModel->beginResetModel();
symbols = Core()->getAllSymbols(); symbols = Core()->getAllSymbols();
symbolsModel->endResetModel(); symbolsModel->endResetModel();
qhelpers::adjustColumns(ui->symbolsTreeView, SymbolsModel::ColumnCount, 0); qhelpers::adjustColumns(ui->treeView, SymbolsModel::ColumnCount, 0);
tree->showItemsNumber(symbolsProxyModel->rowCount());
}
void SymbolsWidget::setScrollMode()
{
qhelpers::setVerticalScrollMode(ui->symbolsTreeView);
} }

View File

@ -7,17 +7,15 @@
#include "core/Cutter.h" #include "core/Cutter.h"
#include "CutterDockWidget.h" #include "CutterDockWidget.h"
#include "CutterTreeWidget.h" #include "widgets/ListDockWidget.h"
class MainWindow; class MainWindow;
class QTreeWidgetItem; class QTreeWidgetItem;
class SymbolsWidget; class SymbolsWidget;
namespace Ui {
class SymbolsWidget;
}
class SymbolsModel: public QAbstractListModel class SymbolsModel: public AddressableItemModel<QAbstractListModel>
{ {
Q_OBJECT Q_OBJECT
@ -32,14 +30,17 @@ public:
SymbolsModel(QList<SymbolDescription> *exports, QObject *parent = nullptr); SymbolsModel(QList<SymbolDescription> *exports, QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const; int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const; QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; 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 SymbolsProxyModel : public QSortFilterProxyModel class SymbolsProxyModel : public AddressableFilterProxyModel
{ {
Q_OBJECT Q_OBJECT
@ -52,7 +53,7 @@ protected:
}; };
class SymbolsWidget : public CutterDockWidget class SymbolsWidget : public ListDockWidget
{ {
Q_OBJECT Q_OBJECT
@ -61,19 +62,12 @@ public:
~SymbolsWidget(); ~SymbolsWidget();
private slots: private slots:
void on_symbolsTreeView_doubleClicked(const QModelIndex &index);
void refreshSymbols(); void refreshSymbols();
private: private:
std::unique_ptr<Ui::SymbolsWidget> ui;
QList<SymbolDescription> symbols; QList<SymbolDescription> symbols;
SymbolsModel *symbolsModel; SymbolsModel *symbolsModel;
SymbolsProxyModel *symbolsProxyModel; SymbolsProxyModel *symbolsProxyModel;
CutterTreeWidget *tree;
void setScrollMode();
}; };
#endif // SYMBOLSWIDGET_H #endif // SYMBOLSWIDGET_H

View File

@ -1,62 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SymbolsWidget</class>
<widget class="QDockWidget" name="SymbolsWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Symbols</string>
</property>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="CutterTreeView" name="symbolsTreeView">
<property name="sortingEnabled">
<bool>true</bool>
</property>
<attribute name="headerShowSortIndicator" stdset="0">
<bool>true</bool>
</attribute>
</widget>
</item>
<item>
<widget class="QuickFilterView" name="quickFilterView" native="true"/>
</item>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>CutterTreeView</class>
<extends>QTreeView</extends>
<header>widgets/CutterTreeView.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QuickFilterView</class>
<extends>QWidget</extends>
<header>widgets/QuickFilterView.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -146,6 +146,7 @@ VTablesWidget::VTablesWidget(MainWindow *main, QAction *action) :
// Esc to clear the filter entry // Esc to clear the filter entry
QShortcut *clear_shortcut = new QShortcut(QKeySequence(Qt::Key_Escape), this); QShortcut *clear_shortcut = new QShortcut(QKeySequence(Qt::Key_Escape), this);
connect(clear_shortcut, &QShortcut::activated, ui->quickFilterView, &QuickFilterView::clearFilter); connect(clear_shortcut, &QShortcut::activated, ui->quickFilterView, &QuickFilterView::clearFilter);
clear_shortcut->setContext(Qt::WidgetWithChildrenShortcut);
// Ctrl-F to show/hide the filter entry // Ctrl-F to show/hide the filter entry
QShortcut *search_shortcut = new QShortcut(QKeySequence::Find, this); QShortcut *search_shortcut = new QShortcut(QKeySequence::Find, this);