2018-06-06 11:05:20 +00:00
|
|
|
#include "StackWidget.h"
|
|
|
|
#include "ui_StackWidget.h"
|
2018-10-17 07:55:53 +00:00
|
|
|
#include "common/JsonModel.h"
|
|
|
|
#include "common/Helpers.h"
|
2018-07-24 23:15:38 +00:00
|
|
|
#include "dialogs/EditInstructionDialog.h"
|
2018-06-06 11:05:20 +00:00
|
|
|
|
2019-02-22 16:50:45 +00:00
|
|
|
#include "core/MainWindow.h"
|
2018-06-06 11:05:20 +00:00
|
|
|
#include "QHeaderView"
|
2018-07-24 23:15:38 +00:00
|
|
|
#include "QMenu"
|
2018-06-06 11:05:20 +00:00
|
|
|
|
|
|
|
StackWidget::StackWidget(MainWindow *main, QAction *action) :
|
|
|
|
CutterDockWidget(main, action),
|
2019-09-25 14:18:30 +00:00
|
|
|
ui(new Ui::StackWidget),
|
2019-10-06 17:35:44 +00:00
|
|
|
menuText(this),
|
2019-09-25 14:18:30 +00:00
|
|
|
addressableItemContextMenu(this, main)
|
2018-06-06 11:05:20 +00:00
|
|
|
{
|
|
|
|
ui->setupUi(this);
|
|
|
|
|
2018-11-05 21:51:27 +00:00
|
|
|
// Setup stack model
|
2018-07-18 10:15:10 +00:00
|
|
|
viewStack->setFont(Config()->getFont());
|
2018-06-06 11:05:20 +00:00
|
|
|
viewStack->setModel(modelStack);
|
2019-12-06 16:35:03 +00:00
|
|
|
viewStack->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch);
|
2018-06-06 11:05:20 +00:00
|
|
|
viewStack->verticalHeader()->hide();
|
2019-12-06 16:35:03 +00:00
|
|
|
viewStack->setShowGrid(false);
|
2018-06-06 11:05:20 +00:00
|
|
|
viewStack->setSortingEnabled(true);
|
2019-12-06 17:50:37 +00:00
|
|
|
viewStack->setAutoScroll(false);
|
2018-07-23 23:14:54 +00:00
|
|
|
viewStack->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
|
2018-06-06 11:05:20 +00:00
|
|
|
ui->verticalLayout->addWidget(viewStack);
|
2019-09-25 14:18:30 +00:00
|
|
|
viewStack->setEditTriggers(viewStack->editTriggers() &
|
|
|
|
~(QAbstractItemView::DoubleClicked | QAbstractItemView::AnyKeyPressed));
|
2018-06-06 11:05:20 +00:00
|
|
|
|
2019-05-26 10:12:23 +00:00
|
|
|
editAction = new QAction(tr("Edit stack value..."), this);
|
2018-07-24 23:15:38 +00:00
|
|
|
viewStack->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
|
|
|
2019-01-13 18:11:59 +00:00
|
|
|
refreshDeferrer = createRefreshDeferrer([this]() {
|
|
|
|
updateContents();
|
|
|
|
});
|
|
|
|
|
2018-06-06 11:05:20 +00:00
|
|
|
connect(Core(), &CutterCore::refreshAll, this, &StackWidget::updateContents);
|
2019-12-20 12:15:04 +00:00
|
|
|
connect(Core(), &CutterCore::registersChanged, this, &StackWidget::updateContents);
|
2018-07-24 23:15:38 +00:00
|
|
|
connect(Core(), &CutterCore::stackChanged, this, &StackWidget::updateContents);
|
2018-07-18 10:15:10 +00:00
|
|
|
connect(Config(), &Configuration::fontsUpdated, this, &StackWidget::fontsUpdatedSlot);
|
2018-07-24 23:15:38 +00:00
|
|
|
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);
|
2019-09-25 14:18:30 +00:00
|
|
|
connect(viewStack->selectionModel(), &QItemSelectionModel::currentChanged,
|
|
|
|
this, &StackWidget::onCurrentChanged);
|
|
|
|
|
|
|
|
addressableItemContextMenu.addAction(editAction);
|
|
|
|
addActions(addressableItemContextMenu.actions());
|
|
|
|
|
|
|
|
menuText.setSeparator(true);
|
|
|
|
qhelpers::prependQAction(&menuText, &addressableItemContextMenu);
|
2018-06-06 11:05:20 +00:00
|
|
|
}
|
|
|
|
|
2019-01-13 18:11:59 +00:00
|
|
|
StackWidget::~StackWidget() = default;
|
2018-06-06 11:05:20 +00:00
|
|
|
|
|
|
|
void StackWidget::updateContents()
|
|
|
|
{
|
2019-01-13 18:11:59 +00:00
|
|
|
if (!refreshDeferrer->attemptRefresh(nullptr)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-06-06 11:05:20 +00:00
|
|
|
setStackGrid();
|
|
|
|
}
|
|
|
|
|
|
|
|
void StackWidget::setStackGrid()
|
|
|
|
{
|
2019-10-06 17:35:44 +00:00
|
|
|
modelStack->reload();
|
2019-09-25 14:18:30 +00:00
|
|
|
viewStack->resizeColumnsToContents();
|
2018-06-06 11:05:20 +00:00
|
|
|
}
|
2018-07-18 10:15:10 +00:00
|
|
|
|
|
|
|
void StackWidget::fontsUpdatedSlot()
|
|
|
|
{
|
|
|
|
viewStack->setFont(Config()->getFont());
|
2018-07-24 23:15:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2019-09-25 14:18:30 +00:00
|
|
|
int column = index.column();
|
2019-10-06 17:35:44 +00:00
|
|
|
if (column <= StackModel::ValueColumn) {
|
2018-07-24 23:15:38 +00:00
|
|
|
QString item = index.data().toString();
|
2019-09-25 14:18:30 +00:00
|
|
|
Core()->seek(item);
|
2019-10-06 17:35:44 +00:00
|
|
|
if (column == StackModel::OffsetColumn) {
|
2019-09-25 14:18:30 +00:00
|
|
|
mainWindow->showMemoryWidget(MemoryWidgetType::Hexdump);
|
|
|
|
} else {
|
|
|
|
Core()->showMemoryWidget();
|
|
|
|
}
|
2018-07-24 23:15:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void StackWidget::customMenuRequested(QPoint pos)
|
|
|
|
{
|
2019-09-25 14:18:30 +00:00
|
|
|
addressableItemContextMenu.exec(viewStack->viewport()->mapToGlobal(pos));
|
2018-07-24 23:15:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void StackWidget::editStack()
|
|
|
|
{
|
|
|
|
bool ok;
|
|
|
|
int row = viewStack->selectionModel()->currentIndex().row();
|
2019-09-25 14:18:30 +00:00
|
|
|
auto model = viewStack->model();
|
2019-10-06 17:35:44 +00:00
|
|
|
QString offset = model->index(row, StackModel::OffsetColumn).data().toString();
|
2019-03-23 06:32:31 +00:00
|
|
|
EditInstructionDialog e(EDIT_NONE, this);
|
|
|
|
e.setWindowTitle(tr("Edit stack at %1").arg(offset));
|
2018-07-24 23:15:38 +00:00
|
|
|
|
2019-10-06 17:35:44 +00:00
|
|
|
QString oldBytes = model->index(row, StackModel::ValueColumn).data().toString();
|
2019-03-23 06:32:31 +00:00
|
|
|
e.setInstruction(oldBytes);
|
2018-07-24 23:15:38 +00:00
|
|
|
|
2019-03-23 06:32:31 +00:00
|
|
|
if (e.exec()) {
|
|
|
|
QString bytes = e.getInstruction();
|
2018-07-24 23:15:38 +00:00
|
|
|
if (bytes != oldBytes) {
|
2018-11-05 21:51:27 +00:00
|
|
|
Core()->editBytesEndian(offset.toULongLong(&ok, 16), bytes);
|
2018-07-24 23:15:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-09-25 14:18:30 +00:00
|
|
|
|
|
|
|
void StackWidget::onCurrentChanged(const QModelIndex ¤t, const QModelIndex &previous)
|
|
|
|
{
|
|
|
|
Q_UNUSED(current)
|
|
|
|
Q_UNUSED(previous)
|
|
|
|
auto currentIndex = viewStack->selectionModel()->currentIndex();
|
|
|
|
QString offsetString;
|
2019-10-06 17:35:44 +00:00
|
|
|
if (currentIndex.column() != StackModel::DescriptionColumn) {
|
2019-09-25 14:18:30 +00:00
|
|
|
offsetString = currentIndex.data().toString();
|
|
|
|
} else {
|
2019-10-06 17:35:44 +00:00
|
|
|
offsetString = currentIndex.sibling(currentIndex.row(), StackModel::ValueColumn).data().toString();
|
2019-09-25 14:18:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
RVA offset = Core()->math(offsetString);
|
|
|
|
addressableItemContextMenu.setTarget(offset);
|
2019-10-06 17:35:44 +00:00
|
|
|
if (currentIndex.column() == StackModel::OffsetColumn) {
|
2019-09-25 14:18:30 +00:00
|
|
|
menuText.setText(tr("Stack position"));
|
|
|
|
} else {
|
|
|
|
menuText.setText(tr("Pointed memory"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-06 17:35:44 +00:00
|
|
|
StackModel::StackModel(QObject *parent)
|
|
|
|
: QAbstractTableModel(parent)
|
2019-09-25 14:18:30 +00:00
|
|
|
{
|
2019-10-06 17:35:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void StackModel::reload()
|
|
|
|
{
|
|
|
|
QJsonArray stackValues = Core()->getStack().array();
|
|
|
|
|
|
|
|
beginResetModel();
|
|
|
|
values.clear();
|
|
|
|
for (const QJsonValue &value : stackValues) {
|
|
|
|
QJsonObject stackItem = value.toObject();
|
|
|
|
Item item;
|
|
|
|
|
|
|
|
item.offset = stackItem["addr"].toVariant().toULongLong();
|
|
|
|
item.value = RAddressString(stackItem["value"].toVariant().toULongLong());
|
|
|
|
|
|
|
|
|
|
|
|
QJsonValue refObject = stackItem["ref"];
|
|
|
|
if (!refObject.isUndefined()) { // check that the key exists
|
|
|
|
QString ref = refObject.toString();
|
|
|
|
if (ref.contains("ascii") && ref.count("-->") == 1) {
|
2019-10-19 08:00:29 +00:00
|
|
|
ref = Core()->cmdj(QString("pszj @ [%1]").arg(item.offset)).object().value("string").toString();
|
2019-10-06 17:35:44 +00:00
|
|
|
}
|
|
|
|
item.description = ref;
|
|
|
|
|
|
|
|
|
|
|
|
if (refObject.toString().contains("ascii") && refObject.toString().count("-->") == 1) {
|
|
|
|
item.descriptionColor = QVariant(QColor(243, 156, 17));
|
|
|
|
} else if (ref.contains("program R X") && ref.count("-->") == 0) {
|
|
|
|
item.descriptionColor = QVariant(QColor(Qt::red));
|
|
|
|
} else if (ref.contains("stack") && ref.count("-->") == 0) {
|
|
|
|
item.descriptionColor = QVariant(QColor(Qt::cyan));
|
|
|
|
} else if (ref.contains("library") && ref.count("-->") == 0) {
|
|
|
|
item.descriptionColor = QVariant(QColor(Qt::green));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
values.push_back(item);
|
2019-09-25 14:18:30 +00:00
|
|
|
}
|
2019-10-06 17:35:44 +00:00
|
|
|
endResetModel();
|
|
|
|
}
|
|
|
|
|
|
|
|
int StackModel::rowCount(const QModelIndex &) const
|
|
|
|
{
|
|
|
|
return this->values.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
int StackModel::columnCount(const QModelIndex &) const
|
|
|
|
{
|
|
|
|
return ColumnCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
QVariant StackModel::data(const QModelIndex &index, int role) const
|
|
|
|
{
|
|
|
|
if (!index.isValid() || index.row() >= values.count())
|
|
|
|
return QVariant();
|
|
|
|
|
|
|
|
const auto &item = values.at(index.row());
|
|
|
|
|
|
|
|
switch (role) {
|
|
|
|
case Qt::DisplayRole:
|
|
|
|
switch (index.column()) {
|
|
|
|
case OffsetColumn:
|
|
|
|
return RAddressString(item.offset);
|
|
|
|
case ValueColumn:
|
|
|
|
return item.value;
|
|
|
|
case DescriptionColumn:
|
|
|
|
return item.description;
|
|
|
|
default:
|
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
case Qt::ForegroundRole:
|
|
|
|
switch (index.column()) {
|
|
|
|
case DescriptionColumn:
|
|
|
|
return item.descriptionColor;
|
|
|
|
default:
|
|
|
|
return QVariant();
|
2019-09-25 14:18:30 +00:00
|
|
|
}
|
2019-10-06 17:35:44 +00:00
|
|
|
case StackDescriptionRole:
|
|
|
|
return QVariant::fromValue(item);
|
|
|
|
default:
|
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QVariant StackModel::headerData(int section, Qt::Orientation orientation, int role) const
|
|
|
|
{
|
|
|
|
Q_UNUSED(orientation);
|
|
|
|
switch (role) {
|
|
|
|
case Qt::DisplayRole:
|
|
|
|
switch (section) {
|
|
|
|
case OffsetColumn:
|
|
|
|
return tr("Offset");
|
|
|
|
case ValueColumn:
|
|
|
|
return tr("Value");
|
|
|
|
case DescriptionColumn:
|
|
|
|
return tr("Reference");
|
|
|
|
default:
|
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool StackModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
|
|
|
{
|
|
|
|
if (role != Qt::EditRole || index.column() != ValueColumn) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto currentData = data(index, StackDescriptionRole);
|
|
|
|
if (!currentData.canConvert<StackModel::Item>()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
auto currentItem = currentData.value<StackModel::Item>();
|
|
|
|
|
|
|
|
Core()->editBytesEndian(currentItem.offset, value.toString());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
Qt::ItemFlags StackModel::flags(const QModelIndex &index) const
|
|
|
|
{
|
|
|
|
switch (index.column()) {
|
|
|
|
case ValueColumn:
|
|
|
|
return QAbstractTableModel::flags(index) | Qt::ItemIsEditable;
|
|
|
|
default:
|
|
|
|
return QAbstractTableModel::flags(index);
|
|
|
|
}
|
2019-09-25 14:18:30 +00:00
|
|
|
}
|