Common behaviour for lists with items that have address part 2 (#1718)

* Seperate addressable item list widget from ListDockWidget.
* Convert ResourceWidget, strings widget, flags widget, search widget, MemoryMapWidget, xrefs dialog
* Don't silently overwrite comment in add comment action.
This commit is contained in:
karliss 2019-09-02 00:30:25 +03:00 committed by GitHub
parent fa759dd660
commit 567f852c3a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 495 additions and 534 deletions

View File

@ -496,7 +496,8 @@ HEADERS += \
common/Decompiler.h \
menus/AddressableItemContextMenu.h \
common/AddressableItemModel.h \
widgets/ListDockWidget.h
widgets/ListDockWidget.h \
widgets/AddressableItemList.h
GRAPHVIZ_HEADERS = widgets/GraphGridLayout.h
@ -536,7 +537,6 @@ FORMS += \
widgets/RegistersWidget.ui \
widgets/BacktraceWidget.ui \
dialogs/OpenFileDialog.ui \
widgets/MemoryMapWidget.ui \
dialogs/preferences/DebugOptionsWidget.ui \
widgets/BreakpointWidget.ui \
dialogs/BreakpointsDialog.ui \

View File

@ -78,7 +78,7 @@ QTreeWidgetItem *appendRow(QTreeWidget *tw, const QString &str, const QString &s
* @param tw - QTreeWidget instance
* @return true - setCurrentItem was set, false - tree is empty
*/
bool selectFirstItem(QTreeWidget* tw)
bool selectFirstItem(QTreeWidget *tw)
{
if (tw->topLevelItem(0)) {
tw->setCurrentItem(tw->topLevelItem(0));
@ -87,6 +87,19 @@ bool selectFirstItem(QTreeWidget* tw)
return false;
}
bool selectFirstItem(QAbstractItemView *itemView)
{
auto selectionModel = itemView->selectionModel();
auto model = itemView->model();
if (model->hasChildren()) {
selectionModel->setCurrentIndex(model->index(0, 0), QItemSelectionModel::SelectCurrent);
return true;
} else {
return false;
}
}
void setVerticalScrollMode(QAbstractItemView *tw)
{
tw->setVerticalScrollMode(scrollMode());
@ -205,7 +218,8 @@ QByteArray applyColorToSvg(const QString &filename, QColor color)
* @param setter functor which has to be called
* for example we need to set an action icon, the functor can be just [](void* o, const QIcon &icon) { static_cast<QAction*>(o)->setIcon(icon); }
*/
void setThemeIcons(QList<QPair<void*, QString>> supportedIconsNames, std::function<void(void *, const QIcon &)> setter)
void setThemeIcons(QList<QPair<void *, QString>> supportedIconsNames,
std::function<void(void *, const QIcon &)> setter)
{
if (supportedIconsNames.isEmpty() || !setter) {
return;

View File

@ -21,7 +21,8 @@ namespace qhelpers {
QString formatBytecount(const long bytecount);
void adjustColumns(QTreeView *tv, int columnCount, int padding);
void adjustColumns(QTreeWidget *tw, int padding);
bool selectFirstItem(QTreeWidget* tw);
bool selectFirstItem(QTreeWidget *tw);
bool selectFirstItem(QAbstractItemView *itemView);
QTreeWidgetItem *appendRow(QTreeWidget *tw, const QString &str, const QString &str2 = QString(),
const QString &str3 = QString(), const QString &str4 = QString(), const QString &str5 = QString());

View File

@ -1,6 +1,8 @@
#include "CommentsDialog.h"
#include "ui_CommentsDialog.h"
#include "core/Cutter.h"
CommentsDialog::CommentsDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::CommentsDialog)
@ -34,6 +36,30 @@ void CommentsDialog::setComment(const QString &comment)
ui->textEdit->document()->setPlainText(comment);
}
void CommentsDialog::addOrEditComment(RVA offset, QWidget *parent)
{
QString oldComment = Core()->cmd("CC." + RAddressString(offset));
// Remove newline at the end added by cmd
oldComment.remove(oldComment.length() - 1, 1);
CommentsDialog c(parent);
if (oldComment.isNull() || oldComment.isEmpty()) {
c.setWindowTitle(tr("Add Comment at %1").arg(RAddressString(offset)));
} else {
c.setWindowTitle(tr("Edit Comment at %1").arg(RAddressString(offset)));
}
c.setComment(oldComment);
if (c.exec()) {
QString comment = c.getComment();
if (comment.isEmpty()) {
Core()->delComment(offset);
} else {
Core()->setComment(offset, comment);
}
}
}
bool CommentsDialog::eventFilter(QObject */*obj*/, QEvent *event)
{
if (event -> type() == QEvent::KeyPress) {

View File

@ -4,6 +4,8 @@
#include <QDialog>
#include <memory>
#include "core/CutterCommon.h"
namespace Ui {
class CommentsDialog;
}
@ -19,6 +21,7 @@ public:
QString getComment();
void setComment(const QString &comment);
static void addOrEditComment(RVA offset, QWidget *parent);
private slots:
void on_buttonBox_accepted();

View File

@ -8,14 +8,22 @@
#include <QJsonArray>
XrefsDialog::XrefsDialog(QWidget *parent) :
XrefsDialog::XrefsDialog(MainWindow *main, QWidget *parent) :
QDialog(parent),
addr(0),
toModel(this),
fromModel(this),
ui(new Ui::XrefsDialog)
{
ui->setupUi(this);
setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));
ui->toTreeWidget->setMainWindow(main);
ui->fromTreeWidget->setMainWindow(main);
ui->toTreeWidget->setModel(&toModel);
ui->fromTreeWidget->setModel(&fromModel);
// Modify the splitter's location to show more Disassembly instead of empty space. Not possible via Designer
ui->splitter->setSizes(QList<int>() << 100 << 200);
@ -30,66 +38,24 @@ XrefsDialog::XrefsDialog(QWidget *parent) :
connect(ui->previewTextEdit, SIGNAL(cursorPositionChanged()), this, SLOT(highlightCurrentLine()));
connect(Config(), SIGNAL(fontsUpdated()), this, SLOT(setupPreviewFont()));
connect(Config(), SIGNAL(colorsUpdated()), this, SLOT(setupPreviewColors()));
connect(ui->toTreeWidget->selectionModel(), &QItemSelectionModel::selectionChanged,
this, &XrefsDialog::onToTreeWidgetItemSelectionChanged);
connect(ui->fromTreeWidget->selectionModel(), &QItemSelectionModel::selectionChanged,
this, &XrefsDialog::onFromTreeWidgetItemSelectionChanged);
// Don't create recursive xref dialogs
auto toContextMenu = ui->toTreeWidget->getItemContextMenu();
connect(toContextMenu, &AddressableItemContextMenu::xrefsTriggered, this, &QWidget::close);
auto fromContextMenu = ui->fromTreeWidget->getItemContextMenu();
connect(fromContextMenu, &AddressableItemContextMenu::xrefsTriggered, this, &QWidget::close);
connect(ui->toTreeWidget, &QAbstractItemView::doubleClicked, this, &QWidget::close);
connect(ui->fromTreeWidget, &QAbstractItemView::doubleClicked, this, &QWidget::close);
}
XrefsDialog::~XrefsDialog() { }
void XrefsDialog::fillRefs(QList<XrefDescription> refs, QList<XrefDescription> xrefs)
{
// Fill refs
ui->fromTreeWidget->clear();
for (const auto &xref : refs) {
auto *tempItem = new QTreeWidgetItem();
tempItem->setText(0, xref.to_str);
if (xref.type != "DATA") {
tempItem->setText(1, Core()->disassembleSingleInstruction(xref.to));
}
tempItem->setText(2, xrefTypeString(xref.type));
tempItem->setData(0, Qt::UserRole, QVariant::fromValue(xref));
ui->fromTreeWidget->insertTopLevelItem(0, tempItem);
}
// Adjust columns to content
qhelpers::adjustColumns(ui->fromTreeWidget, 0);
// Fill Xrefs
ui->toTreeWidget->clear();
for (const auto &xref : xrefs) {
auto *tempItem = new QTreeWidgetItem();
tempItem->setText(0, xref.from_str);
tempItem->setText(1, Core()->disassembleSingleInstruction(xref.from));
tempItem->setText(2, xrefTypeString(xref.type));
tempItem->setData(0, Qt::UserRole, QVariant::fromValue(xref));
ui->toTreeWidget->insertTopLevelItem(0, tempItem);
}
// Adjust columns to content
qhelpers::adjustColumns(ui->toTreeWidget, 0);
// try to select first item from refs or xrefs
if (!qhelpers::selectFirstItem(ui->toTreeWidget)) {
qhelpers::selectFirstItem(ui->fromTreeWidget);
}
}
void XrefsDialog::on_fromTreeWidget_itemDoubleClicked(QTreeWidgetItem *item, int column)
{
Q_UNUSED(column);
XrefDescription xref = item->data(0, Qt::UserRole).value<XrefDescription>();
Core()->seekAndShow(xref.to);
this->close();
}
void XrefsDialog::on_toTreeWidget_itemDoubleClicked(QTreeWidgetItem *item, int column)
{
Q_UNUSED(column);
XrefDescription xref = item->data(0, Qt::UserRole).value<XrefDescription>();
Core()->seekAndShow(xref.from);
this->close();
}
QString XrefsDialog::normalizeAddr(const QString &addr) const
{
QString ret = addr;
@ -129,26 +95,24 @@ void XrefsDialog::highlightCurrentLine()
}
}
void XrefsDialog::on_fromTreeWidget_itemSelectionChanged()
void XrefsDialog::onFromTreeWidgetItemSelectionChanged()
{
if (ui->fromTreeWidget->selectedItems().isEmpty()) {
auto index = ui->fromTreeWidget->currentIndex();
if (!ui->fromTreeWidget->selectionModel()->hasSelection() || !index.isValid()) {
return;
}
ui->toTreeWidget->clearSelection();
QTreeWidgetItem *item = ui->fromTreeWidget->currentItem();
XrefDescription xref = item->data(0, Qt::UserRole).value<XrefDescription>();
updatePreview(xref.to);
updatePreview(fromModel.address(index));
}
void XrefsDialog::on_toTreeWidget_itemSelectionChanged()
void XrefsDialog::onToTreeWidgetItemSelectionChanged()
{
if (ui->toTreeWidget->selectedItems().isEmpty()) {
auto index = ui->toTreeWidget->currentIndex();
if (!ui->toTreeWidget->selectionModel()->hasSelection() || !index.isValid()) {
return;
}
ui->fromTreeWidget->clearSelection();
QTreeWidgetItem *item = ui->toTreeWidget->currentItem();
XrefDescription xref = item->data(0, Qt::UserRole).value<XrefDescription>();
updatePreview(xref.from);
updatePreview(toModel.address(index));
}
void XrefsDialog::updatePreview(RVA addr)
@ -180,24 +144,113 @@ void XrefsDialog::fillRefsForAddress(RVA addr, QString name, bool whole_function
setWindowTitle(tr("X-Refs for %1").arg(name));
updateLabels(name);
// Get Refs and Xrefs
QList<XrefDescription> refs = Core()->getXRefs(addr, false, whole_function);
QList<XrefDescription> xrefs = Core()->getXRefs(addr, true, whole_function);
toModel.readForOffset(addr, true, whole_function);
fromModel.readForOffset(addr, false, whole_function);
fillRefs(refs, xrefs);
// Adjust columns to content
qhelpers::adjustColumns(ui->fromTreeWidget, fromModel.columnCount(), 0);
qhelpers::adjustColumns(ui->toTreeWidget, toModel.columnCount(), 0);
// try to select first item from refs or xrefs
if (!qhelpers::selectFirstItem(ui->toTreeWidget)) {
qhelpers::selectFirstItem(ui->fromTreeWidget);
}
}
QString XrefsDialog::xrefTypeString(const QString &type)
QString XrefModel::xrefTypeString(const QString &type)
{
if (type == "CODE") {
return QString("Code");
return QStringLiteral("Code");
} else if (type == "CALL") {
return QString("Call");
return QStringLiteral("Call");
} else if (type == "DATA") {
return QString("Data");
return QStringLiteral("Data");
} else if (type == "STRING") {
return QString("String");
return QStringLiteral("String");
}
return type;
}
XrefModel::XrefModel(QObject *parent)
: AddressableItemModel(parent)
{
}
void XrefModel::readForOffset(RVA offset, bool to, bool whole_function)
{
beginResetModel();
this->to = to;
xrefs = Core()->getXRefs(offset, to, whole_function);
endResetModel();
}
int XrefModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return xrefs.size();
}
int XrefModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return Columns::COUNT;
}
QVariant XrefModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || index.row() >= xrefs.count()) {
return QVariant();
}
const XrefDescription &xref = xrefs.at(index.row());
switch (role) {
case Qt::DisplayRole:
switch (index.column()) {
case OFFSET:
return to ? xref.from_str : xref.to_str;
case CODE:
if (to || xref.type != "DATA") {
return Core()->disassembleSingleInstruction(xref.from);
} else {
return QString();
}
case TYPE:
return xrefTypeString(xref.type);
}
return QVariant();
case FlagDescriptionRole:
return QVariant::fromValue(xref);
default:
return QVariant();
}
return QVariant();
}
QVariant XrefModel::headerData(int section, Qt::Orientation orientation, int role) const
{
Q_UNUSED(orientation)
switch (role) {
case Qt::DisplayRole:
switch (section) {
case OFFSET:
return tr("Address");
case CODE:
return tr("Code");
case TYPE:
return tr("Type");
default:
return QVariant();
}
default:
return QVariant();
}
}
RVA XrefModel::address(const QModelIndex &index) const
{
const auto &xref = xrefs.at(index.row());
return to ? xref.from : xref.to;
}

View File

@ -6,6 +6,32 @@
#include <memory>
#include "common/Highlighter.h"
#include "core/Cutter.h"
#include "common/AddressableItemModel.h"
class XrefModel: public AddressableItemModel<QAbstractListModel>
{
private:
QList<XrefDescription> xrefs;
bool to;
public:
enum Columns { OFFSET = 0, CODE, TYPE, COUNT };
static const int FlagDescriptionRole = Qt::UserRole;
XrefModel(QObject *parent = nullptr);
void readForOffset(RVA offset, bool to, bool whole_function);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
RVA address(const QModelIndex &index) const override;
static QString xrefTypeString(const QString &type);
};
class MainWindow;
@ -18,37 +44,32 @@ class XrefsDialog : public QDialog
Q_OBJECT
public:
explicit XrefsDialog(QWidget *parent = nullptr);
explicit XrefsDialog(MainWindow *main, QWidget *parent);
~XrefsDialog();
void fillRefsForAddress(RVA addr, QString name, bool whole_function);
private slots:
void on_fromTreeWidget_itemDoubleClicked(QTreeWidgetItem *item, int column);
void on_toTreeWidget_itemDoubleClicked(QTreeWidgetItem *item, int column);
QString normalizeAddr(const QString &addr) const;
void setupPreviewFont();
void setupPreviewColors();
void highlightCurrentLine();
void on_fromTreeWidget_itemSelectionChanged();
void on_toTreeWidget_itemSelectionChanged();
void onFromTreeWidgetItemSelectionChanged();
void onToTreeWidgetItemSelectionChanged();
private:
RVA addr;
QString func_name;
XrefModel toModel;
XrefModel fromModel;
std::unique_ptr<Ui::XrefsDialog> ui;
void fillRefs(QList<XrefDescription> refs, QList<XrefDescription> xrefs);
void updateLabels(QString name);
void updatePreview(RVA addr);
QString xrefTypeString(const QString &type);
};
#endif // XREFSDIALOG_H

View File

@ -50,7 +50,7 @@
</widget>
</item>
<item>
<widget class="QTreeWidget" name="toTreeWidget">
<widget class="AddressableItemList&lt;&gt;" name="toTreeWidget">
<property name="frameShape">
<enum>QFrame::Box</enum>
</property>
@ -60,24 +60,6 @@
<property name="indentation">
<number>5</number>
</property>
<property name="columnCount">
<number>3</number>
</property>
<column>
<property name="text">
<string>Address</string>
</property>
</column>
<column>
<property name="text">
<string>Code</string>
</property>
</column>
<column>
<property name="text">
<string>Type</string>
</property>
</column>
</widget>
</item>
<item>
@ -88,7 +70,7 @@
</widget>
</item>
<item>
<widget class="QTreeWidget" name="fromTreeWidget">
<widget class="AddressableItemList&lt;&gt;" name="fromTreeWidget">
<property name="frameShape">
<enum>QFrame::Box</enum>
</property>
@ -98,24 +80,6 @@
<property name="indentation">
<number>5</number>
</property>
<property name="columnCount">
<number>3</number>
</property>
<column>
<property name="text">
<string>Address</string>
</property>
</column>
<column>
<property name="text">
<string>Code</string>
</property>
</column>
<column>
<property name="text">
<string>Type</string>
</property>
</column>
</widget>
</item>
</layout>
@ -170,6 +134,13 @@
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>AddressableItemList&lt;&gt;</class>
<extends>QTreeView</extends>
<header>widgets/AddressableItemList.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>

View File

@ -30,7 +30,8 @@ AddressableItemContextMenu::AddressableItemContextMenu(QWidget *parent, MainWind
connect(&actionAddcomment, &QAction::triggered, this,
&AddressableItemContextMenu::onActionAddComment);
actionAddcomment.setShortcut({Qt::Key_Semicolon});
actionAddcomment.setShortcutContext(Qt::ShortcutContext::WidgetWithChildrenShortcut);
addAction(&actionShowInMenu);
addAction(&actionCopyAddress);
@ -38,6 +39,7 @@ AddressableItemContextMenu::AddressableItemContextMenu(QWidget *parent, MainWind
addSeparator();
addAction(&actionAddcomment);
setHasTarget(hasTarget);
connect(this, &QMenu::aboutToShow, this, &AddressableItemContextMenu::aboutToShowSlot);
}
@ -59,6 +61,12 @@ void AddressableItemContextMenu::setTarget(RVA offset, QString name)
{
this->offset = offset;
this->name = name;
setHasTarget(true);
}
void AddressableItemContextMenu::clearTarget()
{
setHasTarget(false);
}
void AddressableItemContextMenu::onActionCopyAddress()
@ -69,7 +77,8 @@ void AddressableItemContextMenu::onActionCopyAddress()
void AddressableItemContextMenu::onActionShowXrefs()
{
XrefsDialog dialog(nullptr);
emit xrefsTriggered();
XrefsDialog dialog(mainWindow, nullptr);
QString tmpName = name;
if (name.isEmpty()) {
name = RAddressString(offset);
@ -80,17 +89,7 @@ void AddressableItemContextMenu::onActionShowXrefs()
void AddressableItemContextMenu::onActionAddComment()
{
// Create dialog
CommentsDialog c(this);
if (c.exec()) {
// Get new function name
QString comment = c.getComment();
// Rename function in r2 core
Core()->setComment(offset, comment);
// Seek to new renamed function
Core()->seekAndShow(offset);
}
CommentsDialog::addOrEditComment(offset, this);
}
void AddressableItemContextMenu::aboutToShowSlot()
@ -101,3 +100,11 @@ void AddressableItemContextMenu::aboutToShowSlot()
actionShowInMenu.setMenu(mainWindow->createShowInMenu(this, offset));
}
void AddressableItemContextMenu::setHasTarget(bool hasTarget)
{
this->hasTarget = hasTarget;
for (const auto &action : this->actions()) {
action->setEnabled(hasTarget);
}
}

View File

@ -21,6 +21,9 @@ public:
public slots:
void setOffset(RVA offset);
void setTarget(RVA offset, QString name = QString());
void clearTarget();
signals:
void xrefsTriggered();
private:
void onActionCopyAddress();
void onActionShowXrefs();
@ -31,7 +34,9 @@ private:
MainWindow *mainWindow;
RVA offset;
bool hasTarget = false;
protected:
void setHasTarget(bool hasTarget);
QAction actionShowInMenu;
QAction actionCopyAddress;
QAction actionShowXrefs;

View File

@ -671,26 +671,7 @@ void DisassemblyContextMenu::on_actionSetPC_triggered()
void DisassemblyContextMenu::on_actionAddComment_triggered()
{
QString oldComment = Core()->cmd("CC." + RAddressString(offset));
// Remove newline at the end added by cmd
oldComment.remove(oldComment.length() - 1, 1);
CommentsDialog c(this);
if (oldComment.isNull() || oldComment.isEmpty()) {
c.setWindowTitle(tr("Add Comment at %1").arg(RAddressString(offset)));
} else {
c.setWindowTitle(tr("Edit Comment at %1").arg(RAddressString(offset)));
}
c.setComment(oldComment);
if (c.exec()) {
QString comment = c.getComment();
if (comment.isEmpty()) {
Core()->delComment(offset);
} else {
Core()->setComment(offset, comment);
}
}
CommentsDialog::addOrEditComment(offset, this);
}
void DisassemblyContextMenu::on_actionAnalyzeFunction_triggered()
@ -796,7 +777,7 @@ void DisassemblyContextMenu::on_actionSetFunctionVarTypes_triggered()
void DisassemblyContextMenu::on_actionXRefs_triggered()
{
XrefsDialog dialog(nullptr);
XrefsDialog dialog(mainWindow, nullptr);
dialog.fillRefsForAddress(offset, RAddressString(offset), false);
dialog.exec();
}

View File

@ -0,0 +1,103 @@
#ifndef ADDRESSABLE_ITEM_LIST_H
#define ADDRESSABLE_ITEM_LIST_H
#include <memory>
#include <QAbstractItemModel>
#include <QSortFilterProxyModel>
#include <QAbstractItemView>
#include <QMenu>
#include "core/Cutter.h"
#include "common/AddressableItemModel.h"
#include "CutterDockWidget.h"
#include "CutterTreeWidget.h"
#include "menus/AddressableItemContextMenu.h"
#include "CutterTreeView.h"
class MainWindow;
template<class BaseListWidget = CutterTreeView>
class AddressableItemList : public BaseListWidget
{
static_assert (std::is_base_of<QAbstractItemView, BaseListWidget>::value,
"ParentModel needs to inherit from QAbstractItemModel");
public:
explicit AddressableItemList(QWidget *parent = nullptr) :
BaseListWidget(parent)
{
this->connect(this, &QWidget::customContextMenuRequested, this,
&AddressableItemList<BaseListWidget>::showItemContextMenu);
this->setContextMenuPolicy(Qt::CustomContextMenu);
this->connect(this, &QAbstractItemView::activated, this,
&AddressableItemList<BaseListWidget>::onItemActivated);
}
void setModel(AddressableItemModelI *addressableItemModel)
{
this->addressableModel = addressableItemModel;
BaseListWidget::setModel(this->addressableModel->asItemModel());
this->connect(this->selectionModel(), &QItemSelectionModel::currentChanged, this,
&AddressableItemList<BaseListWidget>::onSelectedItemChanged);
}
void setMainWindow(MainWindow *mainWindow)
{
this->mainWindow = mainWindow;
setItemContextMenu(new AddressableItemContextMenu(this, mainWindow));
this->addActions(this->getItemContextMenu()->actions());
}
AddressableItemContextMenu *getItemContextMenu()
{
return itemContextMenu;
}
void setItemContextMenu(AddressableItemContextMenu *menu)
{
if (itemContextMenu != menu && itemContextMenu) {
itemContextMenu->deleteLater();
}
itemContextMenu = menu;
}
protected:
virtual void showItemContextMenu(const QPoint &pt)
{
auto index = this->currentIndex();
if (index.isValid() && itemContextMenu) {
auto offset = addressableModel->address(index);
auto name = addressableModel->name(index);
itemContextMenu->setTarget(offset, name);
itemContextMenu->exec(this->mapToGlobal(pt));
}
}
virtual void onItemActivated(const QModelIndex &index)
{
if (!index.isValid())
return;
auto offset = addressableModel->address(index);
Core()->seekAndShow(offset);
}
virtual void onSelectedItemChanged(const QModelIndex &index)
{
updateMenuFromItem(index);
}
void updateMenuFromItem(const QModelIndex &index)
{
if (index.isValid()) {
auto offset = addressableModel->address(index);
auto name = addressableModel->name(index);
itemContextMenu->setTarget(offset, name);
} else {
itemContextMenu->clearTarget();
}
}
private:
AddressableItemModelI *addressableModel = nullptr;
AddressableItemContextMenu *itemContextMenu = nullptr;
MainWindow *mainWindow = nullptr;
};
#endif // ADDRESSABLE_ITEM_LIST_H

View File

@ -2,7 +2,6 @@
#include "ui_FlagsWidget.h"
#include "core/MainWindow.h"
#include "dialogs/RenameDialog.h"
#include "dialogs/XrefsDialog.h"
#include "common/Helpers.h"
#include <QComboBox>
@ -11,7 +10,7 @@
#include <QTreeWidget>
FlagsModel::FlagsModel(QList<FlagDescription> *flags, QObject *parent)
: QAbstractListModel(parent),
: AddressableItemModel<QAbstractListModel>(parent),
flags(flags)
{
}
@ -71,14 +70,21 @@ QVariant FlagsModel::headerData(int section, Qt::Orientation, int role) const
}
}
RVA FlagsModel::address(const QModelIndex &index) const
{
const FlagDescription &flag = flags->at(index.row());
return flag.offset;
}
QString FlagsModel::name(const QModelIndex &index) const
{
const FlagDescription &flag = flags->at(index.row());
return flag.name;
}
FlagsSortFilterProxyModel::FlagsSortFilterProxyModel(FlagsModel *source_model, QObject *parent)
: QSortFilterProxyModel(parent)
: AddressableFilterProxyModel(source_model, parent)
{
setSourceModel(source_model);
}
bool FlagsSortFilterProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const
@ -128,6 +134,7 @@ FlagsWidget::FlagsWidget(MainWindow *main, QAction *action) :
flags_proxy_model = new FlagsSortFilterProxyModel(flags_model, this);
connect(ui->filterLineEdit, SIGNAL(textChanged(const QString &)), flags_proxy_model,
SLOT(setFilterWildcard(const QString &)));
ui->flagsTreeView->setMainWindow(mainWindow);
ui->flagsTreeView->setModel(flags_proxy_model);
ui->flagsTreeView->sortByColumn(FlagsModel::OFFSET, Qt::AscendingOrder);
@ -151,32 +158,21 @@ FlagsWidget::FlagsWidget(MainWindow *main, QAction *action) :
tree->showItemsNumber(flags_proxy_model->rowCount());
});
auto xRefShortcut = new QShortcut(QKeySequence{Qt::CTRL + Qt::Key_X}, this);
xRefShortcut->setContext(Qt::WidgetWithChildrenShortcut);
ui->actionXrefs->setShortcut(Qt::CTRL + Qt::Key_X);
connect(xRefShortcut, SIGNAL(activated()), this, SLOT(on_actionXrefs_triggered()));
setScrollMode();
ui->flagsTreeView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->flagsTreeView, SIGNAL(customContextMenuRequested(const QPoint &)), this,
SLOT(showContextMenu(const QPoint &)));
connect(Core(), SIGNAL(flagsChanged()), this, SLOT(flagsChanged()));
connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshFlagspaces()));
auto menu = ui->flagsTreeView->getItemContextMenu();
menu->addSeparator();
menu->addAction(ui->actionRename);
menu->addAction(ui->actionDelete);
addAction(ui->actionRename);
addAction(ui->actionDelete);
}
FlagsWidget::~FlagsWidget() {}
void FlagsWidget::on_flagsTreeView_doubleClicked(const QModelIndex &index)
{
if (!index.isValid())
return;
FlagDescription flag = index.data(FlagsModel::FlagDescriptionRole).value<FlagDescription>();
Core()->seekAndShow(flag.offset);
}
void FlagsWidget::on_flagspaceCombo_currentTextChanged(const QString &arg1)
{
Q_UNUSED(arg1);
@ -204,28 +200,6 @@ void FlagsWidget::on_actionDelete_triggered()
Core()->delFlag(flag.name);
}
void FlagsWidget::on_actionXrefs_triggered()
{
FlagDescription flag = ui->flagsTreeView->selectionModel()->currentIndex().data(
FlagsModel::FlagDescriptionRole).value<FlagDescription>();
XrefsDialog xresfDialog(nullptr);
xresfDialog.fillRefsForAddress(flag.offset, RAddressString(flag.offset), false);
xresfDialog.exec();
}
void FlagsWidget::showContextMenu(const QPoint &pt)
{
QMenu *menu = new QMenu(ui->flagsTreeView);
menu->addAction(ui->actionRename);
menu->addAction(ui->actionDelete);
menu->addSeparator();
menu->addAction(ui->actionXrefs);
menu->exec(ui->flagsTreeView->mapToGlobal(pt));
delete menu;
}
void FlagsWidget::flagsChanged()
{
refreshFlagspaces();

View File

@ -9,16 +9,16 @@
#include "core/Cutter.h"
#include "CutterDockWidget.h"
#include "CutterTreeWidget.h"
#include "AddressableItemList.h"
#include "AddressableItemModel.h"
class MainWindow;
class QTreeWidgetItem;
class FlagsWidget;
class FlagsModel: public QAbstractListModel
class FlagsModel: public AddressableItemModel<QAbstractListModel>
{
Q_OBJECT
friend FlagsWidget;
private:
@ -30,16 +30,19 @@ public:
FlagsModel(QList<FlagDescription> *flags, QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
RVA address(const QModelIndex &index) const override;
QString name(const QModelIndex &index) const override;
};
class FlagsSortFilterProxyModel : public QSortFilterProxyModel
class FlagsSortFilterProxyModel : public AddressableFilterProxyModel
{
Q_OBJECT
@ -66,14 +69,10 @@ public:
~FlagsWidget();
private slots:
void on_flagsTreeView_doubleClicked(const QModelIndex &index);
void on_flagspaceCombo_currentTextChanged(const QString &arg1);
void on_actionRename_triggered();
void on_actionDelete_triggered();
void on_actionXrefs_triggered();
void showContextMenu(const QPoint &pt);
void flagsChanged();
void refreshFlagspaces();

View File

@ -31,7 +31,7 @@
<number>0</number>
</property>
<item>
<widget class="CutterTreeView" name="flagsTreeView">
<widget class="AddressableItemList&lt;&gt;" name="flagsTreeView">
<property name="styleSheet">
<string notr="true">CutterTreeView::item
{
@ -99,7 +99,7 @@
<string>Rename</string>
</property>
<property name="shortcut">
<string>Ctrl+N</string>
<string>N</string>
</property>
</action>
<action name="actionDelete">
@ -110,23 +110,12 @@
<string>Del</string>
</property>
</action>
<action name="actionXrefs">
<property name="text">
<string>X-Refs</string>
</property>
<property name="toolTip">
<string>Show X-Refs of this flag</string>
</property>
<property name="shortcut">
<string>Ctrl+X</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>CutterTreeView</class>
<class>AddressableItemList&lt;&gt;</class>
<extends>QTreeView</extends>
<header>widgets/CutterTreeView.h</header>
<header>widgets/AddressableItemList.h</header>
<container>1</container>
</customwidget>
</customwidgets>

View File

@ -3,9 +3,7 @@
#include "core/MainWindow.h"
#include "common/Helpers.h"
#include "dialogs/CommentsDialog.h"
#include "dialogs/RenameDialog.h"
#include "dialogs/XrefsDialog.h"
#include "common/FunctionsTask.h"
#include "common/TempConfig.h"
#include "menus/AddressableItemContextMenu.h"
@ -18,6 +16,7 @@
#include <QShortcut>
#include <QJsonArray>
#include <QJsonObject>
#include <QResizeEvent>
namespace {
@ -467,7 +466,7 @@ FunctionsWidget::FunctionsWidget(MainWindow *main, QAction *action) :
connect(&actionUndefine, &QAction::triggered, this,
&FunctionsWidget::onActionFunctionsUndefineTriggered);
auto itemConextMenu = getItemContextMenu();
auto itemConextMenu = ui->treeView->getItemContextMenu();
itemConextMenu->addSeparator();
itemConextMenu->addAction(&actionRename);
itemConextMenu->addAction(&actionUndefine);

View File

@ -34,14 +34,9 @@ ListDockWidget::ListDockWidget(MainWindow *main, QAction *action, SearchBarPolic
clearShortcut->setContext(Qt::WidgetWithChildrenShortcut);
}
connect(ui->treeView, &QTreeView::activated, this, &ListDockWidget::onItemActivated);
qhelpers::setVerticalScrollMode(ui->treeView);
itemContextMenu = new AddressableItemContextMenu(ui->treeView, mainWindow);
ui->treeView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->treeView, &QWidget::customContextMenuRequested,
this, &ListDockWidget::showItemContextMenu);
ui->treeView->setMainWindow(mainWindow);
if (searchBarPolicy != SearchBarPolicy::ShowByDefault) {
ui->quickFilterView->closeFilter();
@ -59,6 +54,7 @@ void ListDockWidget::setModels(AddressableFilterProxyModel *objectFilterProxyMod
{
this->objectFilterProxyModel = objectFilterProxyModel;
ui->treeView->setModel(objectFilterProxyModel);
@ -71,48 +67,4 @@ void ListDockWidget::setModels(AddressableFilterProxyModel *objectFilterProxyMod
connect(ui->quickFilterView, &QuickFilterView::filterTextChanged, this, [this] {
tree->showItemsNumber(this->objectFilterProxyModel->rowCount());
});
connect(ui->treeView->selectionModel(), &QItemSelectionModel::currentChanged, this,
&ListDockWidget::onSelectedItemChanged);
addActions(this->getItemContextMenu()->actions());
}
AddressableItemContextMenu *ListDockWidget::getItemContextMenu()
{
return itemContextMenu;
}
void ListDockWidget::setItemContextMenu(AddressableItemContextMenu *menu)
{
this->itemContextMenu = menu;
}
void ListDockWidget::showItemContextMenu(const QPoint &pt)
{
auto index = ui->treeView->currentIndex();
if (index.isValid()) {
auto offset = objectFilterProxyModel->address(index);
auto name = objectFilterProxyModel->name(index);
itemContextMenu->setTarget(offset, name);
itemContextMenu->exec(ui->treeView->mapToGlobal(pt));
}
}
void ListDockWidget::onItemActivated(const QModelIndex &index)
{
if (!index.isValid())
return;
auto offset = objectFilterProxyModel->address(index);
Core()->seekAndShow(offset);
}
void ListDockWidget::onSelectedItemChanged(const QModelIndex &index)
{
if (index.isValid()) {
auto offset = objectFilterProxyModel->address(index);
auto name = objectFilterProxyModel->name(index);
itemContextMenu->setTarget(offset, name);
}
}

View File

@ -39,18 +39,10 @@ public:
protected:
void setModels(AddressableFilterProxyModel *objectFilterProxyModel);
AddressableItemContextMenu *getItemContextMenu();
void setItemContextMenu(AddressableItemContextMenu *menu);
virtual void showItemContextMenu(const QPoint &pt);
virtual void onItemActivated(const QModelIndex &index);
void onSelectedItemChanged(const QModelIndex &index);
std::unique_ptr<Ui::ListDockWidget> ui;
private:
AddressableFilterProxyModel *objectFilterProxyModel;
CutterTreeWidget *tree;
AddressableItemContextMenu *itemContextMenu;
SearchBarPolicy searchBarPolicy;
};

View File

@ -28,7 +28,7 @@
<number>0</number>
</property>
<item>
<widget class="CutterTreeView" name="treeView">
<widget class="AddressableItemList&lt;&gt;" name="treeView">
<property name="styleSheet">
<string notr="true">CutterTreeView::item
{
@ -58,9 +58,9 @@
</widget>
<customwidgets>
<customwidget>
<class>CutterTreeView</class>
<class>AddressableItemList&lt;&gt;</class>
<extends>QTreeView</extends>
<header>widgets/CutterTreeView.h</header>
<header>widgets/AddressableItemList.h</header>
<container>1</container>
</customwidget>
<customwidget>

View File

@ -1,10 +1,10 @@
#include "MemoryMapWidget.h"
#include "ui_MemoryMapWidget.h"
#include "ui_ListDockWidget.h"
#include "core/MainWindow.h"
#include "common/Helpers.h"
MemoryMapModel::MemoryMapModel(QList<MemoryMapDescription> *memoryMaps, QObject *parent)
: QAbstractListModel(parent),
: AddressableItemModel<QAbstractListModel>(parent),
memoryMaps(memoryMaps)
{
}
@ -68,10 +68,15 @@ QVariant MemoryMapModel::headerData(int section, Qt::Orientation, int role) cons
}
}
MemoryProxyModel::MemoryProxyModel(MemoryMapModel *sourceModel, QObject *parent)
: QSortFilterProxyModel(parent)
RVA MemoryMapModel::address(const QModelIndex &index) const
{
const MemoryMapDescription &memoryMap = memoryMaps->at(index.row());
return memoryMap.addrStart;
}
MemoryProxyModel::MemoryProxyModel(MemoryMapModel *sourceModel, QObject *parent)
: AddressableFilterProxyModel(sourceModel, parent)
{
setSourceModel(sourceModel);
}
bool MemoryProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const
@ -106,17 +111,15 @@ bool MemoryProxyModel::lessThan(const QModelIndex &left, const QModelIndex &righ
}
MemoryMapWidget::MemoryMapWidget(MainWindow *main, QAction *action) :
CutterDockWidget(main, action),
ui(new Ui::MemoryMapWidget)
ListDockWidget(main, action, ListDockWidget::SearchBarPolicy::HideByDefault)
{
ui->setupUi(this);
setWindowTitle(tr("Memory Map"));
setObjectName("MemoryMapWidget");
memoryModel = new MemoryMapModel(&memoryMaps, this);
memoryProxyModel = new MemoryProxyModel(memoryModel, this);
ui->memoryTreeView->setModel(memoryProxyModel);
ui->memoryTreeView->sortByColumn(MemoryMapModel::AddrStartColumn, Qt::AscendingOrder);
setScrollMode();
setModels(memoryProxyModel);
ui->treeView->sortByColumn(MemoryMapModel::AddrStartColumn, Qt::AscendingOrder);
refreshDeferrer = createRefreshDeferrer([this]() {
refreshMemoryMap();
@ -124,6 +127,8 @@ MemoryMapWidget::MemoryMapWidget(MainWindow *main, QAction *action) :
connect(Core(), &CutterCore::refreshAll, this, &MemoryMapWidget::refreshMemoryMap);
connect(Core(), &CutterCore::registersChanged, this, &MemoryMapWidget::refreshMemoryMap);
showCount(false);
}
MemoryMapWidget::~MemoryMapWidget() = default;
@ -138,19 +143,7 @@ void MemoryMapWidget::refreshMemoryMap()
memoryMaps = Core()->getMemoryMap();
memoryModel->endResetModel();
ui->memoryTreeView->resizeColumnToContents(0);
ui->memoryTreeView->resizeColumnToContents(1);
ui->memoryTreeView->resizeColumnToContents(2);
}
void MemoryMapWidget::setScrollMode()
{
qhelpers::setVerticalScrollMode(ui->memoryTreeView);
}
void MemoryMapWidget::on_memoryTreeView_doubleClicked(const QModelIndex &index)
{
MemoryMapDescription item = index.data(
MemoryMapModel::MemoryDescriptionRole).value<MemoryMapDescription>();
Core()->seekAndShow(item.addrStart);
ui->treeView->resizeColumnToContents(0);
ui->treeView->resizeColumnToContents(1);
ui->treeView->resizeColumnToContents(2);
}

View File

@ -4,6 +4,7 @@
#include "core/Cutter.h"
#include "CutterDockWidget.h"
#include "ListDockWidget.h"
#include <QAbstractListModel>
#include <QSortFilterProxyModel>
@ -21,7 +22,7 @@ class MainWindow;
class QTreeWidgetItem;
class MemoryMapModel: public QAbstractListModel
class MemoryMapModel: public AddressableItemModel<QAbstractListModel>
{
Q_OBJECT
@ -34,18 +35,20 @@ public:
enum Column { AddrStartColumn = 0, AddrEndColumn, NameColumn, PermColumn, ColumnCount };
enum Role { MemoryDescriptionRole = Qt::UserRole };
MemoryMapModel(QList<MemoryMapDescription> *memoryMaps, QObject *parent = 0);
MemoryMapModel(QList<MemoryMapDescription> *memoryMaps, QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
RVA address(const QModelIndex &index) const override;
};
class MemoryProxyModel : public QSortFilterProxyModel
class MemoryProxyModel : public AddressableFilterProxyModel
{
Q_OBJECT
@ -59,7 +62,7 @@ protected:
class MemoryMapWidget : public CutterDockWidget
class MemoryMapWidget : public ListDockWidget
{
Q_OBJECT
@ -68,18 +71,13 @@ public:
~MemoryMapWidget();
private slots:
void on_memoryTreeView_doubleClicked(const QModelIndex &index);
void refreshMemoryMap();
private:
std::unique_ptr<Ui::MemoryMapWidget> ui;
MemoryMapModel *memoryModel;
MemoryProxyModel *memoryProxyModel;
QList<MemoryMapDescription> memoryMaps;
void setScrollMode();
RefreshDeferrer *refreshDeferrer;
};

View File

@ -1,66 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MemoryMapWidget</class>
<widget class="QDockWidget" name="MemoryMapWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Memory Map</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="memoryTreeView">
<property name="styleSheet">
<string notr="true">CutterTreeView::item
{
padding-top: 1px;
padding-bottom: 1px;
}</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="indentation">
<number>8</number>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>CutterTreeView</class>
<extends>QTreeView</extends>
<header>widgets/CutterTreeView.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -4,7 +4,7 @@
#include <QVBoxLayout>
ResourcesModel::ResourcesModel(QList<ResourcesDescription> *resources, QObject *parent)
: QAbstractListModel(parent),
: AddressableItemModel<QAbstractListModel>(parent),
resources(resources)
{
}
@ -27,11 +27,11 @@ QVariant ResourcesModel::data(const QModelIndex &index, int role) const
case Qt::DisplayRole:
switch (index.column()) {
case NAME:
return res.name;
return QString::number(res.name);
case VADDR:
return RAddressString(res.vaddr);
case INDEX:
return res.index;
return QString::number(res.index);
case TYPE:
return res.type;
case SIZE:
@ -73,25 +73,27 @@ QVariant ResourcesModel::headerData(int section, Qt::Orientation, int role) cons
}
}
RVA ResourcesModel::address(const QModelIndex &index) const
{
const ResourcesDescription &res = resources->at(index.row());
return res.vaddr;
}
ResourcesWidget::ResourcesWidget(MainWindow *main, QAction *action) :
CutterDockWidget(main, action)
ListDockWidget(main, action, ListDockWidget::SearchBarPolicy::HideByDefault)
{
setObjectName("ResourcesWidget");
model = new ResourcesModel(&resources, this);
filterModel = new AddressableFilterProxyModel(model, this);
setModels(filterModel);
showCount(false);
// Configure widget
this->setWindowTitle(tr("Resources"));
// Add resources tree view
view = new CutterTreeView(this);
view->setModel(model);
view->show();
this->setWidget(view);
connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshResources()));
connect(view, SIGNAL(doubleClicked(const QModelIndex &)), this,
SLOT(onDoubleClicked(const QModelIndex &)));
}
void ResourcesWidget::refreshResources()
@ -100,12 +102,3 @@ void ResourcesWidget::refreshResources()
resources = Core()->getAllResources();
model->endResetModel();
}
void ResourcesWidget::onDoubleClicked(const QModelIndex &index)
{
if (!index.isValid())
return;
ResourcesDescription res = index.data(Qt::UserRole).value<ResourcesDescription>();
Core()->seekAndShow(res.vaddr);
}

View File

@ -4,13 +4,14 @@
#include "core/Cutter.h"
#include "CutterDockWidget.h"
#include "CutterTreeView.h"
#include "common/AddressableItemModel.h"
#include "widgets/ListDockWidget.h"
#include <QAbstractListModel>
class MainWindow;
class ResourcesWidget;
class ResourcesModel : public QAbstractListModel
class ResourcesModel : public AddressableItemModel<QAbstractListModel>
{
Q_OBJECT
@ -29,14 +30,17 @@ public:
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
RVA address(const QModelIndex &index) const override;
};
class ResourcesWidget : public CutterDockWidget
class ResourcesWidget : public ListDockWidget
{
Q_OBJECT
private:
ResourcesModel *model;
AddressableFilterProxyModel *filterModel;
CutterTreeView *view;
QList<ResourcesDescription> resources;
@ -45,7 +49,6 @@ public:
private slots:
void refreshResources();
void onDoubleClicked(const QModelIndex &);
};
#endif // RESOURCESWIDGET_H

View File

@ -30,7 +30,7 @@ static const QMap<QString, QString> kSearchBoundariesValues {
};
SearchModel::SearchModel(QList<SearchDescription> *search, QObject *parent)
: QAbstractListModel(parent),
: AddressableItemModel<QAbstractListModel>(parent),
search(search)
{
}
@ -119,11 +119,16 @@ QVariant SearchModel::headerData(int section, Qt::Orientation, int role) const
}
}
RVA SearchModel::address(const QModelIndex &index) const
{
const SearchDescription &exp = search->at(index.row());
return exp.offset;
}
SearchSortFilterProxyModel::SearchSortFilterProxyModel(SearchModel *source_model, QObject *parent)
: QSortFilterProxyModel(parent)
: AddressableFilterProxyModel(source_model, parent)
{
setSourceModel(source_model);
}
bool SearchSortFilterProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const
@ -174,6 +179,7 @@ SearchWidget::SearchWidget(MainWindow *main, QAction *action) :
search_model = new SearchModel(&search, this);
search_proxy_model = new SearchSortFilterProxyModel(search_model, this);
ui->searchTreeView->setModel(search_proxy_model);
ui->searchTreeView->setMainWindow(main);
ui->searchTreeView->sortByColumn(SearchModel::OFFSET, Qt::AscendingOrder);
setScrollMode();
@ -199,16 +205,6 @@ SearchWidget::SearchWidget(MainWindow *main, QAction *action) :
SearchWidget::~SearchWidget() {}
void SearchWidget::on_searchTreeView_doubleClicked(const QModelIndex &index)
{
if (!index.isValid())
return;
SearchDescription search = index.data(
SearchModel::SearchDescriptionRole).value<SearchDescription>();
Core()->seekAndShow(search.offset);
}
void SearchWidget::searchChanged()
{
refreshSearchspaces();

View File

@ -8,13 +8,14 @@
#include "core/Cutter.h"
#include "CutterDockWidget.h"
#include "AddressableItemList.h"
class MainWindow;
class QTreeWidgetItem;
class SearchWidget;
class SearchModel: public QAbstractListModel
class SearchModel: public AddressableItemModel<QAbstractListModel>
{
Q_OBJECT
@ -29,16 +30,18 @@ public:
SearchModel(QList<SearchDescription> *search, QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
RVA address(const QModelIndex &index) const override;
};
class SearchSortFilterProxyModel : public QSortFilterProxyModel
class SearchSortFilterProxyModel : public AddressableFilterProxyModel
{
Q_OBJECT
@ -65,7 +68,6 @@ public:
~SearchWidget();
private slots:
void on_searchTreeView_doubleClicked(const QModelIndex &index);
void on_searchInCombo_currentIndexChanged(int index);
void searchChanged();
void refreshSearchspaces();

View File

@ -31,7 +31,7 @@
<number>0</number>
</property>
<item>
<widget class="CutterTreeView" name="searchTreeView">
<widget class="AddressableItemList&lt;&gt;" name="searchTreeView">
<property name="styleSheet">
<string notr="true">CutterTreeView::item
{
@ -118,9 +118,9 @@
</widget>
<customwidgets>
<customwidget>
<class>CutterTreeView</class>
<class>AddressableItemList&lt;&gt;</class>
<extends>QTreeView</extends>
<header>widgets/CutterTreeView.h</header>
<header>widgets/AddressableItemList.h</header>
<container>1</container>
</customwidget>
</customwidgets>

View File

@ -2,7 +2,6 @@
#include "ui_StringsWidget.h"
#include "core/MainWindow.h"
#include "common/Helpers.h"
#include "dialogs/XrefsDialog.h"
#include "WidgetShortcuts.h"
#include <QClipboard>
@ -11,7 +10,7 @@
#include <QShortcut>
StringsModel::StringsModel(QList<StringDescription> *strings, QObject *parent)
: QAbstractListModel(parent),
: AddressableItemModel<QAbstractListModel>(parent),
strings(strings)
{
}
@ -43,9 +42,9 @@ QVariant StringsModel::data(const QModelIndex &index, int role) const
case StringsModel::TypeColumn:
return str.type.toUpper();
case StringsModel::LengthColumn:
return str.length;
return QString::number(str.length);
case StringsModel::SizeColumn:
return str.size;
return QString::number(str.size);
case StringsModel::SectionColumn:
return str.section;
default:
@ -83,10 +82,15 @@ QVariant StringsModel::headerData(int section, Qt::Orientation, int role) const
}
}
StringsProxyModel::StringsProxyModel(StringsModel *sourceModel, QObject *parent)
: QSortFilterProxyModel(parent)
RVA StringsModel::address(const QModelIndex &index) const
{
const StringDescription &str = strings->at(index.row());
return str.vaddr;
}
StringsProxyModel::StringsProxyModel(StringsModel *sourceModel, QObject *parent)
: AddressableFilterProxyModel(sourceModel, parent)
{
setSourceModel(sourceModel);
setFilterCaseSensitivity(Qt::CaseInsensitive);
setSortCaseSensitivity(Qt::CaseInsensitive);
}
@ -149,10 +153,6 @@ StringsWidget::StringsWidget(MainWindow *main, QAction *action) :
} );
connect(ui->actionCopy_String, SIGNAL(triggered()), this, SLOT(on_actionCopy()));
connect(ui->actionCopy_Address, SIGNAL(triggered()), this, SLOT(on_actionCopy()));
connect(ui->stringsTreeView, SIGNAL(customContextMenuRequested(const QPoint &)),
this, SLOT(showStringsContextMenu(const QPoint &)));
ui->actionFilter->setShortcut(QKeySequence::Find);
@ -160,13 +160,13 @@ StringsWidget::StringsWidget(MainWindow *main, QAction *action) :
model = new StringsModel(&strings, this);
proxyModel = new StringsProxyModel(model, this);
ui->stringsTreeView->setMainWindow(main);
ui->stringsTreeView->setModel(proxyModel);
ui->stringsTreeView->sortByColumn(StringsModel::OffsetColumn, Qt::AscendingOrder);
auto xRefShortcut = new QShortcut(QKeySequence{Qt::CTRL + Qt::Key_X}, this);
xRefShortcut->setContext(Qt::WidgetWithChildrenShortcut);
ui->actionX_refs->setShortcut(Qt::CTRL + Qt::Key_X);
connect(xRefShortcut, SIGNAL(activated()), this, SLOT(on_actionX_refs_triggered()));
//
auto menu = ui->stringsTreeView->getItemContextMenu();
menu->addAction(ui->actionCopy_String);
connect(ui->quickFilterView, SIGNAL(filterTextChanged(const QString &)), proxyModel,
SLOT(setFilterWildcard(const QString &)));
@ -180,7 +180,10 @@ StringsWidget::StringsWidget(MainWindow *main, QAction *action) :
searchShortcut->setContext(Qt::WidgetWithChildrenShortcut);
QShortcut *clearShortcut = new QShortcut(QKeySequence(Qt::Key_Escape), this);
connect(clearShortcut, &QShortcut::activated, ui->quickFilterView, &ComboQuickFilterView::clearFilter);
connect(clearShortcut, &QShortcut::activated, this, [this]() {
ui->quickFilterView->clearFilter();
ui->stringsTreeView->setFocus();
});
clearShortcut->setContext(Qt::WidgetWithChildrenShortcut);
connect(Core(), SIGNAL(refreshAll()), this, SLOT(refreshStrings()));
@ -197,16 +200,6 @@ StringsWidget::StringsWidget(MainWindow *main, QAction *action) :
StringsWidget::~StringsWidget() {}
void StringsWidget::on_stringsTreeView_doubleClicked(const QModelIndex &index)
{
if (!index.isValid()) {
return;
}
StringDescription str = index.data(StringsModel::StringDescriptionRole).value<StringDescription>();
Core()->seekAndShow(str.vaddr);
}
void StringsWidget::refreshStrings()
{
if (task) {
@ -250,32 +243,6 @@ void StringsWidget::stringSearchFinished(const QList<StringDescription> &strings
task = nullptr;
}
void StringsWidget::showStringsContextMenu(const QPoint &pt)
{
QMenu *menu = new QMenu(ui->stringsTreeView);
menu->clear();
menu->addAction(ui->actionCopy_String);
menu->addAction(ui->actionCopy_Address);
menu->addAction(ui->actionFilter);
menu->addSeparator();
menu->addAction(ui->actionX_refs);
menu->exec(ui->stringsTreeView->mapToGlobal(pt));
delete menu;
}
void StringsWidget::on_actionX_refs_triggered()
{
StringDescription str = ui->stringsTreeView->selectionModel()->currentIndex().data(
StringsModel::StringDescriptionRole).value<StringDescription>();
XrefsDialog x(nullptr);
x.fillRefsForAddress(str.vaddr, RAddressString(str.vaddr), false);
x.exec();
}
void StringsWidget::on_actionCopy()
{
QModelIndex current_item = ui->stringsTreeView->currentIndex();
@ -283,11 +250,7 @@ void StringsWidget::on_actionCopy()
QModelIndex index;
if (sender() == ui->actionCopy_String) {
index = ui->stringsTreeView->model()->index(row, 1);
} else if (sender() == ui->actionCopy_Address) {
index = ui->stringsTreeView->model()->index(row, 0);
}
index = ui->stringsTreeView->model()->index(row, 1);
QClipboard *clipboard = QApplication::clipboard();
clipboard->setText(index.data().toString());

View File

@ -7,6 +7,7 @@
#include "CutterDockWidget.h"
#include "common/StringsTask.h"
#include "CutterTreeWidget.h"
#include "AddressableItemModel.h"
#include <QAbstractListModel>
#include <QSortFilterProxyModel>
@ -19,7 +20,7 @@ namespace Ui {
class StringsWidget;
}
class StringsModel: public QAbstractListModel
class StringsModel: public AddressableItemModel<QAbstractListModel>
{
Q_OBJECT
@ -34,16 +35,18 @@ public:
StringsModel(QList<StringDescription> *strings, QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
RVA address(const QModelIndex &index) const override;
};
class StringsProxyModel : public QSortFilterProxyModel
class StringsProxyModel : public AddressableFilterProxyModel
{
Q_OBJECT
@ -69,14 +72,10 @@ public:
~StringsWidget();
private slots:
void on_stringsTreeView_doubleClicked(const QModelIndex &index);
void refreshStrings();
void stringSearchFinished(const QList<StringDescription> &strings);
void refreshSectionCombo();
void showStringsContextMenu(const QPoint &pt);
void on_actionX_refs_triggered();
void on_actionCopy();
private:

View File

@ -31,7 +31,7 @@
<number>0</number>
</property>
<item>
<widget class="CutterTreeView" name="stringsTreeView">
<widget class="AddressableItemList&lt;&gt;" name="stringsTreeView">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@ -71,21 +71,11 @@
</item>
</layout>
</widget>
<action name="actionCopy_Address">
<property name="text">
<string>Copy Address</string>
</property>
</action>
<action name="actionCopy_String">
<property name="text">
<string>Copy String</string>
</property>
</action>
<action name="actionX_refs">
<property name="text">
<string>Xrefs</string>
</property>
</action>
<action name="actionFilter">
<property name="text">
<string>Filter</string>
@ -94,9 +84,9 @@
</widget>
<customwidgets>
<customwidget>
<class>CutterTreeView</class>
<class>AddressableItemList&lt;&gt;</class>
<extends>QTreeView</extends>
<header>widgets/CutterTreeView.h</header>
<header>widgets/AddressableItemList.h</header>
<container>1</container>
</customwidget>
<customwidget>