mirror of
https://github.com/rizinorg/cutter.git
synced 2025-01-18 18:38:51 +00:00
Common behaviour for lists with items that have address (#1700)
This commit is contained in:
parent
cd2dbc4a29
commit
0aa91c328c
@ -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 \
|
||||
|
30
src/common/AddressableItemModel.cpp
Normal file
30
src/common/AddressableItemModel.cpp
Normal 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;
|
||||
}
|
49
src/common/AddressableItemModel.h
Normal file
49
src/common/AddressableItemModel.h
Normal 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
|
@ -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) {
|
||||
|
@ -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 */
|
||||
|
103
src/menus/AddressableItemContextMenu.cpp
Normal file
103
src/menus/AddressableItemContextMenu.cpp
Normal 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));
|
||||
}
|
||||
|
43
src/menus/AddressableItemContextMenu.h
Normal file
43
src/menus/AddressableItemContextMenu.h
Normal 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
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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>
|
@ -23,4 +23,9 @@ void CutterTreeWidget::showItemsNumber(int count)
|
||||
}
|
||||
}
|
||||
|
||||
void CutterTreeWidget::showStatusBar(bool show)
|
||||
{
|
||||
bar->setVisible(show);
|
||||
}
|
||||
|
||||
CutterTreeWidget::~CutterTreeWidget() {}
|
||||
|
@ -16,6 +16,7 @@ public:
|
||||
~CutterTreeWidget();
|
||||
void addStatusBar(QVBoxLayout *pos);
|
||||
void showItemsNumber(int count);
|
||||
void showStatusBar(bool show);
|
||||
|
||||
private:
|
||||
QStatusBar *bar;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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>
|
@ -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;" \
|
||||
|
@ -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
|
||||
|
@ -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>
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
};
|
||||
|
||||
|
||||
|
@ -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>
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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>
|
118
src/widgets/ListDockWidget.cpp
Normal file
118
src/widgets/ListDockWidget.cpp
Normal 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);
|
||||
}
|
||||
}
|
57
src/widgets/ListDockWidget.h
Normal file
57
src/widgets/ListDockWidget.h
Normal 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
|
@ -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>
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 §ion = sections->at(index.row());
|
||||
return section.vaddr;
|
||||
}
|
||||
|
||||
QString SectionsModel::name(const QModelIndex &index) const
|
||||
{
|
||||
const SectionDescription §ion = sections->at(index.row());
|
||||
return section.name;
|
||||
}
|
||||
|
||||
SectionsProxyModel::SectionsProxyModel(SectionsModel *sourceModel, QObject *parent)
|
||||
: AddressableFilterProxyModel(sourceModel, parent)
|
||||
{
|
||||
setSourceModel(sourceModel);
|
||||
setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||
setSortCaseSensitivity(Qt::CaseInsensitive);
|
||||
}
|
||||
@ -138,8 +149,7 @@ bool SectionsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &ri
|
||||
}
|
||||
|
||||
SectionsWidget::SectionsWidget(MainWindow *main, QAction *action) :
|
||||
CutterDockWidget(main, action),
|
||||
main(main)
|
||||
ListDockWidget(main, action)
|
||||
{
|
||||
setObjectName("SectionsWidget");
|
||||
setWindowTitle(QStringLiteral("Sections"));
|
||||
@ -158,42 +168,22 @@ SectionsWidget::~SectionsWidget() = default;
|
||||
|
||||
void SectionsWidget::initSectionsTable()
|
||||
{
|
||||
sectionsTable = new CutterTreeView;
|
||||
sectionsModel = new SectionsModel(§ions, this);
|
||||
proxyModel = new SectionsProxyModel(sectionsModel, this);
|
||||
setModels(proxyModel);
|
||||
|
||||
sectionsTable->setModel(proxyModel);
|
||||
sectionsTable->setIndentation(10);
|
||||
sectionsTable->setSortingEnabled(true);
|
||||
sectionsTable->sortByColumn(SectionsModel::NameColumn, Qt::AscendingOrder);
|
||||
ui->treeView->sortByColumn(SectionsModel::NameColumn, Qt::AscendingOrder);
|
||||
}
|
||||
|
||||
void SectionsWidget::initQuickFilter()
|
||||
{
|
||||
quickFilterView = new QuickFilterView(this, false);
|
||||
quickFilterView->setObjectName(QStringLiteral("quickFilterView"));
|
||||
QSizePolicy sizePolicy1(QSizePolicy::Preferred, QSizePolicy::Maximum);
|
||||
sizePolicy1.setHorizontalStretch(0);
|
||||
sizePolicy1.setVerticalStretch(0);
|
||||
sizePolicy1.setHeightForWidth(quickFilterView->sizePolicy().hasHeightForWidth());
|
||||
quickFilterView->setSizePolicy(sizePolicy1);
|
||||
|
||||
QShortcut *search_shortcut = new QShortcut(QKeySequence::Find, this);
|
||||
search_shortcut->setContext(Qt::WidgetWithChildrenShortcut);
|
||||
connect(search_shortcut, &QShortcut::activated, quickFilterView, &QuickFilterView::showFilter);
|
||||
|
||||
QShortcut *clear_shortcut = new QShortcut(QKeySequence(Qt::Key_Escape), this);
|
||||
clear_shortcut->setContext(Qt::WidgetWithChildrenShortcut);
|
||||
connect(clear_shortcut, &QShortcut::activated, quickFilterView, &QuickFilterView::clearFilter);
|
||||
ui->quickFilterView->closeFilter();
|
||||
}
|
||||
|
||||
void SectionsWidget::initAddrMapDocks()
|
||||
{
|
||||
dockWidgetContents = new QWidget(this);
|
||||
QVBoxLayout *layout = new QVBoxLayout();
|
||||
|
||||
layout->addWidget(sectionsTable);
|
||||
layout->addWidget(quickFilterView);
|
||||
QVBoxLayout *layout = ui->verticalLayout;
|
||||
showCount(false);
|
||||
|
||||
rawAddrDock = new RawAddrDock(sectionsModel, this);
|
||||
virtualAddrDock = new VirtualAddrDock(sectionsModel, this);
|
||||
@ -220,20 +210,11 @@ void SectionsWidget::initAddrMapDocks()
|
||||
toggleButton->setArrowType(Qt::NoArrow);
|
||||
toggleButton->hide();
|
||||
layout->addWidget(toggleButton);
|
||||
|
||||
layout->setMargin(0);
|
||||
dockWidgetContents->setLayout(layout);
|
||||
setWidget(dockWidgetContents);
|
||||
}
|
||||
|
||||
void SectionsWidget::initConnects()
|
||||
{
|
||||
connect(sectionsTable, SIGNAL(doubleClicked(const QModelIndex &)),
|
||||
this, SLOT(onSectionsDoubleClicked(const QModelIndex &)));
|
||||
connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshSections()));
|
||||
connect(quickFilterView, SIGNAL(filterTextChanged(const QString &)), proxyModel,
|
||||
SLOT(setFilterWildcard(const QString &)));
|
||||
connect(quickFilterView, SIGNAL(filterClosed()), sectionsTable, SLOT(setFocus()));
|
||||
connect(Core(), &CutterCore::refreshAll, this, &SectionsWidget::refreshSections);
|
||||
connect(this, &QDockWidget::visibilityChanged, this, [ = ](bool visibility) {
|
||||
if (visibility) {
|
||||
refreshSections();
|
||||
@ -261,7 +242,7 @@ void SectionsWidget::refreshSections()
|
||||
sectionsModel->beginResetModel();
|
||||
sections = Core()->getAllSections();
|
||||
sectionsModel->endResetModel();
|
||||
qhelpers::adjustColumns(sectionsTable, SectionsModel::ColumnCount, 0);
|
||||
qhelpers::adjustColumns(ui->treeView, SectionsModel::ColumnCount, 0);
|
||||
refreshDocks();
|
||||
}
|
||||
|
||||
@ -296,16 +277,6 @@ void SectionsWidget::drawIndicatorOnAddrDocks()
|
||||
}
|
||||
}
|
||||
|
||||
void SectionsWidget::onSectionsDoubleClicked(const QModelIndex &index)
|
||||
{
|
||||
if (!index.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto section = index.data(SectionsModel::SectionDescriptionRole).value<SectionDescription>();
|
||||
Core()->seekAndShow(section.vaddr);
|
||||
}
|
||||
|
||||
void SectionsWidget::resizeEvent(QResizeEvent *event) {
|
||||
CutterDockWidget::resizeEvent(event);
|
||||
refreshDocks();
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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>
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user