cutter/src/widgets/StackWidget.cpp

207 lines
7.7 KiB
C++
Raw Normal View History

#include "StackWidget.h"
#include "ui_StackWidget.h"
2018-10-17 07:55:53 +00:00
#include "common/JsonModel.h"
#include "common/Helpers.h"
#include "dialogs/EditInstructionDialog.h"
#include "core/MainWindow.h"
#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),
addressableItemContextMenu(this, main)
{
ui->setupUi(this);
2018-11-05 21:51:27 +00:00
// Setup stack model
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));
editAction = new QAction(tr("Edit stack value..."), this);
viewStack->setContextMenuPolicy(Qt::CustomContextMenu);
refreshDeferrer = createRefreshDeferrer([this]() {
updateContents();
});
connect(Core(), &CutterCore::refreshAll, this, &StackWidget::updateContents);
connect(Core(), &CutterCore::seekChanged, this, &StackWidget::updateContents);
connect(Core(), &CutterCore::stackChanged, this, &StackWidget::updateContents);
connect(Config(), &Configuration::fontsUpdated, this, &StackWidget::fontsUpdatedSlot);
connect(viewStack, SIGNAL(doubleClicked(const QModelIndex &)), this,
SLOT(onDoubleClicked(const QModelIndex &)));
connect(viewStack, SIGNAL(customContextMenuRequested(QPoint)), SLOT(customMenuRequested(QPoint)));
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;
void StackWidget::updateContents()
{
if (!refreshDeferrer->attemptRefresh(nullptr)) {
return;
}
setStackGrid();
}
void StackWidget::setStackGrid()
{
updatingData = true;
QJsonArray stackValues = Core()->getStack().array();
int i = 0;
for (const QJsonValue &value : stackValues) {
QJsonObject stackItem = value.toObject();
QString addr = RAddressString(stackItem["addr"].toVariant().toULongLong());
QString valueStack = RAddressString(stackItem["value"].toVariant().toULongLong());
QStandardItem *rowOffset = new QStandardItem(addr);
rowOffset->setEditable(false);
QStandardItem *rowValue = new QStandardItem(valueStack);
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();
if (ref.contains("ascii") && ref.count("-->") == 1) {
ref = Core()->cmd("psz @ [" + addr + "]");
}
QStandardItem *rowRef = new QStandardItem(ref);
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(cell, QVariant(QColor(243, 156, 17)),
2018-09-30 20:00:53 +00:00
Qt::ForegroundRole); // orange
} else if (ref.contains("program R X") && ref.count("-->") == 0) {
modelStack->setData(cell, QVariant(QColor(Qt::red)),
2018-09-30 20:00:53 +00:00
Qt::ForegroundRole);
} else if (ref.contains("stack") && ref.count("-->") == 0) {
modelStack->setData(cell, QVariant(QColor(Qt::cyan)),
2018-09-30 20:00:53 +00:00
Qt::ForegroundRole);
} else if (ref.contains("library") && ref.count("-->") == 0) {
modelStack->setData(cell, QVariant(QColor(Qt::green)),
2018-09-30 20:00:53 +00:00
Qt::ForegroundRole);
}
}
i++;
}
viewStack->setModel(modelStack);
viewStack->resizeColumnsToContents();
updatingData = false;
}
void StackWidget::fontsUpdatedSlot()
{
viewStack->setFont(Config()->getFont());
}
void StackWidget::onDoubleClicked(const QModelIndex &index)
{
if (!index.isValid())
return;
2018-11-05 21:51:27 +00:00
// Check if we are clicking on the offset or value columns and seek if it is the case
int column = index.column();
if (column <= COLUMN_VALUE) {
QString item = index.data().toString();
Core()->seek(item);
if (column == COLUMN_OFFSET) {
mainWindow->showMemoryWidget(MemoryWidgetType::Hexdump);
} else {
Core()->showMemoryWidget();
}
}
}
void StackWidget::customMenuRequested(QPoint pos)
{
addressableItemContextMenu.exec(viewStack->viewport()->mapToGlobal(pos));
}
void StackWidget::editStack()
{
bool ok;
int row = viewStack->selectionModel()->currentIndex().row();
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 = model->index(row, COLUMN_VALUE).data().toString();
e.setInstruction(oldBytes);
if (e.exec()) {
QString bytes = e.getInstruction();
if (bytes != oldBytes) {
2018-11-05 21:51:27 +00:00
Core()->editBytesEndian(offset.toULongLong(&ok, 16), bytes);
}
}
}
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);
}