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

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();
}
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)
{
switch (type) {

View File

@ -91,6 +91,13 @@ public:
void delFlag(RVA addr);
void delFlag(const QString &name);
void addFlag(RVA offset, QString name, RVA size);
/**
* @brief Get nearest flag at or before offset.
* @param offset search position
* @param flagOffsetOut adress of returned flag
* @return flag name
*/
QString nearestFlag(RVA offset, RVA *flagOffsetOut);
void triggerFlagsChanged();
/* Edition functions */

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
#include "ExportsWidget.h"
#include "ui_ExportsWidget.h"
#include "ui_ListDockWidget.h"
#include "core/MainWindow.h"
#include "common/Helpers.h"
#include "WidgetShortcuts.h"
@ -7,7 +7,7 @@
#include <QShortcut>
ExportsModel::ExportsModel(QList<ExportDescription> *exports, QObject *parent)
: QAbstractListModel(parent),
: AddressableItemModel<QAbstractListModel>(parent),
exports(exports)
{
}
@ -71,10 +71,21 @@ QVariant ExportsModel::headerData(int section, Qt::Orientation, int role) const
}
}
ExportsProxyModel::ExportsProxyModel(ExportsModel *source_model, QObject *parent)
: QSortFilterProxyModel(parent)
RVA ExportsModel::address(const QModelIndex &index) const
{
const ExportDescription &exp = exports->at(index.row());
return exp.vaddr;
}
QString ExportsModel::name(const QModelIndex &index) const
{
const ExportDescription &exp = exports->at(index.row());
return exp.name;
}
ExportsProxyModel::ExportsProxyModel(ExportsModel *source_model, QObject *parent)
: AddressableFilterProxyModel(source_model, parent)
{
setSourceModel(source_model);
setFilterCaseSensitivity(Qt::CaseInsensitive);
setSortCaseSensitivity(Qt::CaseInsensitive);
}
@ -115,19 +126,15 @@ bool ExportsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &rig
}
ExportsWidget::ExportsWidget(MainWindow *main, QAction *action) :
CutterDockWidget(main, action),
ui(new Ui::ExportsWidget),
tree(new CutterTreeWidget(this))
ListDockWidget(main, action)
{
ui->setupUi(this);
setWindowTitle(tr("Exports"));
setObjectName("ExportsWidget");
// Add Status Bar footer
tree->addStatusBar(ui->verticalLayout);
exportsModel = new ExportsModel(&exports, this);
exportsProxyModel = new ExportsProxyModel(exportsModel, this);
ui->exportsTreeView->setModel(exportsProxyModel);
ui->exportsTreeView->sortByColumn(ExportsModel::OffsetColumn, Qt::AscendingOrder);
setModels(exportsProxyModel);
ui->treeView->sortByColumn(ExportsModel::OffsetColumn, Qt::AscendingOrder);
QShortcut *toggle_shortcut = new QShortcut(widgetShortcuts["ExportsWidget"], main);
connect(toggle_shortcut, &QShortcut::activated, this, [=] (){
@ -135,26 +142,6 @@ ExportsWidget::ExportsWidget(MainWindow *main, QAction *action) :
main->updateDockActionChecked(action);
} );
// Ctrl-F to show/hide the filter entry
QShortcut *searchShortcut = new QShortcut(QKeySequence::Find, this);
connect(searchShortcut, &QShortcut::activated, ui->quickFilterView, &QuickFilterView::showFilter);
searchShortcut->setContext(Qt::WidgetWithChildrenShortcut);
// Esc to clear the filter entry
QShortcut *clearShortcut = new QShortcut(QKeySequence(Qt::Key_Escape), this);
connect(clearShortcut, &QShortcut::activated, ui->quickFilterView, &QuickFilterView::clearFilter);
clearShortcut->setContext(Qt::WidgetWithChildrenShortcut);
connect(ui->quickFilterView, SIGNAL(filterTextChanged(const QString &)),
exportsProxyModel, SLOT(setFilterWildcard(const QString &)));
connect(ui->quickFilterView, SIGNAL(filterClosed()), ui->exportsTreeView, SLOT(setFocus()));
connect(ui->quickFilterView, &QuickFilterView::filterTextChanged, this, [this] {
tree->showItemsNumber(exportsProxyModel->rowCount());
});
setScrollMode();
connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshExports()));
}
@ -166,22 +153,5 @@ void ExportsWidget::refreshExports()
exports = Core()->getAllExports();
exportsModel->endResetModel();
qhelpers::adjustColumns(ui->exportsTreeView, 3, 0);
tree->showItemsNumber(exportsProxyModel->rowCount());
}
void ExportsWidget::setScrollMode()
{
qhelpers::setVerticalScrollMode(ui->exportsTreeView);
}
void ExportsWidget::on_exportsTreeView_doubleClicked(const QModelIndex &index)
{
if (!index.isValid())
return;
ExportDescription exp = index.data(ExportsModel::ExportDescriptionRole).value<ExportDescription>();
Core()->seekAndShow(exp.vaddr);
qhelpers::adjustColumns(ui->treeView, 3, 0);
}

View File

@ -5,7 +5,7 @@
#include "core/Cutter.h"
#include "CutterDockWidget.h"
#include "CutterTreeWidget.h"
#include "widgets/ListDockWidget.h"
#include <QAbstractListModel>
#include <QSortFilterProxyModel>
@ -18,7 +18,7 @@ namespace Ui {
class ExportsWidget;
}
class ExportsModel : public QAbstractListModel
class ExportsModel : public AddressableItemModel<QAbstractListModel>
{
Q_OBJECT
@ -33,14 +33,17 @@ public:
ExportsModel(QList<ExportDescription> *exports, QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
RVA address(const QModelIndex &index) const override;
QString name(const QModelIndex &index) const override;
};
class ExportsProxyModel : public QSortFilterProxyModel
class ExportsProxyModel : public AddressableFilterProxyModel
{
Q_OBJECT
@ -52,7 +55,7 @@ protected:
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
};
class ExportsWidget : public CutterDockWidget
class ExportsWidget : public ListDockWidget
{
Q_OBJECT
@ -61,19 +64,12 @@ public:
~ExportsWidget();
private slots:
void on_exportsTreeView_doubleClicked(const QModelIndex &index);
void refreshExports();
private:
std::unique_ptr<Ui::ExportsWidget> ui;
ExportsModel *exportsModel;
ExportsProxyModel *exportsProxyModel;
QList<ExportDescription> exports;
CutterTreeWidget *tree;
void setScrollMode();
};
#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 "ui_FunctionsWidget.h"
#include "ui_ListDockWidget.h"
#include "core/MainWindow.h"
#include "common/Helpers.h"
@ -8,6 +8,7 @@
#include "dialogs/XrefsDialog.h"
#include "common/FunctionsTask.h"
#include "common/TempConfig.h"
#include "menus/AddressableItemContextMenu.h"
#include <algorithm>
#include <QMenu>
@ -28,7 +29,7 @@ static const int kMaxTooltipHighlightsLines = 5;
FunctionModel::FunctionModel(QList<FunctionDescription> *functions, QSet<RVA> *importAddresses,
ut64 *mainAdress, bool nested, QFont default_font, QFont highlight_font, QObject *parent)
: QAbstractItemModel(parent),
: AddressableItemModel<>(parent),
functions(functions),
importAddresses(importAddresses),
mainAdress(mainAdress),
@ -147,21 +148,21 @@ QVariant FunctionModel::data(const QModelIndex &index, int role) const
case NameColumn:
return function.name;
case SizeColumn:
return function.size;
return QString::number(function.size);
case OffsetColumn:
return RAddressString(function.offset);
case NargsColumn:
return function.nargs;
return QString::number(function.nargs);
case NlocalsColumn:
return function.nlocals;
return QString::number(function.nlocals);
case NbbsColumn:
return function.nbbs;
return QString::number(function.nbbs);
case CalltypeColumn:
return function.calltype;
case EdgesColumn:
return function.edges;
return QString::number(function.edges);
case FrameColumn:
return function.stackframe;
return QString::number(function.stackframe);
default:
return QVariant();
}
@ -187,7 +188,8 @@ QVariant FunctionModel::data(const QModelIndex &index, int role) const
case Qt::ToolTipRole: {
QStringList disasmPreview = Core()->getDisassemblyPreview(function.offset, kMaxTooltipDisasmPreviewLines);
QStringList disasmPreview = Core()->getDisassemblyPreview(function.offset,
kMaxTooltipDisasmPreviewLines);
const QStringList &summary = Core()->cmdList(QString("pdsf @ %1").arg(function.offset));
const QFont &fnt = Config()->getFont();
QFontMetrics fm{ fnt };
@ -204,17 +206,20 @@ QVariant FunctionModel::data(const QModelIndex &index, int role) const
if (disasmPreview.isEmpty() && highlights.isEmpty())
return QVariant();
QString toolTipContent = QString("<html><div style=\"font-family: %1; font-size: %2pt; white-space: nowrap;\">")
.arg(fnt.family())
.arg(qMax(6, fnt.pointSize() - 1)); // slightly decrease font size, to keep more text in the same box
QString toolTipContent =
QString("<html><div style=\"font-family: %1; font-size: %2pt; white-space: nowrap;\">")
.arg(fnt.family())
.arg(qMax(6, fnt.pointSize() -
1)); // slightly decrease font size, to keep more text in the same box
if (!disasmPreview.isEmpty())
toolTipContent += tr("<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>"));
if (!highlights.isEmpty()) {
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>";
return toolTipContent;
@ -282,6 +287,18 @@ void FunctionModel::setNested(bool nested)
endResetModel();
}
RVA FunctionModel::address(const QModelIndex &index) const
{
auto function = data(index, FunctionDescriptionRole).value<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)
{
int previousIndex = currentIndex;
@ -332,9 +349,8 @@ void FunctionModel::functionRenamed(const QString &prev_name, const QString &new
FunctionSortFilterProxyModel::FunctionSortFilterProxyModel(FunctionModel *source_model,
QObject *parent)
: QSortFilterProxyModel(parent)
: AddressableFilterProxyModel(source_model, parent)
{
setSourceModel(source_model);
setFilterCaseSensitivity(Qt::CaseInsensitive);
setSortCaseSensitivity(Qt::CaseInsensitive);
}
@ -411,64 +427,56 @@ bool FunctionSortFilterProxyModel::lessThan(const QModelIndex &left, const QMode
}
FunctionsWidget::FunctionsWidget(MainWindow *main, QAction *action) :
CutterDockWidget(main, action),
ui(new Ui::FunctionsWidget),
tree(new CutterTreeWidget(this))
ListDockWidget(main, action),
actionRename(tr("Rename"), this),
actionUndefine(tr("Undefine"), this),
actionHorizontal(tr("Horizontal"), this),
actionVertical(tr("Vertical"), this)
{
ui->setupUi(this);
setWindowTitle(tr("Functions"));
setObjectName("FunctionsWidget");
// Add Status Bar footer
tree->addStatusBar(ui->verticalLayout);
// Radare core found in:
this->main = main;
setTooltipStylesheet();
connect(Config(), SIGNAL(colorsUpdated()), this, SLOT(setTooltipStylesheet()));
// leave the filter visible by default so users know it exists
//ui->filterLineEdit->setVisible(false);
// Ctrl-F to show/hide the filter entry
QShortcut *search_shortcut = new QShortcut(QKeySequence::Find, this);
connect(search_shortcut, &QShortcut::activated, ui->quickFilterView, &QuickFilterView::showFilter);
search_shortcut->setContext(Qt::WidgetWithChildrenShortcut);
// Esc to clear the filter entry
QShortcut *clear_shortcut = new QShortcut(QKeySequence(Qt::Key_Escape), this);
connect(clear_shortcut, &QShortcut::activated, ui->quickFilterView, &QuickFilterView::clearFilter);
clear_shortcut->setContext(Qt::WidgetWithChildrenShortcut);
QFontInfo font_info = ui->functionsTreeView->fontInfo();
QFontInfo font_info = ui->treeView->fontInfo();
QFont default_font = QFont(font_info.family(), font_info.pointSize());
QFont highlight_font = QFont(font_info.family(), font_info.pointSize(), QFont::Bold);
functionModel = new FunctionModel(&functions, &importAddresses, &mainAdress, false, default_font,
highlight_font, this);
functionProxyModel = new FunctionSortFilterProxyModel(functionModel, this);
ui->functionsTreeView->setModel(functionProxyModel);
ui->functionsTreeView->sortByColumn(FunctionModel::NameColumn, Qt::AscendingOrder);
setModels(functionProxyModel);
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] {
tree->showItemsNumber(functionProxyModel->rowCount());
});
setScrollMode();
titleContextMenu = new QMenu(this);
auto viewTypeGroup = new QActionGroup(titleContextMenu);
actionHorizontal.setCheckable(true);
actionHorizontal.setActionGroup(viewTypeGroup);
connect(&actionHorizontal, &QAction::toggled, this, &FunctionsWidget::onActionHorizontalToggled);
actionVertical.setCheckable(true);
actionVertical.setActionGroup(viewTypeGroup);
connect(&actionVertical, &QAction::toggled, this, &FunctionsWidget::onActionVerticalToggled);
titleContextMenu->addActions(viewTypeGroup->actions());
// Set Functions context menu
connect(ui->functionsTreeView, SIGNAL(customContextMenuRequested(const QPoint &)),
this, SLOT(showFunctionsContextMenu(const QPoint &)));
actionRename.setShortcut({Qt::Key_N});
actionRename.setShortcutContext(Qt::ShortcutContext::WidgetWithChildrenShortcut);
connect(&actionRename, &QAction::triggered, this,
&FunctionsWidget::onActionFunctionsRenameTriggered);
connect(&actionUndefine, &QAction::triggered, this,
&FunctionsWidget::onActionFunctionsUndefineTriggered);
connect(ui->functionsTreeView, SIGNAL(doubleClicked(const QModelIndex &)), this,
SLOT(onFunctionsDoubleClicked(const QModelIndex &)));
auto itemConextMenu = getItemContextMenu();
itemConextMenu->addSeparator();
itemConextMenu->addAction(&actionRename);
itemConextMenu->addAction(&actionUndefine);
itemConextMenu->setWholeFunction(true);
addActions(itemConextMenu->actions());
// Use a custom context menu on the dock title bar
//this->title_bar = this->titleBarWidget();
ui->actionHorizontal->setChecked(true);
actionHorizontal.setChecked(true);
this->setContextMenuPolicy(Qt::CustomContextMenu);
connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
this, SLOT(showTitleContextMenu(const QPoint &)));
@ -503,9 +511,7 @@ void FunctionsWidget::refreshTree()
functionModel->endResetModel();
// resize offset and size columns
qhelpers::adjustColumns(ui->functionsTreeView, 3, 0);
tree->showItemsNumber(functionProxyModel->rowCount());
qhelpers::adjustColumns(ui->treeView, 3, 0);
});
Core()->getAsyncTaskManager()->start(task);
}
@ -515,56 +521,10 @@ void FunctionsWidget::changeSizePolicy(QSizePolicy::Policy hor, QSizePolicy::Pol
ui->dockWidgetContents->setSizePolicy(hor, ver);
}
void FunctionsWidget::onFunctionsDoubleClicked(const QModelIndex &index)
{
if (!index.isValid())
return;
FunctionDescription function = index.data(
FunctionModel::FunctionDescriptionRole).value<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()
void FunctionsWidget::onActionFunctionsRenameTriggered()
{
// Get selected item in functions tree view
FunctionDescription function = ui->functionsTreeView->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(
FunctionDescription function = ui->treeView->selectionModel()->currentIndex().data(
FunctionModel::FunctionDescriptionRole).value<FunctionDescription>();
// 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
FunctionDescription function = ui->functionsTreeView->selectionModel()->currentIndex().data(
FunctionModel::FunctionDescriptionRole).value<FunctionDescription>();
XrefsDialog x(nullptr);
x.fillRefsForAddress(function.offset, function.name, true);
x.exec();
const auto selection = ui->treeView->selectionModel()->selection().indexes();
std::vector<RVA> offsets;
offsets.reserve(selection.size());
for (const auto &index : selection) {
offsets.push_back(functionProxyModel->address(index));
}
for (RVA offset : offsets) {
Core()->delFunction(offset);
}
}
void FunctionsWidget::showTitleContextMenu(const QPoint &pt)
{
// Set functions popup menu
QMenu *menu = new QMenu(this);
menu->clear();
menu->addAction(ui->actionHorizontal);
menu->addAction(ui->actionVertical);
titleContextMenu->exec(this->mapToGlobal(pt));
}
if (!functionModel->isNested()) {
ui->actionHorizontal->setChecked(true);
ui->actionVertical->setChecked(false);
} else {
ui->actionVertical->setChecked(true);
ui->actionHorizontal->setChecked(false);
void FunctionsWidget::onActionHorizontalToggled(bool enable)
{
if (enable) {
functionModel->setNested(false);
ui->treeView->setIndentation(8);
}
this->setContextMenuPolicy(Qt::CustomContextMenu);
menu->exec(this->mapToGlobal(pt));
delete menu;
}
void FunctionsWidget::on_actionHorizontal_triggered()
void FunctionsWidget::onActionVerticalToggled(bool enable)
{
functionModel->setNested(false);
ui->functionsTreeView->setIndentation(8);
}
void FunctionsWidget::on_actionVertical_triggered()
{
functionModel->setNested(true);
ui->functionsTreeView->setIndentation(20);
if (enable) {
functionModel->setNested(true);
ui->treeView->setIndentation(20);
}
}
void FunctionsWidget::resizeEvent(QResizeEvent *event)
{
if (main->responsive && isVisible()) {
if (mainWindow->responsive && isVisible()) {
if (event->size().width() >= event->size().height()) {
// Set horizontal view (list)
on_actionHorizontal_triggered();
actionHorizontal.setChecked(true);
} else {
// Set vertical view (Tree)
on_actionVertical_triggered();
actionVertical.setChecked(true);
}
}
QDockWidget::resizeEvent(event);
}
void FunctionsWidget::setScrollMode()
{
qhelpers::setVerticalScrollMode(ui->functionsTreeView);
}
/**
* @brief a SLOT to set the stylesheet for a tooltip
*/
*/
void FunctionsWidget::setTooltipStylesheet()
{
setStyleSheet(QString("QToolTip { border-width: 1px; max-width: %1px;" \

View File

@ -3,24 +3,15 @@
#include <memory>
#include <QSortFilterProxyModel>
#include "core/Cutter.h"
#include "CutterDockWidget.h"
#include "CutterTreeWidget.h"
#include "CutterTreeView.h"
#include "widgets/ListDockWidget.h"
class MainWindow;
class QTreeWidgetItem;
class FunctionsTask;
class FunctionsWidget;
namespace Ui {
class FunctionsWidget;
}
class FunctionModel : public QAbstractItemModel
class FunctionModel : public AddressableItemModel<>
{
Q_OBJECT
@ -53,14 +44,15 @@ public:
FunctionModel(QList<FunctionDescription> *functions, QSet<RVA> *importAddresses, ut64 *mainAdress,
bool nested, QFont defaultFont, QFont highlightFont, QObject *parent = nullptr);
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
QModelIndex parent(const QModelIndex &index) const;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &index) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
/**
* @return true if the index changed
@ -73,13 +65,15 @@ public:
return nested;
}
RVA address(const QModelIndex &index) const override;
QString name(const QModelIndex &index) const override;
private slots:
void seekChanged(RVA addr);
void functionRenamed(const QString &prev_name, const QString &new_name);
};
class FunctionSortFilterProxyModel : public QSortFilterProxyModel
class FunctionSortFilterProxyModel : public AddressableFilterProxyModel
{
Q_OBJECT
@ -93,24 +87,20 @@ protected:
class FunctionsWidget : public CutterDockWidget
class FunctionsWidget : public ListDockWidget
{
Q_OBJECT
public:
explicit FunctionsWidget(MainWindow *main, QAction *action = nullptr);
~FunctionsWidget();
~FunctionsWidget() override;
void changeSizePolicy(QSizePolicy::Policy hor, QSizePolicy::Policy ver);
private slots:
void onFunctionsDoubleClicked(const QModelIndex &index);
void showFunctionsContextMenu(const QPoint &pt);
void on_actionDisasAdd_comment_triggered();
void on_actionFunctionsRename_triggered();
void on_action_References_triggered();
void on_actionFunctionsUndefine_triggered();
void on_actionHorizontal_triggered();
void on_actionVertical_triggered();
void onActionFunctionsRenameTriggered();
void onActionFunctionsUndefineTriggered();
void onActionHorizontalToggled(bool enable);
void onActionVerticalToggled(bool enable);
void showTitleContextMenu(const QPoint &pt);
void setTooltipStylesheet();
void refreshTree();
@ -119,16 +109,19 @@ protected:
void resizeEvent(QResizeEvent *event) override;
private:
std::unique_ptr<Ui::FunctionsWidget> ui;
MainWindow *main;
QSharedPointer<FunctionsTask> task;
QList<FunctionDescription> functions;
QSet<RVA> importAddresses;
ut64 mainAdress;
FunctionModel *functionModel;
FunctionSortFilterProxyModel *functionProxyModel;
CutterTreeWidget *tree;
void setScrollMode();
QMenu *titleContextMenu;
QAction actionRename;
QAction actionUndefine;
QAction actionHorizontal;
QAction actionVertical;
};
#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 "ui_HeadersWidget.h"
#include "ui_ListDockWidget.h"
#include "core/MainWindow.h"
#include "common/Helpers.h"
HeadersModel::HeadersModel(QList<HeaderDescription> *headers, QObject *parent)
: QAbstractListModel(parent),
: AddressableItemModel<QAbstractListModel>(parent),
headers(headers)
{
}
@ -64,10 +64,21 @@ QVariant HeadersModel::headerData(int section, Qt::Orientation, int role) const
}
}
HeadersProxyModel::HeadersProxyModel(HeadersModel *sourceModel, QObject *parent)
: QSortFilterProxyModel(parent)
RVA HeadersModel::address(const QModelIndex &index) const
{
const HeaderDescription &header = headers->at(index.row());
return header.vaddr;
}
QString HeadersModel::name(const QModelIndex &index) const
{
const HeaderDescription &header = headers->at(index.row());
return header.name;
}
HeadersProxyModel::HeadersProxyModel(HeadersModel *sourceModel, QObject *parent)
: AddressableFilterProxyModel(sourceModel, parent)
{
setSourceModel(sourceModel);
}
bool HeadersProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const
@ -99,17 +110,18 @@ bool HeadersProxyModel::lessThan(const QModelIndex &left, const QModelIndex &rig
}
HeadersWidget::HeadersWidget(MainWindow *main, QAction *action) :
CutterDockWidget(main, action),
ui(new Ui::HeadersWidget)
ListDockWidget(main, action)
{
ui->setupUi(this);
setWindowTitle(tr("Headers"));
setObjectName("HeadersWidget");
headersModel = new HeadersModel(&headers, this);
headersProxyModel = new HeadersProxyModel(headersModel, this);
ui->headersTreeView->setModel(headersProxyModel);
ui->headersTreeView->sortByColumn(HeadersModel::OffsetColumn, Qt::AscendingOrder);
setModels(headersProxyModel);
ui->treeView->sortByColumn(HeadersModel::OffsetColumn, Qt::AscendingOrder);
setScrollMode();
ui->quickFilterView->closeFilter();
showCount(false);
connect(Core(), &CutterCore::refreshAll, this, &HeadersWidget::refreshHeaders);
}
@ -122,17 +134,6 @@ void HeadersWidget::refreshHeaders()
headers = Core()->getAllHeaders();
headersModel->endResetModel();
ui->headersTreeView->resizeColumnToContents(0);
ui->headersTreeView->resizeColumnToContents(1);
}
void HeadersWidget::setScrollMode()
{
qhelpers::setVerticalScrollMode(ui->headersTreeView);
}
void HeadersWidget::on_headersTreeView_doubleClicked(const QModelIndex &index)
{
HeaderDescription item = index.data(HeadersModel::HeaderDescriptionRole).value<HeaderDescription>();
Core()->seekAndShow(item.vaddr);
ui->treeView->resizeColumnToContents(0);
ui->treeView->resizeColumnToContents(1);
}

View File

@ -4,7 +4,7 @@
#include <memory>
#include "core/Cutter.h"
#include "CutterDockWidget.h"
#include "ListDockWidget.h"
#include <QAbstractListModel>
#include <QSortFilterProxyModel>
@ -22,7 +22,7 @@ class QTreeWidgetItem;
class HeadersWidget;
class HeadersModel: public QAbstractListModel
class HeadersModel: public AddressableItemModel<QAbstractListModel>
{
Q_OBJECT
@ -35,18 +35,21 @@ public:
enum Column { OffsetColumn = 0, NameColumn, ValueColumn, ColumnCount };
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 columnCount(const QModelIndex &parent = QModelIndex()) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
RVA address(const QModelIndex &index) const override;
QString name(const QModelIndex &index) const override;
};
class HeadersProxyModel : public QSortFilterProxyModel
class HeadersProxyModel : public AddressableFilterProxyModel
{
Q_OBJECT
@ -60,7 +63,7 @@ protected:
class HeadersWidget : public CutterDockWidget
class HeadersWidget : public ListDockWidget
{
Q_OBJECT
@ -69,18 +72,12 @@ public:
~HeadersWidget();
private slots:
void on_headersTreeView_doubleClicked(const QModelIndex &index);
void refreshHeaders();
private:
std::unique_ptr<Ui::HeadersWidget> ui;
HeadersModel *headersModel;
HeadersProxyModel *headersProxyModel;
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 "ui_ImportsWidget.h"
#include "ui_ListDockWidget.h"
#include "WidgetShortcuts.h"
#include "core/MainWindow.h"
#include "common/Helpers.h"
@ -10,7 +10,7 @@
#include <QTreeWidget>
ImportsModel::ImportsModel(QList<ImportDescription> *imports, QObject *parent) :
QAbstractTableModel(parent),
AddressableItemModel(parent),
imports(imports)
{}
@ -81,10 +81,21 @@ QVariant ImportsModel::headerData(int section, Qt::Orientation, int role) const
return QVariant();
}
ImportsProxyModel::ImportsProxyModel(ImportsModel *sourceModel, QObject *parent)
: QSortFilterProxyModel(parent)
RVA ImportsModel::address(const QModelIndex &index) const
{
const ImportDescription &import = imports->at(index.row());
return import.plt;
}
QString ImportsModel::name(const QModelIndex &index) const
{
const ImportDescription &import = imports->at(index.row());
return import.name;
}
ImportsProxyModel::ImportsProxyModel(ImportsModel *sourceModel, QObject *parent)
: AddressableFilterProxyModel(sourceModel, parent)
{
setSourceModel(sourceModel);
setFilterCaseSensitivity(Qt::CaseInsensitive);
setSortCaseSensitivity(Qt::CaseInsensitive);
}
@ -129,46 +140,21 @@ bool ImportsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &rig
*/
ImportsWidget::ImportsWidget(MainWindow *main, QAction *action) :
CutterDockWidget(main, action),
ui(new Ui::ImportsWidget),
ListDockWidget(main, action),
importsModel(new ImportsModel(&imports, this)),
importsProxyModel(new ImportsProxyModel(importsModel, this)),
tree(new CutterTreeWidget(this))
importsProxyModel(new ImportsProxyModel(importsModel, this))
{
ui->setupUi(this);
// Add Status Bar footer
tree->addStatusBar(ui->verticalLayout);
ui->importsTreeView->setModel(importsProxyModel);
ui->importsTreeView->sortByColumn(ImportsModel::NameColumn, Qt::AscendingOrder);
setWindowTitle(tr("Imports"));
setObjectName("ImportsWidget");
setModels(importsProxyModel);
ui->treeView->sortByColumn(ImportsModel::NameColumn, Qt::AscendingOrder);
QShortcut *toggle_shortcut = new QShortcut(widgetShortcuts["ImportsWidget"], main);
connect(toggle_shortcut, &QShortcut::activated, this, [=] (){
toggleDockWidget(true);
main->updateDockActionChecked(action);
} );
// Ctrl-F to show/hide the filter entry
QShortcut *searchShortcut = new QShortcut(QKeySequence::Find, this);
connect(searchShortcut, &QShortcut::activated, ui->quickFilterView, &QuickFilterView::showFilter);
searchShortcut->setContext(Qt::WidgetWithChildrenShortcut);
// Esc to clear the filter entry
QShortcut *clearShortcut = new QShortcut(QKeySequence(Qt::Key_Escape), this);
connect(clearShortcut, &QShortcut::activated, ui->quickFilterView, &QuickFilterView::clearFilter);
clearShortcut->setContext(Qt::WidgetWithChildrenShortcut);
connect(ui->quickFilterView, SIGNAL(filterTextChanged(const QString &)),
importsProxyModel, SLOT(setFilterWildcard(const QString &)));
connect(ui->quickFilterView, SIGNAL(filterClosed()), ui->importsTreeView, SLOT(setFocus()));
connect(ui->quickFilterView, &QuickFilterView::filterTextChanged, this, [this] {
tree->showItemsNumber(importsProxyModel->rowCount());
});
setScrollMode();
connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshImports()));
}
@ -179,20 +165,5 @@ void ImportsWidget::refreshImports()
importsModel->beginResetModel();
imports = Core()->getAllImports();
importsModel->endResetModel();
qhelpers::adjustColumns(ui->importsTreeView, 4, 0);
tree->showItemsNumber(importsProxyModel->rowCount());
}
void ImportsWidget::setScrollMode()
{
qhelpers::setVerticalScrollMode(ui->importsTreeView);
}
void ImportsWidget::on_importsTreeView_doubleClicked(const QModelIndex &index)
{
if (!index.isValid())
return;
Core()->seekAndShow(index.data(ImportsModel::AddressRole).toLongLong());
qhelpers::adjustColumns(ui->treeView, 4, 0);
}

View File

@ -11,17 +11,14 @@
#include "CutterDockWidget.h"
#include "core/Cutter.h"
#include "CutterTreeWidget.h"
#include "widgets/ListDockWidget.h"
#include "common/AddressableItemModel.h"
class MainWindow;
class QTreeWidget;
class ImportsWidget;
namespace Ui {
class ImportsWidget;
}
class ImportsModel : public QAbstractTableModel
class ImportsModel : public AddressableItemModel<QAbstractTableModel>
{
Q_OBJECT
@ -51,14 +48,17 @@ public:
ImportsModel(QList<ImportDescription> *imports, QObject *parent = nullptr);
int rowCount(const QModelIndex &parent) const;
int columnCount(const QModelIndex &parent) const;
int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
QVariant data(const QModelIndex &index, int role) const override;
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
@ -70,7 +70,7 @@ protected:
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
};
class ImportsWidget : public CutterDockWidget
class ImportsWidget : public ListDockWidget
{
Q_OBJECT
@ -79,20 +79,13 @@ public:
~ImportsWidget();
private slots:
void on_importsTreeView_doubleClicked(const QModelIndex &index);
void refreshImports();
private:
std::unique_ptr<Ui::ImportsWidget> ui;
ImportsModel *importsModel;
ImportsProxyModel *importsProxyModel;
QList<ImportDescription> imports;
CutterTreeWidget *tree;
void highlightUnsafe();
void setScrollMode();
};
#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"?>
<ui version="4.0">
<class>RelocsWidget</class>
<widget class="QDockWidget" name="RelocsWidget">
<class>ListDockWidget</class>
<widget class="QDockWidget" name="ListDockWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
<width>645</width>
<height>250</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Relocs</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>
@ -28,7 +28,7 @@
<number>0</number>
</property>
<item>
<widget class="CutterTreeView" name="relocsTreeView">
<widget class="CutterTreeView" name="treeView">
<property name="styleSheet">
<string notr="true">CutterTreeView::item
{
@ -45,6 +45,9 @@
<property name="sortingEnabled">
<bool>true</bool>
</property>
<attribute name="headerShowSortIndicator" stdset="0">
<bool>true</bool>
</attribute>
</widget>
</item>
<item>

View File

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

View File

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

View File

@ -1,9 +1,9 @@
#include "SectionsWidget.h"
#include "CutterTreeView.h"
#include "QuickFilterView.h"
#include "core/MainWindow.h"
#include "common/Helpers.h"
#include "common/Configuration.h"
#include "ui_ListDockWidget.h"
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsTextItem>
@ -14,7 +14,7 @@
#include <QToolTip>
SectionsModel::SectionsModel(QList<SectionDescription> *sections, QObject *parent)
: QAbstractListModel(parent),
: AddressableItemModel<QAbstractListModel>(parent),
sections(sections)
{
}
@ -105,10 +105,21 @@ QVariant SectionsModel::headerData(int section, Qt::Orientation, int role) const
}
}
SectionsProxyModel::SectionsProxyModel(SectionsModel *sourceModel, QObject *parent)
: QSortFilterProxyModel(parent)
RVA SectionsModel::address(const QModelIndex &index) const
{
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);
setSortCaseSensitivity(Qt::CaseInsensitive);
}
@ -138,8 +149,7 @@ bool SectionsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &ri
}
SectionsWidget::SectionsWidget(MainWindow *main, QAction *action) :
CutterDockWidget(main, action),
main(main)
ListDockWidget(main, action)
{
setObjectName("SectionsWidget");
setWindowTitle(QStringLiteral("Sections"));
@ -158,42 +168,22 @@ SectionsWidget::~SectionsWidget() = default;
void SectionsWidget::initSectionsTable()
{
sectionsTable = new CutterTreeView;
sectionsModel = new SectionsModel(&sections, this);
proxyModel = new SectionsProxyModel(sectionsModel, this);
setModels(proxyModel);
sectionsTable->setModel(proxyModel);
sectionsTable->setIndentation(10);
sectionsTable->setSortingEnabled(true);
sectionsTable->sortByColumn(SectionsModel::NameColumn, Qt::AscendingOrder);
ui->treeView->sortByColumn(SectionsModel::NameColumn, Qt::AscendingOrder);
}
void SectionsWidget::initQuickFilter()
{
quickFilterView = new QuickFilterView(this, false);
quickFilterView->setObjectName(QStringLiteral("quickFilterView"));
QSizePolicy sizePolicy1(QSizePolicy::Preferred, QSizePolicy::Maximum);
sizePolicy1.setHorizontalStretch(0);
sizePolicy1.setVerticalStretch(0);
sizePolicy1.setHeightForWidth(quickFilterView->sizePolicy().hasHeightForWidth());
quickFilterView->setSizePolicy(sizePolicy1);
QShortcut *search_shortcut = new QShortcut(QKeySequence::Find, this);
search_shortcut->setContext(Qt::WidgetWithChildrenShortcut);
connect(search_shortcut, &QShortcut::activated, quickFilterView, &QuickFilterView::showFilter);
QShortcut *clear_shortcut = new QShortcut(QKeySequence(Qt::Key_Escape), this);
clear_shortcut->setContext(Qt::WidgetWithChildrenShortcut);
connect(clear_shortcut, &QShortcut::activated, quickFilterView, &QuickFilterView::clearFilter);
ui->quickFilterView->closeFilter();
}
void SectionsWidget::initAddrMapDocks()
{
dockWidgetContents = new QWidget(this);
QVBoxLayout *layout = new QVBoxLayout();
layout->addWidget(sectionsTable);
layout->addWidget(quickFilterView);
QVBoxLayout *layout = ui->verticalLayout;
showCount(false);
rawAddrDock = new RawAddrDock(sectionsModel, this);
virtualAddrDock = new VirtualAddrDock(sectionsModel, this);
@ -220,20 +210,11 @@ void SectionsWidget::initAddrMapDocks()
toggleButton->setArrowType(Qt::NoArrow);
toggleButton->hide();
layout->addWidget(toggleButton);
layout->setMargin(0);
dockWidgetContents->setLayout(layout);
setWidget(dockWidgetContents);
}
void SectionsWidget::initConnects()
{
connect(sectionsTable, SIGNAL(doubleClicked(const QModelIndex &)),
this, SLOT(onSectionsDoubleClicked(const QModelIndex &)));
connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshSections()));
connect(quickFilterView, SIGNAL(filterTextChanged(const QString &)), proxyModel,
SLOT(setFilterWildcard(const QString &)));
connect(quickFilterView, SIGNAL(filterClosed()), sectionsTable, SLOT(setFocus()));
connect(Core(), &CutterCore::refreshAll, this, &SectionsWidget::refreshSections);
connect(this, &QDockWidget::visibilityChanged, this, [ = ](bool visibility) {
if (visibility) {
refreshSections();
@ -261,7 +242,7 @@ void SectionsWidget::refreshSections()
sectionsModel->beginResetModel();
sections = Core()->getAllSections();
sectionsModel->endResetModel();
qhelpers::adjustColumns(sectionsTable, SectionsModel::ColumnCount, 0);
qhelpers::adjustColumns(ui->treeView, SectionsModel::ColumnCount, 0);
refreshDocks();
}
@ -296,16 +277,6 @@ void SectionsWidget::drawIndicatorOnAddrDocks()
}
}
void SectionsWidget::onSectionsDoubleClicked(const QModelIndex &index)
{
if (!index.isValid()) {
return;
}
auto section = index.data(SectionsModel::SectionDescriptionRole).value<SectionDescription>();
Core()->seekAndShow(section.vaddr);
}
void SectionsWidget::resizeEvent(QResizeEvent *event) {
CutterDockWidget::resizeEvent(event);
refreshDocks();

View File

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

View File

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

View File

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

View File

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

View File

@ -7,17 +7,15 @@
#include "core/Cutter.h"
#include "CutterDockWidget.h"
#include "CutterTreeWidget.h"
#include "widgets/ListDockWidget.h"
class MainWindow;
class QTreeWidgetItem;
class SymbolsWidget;
namespace Ui {
class SymbolsWidget;
}
class SymbolsModel: public QAbstractListModel
class SymbolsModel: public AddressableItemModel<QAbstractListModel>
{
Q_OBJECT
@ -32,14 +30,17 @@ public:
SymbolsModel(QList<SymbolDescription> *exports, QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
RVA address(const QModelIndex &index) const override;
QString name(const QModelIndex &index) const override;
};
class SymbolsProxyModel : public QSortFilterProxyModel
class SymbolsProxyModel : public AddressableFilterProxyModel
{
Q_OBJECT
@ -52,7 +53,7 @@ protected:
};
class SymbolsWidget : public CutterDockWidget
class SymbolsWidget : public ListDockWidget
{
Q_OBJECT
@ -61,19 +62,12 @@ public:
~SymbolsWidget();
private slots:
void on_symbolsTreeView_doubleClicked(const QModelIndex &index);
void refreshSymbols();
private:
std::unique_ptr<Ui::SymbolsWidget> ui;
QList<SymbolDescription> symbols;
SymbolsModel *symbolsModel;
SymbolsProxyModel *symbolsProxyModel;
CutterTreeWidget *tree;
void setScrollMode();
};
#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
QShortcut *clear_shortcut = new QShortcut(QKeySequence(Qt::Key_Escape), this);
connect(clear_shortcut, &QShortcut::activated, ui->quickFilterView, &QuickFilterView::clearFilter);
clear_shortcut->setContext(Qt::WidgetWithChildrenShortcut);
// Ctrl-F to show/hide the filter entry
QShortcut *search_shortcut = new QShortcut(QKeySequence::Find, this);