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/ByteAddressContainer.cpp
|
||||||
src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteAddressItem.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/AnnotationItem.cpp
|
||||||
|
src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ValueAnnotationItem.cpp
|
||||||
|
|
||||||
# Memory region manager window
|
# Memory region manager window
|
||||||
src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/MemoryRegionManagerWindow.cpp
|
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);
|
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) {
|
void AnnotationItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
|
||||||
auto lineColor = this->getLineColor();
|
auto lineColor = this->getLineColor();
|
||||||
auto labelFontColor = this->getLabelFontColor();
|
auto labelFontColor = this->getLabelFontColor();
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
#include <QGraphicsItem>
|
#include <QGraphicsItem>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/FocusedMemoryRegion.hpp"
|
||||||
|
|
||||||
namespace Bloom::Widgets
|
namespace Bloom::Widgets
|
||||||
{
|
{
|
||||||
enum class AnnotationItemPosition: std::uint8_t
|
enum class AnnotationItemPosition: std::uint8_t
|
||||||
@@ -31,6 +33,15 @@ namespace Bloom::Widgets
|
|||||||
AnnotationItemPosition position
|
AnnotationItemPosition position
|
||||||
);
|
);
|
||||||
|
|
||||||
|
AnnotationItem(
|
||||||
|
const Targets::TargetMemoryAddressRange&
|
||||||
|
addressRange,
|
||||||
|
const QString& labelText,
|
||||||
|
AnnotationItemPosition position
|
||||||
|
);
|
||||||
|
|
||||||
|
AnnotationItem(const FocusedMemoryRegion& focusedMemoryRegion, AnnotationItemPosition position);
|
||||||
|
|
||||||
[[nodiscard]] QRectF boundingRect() const override {
|
[[nodiscard]] QRectF boundingRect() const override {
|
||||||
return QRectF(0, 0, this->width, this->height);
|
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->setValue(buffer.at(byteWidget->byteIndex));
|
||||||
byteWidget->update();
|
byteWidget->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->updateAnnotationValues(buffer);
|
||||||
|
this->lastValueBuffer = buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ByteItemGraphicsScene::refreshRegions() {
|
void ByteItemGraphicsScene::refreshRegions() {
|
||||||
@@ -88,23 +91,29 @@ void ByteItemGraphicsScene::refreshRegions() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Refresh annotation items
|
// Refresh annotation items
|
||||||
for (auto [startAddress, annotationItem] : this->annotationItemsByStartAddress) {
|
for (auto* annotationItem : this->annotationItems) {
|
||||||
this->removeItem(annotationItem);
|
this->removeItem(annotationItem);
|
||||||
delete annotationItem;
|
delete annotationItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->annotationItemsByStartAddress.clear();
|
this->annotationItems.clear();
|
||||||
|
this->valueAnnotationItems.clear();
|
||||||
|
|
||||||
for (const auto& focusedRegion : this->focusedMemoryRegions) {
|
for (const auto& focusedRegion : this->focusedMemoryRegions) {
|
||||||
const auto addressRange = focusedRegion.getAbsoluteAddressRange();
|
auto* annotationItem = new AnnotationItem(focusedRegion, AnnotationItemPosition::BOTTOM);
|
||||||
auto* annotationItem = new AnnotationItem(
|
|
||||||
addressRange.startAddress,
|
|
||||||
addressRange.endAddress - addressRange.startAddress + 1,
|
|
||||||
focusedRegion.name,
|
|
||||||
AnnotationItemPosition::BOTTOM
|
|
||||||
);
|
|
||||||
this->addItem(annotationItem);
|
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);
|
this->adjustSize(true);
|
||||||
@@ -168,7 +177,7 @@ void ByteItemGraphicsScene::setEnabled(bool enabled) {
|
|||||||
byteItem->setEnabled(this->enabled);
|
byteItem->setEnabled(this->enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& [startAddress, annotationItem] : this->annotationItemsByStartAddress) {
|
for (auto* annotationItem : this->annotationItems) {
|
||||||
annotationItem->setEnabled(this->enabled);
|
annotationItem->setEnabled(this->enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,7 +192,7 @@ void ByteItemGraphicsScene::invalidateChildItemCaches() {
|
|||||||
byteWidget->update();
|
byteWidget->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& [startAddress, annotationItem] : this->annotationItemsByStartAddress) {
|
for (auto* annotationItem : this->annotationItems) {
|
||||||
annotationItem->update();
|
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() {
|
void ByteItemGraphicsScene::adjustByteItemPositions() {
|
||||||
const auto columnCount = static_cast<std::size_t>(
|
const auto columnCount = static_cast<std::size_t>(
|
||||||
std::floor(
|
std::floor(
|
||||||
@@ -240,9 +266,9 @@ void ByteItemGraphicsScene::adjustByteItemPositions() {
|
|||||||
auto rowIndicesWithBottomAnnotations = std::set<std::size_t>();
|
auto rowIndicesWithBottomAnnotations = std::set<std::size_t>();
|
||||||
const auto& memoryAddressRange = this->targetMemoryDescriptor.addressRange;
|
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>(
|
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
|
/ static_cast<double>(columnCount)) - 1
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -254,7 +280,13 @@ void ByteItemGraphicsScene::adjustByteItemPositions() {
|
|||||||
// We only display annotations that span a single row.
|
// We only display annotations that span a single row.
|
||||||
if (firstByteRowIndex == lastByteRowIndex) {
|
if (firstByteRowIndex == lastByteRowIndex) {
|
||||||
annotationItem->show();
|
annotationItem->show();
|
||||||
rowIndicesWithBottomAnnotations.insert(firstByteRowIndex);
|
|
||||||
|
if (annotationItem->position == AnnotationItemPosition::TOP) {
|
||||||
|
rowIndicesWithTopAnnotations.insert(firstByteRowIndex);
|
||||||
|
|
||||||
|
} else if (annotationItem->position == AnnotationItemPosition::BOTTOM) {
|
||||||
|
rowIndicesWithBottomAnnotations.insert(firstByteRowIndex);
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
annotationItem->hide();
|
annotationItem->hide();
|
||||||
@@ -317,18 +349,18 @@ void ByteItemGraphicsScene::adjustAnnotationItemPositions() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& [startAddress, annotationItem] : this->annotationItemsByStartAddress) {
|
for (auto* annotationItem : this->annotationItems) {
|
||||||
if (!this->byteItemsByAddress.contains(startAddress)) {
|
if (!this->byteItemsByAddress.contains(annotationItem->startAddress)) {
|
||||||
annotationItem->hide();
|
annotationItem->hide();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto firstByteItemPosition = this->byteItemsByAddress.at(startAddress)->pos();
|
const auto firstByteItemPosition = this->byteItemsByAddress.at(annotationItem->startAddress)->pos();
|
||||||
|
|
||||||
if (annotationItem->position == AnnotationItemPosition::TOP) {
|
if (annotationItem->position == AnnotationItemPosition::TOP) {
|
||||||
annotationItem->setPos(
|
annotationItem->setPos(
|
||||||
firstByteItemPosition.x(),
|
firstByteItemPosition.x(),
|
||||||
firstByteItemPosition.y() - AnnotationItem::TOP_HEIGHT
|
firstByteItemPosition.y() - AnnotationItem::TOP_HEIGHT - 1
|
||||||
);
|
);
|
||||||
|
|
||||||
} else if (annotationItem->position == AnnotationItemPosition::BOTTOM) {
|
} else if (annotationItem->position == AnnotationItemPosition::BOTTOM) {
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
#include "ByteItem.hpp"
|
#include "ByteItem.hpp"
|
||||||
#include "ByteAddressContainer.hpp"
|
#include "ByteAddressContainer.hpp"
|
||||||
#include "AnnotationItem.hpp"
|
#include "AnnotationItem.hpp"
|
||||||
|
#include "ValueAnnotationItem.hpp"
|
||||||
#include "HexViewerWidgetSettings.hpp"
|
#include "HexViewerWidgetSettings.hpp"
|
||||||
|
|
||||||
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegion.hpp"
|
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegion.hpp"
|
||||||
@@ -68,8 +69,11 @@ namespace Bloom::Widgets
|
|||||||
std::vector<FocusedMemoryRegion>& focusedMemoryRegions;
|
std::vector<FocusedMemoryRegion>& focusedMemoryRegions;
|
||||||
std::vector<ExcludedMemoryRegion>& excludedMemoryRegions;
|
std::vector<ExcludedMemoryRegion>& excludedMemoryRegions;
|
||||||
|
|
||||||
|
Targets::TargetMemoryBuffer lastValueBuffer;
|
||||||
|
|
||||||
std::map<std::uint32_t, ByteItem*> byteItemsByAddress;
|
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*>> byteItemsByRowIndex;
|
||||||
std::map<std::size_t, std::vector<ByteItem*>> byteItemsByColumnIndex;
|
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;
|
return std::max(this->parent->viewport()->width(), 400) - 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void updateAnnotationValues(const Targets::TargetMemoryBuffer& buffer);
|
||||||
void adjustByteItemPositions();
|
void adjustByteItemPositions();
|
||||||
void adjustAnnotationItemPositions();
|
void adjustAnnotationItemPositions();
|
||||||
void onTargetStateChanged(Targets::TargetState newState);
|
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