Refactored byte item selection and highlighting in hex viewer
This commit is contained in:
@@ -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);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ public:
|
||||
QJsonObject toJson() const;
|
||||
|
||||
bool isCompatible(const Targets::TargetMemoryDescriptor& memoryDescriptor) const;
|
||||
std::set<Targets::TargetMemoryAddress> excludedAddresses() const;
|
||||
|
||||
virtual ~MemorySnapshot() = default;
|
||||
|
||||
|
||||
@@ -12,5 +12,6 @@ namespace Widgets
|
||||
bool syncingScroll = false;
|
||||
bool syncingHover = false;
|
||||
bool syncingSelection = false;
|
||||
bool syncingHighlightedRanges = false;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user