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:
karliss 2019-09-25 17:18:30 +03:00 committed by Itay Cohen
parent 75e98cb05d
commit 23561f4337
4 changed files with 109 additions and 30 deletions

View File

@ -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

View File

@ -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

View File

@ -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 &current, 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);
}

View File

@ -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 &current, 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;
};