Play-/Source/ui_qt/DebugSupport/MemoryViewTable.cpp

296 lines
7.6 KiB
C++
Raw Normal View History

2020-01-05 22:46:47 +00:00
#include <QAction>
#include <QApplication>
#include <QInputDialog>
#include <QMenu>
#include <QMessageBox>
#include <QHeaderView>
2020-07-03 22:13:49 +01:00
#include <QFont>
2020-01-05 22:46:47 +00:00
#include <QVBoxLayout>
2021-02-13 11:51:05 -05:00
#include <QTextLayout>
2021-03-03 20:23:38 +00:00
#include <cmath>
2020-01-05 22:46:47 +00:00
#include "string_format.h"
#include "MemoryViewTable.h"
#include "DebugExpressionEvaluator.h"
#include "DebugUtils.h"
2020-01-05 22:46:47 +00:00
2020-01-06 12:40:18 +00:00
CMemoryViewTable::CMemoryViewTable(QWidget* parent)
2020-02-06 12:39:36 +00:00
: QTableView(parent)
2020-01-05 22:46:47 +00:00
{
m_model = new CQtMemoryViewModel(this);
setModel(m_model);
SetBytesPerLine(32);
auto fixedFont = DebugUtils::CreateMonospaceFont();
2020-01-05 22:46:47 +00:00
setFont(fixedFont);
QFontMetrics metric(fixedFont);
m_cwidth = metric.maxWidth();
auto header = horizontalHeader();
2021-02-13 11:51:05 -05:00
header->setMinimumSectionSize(1);
2021-01-21 23:08:28 +00:00
header->setStretchLastSection(true);
header->setSectionResizeMode(QHeaderView::ResizeToContents);
2020-01-05 22:46:47 +00:00
header->hide();
2021-01-21 23:08:28 +00:00
m_model->Redraw();
2020-01-05 22:46:47 +00:00
setContextMenuPolicy(Qt::CustomContextMenu);
connect(this, &QTableView::customContextMenuRequested, this, &CMemoryViewTable::ShowContextMenu);
connect(selectionModel(), &QItemSelectionModel::selectionChanged, this, &CMemoryViewTable::SelectionChanged);
}
int CMemoryViewTable::sizeHintForColumn(int col) const
{
if(col < m_model->columnCount() - 1)
{
2021-02-13 11:51:05 -05:00
return ComputeItemCellWidth();
}
return 0;
}
2021-02-13 11:51:05 -05:00
int CMemoryViewTable::ComputeItemCellWidth() const
{
auto modelString = std::string(m_model->CharsPerUnit(), '0');
int marginWidth = style()->pixelMetric(QStyle::PM_FocusFrameHMargin, nullptr, this) + 1;
QTextLayout layout(modelString.c_str(), font());
layout.beginLayout();
auto line = layout.createLine();
line.setLineWidth(1000);
int textWidth = ceil(line.naturalTextWidth());
layout.endLayout();
int result = textWidth + (2 * marginWidth) + 1;
return result;
}
2020-01-06 12:40:18 +00:00
void CMemoryViewTable::Setup(CVirtualMachine* virtualMachine, CMIPS* ctx, bool memoryJumps)
{
m_virtualMachine = virtualMachine;
m_context = ctx;
m_enableMemoryJumps = memoryJumps;
if(m_enableMemoryJumps)
{
assert(m_virtualMachine);
assert(m_context);
}
}
2020-01-05 22:46:47 +00:00
void CMemoryViewTable::SetData(CQtMemoryViewModel::getByteProto getByte, int size)
{
m_model->SetData(getByte, size);
}
void CMemoryViewTable::ShowEvent()
{
AutoColumn();
}
void CMemoryViewTable::ResizeEvent()
{
if(!m_model || m_bytesPerLine)
return;
AutoColumn();
}
void CMemoryViewTable::HandleMachineStateChange()
{
m_model->Redraw();
}
int CMemoryViewTable::GetBytesPerLine()
{
return m_bytesPerLine;
}
void CMemoryViewTable::SetBytesPerLine(int bytesForLine)
{
m_bytesPerLine = bytesForLine;
if(bytesForLine)
{
2020-12-25 03:13:53 +00:00
m_model->SetColumnCount(bytesForLine);
2020-01-05 22:46:47 +00:00
m_model->Redraw();
}
else
{
AutoColumn();
}
}
void CMemoryViewTable::ShowContextMenu(const QPoint& pos)
{
auto rightClickMenu = new QMenu(this);
{
auto bytesPerLineMenu = rightClickMenu->addMenu(tr("&Bytes Per Line"));
auto bytesPerLineActionGroup = new QActionGroup(this);
bytesPerLineActionGroup->setExclusive(true);
auto autoAction = bytesPerLineMenu->addAction("auto");
autoAction->setChecked(m_bytesPerLine == 0);
autoAction->setCheckable(true);
bytesPerLineActionGroup->addAction(autoAction);
connect(autoAction, &QAction::triggered, std::bind(&CMemoryViewTable::SetBytesPerLine, this, 0));
auto byte16Action = bytesPerLineMenu->addAction("16 bytes");
byte16Action->setChecked(m_bytesPerLine == 16);
byte16Action->setCheckable(true);
bytesPerLineActionGroup->addAction(byte16Action);
connect(byte16Action, &QAction::triggered, std::bind(&CMemoryViewTable::SetBytesPerLine, this, 16));
auto byte32Action = bytesPerLineMenu->addAction("32 bytes");
byte32Action->setChecked(m_bytesPerLine == 32);
byte32Action->setCheckable(true);
bytesPerLineActionGroup->addAction(byte32Action);
connect(byte32Action, &QAction::triggered, std::bind(&CMemoryViewTable::SetBytesPerLine, this, 32));
}
{
auto unitMenu = rightClickMenu->addMenu(tr("&Display Unit"));
auto unitActionGroup = new QActionGroup(this);
unitActionGroup->setExclusive(true);
auto activeUnit = m_model->GetActiveUnit();
for(uint32 i = 0; i < CQtMemoryViewModel::g_units.size(); i++)
{
const auto& unit = CQtMemoryViewModel::g_units[i];
auto itemAction = unitMenu->addAction(unit.description);
itemAction->setChecked(i == activeUnit);
itemAction->setCheckable(true);
unitActionGroup->addAction(itemAction);
connect(itemAction, &QAction::triggered, std::bind(&CMemoryViewTable::SetActiveUnit, this, i));
}
}
if(m_enableMemoryJumps)
{
rightClickMenu->addSeparator();
{
auto itemAction = rightClickMenu->addAction("Goto Address...");
connect(itemAction, &QAction::triggered, std::bind(&CMemoryViewTable::GotoAddress, this));
}
{
uint32 selection = m_selected;
if((selection & 0x03) == 0)
{
uint32 valueAtSelection = m_context->m_pMemoryMap->GetWord(selection);
auto followPointerText = string_format("Follow Pointer (0x%08X)", valueAtSelection);
auto itemAction = rightClickMenu->addAction(followPointerText.c_str());
connect(itemAction, &QAction::triggered, std::bind(&CMemoryViewTable::FollowPointer, this));
}
}
}
rightClickMenu->popup(viewport()->mapToGlobal(pos));
}
void CMemoryViewTable::AutoColumn()
{
if(m_bytesPerLine)
return;
2021-01-21 23:08:28 +00:00
int columnHeaderWidth = verticalHeader()->size().width();
int columnCellWidth = columnWidth(0);
2020-01-05 22:46:47 +00:00
int tableWidth = size().width() - columnHeaderWidth - style()->pixelMetric(QStyle::PM_ScrollBarExtent);
2021-01-21 23:08:28 +00:00
auto bytesPerUnit = m_model->GetBytesPerUnit();
2020-12-25 03:13:53 +00:00
int asciiCell = m_cwidth * bytesPerUnit;
2020-01-05 22:46:47 +00:00
int i = 16;
2020-01-05 22:46:47 +00:00
while(true)
{
2021-01-21 23:08:28 +00:00
int valueCellsWidth = i * columnCellWidth;
2020-01-05 22:46:47 +00:00
int asciiCellsWidth = (i * asciiCell) + (m_cwidth * 2);
int totalWidth = valueCellsWidth + asciiCellsWidth;
if(totalWidth > tableWidth)
{
--i;
break;
}
++i;
}
2020-12-25 03:13:53 +00:00
m_model->SetColumnCount(i * bytesPerUnit);
2020-01-05 22:46:47 +00:00
m_model->Redraw();
}
void CMemoryViewTable::GotoAddress()
{
if(m_virtualMachine->GetStatus() == CVirtualMachine::RUNNING)
{
QApplication::beep();
return;
}
std::string sValue;
{
bool ok;
QString res = QInputDialog::getText(this, tr("Goto Address"),
tr("Enter new address:"), QLineEdit::Normal,
tr("00000000"), &ok);
if(!ok || res.isEmpty())
return;
sValue = res.toStdString();
}
try
{
uint32 nAddress = CDebugExpressionEvaluator::Evaluate(sValue.c_str(), m_context);
SetSelectionStart(nAddress);
}
catch(const std::exception& exception)
{
std::string message = std::string("Error evaluating expression: ") + exception.what();
QMessageBox::critical(this, tr("Error"), tr(message.c_str()), QMessageBox::Ok, QMessageBox::Ok);
}
}
void CMemoryViewTable::FollowPointer()
{
if(m_virtualMachine->GetStatus() == CVirtualMachine::RUNNING)
{
QApplication::beep();
return;
}
uint32 valueAtSelection = m_context->m_pMemoryMap->GetWord(m_selected);
SetSelectionStart(valueAtSelection);
}
void CMemoryViewTable::SetActiveUnit(int index)
{
m_model->SetActiveUnit(index);
m_model->Redraw();
2020-01-05 22:46:47 +00:00
AutoColumn();
}
void CMemoryViewTable::SetSelectionStart(uint32 address)
{
2021-01-01 00:27:45 +00:00
auto column = (address % m_model->BytesForCurrentLine()) / m_model->GetBytesPerUnit();
2020-12-25 03:13:53 +00:00
auto row = (address - column) / m_model->BytesForCurrentLine();
2020-01-05 22:46:47 +00:00
auto index = m_model->index(row, column);
selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect);
scrollTo(index, QAbstractItemView::PositionAtCenter);
}
void CMemoryViewTable::SelectionChanged()
{
auto indexes = selectionModel()->selectedIndexes();
if(!indexes.empty())
{
auto index = indexes.first();
2020-12-25 03:13:53 +00:00
int address = index.row() * (m_model->BytesForCurrentLine());
2020-12-25 03:25:57 +00:00
if(m_model->columnCount() - 1 != index.column())
2020-01-05 22:46:47 +00:00
{
2020-12-25 03:13:53 +00:00
address += index.column() * m_model->GetBytesPerUnit();
2020-01-05 22:46:47 +00:00
}
m_selected = address;
OnSelectionChange(m_selected);
}
}