Expand Up
@@ -83,7 +83,8 @@ void WatchWidget::CreateWidgets()
m_table->setColumnCount (NUM_COLUMNS);
m_table->verticalHeader ()->setHidden (true );
m_table->setContextMenuPolicy (Qt::CustomContextMenu);
m_table->setSelectionMode (QAbstractItemView::SingleSelection);
m_table->setSelectionMode (QAbstractItemView::ExtendedSelection);
m_table->setSelectionBehavior (QAbstractItemView::SelectRows);
m_new = m_toolbar->addAction (tr (" New" ), this , &WatchWidget::OnNewWatch);
m_delete = m_toolbar->addAction (tr (" Delete" ), this , &WatchWidget::OnDelete);
Expand Down
Expand Up
@@ -158,11 +159,11 @@ void WatchWidget::Update()
// i18n: Data type used in computing
tr (" String" ),
// i18n: Floating-point (non-integer) number
tr (" Float" )});
tr (" Float" ), tr ( " Locked " ) });
for (int i = 0 ; i < size; i++)
{
auto entry = PowerPC::debug_interface.GetWatch (i);
const auto & entry = PowerPC::debug_interface.GetWatch (i);
auto * label = new QTableWidgetItem (QString::fromStdString (entry.name ));
auto * address =
Expand All
@@ -172,8 +173,11 @@ void WatchWidget::Update()
auto * string = new QTableWidgetItem;
auto * floatValue = new QTableWidgetItem;
std::array<QTableWidgetItem*, NUM_COLUMNS> items = {label, address, hex,
decimal, string, floatValue};
auto * lockValue = new QTableWidgetItem;
lockValue->setFlags (Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable);
std::array<QTableWidgetItem*, NUM_COLUMNS> items = {label, address, hex, decimal,
string, floatValue, lockValue};
QBrush brush = QPalette ().brush (QPalette::Text);
Expand All
@@ -189,6 +193,7 @@ void WatchWidget::Update()
decimal->setText (QString::number (PowerPC::HostRead_U32 (entry.address )));
string->setText (QString::fromStdString (PowerPC::HostGetString (entry.address , 32 )));
floatValue->setText (QString::number (PowerPC::HostRead_F32 (entry.address )));
lockValue->setCheckState (entry.locked ? Qt::Checked : Qt::Unchecked);
}
}
Expand Down
Expand Up
@@ -235,11 +240,7 @@ void WatchWidget::OnDelete()
if (m_table->selectedItems ().empty ())
return ;
auto row_variant = m_table->selectedItems ()[0 ]->data (Qt::UserRole);
if (row_variant.isNull ())
return ;
DeleteWatch (row_variant.toInt ());
DeleteSelectedWatches ();
}
void WatchWidget::OnClear ()
Expand Down
Expand Up
@@ -280,6 +281,10 @@ void WatchWidget::OnLoad()
if (ini.GetLines (" Watches" , &watches, false ))
{
for (const auto & watch : PowerPC::debug_interface.GetWatches ())
{
PowerPC::debug_interface.UnsetPatch (watch.address );
}
PowerPC::debug_interface.ClearWatches ();
PowerPC::debug_interface.LoadWatchesFromStrings (watches);
}
Expand All
@@ -302,20 +307,30 @@ void WatchWidget::ShowContextMenu()
if (!m_table->selectedItems ().empty ())
{
auto row_variant = m_table->selectedItems ()[0 ]->data (Qt::UserRole);
if (!row_variant.isNull ())
const std::size_t count = m_table->selectionModel ()->selectedRows ().count ();
if (count > 1 )
{
int row = row_variant.toInt ();
menu->addAction (tr (" &Delete Watches" ), this , [this ] { DeleteSelectedWatches (); });
menu->addAction (tr (" &Lock Watches" ), this , [this ] { LockSelectedWatches (); });
menu->addAction (tr (" &Unlock Watches" ), this , [this ] { UnlockSelectedWatches (); });
}
else if (count == 1 )
{
auto row_variant = m_table->selectedItems ()[0 ]->data (Qt::UserRole);
if (row >= 0 )
if (!row_variant. isNull () )
{
menu->addAction (tr (" Show in Memory" ), this , [this , row] { ShowInMemory (row); });
// i18n: This kind of "watch" is used for watching emulated memory.
// It's not related to timekeeping devices.
menu->addAction (tr (" &Delete Watch" ), this , [this , row] { DeleteWatch (row); });
menu->addAction (tr (" &Add Memory Breakpoint" ), this ,
[this , row] { AddWatchBreakpoint (row); });
int row = row_variant.toInt ();
if (row >= 0 )
{
menu->addAction (tr (" Show in Memory" ), this , [this , row] { ShowInMemory (row); });
// i18n: This kind of "watch" is used for watching emulated memory.
// It's not related to timekeeping devices.
menu->addAction (tr (" &Delete Watch" ), this , [this , row] { DeleteWatchAndUpdate (row); });
menu->addAction (tr (" &Add Memory Breakpoint" ), this ,
[this , row] { AddWatchBreakpoint (row); });
}
}
}
}
Expand Down
Expand Up
@@ -349,45 +364,104 @@ void WatchWidget::OnItemChanged(QTableWidgetItem* item)
{
switch (column)
{
// Label
case 0 :
case COLUMN_INDEX_LABEL:
if (item->text ().isEmpty ())
DeleteWatch (row);
DeleteWatchAndUpdate (row);
else
PowerPC::debug_interface.UpdateWatchName (row, item->text ().toStdString ());
break ;
// Address
// Hexadecimal
// Decimal
case 1 :
case 2 :
case 3 :
case COLUMN_INDEX_ADDRESS:
case COLUMN_INDEX_HEX:
case COLUMN_INDEX_DECIMAL:
{
bool good;
quint32 value = item->text ().toUInt (&good, column < 3 ? 16 : 10 );
const bool column_uses_hex_formatting =
column == COLUMN_INDEX_ADDRESS || column == COLUMN_INDEX_HEX;
quint32 value = item->text ().toUInt (&good, column_uses_hex_formatting ? 16 : 10 );
if (good)
{
if (column == 1 )
if (column == COLUMN_INDEX_ADDRESS)
{
const auto & watch = PowerPC::debug_interface.GetWatch (row);
PowerPC::debug_interface.UnsetPatch (watch.address );
PowerPC::debug_interface.UpdateWatchAddress (row, value);
if (watch.locked )
LockWatchAddress (value);
}
else
{
PowerPC::HostWrite_U32 (value, PowerPC::debug_interface.GetWatch (row).address );
}
}
else
{
ModalMessageBox::critical (this , tr (" Error" ), tr (" Invalid input provided" ));
}
break ;
}
case COLUMN_INDEX_LOCK:
{
PowerPC::debug_interface.UpdateWatchLockedState (row, item->checkState () == Qt::Checked);
const auto & watch = PowerPC::debug_interface.GetWatch (row);
if (watch.locked )
LockWatchAddress (watch.address );
else
PowerPC::debug_interface.UnsetPatch (watch.address );
break ;
}
}
Update ();
}
}
void WatchWidget::LockWatchAddress (u32 address)
{
const std::string memory_data_as_string = PowerPC::HostGetString (address, 4 );
std::vector<u8> bytes;
for (const char c : memory_data_as_string)
{
bytes.push_back (static_cast <u8>(c));
}
PowerPC::debug_interface.SetFramePatch (address, bytes);
}
void WatchWidget::DeleteSelectedWatches ()
{
std::vector<int > row_indices;
for (const auto & index : m_table->selectionModel ()->selectedRows ())
{
const auto * item = m_table->item (index .row (), index .column ());
const auto row_variant = item->data (Qt::UserRole);
if (row_variant.isNull ())
continue ;
row_indices.push_back (row_variant.toInt ());
}
// Sort greatest to smallest, so we
// don't stomp on existing indices
std::sort (row_indices.begin (), row_indices.end (), std::greater{});
for (const int row : row_indices)
{
DeleteWatch (row);
}
Update ();
}
void WatchWidget::DeleteWatch (int row)
{
PowerPC::debug_interface.UnsetPatch (PowerPC::debug_interface.GetWatch (row).address );
PowerPC::debug_interface.RemoveWatch (row);
}
void WatchWidget::DeleteWatchAndUpdate (int row)
{
DeleteWatch (row);
Update ();
}
Expand All
@@ -406,3 +480,41 @@ void WatchWidget::AddWatch(QString name, u32 addr)
PowerPC::debug_interface.SetWatch (addr, name.toStdString ());
Update ();
}
void WatchWidget::LockSelectedWatches ()
{
for (const auto & index : m_table->selectionModel ()->selectedRows ())
{
const auto * item = m_table->item (index .row (), index .column ());
const auto row_variant = item->data (Qt::UserRole);
if (row_variant.isNull ())
continue ;
const int row = row_variant.toInt ();
const auto & watch = PowerPC::debug_interface.GetWatch (row);
if (watch.locked )
continue ;
PowerPC::debug_interface.UpdateWatchLockedState (row, true );
LockWatchAddress (watch.address );
}
Update ();
}
void WatchWidget::UnlockSelectedWatches ()
{
for (const auto & index : m_table->selectionModel ()->selectedRows ())
{
const auto * item = m_table->item (index .row (), index .column ());
const auto row_variant = item->data (Qt::UserRole);
if (row_variant.isNull ())
continue ;
const int row = row_variant.toInt ();
const auto & watch = PowerPC::debug_interface.GetWatch (row);
if (!watch.locked )
continue ;
PowerPC::debug_interface.UpdateWatchLockedState (row, false );
PowerPC::debug_interface.UnsetPatch (watch.address );
}
Update ();
}