diff options
Diffstat (limited to 'Source/Core/DolphinQt/Debugger/MemoryViewWidget.cpp')
| -rw-r--r-- | Source/Core/DolphinQt/Debugger/MemoryViewWidget.cpp | 441 |
1 files changed, 223 insertions, 218 deletions
diff --git a/Source/Core/DolphinQt/Debugger/MemoryViewWidget.cpp b/Source/Core/DolphinQt/Debugger/MemoryViewWidget.cpp index c12fbf0d3b..81178106af 100644 --- a/Source/Core/DolphinQt/Debugger/MemoryViewWidget.cpp +++ b/Source/Core/DolphinQt/Debugger/MemoryViewWidget.cpp @@ -33,6 +33,8 @@ // 120; i.e., 120 units * 1/8 = 15 degrees." (http://doc.qt.io/qt-5/qwheelevent.html#angleDelta) constexpr double SCROLL_FRACTION_DEGREES = 15.; +// Number of columns that don't contain the value of a memory address. +constexpr int MISC_COLUMNS = 2; constexpr auto USER_ROLE_IS_ROW_BREAKPOINT_CELL = Qt::UserRole; constexpr auto USER_ROLE_CELL_ADDRESS = Qt::UserRole + 1; constexpr auto USER_ROLE_VALUE_TYPE = Qt::UserRole + 2; @@ -55,6 +57,9 @@ public: setShowGrid(false); setContextMenuPolicy(Qt::CustomContextMenu); setSelectionMode(SingleSelection); + setTextElideMode(Qt::TextElideMode::ElideNone); + setRowCount(30); + setColumnCount(8); connect(this, &MemoryViewTable::customContextMenuRequested, m_view, &MemoryViewWidget::OnContextMenu); @@ -64,7 +69,7 @@ public: void resizeEvent(QResizeEvent* event) override { QTableWidget::resizeEvent(event); - m_view->Update(); + m_view->CreateTable(); } void keyPressEvent(QKeyEvent* event) override @@ -175,11 +180,12 @@ MemoryViewWidget::MemoryViewWidget(QWidget* parent) : QWidget(parent) this->setLayout(layout); connect(&Settings::Instance(), &Settings::DebugFontChanged, this, &MemoryViewWidget::UpdateFont); - connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, [this] { Update(); }); - connect(Host::GetInstance(), &Host::UpdateDisasmDialog, this, &MemoryViewWidget::Update); + connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, + &MemoryViewWidget::UpdateColumns); + connect(Host::GetInstance(), &Host::UpdateDisasmDialog, this, &MemoryViewWidget::UpdateColumns); connect(&Settings::Instance(), &Settings::ThemeChanged, this, &MemoryViewWidget::Update); - // Also calls update. + // Also calls create table. UpdateFont(); } @@ -196,7 +202,8 @@ void MemoryViewWidget::UpdateFont() m_font_width = fm.width(QLatin1Char('0')); #endif m_table->setFont(Settings::Instance().GetDebugFont()); - Update(); + + CreateTable(); } constexpr int GetTypeSize(MemoryViewWidget::Type type) @@ -258,269 +265,265 @@ constexpr int GetCharacterCount(MemoryViewWidget::Type type) } } -void MemoryViewWidget::Update() +void MemoryViewWidget::CreateTable() { - const QSignalBlocker blocker(m_table); - - m_table->clearSelection(); - - u32 address = m_address; - address = Common::AlignDown(address, m_alignment); - - const int data_columns = m_bytes_per_row / GetTypeSize(m_type); - - if (m_dual_view) - m_table->setColumnCount(2 + 2 * data_columns); - else - m_table->setColumnCount(2 + data_columns); - - if (m_table->rowCount() == 0) - m_table->setRowCount(1); + m_table->clearContents(); // This sets all row heights and determines horizontal ascii spacing. + // Could be placed in UpdateFont() but doesn't apply correctly unless called more. m_table->verticalHeader()->setDefaultSectionSize(m_font_vspace - 1); m_table->verticalHeader()->setMinimumSectionSize(m_font_vspace - 1); m_table->horizontalHeader()->setMinimumSectionSize(m_font_width * 2); - m_table->setTextElideMode(Qt::TextElideMode::ElideNone); - const AddressSpace::Accessors* accessors = AddressSpace::GetAccessors(m_address_space); + const QSignalBlocker blocker(m_table); + + // Set column and row parameters. + // Span is the number of unique memory values covered in one row. + const int data_span = m_bytes_per_row / GetTypeSize(m_type); + m_data_columns = m_dual_view ? data_span * 2 : data_span; + const int total_columns = MISC_COLUMNS + m_data_columns; - // Calculate (roughly) how many rows will fit in our table const int rows = std::round((m_table->height() / static_cast<float>(m_table->rowHeight(0))) - 0.25); + m_table->setColumnCount(total_columns); m_table->setRowCount(rows); + m_table->setColumnWidth(0, m_table->rowHeight(0)); - for (int i = 0; i < rows; i++) - { - u32 row_address = address - ((m_table->rowCount() / 2) * m_bytes_per_row) + i * m_bytes_per_row; - - auto* bp_item = new QTableWidgetItem; - bp_item->setFlags(Qt::ItemIsEnabled); - bp_item->setData(USER_ROLE_IS_ROW_BREAKPOINT_CELL, true); - bp_item->setData(USER_ROLE_CELL_ADDRESS, row_address); - bp_item->setData(USER_ROLE_VALUE_TYPE, static_cast<int>(Type::Null)); + // Get optional dual-view type + std::optional<Type> left_type = std::nullopt; - m_table->setItem(i, 0, bp_item); + if (m_dual_view) + { + if (GetTypeSize(m_type) == 1) + left_type = Type::Hex8; + else if (GetTypeSize(m_type) == 2) + left_type = Type::Hex16; + else if (GetTypeSize(m_type) == 8) + left_type = Type::Hex64; + else + left_type = Type::Hex32; + } - auto* row_item = - new QTableWidgetItem(QStringLiteral("%1").arg(row_address, 8, 16, QLatin1Char('0'))); + // Create cells and add data that won't be changing. + // Breakpoint buttons + auto* bp_item = new QTableWidgetItem; + bp_item->setFlags(Qt::ItemIsEnabled); + bp_item->setData(USER_ROLE_IS_ROW_BREAKPOINT_CELL, true); + bp_item->setData(USER_ROLE_VALUE_TYPE, static_cast<int>(Type::Null)); - row_item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); - row_item->setData(USER_ROLE_IS_ROW_BREAKPOINT_CELL, false); - row_item->setData(USER_ROLE_CELL_ADDRESS, row_address); - row_item->setData(USER_ROLE_VALUE_TYPE, static_cast<int>(Type::Null)); + // Row Addresses + auto* row_item = new QTableWidgetItem(QStringLiteral("-")); + row_item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + row_item->setData(USER_ROLE_IS_ROW_BREAKPOINT_CELL, false); + row_item->setData(USER_ROLE_VALUE_TYPE, static_cast<int>(Type::Null)); - m_table->setItem(i, 1, row_item); + // Data item + auto* item = new QTableWidgetItem(QStringLiteral("-")); + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable); + item->setData(USER_ROLE_IS_ROW_BREAKPOINT_CELL, false); - if (row_address == address) - row_item->setSelected(true); + for (int i = 0; i < rows; i++) + { + m_table->setItem(i, 0, bp_item->clone()); + m_table->setItem(i, 1, row_item->clone()); - if (Core::GetState() != Core::State::Paused || !accessors->IsValidAddress(row_address)) + for (int c = 0; c < m_data_columns; c++) { - for (int c = 2; c < m_table->columnCount(); c++) + if (left_type && c < data_span) + { + item->setData(USER_ROLE_VALUE_TYPE, static_cast<int>(left_type.value())); + } + else { - auto* item = new QTableWidgetItem(QStringLiteral("-")); - item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); - item->setData(USER_ROLE_IS_ROW_BREAKPOINT_CELL, false); - item->setData(USER_ROLE_CELL_ADDRESS, row_address); - item->setData(USER_ROLE_VALUE_TYPE, static_cast<int>(Type::Null)); + item->setData(USER_ROLE_VALUE_TYPE, static_cast<int>(m_type)); - m_table->setItem(i, c, item); + // Left type will never be these. + auto text_alignment = Qt::AlignLeft; + if (m_type == Type::Signed32 || m_type == Type::Unsigned32 || m_type == Type::Signed16 || + m_type == Type::Unsigned16 || m_type == Type::Signed8 || m_type == Type::Unsigned8) + { + text_alignment = Qt::AlignRight; + } + item->setTextAlignment(text_alignment | Qt::AlignVCenter); } - continue; + m_table->setItem(i, c + MISC_COLUMNS, item->clone()); } } - int starting_column = 2; + // Update column width + int start_fill = MISC_COLUMNS; + if (left_type) + { + const int width_left = m_font_width * GetCharacterCount(left_type.value()); - if (m_dual_view) + for (int i = 0; i < data_span - 1; i++) + m_table->setColumnWidth(i + start_fill, width_left); + + // Extra spacing between dual views. + m_table->setColumnWidth(start_fill + data_span - 1, width_left + m_font_width * 2); + + start_fill += data_span; + } + + // If dual-view, updates the right-side columns only. + const int width = m_font_width * GetCharacterCount(m_type); + for (int i = start_fill; i < total_columns; i++) + m_table->setColumnWidth(i, width); + + Update(); +} + +void MemoryViewWidget::Update() +{ + // Check if table is created + if (m_table->item(1, 1) == nullptr) + return; + + const QSignalBlocker blocker(m_table); + m_table->clearSelection(); + + // Update addresses + const u32 address = Common::AlignDown(m_address, m_alignment); + u32 row_address = address - (m_table->rowCount() / 2) * m_bytes_per_row; + const int data_span = m_bytes_per_row / GetTypeSize(m_type); + + for (int i = 0; i < m_table->rowCount(); i++, row_address += m_bytes_per_row) { - // Match left columns to number of right columns. - Type left_type = Type::Hex32; - if (GetTypeSize(m_type) == 1) - left_type = Type::Hex8; - else if (GetTypeSize(m_type) == 2) - left_type = Type::Hex16; - else if (GetTypeSize(m_type) == 8) - left_type = Type::Hex64; + auto* bp_item = m_table->item(i, 0); + bp_item->setData(USER_ROLE_CELL_ADDRESS, row_address); - UpdateColumns(left_type, starting_column); + auto* row_item = m_table->item(i, 1); + row_item->setText(QStringLiteral("%1").arg(row_address, 8, 16, QLatin1Char('0'))); + row_item->setData(USER_ROLE_CELL_ADDRESS, row_address); - const int column_count = m_bytes_per_row / GetTypeSize(left_type); + if (row_address == address) + row_item->setSelected(true); - // Update column width - for (int i = starting_column; i < starting_column + column_count - 1; i++) - m_table->setColumnWidth(i, m_font_width * GetCharacterCount(left_type)); + for (int c = 0; c < m_data_columns; c++) + { + auto* item = m_table->item(i, c + MISC_COLUMNS); - // Extra spacing between dual views. - m_table->setColumnWidth(starting_column + column_count - 1, - m_font_width * (GetCharacterCount(left_type) + 2)); + u32 item_address; + if (m_dual_view && c >= data_span) + item_address = row_address + (c - data_span) * GetTypeSize(m_type); + else + item_address = row_address + c * GetTypeSize(m_type); - starting_column += column_count; + item->setData(USER_ROLE_CELL_ADDRESS, item_address); + } } - UpdateColumns(m_type, starting_column); + UpdateColumns(); UpdateBreakpointTags(); - m_table->setColumnWidth(0, m_table->rowHeight(0)); - - for (int i = starting_column; i <= m_table->columnCount(); i++) - m_table->setColumnWidth(i, m_font_width * GetCharacterCount(m_type)); - m_table->viewport()->update(); m_table->update(); update(); } -void MemoryViewWidget::UpdateColumns(Type type, int first_column) +void MemoryViewWidget::UpdateColumns() { - if (Core::GetState() != Core::State::Paused) + // Check if table is created + if (m_table->item(1, 1) == nullptr) return; - const int data_columns = m_bytes_per_row / GetTypeSize(type); - const AddressSpace::Accessors* accessors = AddressSpace::GetAccessors(m_address_space); - - auto text_alignment = Qt::AlignLeft; - if (type == Type::Signed32 || type == Type::Unsigned32 || type == Type::Signed16 || - type == Type::Unsigned16 || type == Type::Signed8 || type == Type::Unsigned8) - { - text_alignment = Qt::AlignRight; - } + const QSignalBlocker blocker(m_table); for (int i = 0; i < m_table->rowCount(); i++) { - u32 row_address = m_table->item(i, 1)->data(USER_ROLE_CELL_ADDRESS).toUInt(); - if (!accessors->IsValidAddress(row_address)) - continue; + for (int c = 0; c < m_data_columns; c++) + { + auto* cell_item = m_table->item(i, c + MISC_COLUMNS); + const u32 cell_address = cell_item->data(USER_ROLE_CELL_ADDRESS).toUInt(); + const Type type = static_cast<Type>(cell_item->data(USER_ROLE_VALUE_TYPE).toInt()); - auto update_values = [&](auto value_to_string) { - for (int c = 0; c < data_columns; c++) - { - auto* cell_item = new QTableWidgetItem; - cell_item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable); - cell_item->setTextAlignment(text_alignment); + cell_item->setText(ValueToString(cell_address, type)); + } + } +} - const u32 cell_address = row_address + c * GetTypeSize(type); +QString MemoryViewWidget::ValueToString(u32 address, Type type) +{ + const AddressSpace::Accessors* accessors = AddressSpace::GetAccessors(m_address_space); + if (!accessors->IsValidAddress(address) || Core::GetState() != Core::State::Paused) + return QStringLiteral("-"); - m_table->setItem(i, first_column + c, cell_item); + switch (type) + { + case Type::Hex8: + { + const u8 value = accessors->ReadU8(address); + return QStringLiteral("%1").arg(value, 2, 16, QLatin1Char('0')); + } + case Type::ASCII: + { + const char value = accessors->ReadU8(address); + return IsPrintableCharacter(value) ? QString{QChar::fromLatin1(value)} : + QString{QChar::fromLatin1('.')}; + } + case Type::Hex16: + { + const u16 value = accessors->ReadU16(address); + return QStringLiteral("%1").arg(value, 4, 16, QLatin1Char('0')); + } + case Type::Hex32: + { + const u32 value = accessors->ReadU32(address); + return QStringLiteral("%1").arg(value, 8, 16, QLatin1Char('0')); + } + case Type::Hex64: + { + const u64 value = accessors->ReadU64(address); + return QStringLiteral("%1").arg(value, 16, 16, QLatin1Char('0')); + } + case Type::Unsigned8: + return QString::number(accessors->ReadU8(address)); + case Type::Unsigned16: + return QString::number(accessors->ReadU16(address)); + case Type::Unsigned32: + return QString::number(accessors->ReadU32(address)); + case Type::Signed8: + return QString::number(Common::BitCast<s8>(accessors->ReadU8(address))); + case Type::Signed16: + return QString::number(Common::BitCast<s16>(accessors->ReadU16(address))); + case Type::Signed32: + return QString::number(Common::BitCast<s32>(accessors->ReadU32(address))); + case Type::Float32: + { + QString string = QString::number(accessors->ReadF32(address), 'g', 4); + // Align to first digit. + if (!string.startsWith(QLatin1Char('-'))) + string.prepend(QLatin1Char(' ')); - if (accessors->IsValidAddress(cell_address)) - { - cell_item->setText(value_to_string(cell_address)); - cell_item->setData(USER_ROLE_IS_ROW_BREAKPOINT_CELL, false); - cell_item->setData(USER_ROLE_CELL_ADDRESS, cell_address); - cell_item->setData(USER_ROLE_VALUE_TYPE, static_cast<int>(type)); - } - else - { - cell_item->setText(QStringLiteral("-")); - cell_item->setData(USER_ROLE_IS_ROW_BREAKPOINT_CELL, false); - cell_item->setData(USER_ROLE_CELL_ADDRESS, cell_address); - cell_item->setData(USER_ROLE_VALUE_TYPE, static_cast<int>(Type::Null)); - } - } - }; - switch (type) - { - case Type::Hex8: - update_values([&accessors](u32 address) { - const u8 value = accessors->ReadU8(address); - return QStringLiteral("%1").arg(value, 2, 16, QLatin1Char('0')); - }); - break; - case Type::ASCII: - update_values([&accessors](u32 address) { - const char value = accessors->ReadU8(address); - return IsPrintableCharacter(value) ? QString{QChar::fromLatin1(value)} : - QString{QChar::fromLatin1('.')}; - }); - break; - case Type::Hex16: - update_values([&accessors](u32 address) { - const u16 value = accessors->ReadU16(address); - return QStringLiteral("%1").arg(value, 4, 16, QLatin1Char('0')); - }); - break; - case Type::Hex32: - update_values([&accessors](u32 address) { - const u32 value = accessors->ReadU32(address); - return QStringLiteral("%1").arg(value, 8, 16, QLatin1Char('0')); - }); - break; - case Type::Hex64: - update_values([&accessors](u32 address) { - const u64 value = accessors->ReadU64(address); - return QStringLiteral("%1").arg(value, 16, 16, QLatin1Char('0')); - }); - break; - case Type::Unsigned8: - update_values( - [&accessors](u32 address) { return QString::number(accessors->ReadU8(address)); }); - break; - case Type::Unsigned16: - update_values( - [&accessors](u32 address) { return QString::number(accessors->ReadU16(address)); }); - break; - case Type::Unsigned32: - update_values( - [&accessors](u32 address) { return QString::number(accessors->ReadU32(address)); }); - break; - case Type::Signed8: - update_values([&accessors](u32 address) { - return QString::number(Common::BitCast<s8>(accessors->ReadU8(address))); - }); - break; - case Type::Signed16: - update_values([&accessors](u32 address) { - return QString::number(Common::BitCast<s16>(accessors->ReadU16(address))); - }); - break; - case Type::Signed32: - update_values([&accessors](u32 address) { - return QString::number(Common::BitCast<s32>(accessors->ReadU32(address))); - }); - break; - case Type::Float32: - update_values([&accessors](u32 address) { - QString string = QString::number(accessors->ReadF32(address), 'g', 4); - // Align to first digit. - if (!string.startsWith(QLatin1Char('-'))) - string.prepend(QLatin1Char(' ')); - - return string; - }); - break; - case Type::Double: - update_values([&accessors](u32 address) { - QString string = - QString::number(Common::BitCast<double>(accessors->ReadU64(address)), 'g', 4); - // Align to first digit. - if (!string.startsWith(QLatin1Char('-'))) - string.prepend(QLatin1Char(' ')); - - return string; - }); - break; - } + return string; + } + case Type::Double: + { + QString string = QString::number(Common::BitCast<double>(accessors->ReadU64(address)), 'g', 4); + // Align to first digit. + if (!string.startsWith(QLatin1Char('-'))) + string.prepend(QLatin1Char(' ')); + + return string; + } + default: + return QStringLiteral("-"); } } void MemoryViewWidget::UpdateBreakpointTags() { - if (Core::GetState() != Core::State::Paused) - return; - for (int i = 0; i < m_table->rowCount(); i++) { bool row_breakpoint = false; - for (int c = 2; c < m_table->columnCount(); c++) + for (int c = 0; c < m_data_columns; c++) { // Pull address from cell itself, helpful for dual column view. - auto cell = m_table->item(i, c); - u32 address = cell->data(USER_ROLE_CELL_ADDRESS).toUInt(); + auto cell = m_table->item(i, c + MISC_COLUMNS); + const u32 address = cell->data(USER_ROLE_CELL_ADDRESS).toUInt(); if (address == 0) { @@ -528,14 +531,16 @@ void MemoryViewWidget::UpdateBreakpointTags() continue; } - // In dual view the only sizes that dont match up on both left and right views are for - // Double, which uses two or four columns of hex32. if (m_address_space == AddressSpace::Type::Effective && PowerPC::memchecks.GetMemCheck(address, GetTypeSize(m_type)) != nullptr) { row_breakpoint = true; cell->setBackground(Qt::red); } + else + { + cell->setBackground(Qt::white); + } } if (row_breakpoint) @@ -545,6 +550,10 @@ void MemoryViewWidget::UpdateBreakpointTags() Resources::GetScaledThemeIcon("debugger_breakpoint") .pixmap(QSize(m_table->rowHeight(0) - 3, m_table->rowHeight(0) - 3))); } + else + { + m_table->item(i, 0)->setData(Qt::DecorationRole, QIcon()); + } } } @@ -724,7 +733,7 @@ void MemoryViewWidget::SetDisplay(Type type, int bytes_per_row, int alignment, b else m_alignment = alignment; - Update(); + CreateTable(); } void MemoryViewWidget::SetBPType(BPType type) @@ -815,25 +824,21 @@ void MemoryViewWidget::OnContextMenu(const QPoint& pos) if (!item_selected || item_selected->data(USER_ROLE_IS_ROW_BREAKPOINT_CELL).toBool()) return; - const bool item_has_value = - item_selected->data(USER_ROLE_VALUE_TYPE).toInt() != static_cast<int>(Type::Null); const u32 addr = item_selected->data(USER_ROLE_CELL_ADDRESS).toUInt(); + const AddressSpace::Accessors* accessors = AddressSpace::GetAccessors(m_address_space); + const bool item_has_value = + item_selected->data(USER_ROLE_VALUE_TYPE).toInt() != static_cast<int>(Type::Null) && + accessors->IsValidAddress(addr); auto* menu = new QMenu(this); menu->addAction(tr("Copy Address"), this, [this, addr] { OnCopyAddress(addr); }); auto* copy_hex = menu->addAction(tr("Copy Hex"), this, [this, addr] { OnCopyHex(addr); }); + copy_hex->setEnabled(item_has_value); - const AddressSpace::Accessors* accessors = AddressSpace::GetAccessors(m_address_space); - copy_hex->setEnabled(item_has_value && Core::GetState() != Core::State::Uninitialized && - accessors->IsValidAddress(addr)); - - auto* copy_value = menu->addAction(tr("Copy Value"), this, [this, &pos] { - // Re-fetch the item in case the underlying table has refreshed since the menu was opened. - auto* item = m_table->itemAt(pos); - if (item && item->data(USER_ROLE_VALUE_TYPE).toInt() != static_cast<int>(Type::Null)) - QApplication::clipboard()->setText(item->text()); + auto* copy_value = menu->addAction(tr("Copy Value"), this, [this, item_selected] { + QApplication::clipboard()->setText(item_selected->text()); }); copy_value->setEnabled(item_has_value); |
