Refactored byte item selection and highlighting in hex viewer

This commit is contained in:
Nav
2023-08-24 17:25:28 +01:00
parent b07be04a3c
commit 586c11157c
16 changed files with 200 additions and 138 deletions

View File

@@ -4,6 +4,7 @@
#include <QWidget>
#include <functional>
#include <QString>
#include <set>
#include <optional>
#include "src/Targets/TargetMemory.hpp"
@@ -28,7 +29,7 @@ namespace Widgets
* always be enabled.
*/
using IsEnabledCallbackType = std::function<
bool(const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>&)
bool(const std::set<Targets::TargetMemoryAddress>&)
>;
public:
@@ -41,6 +42,6 @@ namespace Widgets
);
signals:
void invoked(const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>& selectedByteItemsByAddress);
void invoked(const std::set<Targets::TargetMemoryAddress>& selectedByteItemAddresses);
};
}

View File

@@ -20,6 +20,7 @@ namespace Widgets
ByteItem* hoveredByteItem = nullptr;
std::optional<Targets::TargetStackPointer> currentStackPointer;
bool highlightingEnabled = false;
std::set<Targets::TargetMemoryAddressRange> highlightedAddressRanges;
HexViewerSharedState(
const Targets::TargetMemoryDescriptor& memoryDescriptor,

View File

@@ -242,22 +242,17 @@ namespace Widgets
this->byteItemGraphicsScene->addExternalContextMenuAction(action);
}
void HexViewerWidget::highlightBytes(const std::set<Targets::TargetMemoryAddress>& addresses) {
this->byteItemGraphicsScene->highlightByteItems(addresses);
}
void HexViewerWidget::highlightBytes(const std::set<Targets::TargetMemoryAddressRange>& addressRanges) {
this->byteItemGraphicsScene->highlightByteItems(this->addressRangesToAddresses(addressRanges));
void HexViewerWidget::highlightByteItemRanges(const std::set<Targets::TargetMemoryAddressRange>& addressRanges) {
this->byteItemGraphicsScene->highlightByteItemRanges(addressRanges);
}
void HexViewerWidget::clearHighlighting() {
this->highlightBytes(std::set<Targets::TargetMemoryAddress>());
this->byteItemGraphicsScene->clearByteItemHighlighting();
}
void HexViewerWidget::selectAndHighlightBytes(const std::set<Targets::TargetMemoryAddressRange>& addressRanges) {
const auto addresses = this->addressRangesToAddresses(addressRanges);
this->byteItemGraphicsScene->highlightByteItems(addresses);
this->byteItemGraphicsScene->selectByteItems(addresses);
this->byteItemGraphicsScene->highlightByteItemRanges(addressRanges);
this->byteItemGraphicsScene->selectByteItemRanges(addressRanges);
}
void HexViewerWidget::centerOnByte(Targets::TargetMemoryAddress address) {
@@ -377,9 +372,9 @@ namespace Widgets
}
void HexViewerWidget::onByteSelectionChanged(
const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>& selectedByteItemsByAddress
const std::set<Targets::TargetMemoryAddress>& selectedByteItemAddresses
) {
const auto selectionCount = selectedByteItemsByAddress.size();
const auto selectionCount = selectedByteItemAddresses.size();
if (selectionCount == 0) {
this->selectionCountLabel->hide();

View File

@@ -42,8 +42,7 @@ namespace Widgets
void refreshRegions();
void setStackPointer(Targets::TargetStackPointer stackPointer);
void addExternalContextMenuAction(ContextMenuAction* action);
void highlightBytes(const std::set<Targets::TargetMemoryAddress>& addresses);
void highlightBytes(const std::set<Targets::TargetMemoryAddressRange>& addressRanges);
void highlightByteItemRanges(const std::set<Targets::TargetMemoryAddressRange>& addressRanges);
void clearHighlighting();
void selectAndHighlightBytes(const std::set<Targets::TargetMemoryAddressRange>& addressRanges);
void centerOnByte(Targets::TargetMemoryAddress address);
@@ -91,9 +90,7 @@ namespace Widgets
void setDisplayAsciiEnabled(bool enabled);
void onGoToAddressInputChanged();
void onHoveredAddress(const std::optional<Targets::TargetMemoryAddress>& address);
void onByteSelectionChanged(
const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>& selectedByteItemsByAddress
);
void onByteSelectionChanged(const std::set<Targets::TargetMemoryAddress>& selectedByteItemAddresses);
std::set<Targets::TargetMemoryAddress> addressRangesToAddresses(
const std::set<Targets::TargetMemoryAddressRange>& addressRanges
);

View File

@@ -215,18 +215,18 @@ namespace Widgets
InsightWorker::queueTask(constructHexViewerTopLevelGroupItem);
}
void ItemGraphicsScene::updateStackPointer(std::uint32_t stackPointer) {
void ItemGraphicsScene::updateStackPointer(Targets::TargetStackPointer stackPointer) {
this->state.currentStackPointer = stackPointer;
this->rebuildItemHierarchy();
}
void ItemGraphicsScene::selectByteItems(const std::set<std::uint32_t>& addresses) {
this->selectedByteItemsByAddress.clear();
void ItemGraphicsScene::selectByteItems(const std::set<Targets::TargetMemoryAddress>& addresses) {
this->selectedByteItemAddresses.clear();
for (auto& [address, byteItem] : this->topLevelGroup->byteItemsByAddress) {
if (addresses.contains(address)) {
byteItem.selected = true;
this->selectedByteItemsByAddress.insert(std::pair(byteItem.startAddress, &byteItem));
this->selectedByteItemAddresses.insert(byteItem.startAddress);
} else if (byteItem.selected) {
byteItem.selected = false;
@@ -234,16 +234,37 @@ namespace Widgets
}
this->update();
emit this->selectionChanged(this->selectedByteItemsByAddress);
emit this->selectionChanged(addresses);
}
void ItemGraphicsScene::highlightByteItems(const std::set<std::uint32_t>& addresses) {
for (auto& [address, byteItem] : this->topLevelGroup->byteItemsByAddress) {
byteItem.highlighted = addresses.contains(address);
void ItemGraphicsScene::selectByteItemRanges(const std::set<Targets::TargetMemoryAddressRange>& addressRanges) {
return this->selectByteItems(this->addressRangesToAddresses(addressRanges));
}
void ItemGraphicsScene::highlightByteItemRanges(const std::set<Targets::TargetMemoryAddressRange>& addressRanges) {
/*
* Don't bother updating the byte items if addressRanges is empty - updating the this->state.highlightingEnabled
* flag will prevent the highlighting, and the byte items will be updated the next time we actually want to
* highlight something.
*
* Not pretty but it saves a lot of cycles.
*/
if (!addressRanges.empty()) {
const auto addresses = this->addressRangesToAddresses(addressRanges);
for (auto& [address, byteItem] : this->topLevelGroup->byteItemsByAddress) {
byteItem.highlighted = addresses.contains(address);
}
}
this->state.highlightingEnabled = !addresses.empty();
this->state.highlightingEnabled = !addressRanges.empty();
this->state.highlightedAddressRanges = std::move(addressRanges);
this->update();
emit this->highlightingChanged(addressRanges);
}
void ItemGraphicsScene::clearByteItemHighlighting() {
this->highlightByteItemRanges({});
}
void ItemGraphicsScene::rebuildItemHierarchy() {
@@ -323,7 +344,7 @@ namespace Widgets
void ItemGraphicsScene::addExternalContextMenuAction(ContextMenuAction* action) {
QObject::connect(action, &QAction::triggered, this, [this, action] () {
emit action->invoked(this->selectedByteItemsByAddress);
emit action->invoked(this->selectedByteItemAddresses);
});
this->externalContextMenuActions.push_back(action);
@@ -367,8 +388,7 @@ namespace Widgets
const auto mousePosition = mouseEvent->buttonDownScenePos(button);
if (this->state.highlightingEnabled) {
this->state.highlightingEnabled = false;
this->update();
this->clearByteItemHighlighting();
}
if (mousePosition.x() <= this->byteAddressContainer->boundingRect().width()) {
@@ -424,12 +444,12 @@ namespace Widgets
this->toggleByteItemSelection(byteItem);
}
emit this->selectionChanged(this->selectedByteItemsByAddress);
emit this->selectionChanged(this->selectedByteItemAddresses);
return;
}
this->toggleByteItemSelection(*clickedByteItem);
emit this->selectionChanged(this->selectedByteItemsByAddress);
emit this->selectionChanged(this->selectedByteItemAddresses);
}
}
@@ -463,7 +483,7 @@ namespace Widgets
for (auto& byteItem : items) {
this->selectByteItem(*byteItem);
}
emit this->selectionChanged(this->selectedByteItemsByAddress);
emit this->selectionChanged(this->selectedByteItemAddresses);
}
auto* hoveredByteItem = this->itemIndex->byteItemAt(mousePosition);
@@ -485,7 +505,7 @@ namespace Widgets
void ItemGraphicsScene::keyPressEvent(QKeyEvent* keyEvent) {
const auto key = keyEvent->key();
if (key == Qt::Key_Escape && !this->selectedByteItemsByAddress.empty()) {
if (key == Qt::Key_Escape && !this->selectedByteItemAddresses.empty()) {
this->clearByteItemSelection();
return;
}
@@ -514,7 +534,7 @@ namespace Widgets
return;
}
const auto itemsSelected = !this->selectedByteItemsByAddress.empty();
const auto itemsSelected = !this->selectedByteItemAddresses.empty();
auto* menu = new QMenu(this->parent);
menu->setLayoutDirection(Qt::LayoutDirection::LeftToRight);
@@ -548,7 +568,7 @@ namespace Widgets
itemsSelected
&& (
!externalAction->isEnabledCallback.has_value()
|| externalAction->isEnabledCallback.value()(this->selectedByteItemsByAddress)
|| externalAction->isEnabledCallback.value()(this->selectedByteItemAddresses)
)
);
@@ -620,12 +640,12 @@ namespace Widgets
void ItemGraphicsScene::selectByteItem(ByteItem& byteItem) {
byteItem.selected = true;
this->selectedByteItemsByAddress.insert(std::pair(byteItem.startAddress, &byteItem));
this->selectedByteItemAddresses.insert(byteItem.startAddress);
}
void ItemGraphicsScene::deselectByteItem(ByteItem& byteItem) {
byteItem.selected = false;
this->selectedByteItemsByAddress.erase(byteItem.startAddress);
this->selectedByteItemAddresses.erase(byteItem.startAddress);
}
void ItemGraphicsScene::toggleByteItemSelection(ByteItem& byteItem) {
@@ -638,23 +658,24 @@ namespace Widgets
}
void ItemGraphicsScene::clearByteItemSelection() {
for (auto& [address, byteItem] : this->selectedByteItemsByAddress) {
byteItem->selected = false;
for (const auto& address : this->selectedByteItemAddresses) {
auto& byteItem = this->topLevelGroup->byteItemsByAddress.at(address);
byteItem.selected = false;
}
this->selectedByteItemsByAddress.clear();
this->selectedByteItemAddresses.clear();
this->update();
emit this->selectionChanged(this->selectedByteItemsByAddress);
emit this->selectionChanged(this->selectedByteItemAddresses);
}
void ItemGraphicsScene::selectAllByteItems() {
for (auto& [address, byteItem] : this->topLevelGroup->byteItemsByAddress) {
byteItem.selected = true;
this->selectedByteItemsByAddress.insert(std::pair(byteItem.startAddress, &byteItem));
this->selectedByteItemAddresses.insert(byteItem.startAddress);
}
this->update();
emit this->selectionChanged(this->selectedByteItemsByAddress);
emit this->selectionChanged(this->selectedByteItemAddresses);
}
void ItemGraphicsScene::setAddressType(AddressType type) {
@@ -666,34 +687,31 @@ namespace Widgets
this->byteAddressContainer->invalidateChildItemCaches();
}
std::map<Targets::TargetMemoryAddress, ByteItem*> ItemGraphicsScene::sortedByteItemsByAddress() {
auto sortedByteItemsByAddress = std::map<Targets::TargetMemoryAddress, ByteItem*>();
std::transform(
this->selectedByteItemsByAddress.begin(),
this->selectedByteItemsByAddress.end(),
std::inserter(sortedByteItemsByAddress, sortedByteItemsByAddress.end()),
[] (const decltype(this->selectedByteItemsByAddress)::value_type& pair) {
return pair;
}
);
std::set<Targets::TargetMemoryAddress> ItemGraphicsScene::excludedAddresses() {
auto output = std::set<Targets::TargetMemoryAddress>();
return sortedByteItemsByAddress;
for (const auto& excludedRegion : this->excludedMemoryRegions) {
const auto regionAddresses = excludedRegion.addressRange.addresses();
output.insert(regionAddresses.begin(), regionAddresses.end());
}
return output;
}
void ItemGraphicsScene::copyAddressesToClipboard(AddressType type) {
if (this->selectedByteItemsByAddress.empty()) {
if (this->selectedByteItemAddresses.empty()) {
return;
}
auto data = QString();
const auto memoryStartAddress = this->state.memoryDescriptor.addressRange.startAddress;
for (const auto& [address, byteItem] : this->sortedByteItemsByAddress()) {
for (const auto& address : this->selectedByteItemAddresses) {
data.append(
"0x" + QString::number(
type == AddressType::RELATIVE
? byteItem->startAddress - memoryStartAddress
: byteItem->startAddress,
? address - memoryStartAddress
: address,
16
).rightJustified(8, '0').toUpper() + "\n"
);
@@ -703,16 +721,17 @@ namespace Widgets
}
void ItemGraphicsScene::copyHexValuesToClipboard(bool withDelimiters) {
if (this->selectedByteItemsByAddress.empty()) {
if (this->selectedByteItemAddresses.empty()) {
return;
}
const auto excludedAddresses = this->excludedAddresses();
auto data = QString();
for (const auto& [address, byteItem] : this->sortedByteItemsByAddress()) {
const unsigned char byteValue = byteItem->excluded
for (const auto& address : this->selectedByteItemAddresses) {
const unsigned char byteValue = excludedAddresses.contains(address)
? 0x00
: (*this->state.data)[byteItem->startAddress - this->state.memoryDescriptor.addressRange.startAddress];
: (*this->state.data)[address - this->state.memoryDescriptor.addressRange.startAddress];
data.append(
withDelimiters
@@ -725,16 +744,17 @@ namespace Widgets
}
void ItemGraphicsScene::copyDecimalValuesToClipboard() {
if (this->selectedByteItemsByAddress.empty() || !this->state.data.has_value()) {
if (this->selectedByteItemAddresses.empty() || !this->state.data.has_value()) {
return;
}
const auto excludedAddresses = this->excludedAddresses();
auto data = QString();
for (const auto& [address, byteItem] : this->sortedByteItemsByAddress()) {
const unsigned char byteValue = byteItem->excluded
for (const auto& address : this->selectedByteItemAddresses) {
const unsigned char byteValue = excludedAddresses.contains(address)
? 0x00
: (*this->state.data)[byteItem->startAddress - this->state.memoryDescriptor.addressRange.startAddress];
: (*this->state.data)[address - this->state.memoryDescriptor.addressRange.startAddress];
data.append(QString::number(byteValue, 10) + "\n");
}
@@ -742,16 +762,17 @@ namespace Widgets
}
void ItemGraphicsScene::copyBinaryBitStringToClipboard(bool withDelimiters) {
if (this->selectedByteItemsByAddress.empty()) {
if (this->selectedByteItemAddresses.empty()) {
return;
}
const auto excludedAddresses = this->excludedAddresses();
auto data = QString();
for (const auto& [address, byteItem] : this->sortedByteItemsByAddress()) {
const unsigned char byteValue = byteItem->excluded
for (const auto& address : this->selectedByteItemAddresses) {
const unsigned char byteValue = excludedAddresses.contains(address)
? 0x00
: (*this->state.data)[byteItem->startAddress - this->state.memoryDescriptor.addressRange.startAddress];
: (*this->state.data)[address - this->state.memoryDescriptor.addressRange.startAddress];
data.append(
withDelimiters
@@ -764,16 +785,17 @@ namespace Widgets
}
void ItemGraphicsScene::copyValueMappingToClipboard() {
if (this->selectedByteItemsByAddress.empty() || !this->state.data.has_value()) {
if (this->selectedByteItemAddresses.empty() || !this->state.data.has_value()) {
return;
}
const auto excludedAddresses = this->excludedAddresses();
auto data = QJsonObject();
for (const auto& [address, byteItem] : this->sortedByteItemsByAddress()) {
const unsigned char byteValue = byteItem->excluded
for (const auto& address : this->selectedByteItemAddresses) {
const unsigned char byteValue = excludedAddresses.contains(address)
? 0x00
: (*this->state.data)[byteItem->startAddress - this->state.memoryDescriptor.addressRange.startAddress];
: (*this->state.data)[address - this->state.memoryDescriptor.addressRange.startAddress];
data.insert(
"0x" + QString::number(address, 16).rightJustified(8, '0').toUpper(),
@@ -785,17 +807,18 @@ namespace Widgets
}
void ItemGraphicsScene::copyAsciiValueToClipboard() {
if (this->selectedByteItemsByAddress.empty() || !this->state.data.has_value()) {
if (this->selectedByteItemAddresses.empty() || !this->state.data.has_value()) {
return;
}
const auto excludedAddresses = this->excludedAddresses();
auto data = QString();
for (const auto& [address, byteItem] : this->sortedByteItemsByAddress()) {
for (const auto& address : this->selectedByteItemAddresses) {
const unsigned char byteValue =
(*this->state.data)[byteItem->startAddress - this->state.memoryDescriptor.addressRange.startAddress];
(*this->state.data)[address - this->state.memoryDescriptor.addressRange.startAddress];
if (byteItem->excluded || byteValue < 32 || byteValue > 126) {
if (excludedAddresses.contains(address) || byteValue < 32 || byteValue > 126) {
continue;
}
@@ -804,4 +827,17 @@ namespace Widgets
QApplication::clipboard()->setText(std::move(data));
}
std::set<Targets::TargetMemoryAddress> ItemGraphicsScene::addressRangesToAddresses(
const std::set<Targets::TargetMemoryAddressRange>& addressRanges
) {
auto addresses = std::set<Targets::TargetMemoryAddress>();
for (const auto& range : addressRanges) {
const auto rangeAddresses = range.addresses();
addresses.insert(rangeAddresses.begin(), rangeAddresses.end());
}
return addresses;
}
}

View File

@@ -56,7 +56,9 @@ namespace Widgets
void init();
void updateStackPointer(Targets::TargetStackPointer stackPointer);
void selectByteItems(const std::set<Targets::TargetMemoryAddress>& addresses);
void highlightByteItems(const std::set<Targets::TargetMemoryAddress>& addresses);
void selectByteItemRanges(const std::set<Targets::TargetMemoryAddressRange>& addressRanges);
void highlightByteItemRanges(const std::set<Targets::TargetMemoryAddressRange>& addressRanges);
void clearByteItemHighlighting();
void rebuildItemHierarchy();
void adjustSize();
void setEnabled(bool enabled);
@@ -67,9 +69,8 @@ namespace Widgets
signals:
void ready();
void hoveredAddress(const std::optional<Targets::TargetMemoryAddress>& address);
void selectionChanged(
const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>& selectedByteItemsByAddress
);
void selectionChanged(const std::set<Targets::TargetMemoryAddress>& addresses);
void highlightingChanged(const std::set<Targets::TargetMemoryAddressRange>& addressRanges);
protected:
bool enabled = true;
@@ -91,6 +92,7 @@ namespace Widgets
ByteAddressContainer* byteAddressContainer = nullptr;
std::unordered_map<Targets::TargetMemoryAddress, ByteItem*> selectedByteItemsByAddress;
std::set<Targets::TargetMemoryAddress> selectedByteItemAddresses;
QGraphicsRectItem* rubberBandRectItem = nullptr;
std::optional<QPointF> rubberBandInitPoint = std::nullopt;
@@ -154,12 +156,15 @@ namespace Widgets
void clearByteItemSelection();
void selectAllByteItems();
void setAddressType(AddressType type);
std::map<Targets::TargetMemoryAddress, ByteItem*> sortedByteItemsByAddress();
std::set<Targets::TargetMemoryAddress> excludedAddresses();
void copyAddressesToClipboard(AddressType type);
void copyHexValuesToClipboard(bool withDelimiters);
void copyDecimalValuesToClipboard();
void copyBinaryBitStringToClipboard(bool withDelimiters);
void copyValueMappingToClipboard();
void copyAsciiValueToClipboard();
std::set<Targets::TargetMemoryAddress> addressRangesToAddresses(
const std::set<Targets::TargetMemoryAddressRange>& addressRanges
);
};
}

View File

@@ -138,3 +138,14 @@ bool MemorySnapshot::isCompatible(const Targets::TargetMemoryDescriptor& memoryD
return true;
}
std::set<Targets::TargetMemoryAddress> MemorySnapshot::excludedAddresses() const {
auto output = std::set<Targets::TargetMemoryAddress>();
for (const auto& excludedRegion : this->excludedRegions) {
const auto regionAddresses = excludedRegion.addressRange.addresses();
output.insert(regionAddresses.begin(), regionAddresses.end());
}
return output;
}

View File

@@ -43,6 +43,7 @@ public:
QJsonObject toJson() const;
bool isCompatible(const Targets::TargetMemoryDescriptor& memoryDescriptor) const;
std::set<Targets::TargetMemoryAddress> excludedAddresses() const;
virtual ~MemorySnapshot() = default;

View File

@@ -12,5 +12,6 @@ namespace Widgets
bool syncingScroll = false;
bool syncingHover = false;
bool syncingSelection = false;
bool syncingHighlightedRanges = false;
};
}

View File

@@ -44,6 +44,13 @@ namespace Widgets
this,
&DifferentialItemGraphicsScene::onOtherSelectionChanged
);
QObject::connect(
this->other,
&DifferentialItemGraphicsScene::highlightingChanged,
this,
&DifferentialItemGraphicsScene::onOtherHighlightedByteRangesChanged
);
}
ByteItem* DifferentialItemGraphicsScene::byteItemAtViewportTop() {
@@ -97,23 +104,27 @@ namespace Widgets
}
void DifferentialItemGraphicsScene::onOtherSelectionChanged(
const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>& selectedByteItemsByAddress
const std::set<Targets::TargetMemoryAddress>& addresses
) {
if (!this->snapshotDiffSettings.syncHexViewerSelection || this->diffHexViewerState.syncingSelection) {
return;
}
this->diffHexViewerState.syncingSelection = true;
this->clearByteItemSelection();
this->selectByteItems(addresses);
this->diffHexViewerState.syncingSelection = false;
}
for (const auto& [address, otherByteItem] : selectedByteItemsByAddress) {
auto& byteItem = this->topLevelGroup->byteItemsByAddress.at(address);
byteItem.selected = true;
this->selectedByteItemsByAddress.insert(std::pair(byteItem.startAddress, &byteItem));
void DifferentialItemGraphicsScene::onOtherHighlightedByteRangesChanged(
const std::set<Targets::TargetMemoryAddressRange>& addressRanges
) {
if (this->diffHexViewerState.syncingHighlightedRanges) {
return;
}
emit this->selectionChanged(this->selectedByteItemsByAddress);
this->diffHexViewerState.syncingSelection = false;
this->diffHexViewerState.syncingHighlightedRanges = true;
this->highlightByteItemRanges(addressRanges);
this->diffHexViewerState.syncingHighlightedRanges = false;
this->update();
}

View File

@@ -41,8 +41,7 @@ namespace Widgets
QMargins margins() override;
void onOtherHoveredAddress(const std::optional<Targets::TargetMemoryAddress>& address);
void onOtherSelectionChanged(
const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>& selectedByteItemsByAddress
);
void onOtherSelectionChanged(const std::set<Targets::TargetMemoryAddress>& addresses);
void onOtherHighlightedByteRangesChanged(const std::set<Targets::TargetMemoryAddressRange>& addressRanges);
};
}

View File

@@ -85,7 +85,7 @@ namespace Widgets
this->restoreBytesAction = new ContextMenuAction(
"Restore Selection",
[this] (const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>&) {
[this] (const std::set<Targets::TargetMemoryAddress>&) {
return this->memoryDescriptor.access.writeableDuringDebugSession;
},
this
@@ -95,8 +95,8 @@ namespace Widgets
this->restoreBytesAction,
&ContextMenuAction::invoked,
this,
[this] (const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>& selectedByteItemsByAddress) {
this->restoreSelectedBytes(selectedByteItemsByAddress, true);
[this] (const std::set<Targets::TargetMemoryAddress>& selectedByteItemAddresses) {
this->restoreSelectedBytes(selectedByteItemAddresses, true);
}
);
}
@@ -446,20 +446,20 @@ namespace Widgets
}
void SnapshotDiff::restoreSelectedBytes(
const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>& selectedByteItemsByAddress,
std::set<Targets::TargetMemoryAddress> addresses,
bool confirmationPromptEnabled
) {
auto sortedByteItemsByAddress = std::map<Targets::TargetMemoryAddress, ByteItem*>();
for (const auto& pair : selectedByteItemsByAddress) {
if (pair.second->excluded) {
continue;
}
sortedByteItemsByAddress.insert(pair);
auto excludedAddresses = std::set<Targets::TargetMemoryAddress>();
for (const auto& excludedRegion : this->excludedRegionsA) {
const auto regionAddresses = excludedRegion.addressRange.addresses();
excludedAddresses.insert(regionAddresses.begin(), regionAddresses.end());
}
if (sortedByteItemsByAddress.empty()) {
std::erase_if(addresses, [excludedAddresses] (const auto& address) {
return excludedAddresses.contains(address);
});
if (addresses.empty()) {
// The user has only selected bytes that are within an excluded region - nothing to do here
return;
}
@@ -467,8 +467,7 @@ namespace Widgets
if (confirmationPromptEnabled) {
auto* confirmationDialog = new ConfirmationDialog(
"Restore selected bytes",
"This operation will write " + QString::number(sortedByteItemsByAddress.size())
+ " byte(s) to the target's "
"This operation will write " + QString::number(addresses.size()) + " byte(s) to the target's "
+ EnumToStringMappings::targetMemoryTypes.at(this->memoryDescriptor.type).toUpper()
+ ".<br/><br/>Are you sure you want to proceed?",
"Proceed",
@@ -480,8 +479,8 @@ namespace Widgets
confirmationDialog,
&ConfirmationDialog::confirmed,
this,
[this, selectedByteItemsByAddress] {
this->restoreSelectedBytes(selectedByteItemsByAddress, false);
[this, addresses] {
this->restoreSelectedBytes(addresses, false);
}
);
@@ -491,10 +490,10 @@ namespace Widgets
auto writeBlocks = std::vector<WriteTargetMemory::Block>();
Targets::TargetMemoryAddress blockStartAddress = sortedByteItemsByAddress.begin()->first;
Targets::TargetMemoryAddress blockStartAddress = *(addresses.begin());
Targets::TargetMemoryAddress blockEndAddress = blockStartAddress;
for (const auto& [address, byteItem] : sortedByteItemsByAddress) {
for (const auto& address : addresses) {
if (address > (blockEndAddress + 1)) {
// Commit the block
const auto dataBeginOffset = blockStartAddress - this->memoryDescriptor.addressRange.startAddress;

View File

@@ -128,7 +128,7 @@ namespace Widgets
void setSyncHexViewerSelectionEnabled(bool enabled);
void restoreSelectedBytes(
const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>& selectedByteItemsByAddress,
std::set<Targets::TargetMemoryAddress> addresses,
bool confirmationPromptEnabled
);
};

View File

@@ -126,7 +126,7 @@ namespace Widgets
this->restoreBytesAction = new ContextMenuAction(
"Restore Selection",
[this] (const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>&) {
[this] (const std::set<Targets::TargetMemoryAddress>&) {
return this->memoryDescriptor.access.writeableDuringDebugSession;
},
this
@@ -170,8 +170,8 @@ namespace Widgets
this->restoreBytesAction,
&ContextMenuAction::invoked,
this,
[this] (const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>& selectedByteItemsByAddress) {
this->restoreSelectedBytes(selectedByteItemsByAddress, true);
[this] (const std::set<Targets::TargetMemoryAddress>& selectedByteItemAddresses) {
this->restoreSelectedBytes(selectedByteItemAddresses, true);
}
);
@@ -197,21 +197,15 @@ namespace Widgets
}
void SnapshotViewer::restoreSelectedBytes(
const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>& selectedByteItemsByAddress,
std::set<Targets::TargetMemoryAddress> addresses,
bool confirmationPromptEnabled
) {
auto sortedByteItemsByAddress = std::map<Targets::TargetMemoryAddress, ByteItem*>();
const auto excludedAddresses = this->snapshot.excludedAddresses();
std::erase_if(addresses, [excludedAddresses] (const auto& address) {
return excludedAddresses.contains(address);
});
// Ideally, we'd use std::transform here, but that would require an additional pass to remove excluded bytes
for (const auto& pair : selectedByteItemsByAddress) {
if (pair.second->excluded) {
continue;
}
sortedByteItemsByAddress.insert(pair);
}
if (sortedByteItemsByAddress.empty()) {
if (addresses.empty()) {
// The user has only selected bytes that are within an excluded region - nothing to do here
return;
}
@@ -219,8 +213,7 @@ namespace Widgets
if (confirmationPromptEnabled) {
auto* confirmationDialog = new ConfirmationDialog(
"Restore selected bytes",
"This operation will write " + QString::number(sortedByteItemsByAddress.size())
+ " byte(s) to the target's "
"This operation will write " + QString::number(addresses.size()) + " byte(s) to the target's "
+ EnumToStringMappings::targetMemoryTypes.at(this->memoryDescriptor.type).toUpper()
+ ".<br/><br/>Are you sure you want to proceed?",
"Proceed",
@@ -232,8 +225,8 @@ namespace Widgets
confirmationDialog,
&ConfirmationDialog::confirmed,
this,
[this, selectedByteItemsByAddress] {
this->restoreSelectedBytes(selectedByteItemsByAddress, false);
[this, addresses] {
this->restoreSelectedBytes(addresses, false);
}
);
@@ -243,10 +236,10 @@ namespace Widgets
auto writeBlocks = std::vector<WriteTargetMemory::Block>();
Targets::TargetMemoryAddress blockStartAddress = sortedByteItemsByAddress.begin()->first;
Targets::TargetMemoryAddress blockStartAddress = *(addresses.begin());
Targets::TargetMemoryAddress blockEndAddress = blockStartAddress;
for (const auto& [address, byteItem] : sortedByteItemsByAddress) {
for (const auto& address : addresses) {
if (address > (blockEndAddress + 1)) {
// Commit the block
const auto dataBeginOffset = blockStartAddress - this->memoryDescriptor.addressRange.startAddress;

View File

@@ -63,7 +63,7 @@ namespace Widgets
void onHexViewerReady();
void restoreSelectedBytes(
const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>& selectedByteItemsByAddress,
std::set<Targets::TargetMemoryAddress> addresses,
bool confirmationPromptEnabled
);
};