mirror of
https://github.com/rizinorg/cutter.git
synced 2024-12-18 19:06:10 +00:00
Add addressable item context menu to stack widget (#1779)
* Use addresableItemContextMenu in stack widget. * Show hexwidget when seeking to stack.
This commit is contained in:
parent
75e98cb05d
commit
23561f4337
@ -12,6 +12,7 @@
|
||||
#include <QAbstractItemView>
|
||||
#include <QAbstractButton>
|
||||
#include <QDockWidget>
|
||||
#include <QMenu>
|
||||
|
||||
static QAbstractItemView::ScrollMode scrollMode()
|
||||
{
|
||||
@ -241,4 +242,14 @@ void setThemeIcons(QList<QPair<void *, QString>> supportedIconsNames,
|
||||
}
|
||||
}
|
||||
|
||||
void prependQAction(QAction *action, QMenu *menu)
|
||||
{
|
||||
auto actions = menu->actions();
|
||||
if (actions.empty()) {
|
||||
menu->addAction(action);
|
||||
} else {
|
||||
menu->insertAction(actions.first(), action);
|
||||
}
|
||||
}
|
||||
|
||||
} // end namespace
|
||||
|
@ -16,6 +16,8 @@ class QAbstractItemView;
|
||||
class QAbstractButton;
|
||||
class QWidget;
|
||||
class QTreeView;
|
||||
class QAction;
|
||||
class QMenu;
|
||||
|
||||
namespace qhelpers {
|
||||
QString formatBytecount(const long bytecount);
|
||||
@ -51,6 +53,8 @@ QByteArray applyColorToSvg(const QString &filename, QColor color);
|
||||
|
||||
void setThemeIcons(QList<QPair<void*, QString>> supportedIconsNames, std::function<void(void *, const QIcon &)> setter);
|
||||
|
||||
void prependQAction(QAction *action, QMenu *menu);
|
||||
|
||||
} // qhelpers
|
||||
|
||||
#endif // HELPERS_H
|
||||
|
@ -8,24 +8,32 @@
|
||||
#include "QHeaderView"
|
||||
#include "QMenu"
|
||||
|
||||
enum ColumnIndex {
|
||||
COLUMN_OFFSET = 0,
|
||||
COLUMN_VALUE,
|
||||
COLUMN_DESCRIPTION
|
||||
};
|
||||
|
||||
StackWidget::StackWidget(MainWindow *main, QAction *action) :
|
||||
CutterDockWidget(main, action),
|
||||
ui(new Ui::StackWidget)
|
||||
ui(new Ui::StackWidget),
|
||||
addressableItemContextMenu(this, main)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
// Setup stack model
|
||||
modelStack->setHorizontalHeaderItem(0, new QStandardItem(tr("Offset")));
|
||||
modelStack->setHorizontalHeaderItem(1, new QStandardItem(tr("Value")));
|
||||
modelStack->setHorizontalHeaderItem(2, new QStandardItem(tr("Reference")));
|
||||
modelStack->setHorizontalHeaderItem(COLUMN_OFFSET, new QStandardItem(tr("Offset")));
|
||||
modelStack->setHorizontalHeaderItem(COLUMN_VALUE, new QStandardItem(tr("Value")));
|
||||
modelStack->setHorizontalHeaderItem(COLUMN_DESCRIPTION, new QStandardItem(tr("Reference")));
|
||||
viewStack->setFont(Config()->getFont());
|
||||
viewStack->setModel(modelStack);
|
||||
viewStack->verticalHeader()->hide();
|
||||
viewStack->setSortingEnabled(true);
|
||||
viewStack->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||
ui->verticalLayout->addWidget(viewStack);
|
||||
viewStack->setEditTriggers(viewStack->editTriggers() &
|
||||
~(QAbstractItemView::DoubleClicked | QAbstractItemView::AnyKeyPressed));
|
||||
|
||||
seekAction = new QAction(tr("Seek to this offset"), this);
|
||||
editAction = new QAction(tr("Edit stack value..."), this);
|
||||
viewStack->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
@ -40,8 +48,16 @@ StackWidget::StackWidget(MainWindow *main, QAction *action) :
|
||||
connect(viewStack, SIGNAL(doubleClicked(const QModelIndex &)), this,
|
||||
SLOT(onDoubleClicked(const QModelIndex &)));
|
||||
connect(viewStack, SIGNAL(customContextMenuRequested(QPoint)), SLOT(customMenuRequested(QPoint)));
|
||||
connect(seekAction, &QAction::triggered, this, &StackWidget::seekOffset);
|
||||
connect(editAction, &QAction::triggered, this, &StackWidget::editStack);
|
||||
connect(viewStack->selectionModel(), &QItemSelectionModel::currentChanged,
|
||||
this, &StackWidget::onCurrentChanged);
|
||||
connect(modelStack, &QStandardItemModel::itemChanged, this, &StackWidget::onItemChanged);
|
||||
|
||||
addressableItemContextMenu.addAction(editAction);
|
||||
addActions(addressableItemContextMenu.actions());
|
||||
|
||||
menuText.setSeparator(true);
|
||||
qhelpers::prependQAction(&menuText, &addressableItemContextMenu);
|
||||
}
|
||||
|
||||
StackWidget::~StackWidget() = default;
|
||||
@ -57,6 +73,7 @@ void StackWidget::updateContents()
|
||||
|
||||
void StackWidget::setStackGrid()
|
||||
{
|
||||
updatingData = true;
|
||||
QJsonArray stackValues = Core()->getStack().array();
|
||||
int i = 0;
|
||||
for (const QJsonValue &value : stackValues) {
|
||||
@ -66,8 +83,8 @@ void StackWidget::setStackGrid()
|
||||
QStandardItem *rowOffset = new QStandardItem(addr);
|
||||
rowOffset->setEditable(false);
|
||||
QStandardItem *rowValue = new QStandardItem(valueStack);
|
||||
modelStack->setItem(i, 0, rowOffset);
|
||||
modelStack->setItem(i, 1, rowValue);
|
||||
modelStack->setItem(i, COLUMN_OFFSET, rowOffset);
|
||||
modelStack->setItem(i, COLUMN_VALUE, rowValue);
|
||||
QJsonValue refObject = stackItem["ref"];
|
||||
if (!refObject.isUndefined()) { // check that the key exists
|
||||
QString ref = refObject.toString();
|
||||
@ -75,25 +92,28 @@ void StackWidget::setStackGrid()
|
||||
ref = Core()->cmd("psz @ [" + addr + "]");
|
||||
}
|
||||
QStandardItem *rowRef = new QStandardItem(ref);
|
||||
modelStack->setItem(i, 2, rowRef);
|
||||
rowRef->setEditable(false);
|
||||
QModelIndex cell = modelStack->index(i, COLUMN_DESCRIPTION);
|
||||
modelStack->setItem(i, COLUMN_DESCRIPTION, rowRef);
|
||||
if (refObject.toString().contains("ascii") && refObject.toString().count("-->") == 1) {
|
||||
modelStack->setData(modelStack->index(i, 2, QModelIndex()), QVariant(QColor(243, 156, 17)),
|
||||
modelStack->setData(cell, QVariant(QColor(243, 156, 17)),
|
||||
Qt::ForegroundRole); // orange
|
||||
} else if (ref.contains("program R X") && ref.count("-->") == 0) {
|
||||
modelStack->setData(modelStack->index(i, 2, QModelIndex()), QVariant(QColor(Qt::red)),
|
||||
modelStack->setData(cell, QVariant(QColor(Qt::red)),
|
||||
Qt::ForegroundRole);
|
||||
} else if (ref.contains("stack") && ref.count("-->") == 0) {
|
||||
modelStack->setData(modelStack->index(i, 2, QModelIndex()), QVariant(QColor(Qt::cyan)),
|
||||
modelStack->setData(cell, QVariant(QColor(Qt::cyan)),
|
||||
Qt::ForegroundRole);
|
||||
} else if (ref.contains("library") && ref.count("-->") == 0) {
|
||||
modelStack->setData(modelStack->index(i, 2, QModelIndex()), QVariant(QColor(Qt::green)),
|
||||
modelStack->setData(cell, QVariant(QColor(Qt::green)),
|
||||
Qt::ForegroundRole);
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
viewStack->setModel(modelStack);
|
||||
viewStack->resizeColumnsToContents();;
|
||||
viewStack->resizeColumnsToContents();
|
||||
updatingData = false;
|
||||
}
|
||||
|
||||
void StackWidget::fontsUpdatedSlot()
|
||||
@ -106,35 +126,33 @@ void StackWidget::onDoubleClicked(const QModelIndex &index)
|
||||
if (!index.isValid())
|
||||
return;
|
||||
// Check if we are clicking on the offset or value columns and seek if it is the case
|
||||
if (index.column() <= 1) {
|
||||
int column = index.column();
|
||||
if (column <= COLUMN_VALUE) {
|
||||
QString item = index.data().toString();
|
||||
Core()->seekAndShow(item);
|
||||
Core()->seek(item);
|
||||
if (column == COLUMN_OFFSET) {
|
||||
mainWindow->showMemoryWidget(MemoryWidgetType::Hexdump);
|
||||
} else {
|
||||
Core()->showMemoryWidget();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StackWidget::customMenuRequested(QPoint pos)
|
||||
{
|
||||
QMenu *menu = new QMenu(this);
|
||||
menu->addAction(seekAction);
|
||||
menu->addAction(editAction);
|
||||
menu->popup(viewStack->viewport()->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void StackWidget::seekOffset()
|
||||
{
|
||||
QString offset = viewStack->selectionModel()->currentIndex().data().toString();
|
||||
Core()->seekAndShow(offset);
|
||||
addressableItemContextMenu.exec(viewStack->viewport()->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void StackWidget::editStack()
|
||||
{
|
||||
bool ok;
|
||||
int row = viewStack->selectionModel()->currentIndex().row();
|
||||
QString offset = viewStack->selectionModel()->currentIndex().sibling(row, 0).data().toString();
|
||||
auto model = viewStack->model();
|
||||
QString offset = model->index(row, COLUMN_OFFSET).data().toString();
|
||||
EditInstructionDialog e(EDIT_NONE, this);
|
||||
e.setWindowTitle(tr("Edit stack at %1").arg(offset));
|
||||
|
||||
QString oldBytes = viewStack->selectionModel()->currentIndex().sibling(row, 1).data().toString();
|
||||
QString oldBytes = model->index(row, COLUMN_VALUE).data().toString();
|
||||
e.setInstruction(oldBytes);
|
||||
|
||||
if (e.exec()) {
|
||||
@ -144,3 +162,45 @@ void StackWidget::editStack()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StackWidget::onCurrentChanged(const QModelIndex ¤t, const QModelIndex &previous)
|
||||
{
|
||||
Q_UNUSED(current)
|
||||
Q_UNUSED(previous)
|
||||
auto currentIndex = viewStack->selectionModel()->currentIndex();
|
||||
QString offsetString;
|
||||
if (currentIndex.column() != COLUMN_DESCRIPTION) {
|
||||
offsetString = currentIndex.data().toString();
|
||||
} else {
|
||||
offsetString = currentIndex.sibling(currentIndex.row(), COLUMN_VALUE).data().toString();
|
||||
}
|
||||
|
||||
RVA offset = Core()->math(offsetString);
|
||||
addressableItemContextMenu.setTarget(offset);
|
||||
if (currentIndex.column() == COLUMN_OFFSET) {
|
||||
menuText.setText(tr("Stack position"));
|
||||
} else {
|
||||
menuText.setText(tr("Pointed memory"));
|
||||
}
|
||||
}
|
||||
|
||||
void StackWidget::onItemChanged(QStandardItem *item)
|
||||
{
|
||||
if (updatingData || item->column() != COLUMN_VALUE) {
|
||||
return;
|
||||
}
|
||||
QModelIndex index = item->index();
|
||||
int row = item->row();
|
||||
QString text = item->text();
|
||||
// Queue the update instead of performing immediately. Editing will trigger reload.
|
||||
// Performing reload while itemChanged signal is on stack would result
|
||||
// in itemView getting stuck in EditingState and preventing further edits.
|
||||
QMetaObject::invokeMethod(this, [this, index, row, text]() {
|
||||
QString offsetString = index.sibling(row, COLUMN_OFFSET).data().toString();
|
||||
bool ok = false;
|
||||
auto offset = offsetString.toULongLong(&ok, 16);
|
||||
if (ok) {
|
||||
Core()->editBytesEndian(offset, text);
|
||||
}
|
||||
}, Qt::QueuedConnection);
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include "core/Cutter.h"
|
||||
#include "CutterDockWidget.h"
|
||||
#include "menus/AddressableItemContextMenu.h"
|
||||
|
||||
class MainWindow;
|
||||
|
||||
@ -28,14 +29,17 @@ private slots:
|
||||
void fontsUpdatedSlot();
|
||||
void onDoubleClicked(const QModelIndex &index);
|
||||
void customMenuRequested(QPoint pos);
|
||||
void seekOffset();
|
||||
void editStack();
|
||||
void onCurrentChanged(const QModelIndex ¤t, const QModelIndex &previous);
|
||||
void onItemChanged(QStandardItem *item);
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::StackWidget> ui;
|
||||
QTableView *viewStack = new QTableView;
|
||||
QStandardItemModel *modelStack = new QStandardItemModel(1, 3, this);
|
||||
QAction *seekAction;
|
||||
QAction *editAction;
|
||||
QAction menuText;
|
||||
RefreshDeferrer *refreshDeferrer;
|
||||
AddressableItemContextMenu addressableItemContextMenu;
|
||||
bool updatingData = false;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user