From a7cb0856b05bf8977f0733406311cca2ece0acc6 Mon Sep 17 00:00:00 2001 From: jahorta Date: Wed, 17 Jul 2024 15:23:45 -0500 Subject: [PATCH] Add options to copy pointer addresses (or base address) of a watch entry (#167) * feat: Added right click menu to copy pointer address in Edit Address widget Added a right click menu to m_pointerWidget which activates only if an address label is right clicked. It provides a single option (Copy address) which copies the address from that pointer level to clipboard. Added an option (copy address) to the right click menu of a MemWatchWidget. Selecting Copy address on a pointer watch can copy the address at any pointer level to the clipboard. Selecting Copy address on a non-pointer watch will copy the base address of the watch to the clipboard. Instead of finding the QLabel for the address when the pointer widget is clicked, the context menu is now registered to each pointer address label in the pointer widget. A property is also set (addr) which contains the numeric address at that pointer level. If addr is zero, the Copy Address is not enabled. Otherwise it is converted to a hex string which is copied to the clipboard if Copy Address is selected. --- .../MemWatcher/Dialogs/DlgAddWatchEntry.cpp | 36 ++++++++++++++++++- .../GUI/MemWatcher/Dialogs/DlgAddWatchEntry.h | 1 + Source/GUI/MemWatcher/MemWatchWidget.cpp | 34 ++++++++++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/Source/GUI/MemWatcher/Dialogs/DlgAddWatchEntry.cpp b/Source/GUI/MemWatcher/Dialogs/DlgAddWatchEntry.cpp index e017e57..337509d 100644 --- a/Source/GUI/MemWatcher/Dialogs/DlgAddWatchEntry.cpp +++ b/Source/GUI/MemWatcher/Dialogs/DlgAddWatchEntry.cpp @@ -1,9 +1,12 @@ #include "DlgAddWatchEntry.h" +#include +#include #include #include #include #include +#include #include #include #include @@ -167,6 +170,10 @@ void DlgAddWatchEntry::fillFields(MemWatchEntry* entry) QLabel* lblAddressOfPath = new QLabel(); lblAddressOfPath->setText( QString::fromStdString(" -> " + m_entry->getAddressStringForPointerLevel(i + 1))); + lblAddressOfPath->setProperty("addr", m_entry->getAddressForPointerLevel(i + 1)); + lblAddressOfPath->setContextMenuPolicy(Qt::CustomContextMenu); + connect(lblAddressOfPath, &QWidget::customContextMenuRequested, this, + &DlgAddWatchEntry::onPointerOffsetContextMenuRequested); m_addressPath.append(lblAddressOfPath); m_offsetsLayout->addWidget(lblLevel, i, 0); m_offsetsLayout->addWidget(txbOffset, i, 1); @@ -190,6 +197,12 @@ void DlgAddWatchEntry::addPointerOffset() QLineEdit* txbOffset = new QLineEdit(); m_offsets.append(txbOffset); QLabel* lblAddressOfPath = new QLabel(" -> "); + lblAddressOfPath->setText( + QString::fromStdString(" -> " + m_entry->getAddressStringForPointerLevel(level + 1))); + lblAddressOfPath->setProperty("addr", m_entry->getAddressForPointerLevel(level + 1)); + lblAddressOfPath->setContextMenuPolicy(Qt::CustomContextMenu); + connect(lblAddressOfPath, &QWidget::customContextMenuRequested, this, + &DlgAddWatchEntry::onPointerOffsetContextMenuRequested); m_addressPath.append(lblAddressOfPath); m_offsetsLayout->addWidget(lblLevel, level, 0); m_offsetsLayout->addWidget(txbOffset, level, 1); @@ -385,9 +398,10 @@ void DlgAddWatchEntry::updatePreview() for (int i = 0; i < level; ++i) { QLabel* lblAddressOfPath = - static_cast(m_offsetsLayout->itemAtPosition(i, 2)->widget()); + qobject_cast(m_offsetsLayout->itemAtPosition(i, 2)->widget()); lblAddressOfPath->setText( QString::fromStdString(" -> " + m_entry->getAddressStringForPointerLevel(i + 1))); + lblAddressOfPath->setProperty("addr", m_entry->getAddressForPointerLevel(i + 1)); } } } @@ -424,3 +438,23 @@ MemWatchEntry* DlgAddWatchEntry::stealEntry() m_entry = nullptr; return entry; } + +void DlgAddWatchEntry::onPointerOffsetContextMenuRequested(const QPoint& pos) +{ + QLabel* const lbl = qobject_cast(sender()); + + QMenu* const contextMenu = new QMenu(this); + QAction* const copyAddr = new QAction(tr("&Copy Address"), this); + + const QString text{QString::number(lbl->property("addr").toUInt(), 16).toUpper()}; + connect(copyAddr, &QAction::triggered, this, + [text] { QApplication::clipboard()->setText(text); }); + contextMenu->addAction(copyAddr); + + if (!lbl->property("addr").toUInt()) + { + copyAddr->setEnabled(false); + } + + contextMenu->popup(lbl->mapToGlobal(pos)); +} diff --git a/Source/GUI/MemWatcher/Dialogs/DlgAddWatchEntry.h b/Source/GUI/MemWatcher/Dialogs/DlgAddWatchEntry.h index b271ace..d5d940c 100644 --- a/Source/GUI/MemWatcher/Dialogs/DlgAddWatchEntry.h +++ b/Source/GUI/MemWatcher/Dialogs/DlgAddWatchEntry.h @@ -44,6 +44,7 @@ class DlgAddWatchEntry : public QDialog void addPointerOffset(); void removePointerOffset(); void removeAllPointerOffset(); + void onPointerOffsetContextMenuRequested(const QPoint& pos); MemWatchEntry* m_entry{}; AddressInputWidget* m_txbAddress{}; diff --git a/Source/GUI/MemWatcher/MemWatchWidget.cpp b/Source/GUI/MemWatcher/MemWatchWidget.cpp index 47397fb..0d9b08f 100644 --- a/Source/GUI/MemWatcher/MemWatchWidget.cpp +++ b/Source/GUI/MemWatcher/MemWatchWidget.cpp @@ -273,6 +273,40 @@ void MemWatchWidget::onMemWatchContextMenuRequested(const QPoint& pos) connect(copy, &QAction::triggered, this, [this] { copySelectedWatchesToClipBoard(); }); contextMenu->addAction(copy); + if (index.isValid()) + { + MemWatchEntry* const entry = m_watchModel->getEntryFromIndex(index); + if (entry->isBoundToPointer()) + { + QMenu* const copyAddrSubmenu = contextMenu->addMenu(tr("Copy add&ress...")); + QAction* const copyPointer = new QAction(tr("Copy &base address..."), this); + const QString addrString{QString::number(entry->getConsoleAddress(), 16).toUpper()}; + connect(copyPointer, &QAction::triggered, this, + [addrString] { QApplication::clipboard()->setText(addrString); }); + copyAddrSubmenu->addAction(copyPointer); + for (int i = 0; i < static_cast(entry->getPointerLevel()); ++i) + { + if (!entry->getAddressForPointerLevel(i + 1)) + break; + QAction* const copyAddrOfPointer = + new QAction(tr("Copy pointed address at &level %1...").arg(i + 1), this); + const QString addrString{ + QString::number(entry->getAddressForPointerLevel(i + 1), 16).toUpper()}; + connect(copyAddrOfPointer, &QAction::triggered, this, + [addrString] { QApplication::clipboard()->setText(addrString); }); + copyAddrSubmenu->addAction(copyAddrOfPointer); + } + } + else + { + QAction* const copyPointer = new QAction(tr("Copy add&ress"), this); + const QString addrString{QString::number(entry->getConsoleAddress(), 16).toUpper()}; + connect(copyPointer, &QAction::triggered, this, + [addrString] { QApplication::clipboard()->setText(addrString); }); + contextMenu->addAction(copyPointer); + } + } + QAction* paste = new QAction(tr("&Paste"), this); connect(paste, &QAction::triggered, this, [this, index] { pasteWatchFromClipBoard(index); }); contextMenu->addAction(paste);