Value annotations in hex viewer widget
Some refactoring of byte item positioning, to better accommodate value annotations
This commit is contained in:
@@ -199,6 +199,7 @@ add_executable(Bloom
|
||||
src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteAddressContainer.cpp
|
||||
src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteAddressItem.cpp
|
||||
src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/AnnotationItem.cpp
|
||||
src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ValueAnnotationItem.cpp
|
||||
|
||||
# Memory region manager window
|
||||
src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/MemoryRegionManagerWindow.cpp
|
||||
|
||||
@@ -19,6 +19,24 @@ height(position == AnnotationItemPosition::TOP ? AnnotationItem::TOP_HEIGHT : An
|
||||
this->setToolTip(this->labelText);
|
||||
}
|
||||
|
||||
AnnotationItem::AnnotationItem(
|
||||
const Targets::TargetMemoryAddressRange& addressRange,
|
||||
const QString& labelText,
|
||||
AnnotationItemPosition position
|
||||
): AnnotationItem(
|
||||
addressRange.startAddress,
|
||||
addressRange.endAddress - addressRange.startAddress + 1,
|
||||
labelText,
|
||||
position
|
||||
) {}
|
||||
|
||||
AnnotationItem::AnnotationItem(const FocusedMemoryRegion& focusedMemoryRegion, AnnotationItemPosition position)
|
||||
: AnnotationItem(
|
||||
focusedMemoryRegion.getAbsoluteAddressRange(),
|
||||
focusedMemoryRegion.name,
|
||||
position
|
||||
) {}
|
||||
|
||||
void AnnotationItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
|
||||
auto lineColor = this->getLineColor();
|
||||
auto labelFontColor = this->getLabelFontColor();
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <QGraphicsItem>
|
||||
#include <cstdint>
|
||||
|
||||
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/FocusedMemoryRegion.hpp"
|
||||
|
||||
namespace Bloom::Widgets
|
||||
{
|
||||
enum class AnnotationItemPosition: std::uint8_t
|
||||
@@ -31,6 +33,15 @@ namespace Bloom::Widgets
|
||||
AnnotationItemPosition position
|
||||
);
|
||||
|
||||
AnnotationItem(
|
||||
const Targets::TargetMemoryAddressRange&
|
||||
addressRange,
|
||||
const QString& labelText,
|
||||
AnnotationItemPosition position
|
||||
);
|
||||
|
||||
AnnotationItem(const FocusedMemoryRegion& focusedMemoryRegion, AnnotationItemPosition position);
|
||||
|
||||
[[nodiscard]] QRectF boundingRect() const override {
|
||||
return QRectF(0, 0, this->width, this->height);
|
||||
}
|
||||
|
||||
@@ -61,6 +61,9 @@ void ByteItemGraphicsScene::updateValues(const Targets::TargetMemoryBuffer& buff
|
||||
byteWidget->setValue(buffer.at(byteWidget->byteIndex));
|
||||
byteWidget->update();
|
||||
}
|
||||
|
||||
this->updateAnnotationValues(buffer);
|
||||
this->lastValueBuffer = buffer;
|
||||
}
|
||||
|
||||
void ByteItemGraphicsScene::refreshRegions() {
|
||||
@@ -88,23 +91,29 @@ void ByteItemGraphicsScene::refreshRegions() {
|
||||
}
|
||||
|
||||
// Refresh annotation items
|
||||
for (auto [startAddress, annotationItem] : this->annotationItemsByStartAddress) {
|
||||
for (auto* annotationItem : this->annotationItems) {
|
||||
this->removeItem(annotationItem);
|
||||
delete annotationItem;
|
||||
}
|
||||
|
||||
this->annotationItemsByStartAddress.clear();
|
||||
this->annotationItems.clear();
|
||||
this->valueAnnotationItems.clear();
|
||||
|
||||
for (const auto& focusedRegion : this->focusedMemoryRegions) {
|
||||
const auto addressRange = focusedRegion.getAbsoluteAddressRange();
|
||||
auto* annotationItem = new AnnotationItem(
|
||||
addressRange.startAddress,
|
||||
addressRange.endAddress - addressRange.startAddress + 1,
|
||||
focusedRegion.name,
|
||||
AnnotationItemPosition::BOTTOM
|
||||
);
|
||||
auto* annotationItem = new AnnotationItem(focusedRegion, AnnotationItemPosition::BOTTOM);
|
||||
this->addItem(annotationItem);
|
||||
this->annotationItemsByStartAddress.insert(std::pair(addressRange.startAddress, annotationItem));
|
||||
this->annotationItems.emplace_back(annotationItem);
|
||||
|
||||
if (focusedRegion.dataType != MemoryRegionDataType::UNKNOWN) {
|
||||
auto* valueAnnotationItem = new ValueAnnotationItem(focusedRegion);
|
||||
this->addItem(valueAnnotationItem);
|
||||
this->annotationItems.emplace_back(valueAnnotationItem);
|
||||
this->valueAnnotationItems.emplace_back(valueAnnotationItem);
|
||||
}
|
||||
}
|
||||
|
||||
if (this->targetState == Targets::TargetState::STOPPED && this->enabled && !this->lastValueBuffer.empty()) {
|
||||
this->updateAnnotationValues(this->lastValueBuffer);
|
||||
}
|
||||
|
||||
this->adjustSize(true);
|
||||
@@ -168,7 +177,7 @@ void ByteItemGraphicsScene::setEnabled(bool enabled) {
|
||||
byteItem->setEnabled(this->enabled);
|
||||
}
|
||||
|
||||
for (auto& [startAddress, annotationItem] : this->annotationItemsByStartAddress) {
|
||||
for (auto* annotationItem : this->annotationItems) {
|
||||
annotationItem->setEnabled(this->enabled);
|
||||
}
|
||||
|
||||
@@ -183,7 +192,7 @@ void ByteItemGraphicsScene::invalidateChildItemCaches() {
|
||||
byteWidget->update();
|
||||
}
|
||||
|
||||
for (auto& [startAddress, annotationItem] : this->annotationItemsByStartAddress) {
|
||||
for (auto* annotationItem : this->annotationItems) {
|
||||
annotationItem->update();
|
||||
}
|
||||
}
|
||||
@@ -225,6 +234,23 @@ void ByteItemGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent* mouseEvent)
|
||||
}
|
||||
}
|
||||
|
||||
void ByteItemGraphicsScene::updateAnnotationValues(const Targets::TargetMemoryBuffer& buffer) {
|
||||
const auto memoryStartAddress = this->targetMemoryDescriptor.addressRange.startAddress;
|
||||
for (auto* valueAnnotationItem : this->valueAnnotationItems) {
|
||||
if (valueAnnotationItem->size > buffer.size()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto relativeStartAddress = valueAnnotationItem->startAddress - memoryStartAddress;
|
||||
const auto relativeEndAddress = valueAnnotationItem->endAddress - memoryStartAddress;
|
||||
|
||||
valueAnnotationItem->setValue(Targets::TargetMemoryBuffer(
|
||||
buffer.begin() + relativeStartAddress,
|
||||
buffer.begin() + relativeEndAddress + 1
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
void ByteItemGraphicsScene::adjustByteItemPositions() {
|
||||
const auto columnCount = static_cast<std::size_t>(
|
||||
std::floor(
|
||||
@@ -240,9 +266,9 @@ void ByteItemGraphicsScene::adjustByteItemPositions() {
|
||||
auto rowIndicesWithBottomAnnotations = std::set<std::size_t>();
|
||||
const auto& memoryAddressRange = this->targetMemoryDescriptor.addressRange;
|
||||
|
||||
for (auto [startAddress, annotationItem] : this->annotationItemsByStartAddress) {
|
||||
for (auto* annotationItem : this->annotationItems) {
|
||||
const auto firstByteRowIndex = static_cast<std::size_t>(
|
||||
std::ceil(static_cast<double>((startAddress - memoryAddressRange.startAddress) + 1)
|
||||
std::ceil(static_cast<double>((annotationItem->startAddress - memoryAddressRange.startAddress) + 1)
|
||||
/ static_cast<double>(columnCount)) - 1
|
||||
);
|
||||
|
||||
@@ -254,7 +280,13 @@ void ByteItemGraphicsScene::adjustByteItemPositions() {
|
||||
// We only display annotations that span a single row.
|
||||
if (firstByteRowIndex == lastByteRowIndex) {
|
||||
annotationItem->show();
|
||||
rowIndicesWithBottomAnnotations.insert(firstByteRowIndex);
|
||||
|
||||
if (annotationItem->position == AnnotationItemPosition::TOP) {
|
||||
rowIndicesWithTopAnnotations.insert(firstByteRowIndex);
|
||||
|
||||
} else if (annotationItem->position == AnnotationItemPosition::BOTTOM) {
|
||||
rowIndicesWithBottomAnnotations.insert(firstByteRowIndex);
|
||||
}
|
||||
|
||||
} else {
|
||||
annotationItem->hide();
|
||||
@@ -317,18 +349,18 @@ void ByteItemGraphicsScene::adjustAnnotationItemPositions() {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& [startAddress, annotationItem] : this->annotationItemsByStartAddress) {
|
||||
if (!this->byteItemsByAddress.contains(startAddress)) {
|
||||
for (auto* annotationItem : this->annotationItems) {
|
||||
if (!this->byteItemsByAddress.contains(annotationItem->startAddress)) {
|
||||
annotationItem->hide();
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto firstByteItemPosition = this->byteItemsByAddress.at(startAddress)->pos();
|
||||
const auto firstByteItemPosition = this->byteItemsByAddress.at(annotationItem->startAddress)->pos();
|
||||
|
||||
if (annotationItem->position == AnnotationItemPosition::TOP) {
|
||||
annotationItem->setPos(
|
||||
firstByteItemPosition.x(),
|
||||
firstByteItemPosition.y() - AnnotationItem::TOP_HEIGHT
|
||||
firstByteItemPosition.y() - AnnotationItem::TOP_HEIGHT - 1
|
||||
);
|
||||
|
||||
} else if (annotationItem->position == AnnotationItemPosition::BOTTOM) {
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "ByteItem.hpp"
|
||||
#include "ByteAddressContainer.hpp"
|
||||
#include "AnnotationItem.hpp"
|
||||
#include "ValueAnnotationItem.hpp"
|
||||
#include "HexViewerWidgetSettings.hpp"
|
||||
|
||||
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegion.hpp"
|
||||
@@ -68,8 +69,11 @@ namespace Bloom::Widgets
|
||||
std::vector<FocusedMemoryRegion>& focusedMemoryRegions;
|
||||
std::vector<ExcludedMemoryRegion>& excludedMemoryRegions;
|
||||
|
||||
Targets::TargetMemoryBuffer lastValueBuffer;
|
||||
|
||||
std::map<std::uint32_t, ByteItem*> byteItemsByAddress;
|
||||
std::map<std::uint32_t, AnnotationItem*> annotationItemsByStartAddress;
|
||||
std::vector<AnnotationItem*> annotationItems;
|
||||
std::vector<ValueAnnotationItem*> valueAnnotationItems;
|
||||
std::map<std::size_t, std::vector<ByteItem*>> byteItemsByRowIndex;
|
||||
std::map<std::size_t, std::vector<ByteItem*>> byteItemsByColumnIndex;
|
||||
|
||||
@@ -96,6 +100,7 @@ namespace Bloom::Widgets
|
||||
return std::max(this->parent->viewport()->width(), 400) - 2;
|
||||
}
|
||||
|
||||
void updateAnnotationValues(const Targets::TargetMemoryBuffer& buffer);
|
||||
void adjustByteItemPositions();
|
||||
void adjustAnnotationItemPositions();
|
||||
void onTargetStateChanged(Targets::TargetState newState);
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
#include "ValueAnnotationItem.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using namespace Bloom::Widgets;
|
||||
|
||||
ValueAnnotationItem::ValueAnnotationItem(const FocusedMemoryRegion& focusedMemoryRegion)
|
||||
: AnnotationItem(focusedMemoryRegion, AnnotationItemPosition::TOP), focusedMemoryRegion(focusedMemoryRegion) {
|
||||
this->labelText = QString(ValueAnnotationItem::DEFAULT_LABEL_TEXT);
|
||||
}
|
||||
|
||||
void ValueAnnotationItem::setValue(const Targets::TargetMemoryBuffer& value) {
|
||||
this->value = value;
|
||||
this->refreshLabelText();
|
||||
this->setToolTip(this->labelText);
|
||||
}
|
||||
|
||||
void ValueAnnotationItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
|
||||
auto font = painter->font();
|
||||
font.setItalic(true);
|
||||
painter->setFont(font);
|
||||
|
||||
AnnotationItem::paint(painter, option, widget);
|
||||
}
|
||||
|
||||
void ValueAnnotationItem::refreshLabelText() {
|
||||
this->update();
|
||||
|
||||
const auto& data = this->value;
|
||||
|
||||
if (data.empty()) {
|
||||
this->labelText = QString(ValueAnnotationItem::DEFAULT_LABEL_TEXT);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (this->focusedMemoryRegion.dataType) {
|
||||
case MemoryRegionDataType::INTEGER: {
|
||||
std::uint64_t integerValue = 0;
|
||||
for (const auto& byte : data) {
|
||||
integerValue = (integerValue << 8) | byte;
|
||||
}
|
||||
|
||||
this->labelText = QString::number(integerValue);
|
||||
break;
|
||||
}
|
||||
case MemoryRegionDataType::ASCII_STRING: {
|
||||
// Replace non-ASCII chars with '?'
|
||||
auto asciiData = data;
|
||||
|
||||
std::replace_if(
|
||||
asciiData.begin(),
|
||||
asciiData.end(),
|
||||
[] (unsigned char value) {
|
||||
/*
|
||||
* We only care about non-control characters (with the exception of the white space character) in
|
||||
* the standard ASCII range.
|
||||
*/
|
||||
constexpr auto asciiRangeStart = 32;
|
||||
constexpr auto asciiRangeEnd = 126;
|
||||
return value < asciiRangeStart || value > asciiRangeEnd;
|
||||
},
|
||||
'?'
|
||||
);
|
||||
|
||||
this->labelText = "'" + QString::fromLatin1(
|
||||
reinterpret_cast<const char*>(asciiData.data()),
|
||||
static_cast<qsizetype>(asciiData.size())
|
||||
) + "'";
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
this->labelText = QString(ValueAnnotationItem::DEFAULT_LABEL_TEXT);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <QPainter>
|
||||
#include <QWidget>
|
||||
#include <QStyleOptionGraphicsItem>
|
||||
|
||||
#include "AnnotationItem.hpp"
|
||||
|
||||
#include "src/Targets/TargetMemory.hpp"
|
||||
|
||||
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/FocusedMemoryRegion.hpp"
|
||||
|
||||
namespace Bloom::Widgets
|
||||
{
|
||||
class ValueAnnotationItem: public AnnotationItem
|
||||
{
|
||||
public:
|
||||
explicit ValueAnnotationItem(const FocusedMemoryRegion& focusedMemoryRegion);
|
||||
void setValue(const Targets::TargetMemoryBuffer& value);
|
||||
void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override;
|
||||
|
||||
protected:
|
||||
[[nodiscard]] QColor getLabelFontColor() const override {
|
||||
return QColor(0x94, 0x6F, 0x30);
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr auto DEFAULT_LABEL_TEXT = "??";
|
||||
|
||||
FocusedMemoryRegion focusedMemoryRegion;
|
||||
Targets::TargetMemoryBuffer value;
|
||||
|
||||
void refreshLabelText();
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user