Initial work for the SnapshotDiff window

This commit is contained in:
Nav
2023-05-01 15:05:34 +01:00
parent 5e4d917b57
commit c0269e8378
21 changed files with 2129 additions and 32 deletions

View File

@@ -109,6 +109,10 @@ target_sources(
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/CreateSnapshotWindow/CreateSnapshotWindow.cpp
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotViewer/SnapshotViewer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotViewer/MemoryRegionItem.cpp
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/SnapshotDiff.cpp
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialHexViewerWidget.cpp
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialItemGraphicsView.cpp
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/DifferentialHexViewerWidget/DifferentialItemGraphicsScene.cpp
# Memory region manager window
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/MemoryRegionManagerWindow.cpp
@@ -182,18 +186,29 @@ qt_add_resources(
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/Images/display-annotations-disabled.svg"
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/Images/ascii-view.svg"
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/Images/ascii-view-disabled.svg"
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/Images/new-snapshot-icon.svg"
# Hex viewer widget
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/UiFiles/HexViewerWidget.ui"
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/Stylesheets/HexViewerWidget.qss"
# Memory snapshots
# Memory snapshot manager
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/UiFiles/SnapshotManager.ui"
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/CreateSnapshotWindow/UiFiles/CreateSnapshotWindow.ui"
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/Images/new-snapshot-icon.svg"
# Memory snapshot viewer
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotViewer/UiFiles/SnapshotViewer.ui"
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotViewer/Stylesheets/SnapshotViewer.qss"
# Memory snapshot diff
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/UiFiles/SnapshotDiff.ui"
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Stylesheets/SnapshotDiff.qss"
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Images/sync-hex-viewer-settings.svg"
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Images/sync-hex-viewer-scroll.svg"
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Images/sync-hex-viewer-hover.svg"
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Images/sync-hex-viewer-selection.svg"
# Memory region manager window
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/UiFiles/MemoryRegionManagerWindow.ui"
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/Stylesheets/MemoryRegionManagerWindow.qss"

View File

@@ -87,6 +87,22 @@ namespace Bloom::Widgets
void ListScene::mousePressEvent(QGraphicsSceneMouseEvent* mouseEvent) {
const auto button = mouseEvent->button();
const auto mousePosition = mouseEvent->buttonDownScenePos(button);
const auto items = this->items(mousePosition);
if (items.empty()) {
return;
}
auto* clickedListItem = dynamic_cast<ListItem*>(items.first());
if (clickedListItem == nullptr) {
return;
}
if (clickedListItem->selected && button == Qt::MouseButton::RightButton) {
return;
}
const auto selectedItemCount = this->selectedItems.size();
if (selectedItemCount > 0) {
const auto ctrlModifierEnabled = (mouseEvent->modifiers() & Qt::ControlModifier) != 0;
@@ -109,18 +125,6 @@ namespace Bloom::Widgets
}
}
const auto mousePosition = mouseEvent->buttonDownScenePos(button);
const auto items = this->items(mousePosition);
if (items.empty()) {
return;
}
auto* clickedListItem = dynamic_cast<ListItem*>(items.first());
if (clickedListItem == nullptr) {
return;
}
if (this->selectionLimit > 0) {
this->selectedItems.push_back(clickedListItem);
clickedListItem->selected = true;
@@ -148,6 +152,14 @@ namespace Bloom::Widgets
return;
}
if (!clickedListItem->selected) {
/*
* Sometimes, QT won't trigger a press event when the user double clicks. This usually happens when the
* first click closes a context menu.
*/
this->mousePressEvent(mouseEvent);
}
emit this->itemDoubleClicked(clickedListItem);
}

View File

@@ -0,0 +1,12 @@
#pragma once
namespace Bloom::Widgets
{
struct DifferentialHexViewerSharedState
{
bool syncingSettings = false;
bool syncingScroll = false;
bool syncingHover = false;
bool syncingSelection = false;
};
}

View File

@@ -0,0 +1,120 @@
#include "DifferentialHexViewerWidget.hpp"
#include <QVBoxLayout>
#include <cassert>
namespace Bloom::Widgets
{
DifferentialHexViewerWidget::DifferentialHexViewerWidget(
DifferentialHexViewerWidgetType type,
DifferentialHexViewerSharedState& state,
const SnapshotDiffSettings& snapshotDiffSettings,
const Targets::TargetMemoryDescriptor& targetMemoryDescriptor,
const std::optional<Targets::TargetMemoryBuffer>& data,
HexViewerWidgetSettings& settings,
const std::vector<FocusedMemoryRegion>& focusedMemoryRegions,
const std::vector<ExcludedMemoryRegion>& excludedMemoryRegions,
QWidget* parent
)
: HexViewerWidget(
targetMemoryDescriptor,
data,
settings,
focusedMemoryRegions,
excludedMemoryRegions,
parent
)
, type(type)
, state(state)
, snapshotDiffSettings(snapshotDiffSettings)
{}
void DifferentialHexViewerWidget::init() {
this->differentialView = new DifferentialItemGraphicsView(
this->state,
this->snapshotDiffSettings,
this->targetMemoryDescriptor,
this->data,
this->focusedMemoryRegions,
this->excludedMemoryRegions,
this->settings,
this->container
);
if (this->type == DifferentialHexViewerWidgetType::PRIMARY) {
this->differentialView->setLayoutDirection(Qt::LayoutDirection::RightToLeft);
}
this->byteItemGraphicsView = this->differentialView;
this->byteItemGraphicsView->hide();
auto* containerLayout = this->container->findChild<QVBoxLayout*>("hex-viewer-layout");
containerLayout->insertWidget(2, this->byteItemGraphicsView);
QObject::connect(
this->byteItemGraphicsView,
&ItemGraphicsView::sceneReady,
this,
[this] {
this->differentialScene = dynamic_cast<DifferentialItemGraphicsScene*>(
this->byteItemGraphicsView->getScene()
);
this->byteItemGraphicsScene = this->differentialScene;
QObject::connect(
this->byteItemGraphicsScene,
&ItemGraphicsScene::hoveredAddress,
this,
&DifferentialHexViewerWidget::onHoveredAddress
);
QObject::connect(
this->byteItemGraphicsScene,
&ItemGraphicsScene::selectionChanged,
this,
&DifferentialHexViewerWidget::onByteSelectionChanged
);
this->loadingHexViewerLabel->hide();
this->byteItemGraphicsView->show();
emit this->ready();
}
);
this->byteItemGraphicsView->initScene();
}
void DifferentialHexViewerWidget::setOther(DifferentialHexViewerWidget* other) {
assert(other->byteItemGraphicsView != nullptr);
assert(other->byteItemGraphicsScene != nullptr);
this->other = other;
this->differentialView->setOther(this->other->differentialView);
QObject::connect(
this->other,
&HexViewerWidget::settingsChanged,
this,
&DifferentialHexViewerWidget::onOtherSettingsChanged
);
}
void DifferentialHexViewerWidget::onOtherSettingsChanged(const HexViewerWidgetSettings& settings) {
if (!this->snapshotDiffSettings.syncHexViewerSettings || this->state.syncingSettings) {
return;
}
this->state.syncingSettings = true;
this->setStackMemoryGroupingEnabled(settings.groupStackMemory);
this->setFocusedMemoryHighlightingEnabled(settings.highlightFocusedMemory);
this->setHoveredRowAndColumnHighlightingEnabled(settings.highlightHoveredRowAndCol);
this->setDisplayAsciiEnabled(settings.displayAsciiValues);
this->setAnnotationsEnabled(settings.displayAnnotations);
this->state.syncingSettings = false;
this->differentialView->update();
}
}

View File

@@ -0,0 +1,46 @@
#pragma once
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerWidget.hpp"
#include "DifferentialHexViewerWidgetType.hpp"
#include "DifferentialHexViewerSharedState.hpp"
#include "DifferentialItemGraphicsView.hpp"
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/SnapshotDiffSettings.hpp"
namespace Bloom::Widgets
{
class DifferentialHexViewerWidget final: public HexViewerWidget
{
Q_OBJECT
public:
DifferentialHexViewerWidget(
DifferentialHexViewerWidgetType type,
DifferentialHexViewerSharedState& state,
const SnapshotDiffSettings& snapshotDiffSettings,
const Targets::TargetMemoryDescriptor& targetMemoryDescriptor,
const std::optional<Targets::TargetMemoryBuffer>& data,
HexViewerWidgetSettings& settings,
const std::vector<FocusedMemoryRegion>& focusedMemoryRegions,
const std::vector<ExcludedMemoryRegion>& excludedMemoryRegions,
QWidget* parent
);
void init() override;
void setOther(DifferentialHexViewerWidget* other);
private:
DifferentialHexViewerWidgetType type;
DifferentialHexViewerSharedState& state;
const SnapshotDiffSettings& snapshotDiffSettings;
DifferentialItemGraphicsView* differentialView = nullptr;
DifferentialItemGraphicsScene* differentialScene = nullptr;
DifferentialHexViewerWidget* other = nullptr;
void onOtherSettingsChanged(const HexViewerWidgetSettings& settings);
};
}

View File

@@ -0,0 +1,12 @@
#pragma once
#include <cstdint>
namespace Bloom::Widgets
{
enum class DifferentialHexViewerWidgetType: std::uint8_t
{
PRIMARY,
SECONDARY,
};
}

View File

@@ -0,0 +1,120 @@
#include "DifferentialItemGraphicsScene.hpp"
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ItemGraphicsView.hpp"
namespace Bloom::Widgets
{
DifferentialItemGraphicsScene::DifferentialItemGraphicsScene(
DifferentialHexViewerSharedState& state,
const SnapshotDiffSettings& snapshotDiffSettings,
const Targets::TargetMemoryDescriptor& targetMemoryDescriptor,
const std::optional<Targets::TargetMemoryBuffer>& data,
const std::vector<FocusedMemoryRegion>& focusedMemoryRegions,
const std::vector<ExcludedMemoryRegion>& excludedMemoryRegions,
HexViewerWidgetSettings& settings,
QGraphicsView* parent
)
: ItemGraphicsScene(
targetMemoryDescriptor,
data,
focusedMemoryRegions,
excludedMemoryRegions,
settings,
parent
)
, diffHexViewerState(state)
, snapshotDiffSettings(snapshotDiffSettings)
{}
void DifferentialItemGraphicsScene::setOther(DifferentialItemGraphicsScene* other) {
this->other = other;
QObject::connect(
this->other,
&DifferentialItemGraphicsScene::hoveredAddress,
this,
&DifferentialItemGraphicsScene::onOtherHoveredAddress
);
QObject::connect(
this->other,
&DifferentialItemGraphicsScene::selectionChanged,
this,
&DifferentialItemGraphicsScene::onOtherSelectionChanged
);
}
ByteItem* DifferentialItemGraphicsScene::byteItemAtViewportVerticalCenter() {
const auto scrollBarValue = this->getScrollbarValue();
const auto midPosition = static_cast<qreal>(
scrollBarValue
);
const auto gridPointIndex = static_cast<decltype(this->gridPoints)::size_type>(
std::min(
static_cast<int>(
std::floor(static_cast<float>(midPosition) / static_cast<float>(ItemGraphicsScene::GRID_SIZE)) + 1
),
static_cast<int>(this->gridPoints.size() - 1)
)
);
return static_cast<ByteItem*>(*(this->gridPoints[gridPointIndex]));
}
void DifferentialItemGraphicsScene::onOtherHoveredAddress(
const std::optional<Targets::TargetMemoryAddress>& address
) {
if (!this->snapshotDiffSettings.syncHexViewerHover || this->diffHexViewerState.syncingHover) {
return;
}
if (!address.has_value()) {
if (this->state.hoveredByteItem != nullptr) {
this->onByteItemLeave();
}
return;
}
auto& byteItem = this->topLevelGroup->byteItemsByAddress.find(*address)->second;
const auto itemPosition = byteItem.position().y();
const auto scrollbarValue = this->getScrollbarValue();
if (
byteItem.allocatedGraphicsItem == nullptr
|| itemPosition < scrollbarValue
|| itemPosition > (scrollbarValue + this->views().first()->viewport()->height())
) {
// The item isn't visible
return;
}
this->diffHexViewerState.syncingHover = true;
this->onByteItemEnter(byteItem);
this->diffHexViewerState.syncingHover = false;
}
void DifferentialItemGraphicsScene::onOtherSelectionChanged(
const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>& selectedByteItemsByAddress
) {
if (!this->snapshotDiffSettings.syncHexViewerSelection || this->diffHexViewerState.syncingSelection) {
return;
}
this->diffHexViewerState.syncingSelection = true;
this->clearByteItemSelection();
for (const auto& [address, otherByteItem] : selectedByteItemsByAddress) {
auto& byteItem = this->topLevelGroup->byteItemsByAddress.at(address);
byteItem.selected = true;
this->selectedByteItemsByAddress.insert(std::pair(byteItem.startAddress, &byteItem));
}
emit this->selectionChanged(this->selectedByteItemsByAddress);
this->diffHexViewerState.syncingSelection = false;
this->update();
}
}

View File

@@ -0,0 +1,40 @@
#pragma once
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ItemGraphicsScene.hpp"
#include "DifferentialHexViewerSharedState.hpp"
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/SnapshotDiffSettings.hpp"
namespace Bloom::Widgets
{
class DifferentialItemGraphicsScene final: public ItemGraphicsScene
{
Q_OBJECT
public:
DifferentialItemGraphicsScene(
DifferentialHexViewerSharedState& state,
const SnapshotDiffSettings& snapshotDiffSettings,
const Targets::TargetMemoryDescriptor& targetMemoryDescriptor,
const std::optional<Targets::TargetMemoryBuffer>& data,
const std::vector<FocusedMemoryRegion>& focusedMemoryRegions,
const std::vector<ExcludedMemoryRegion>& excludedMemoryRegions,
HexViewerWidgetSettings& settings,
QGraphicsView* parent
);
void setOther(DifferentialItemGraphicsScene* other);
ByteItem* byteItemAtViewportVerticalCenter();
protected:
DifferentialHexViewerSharedState& diffHexViewerState;
const SnapshotDiffSettings& snapshotDiffSettings;
DifferentialItemGraphicsScene* other = nullptr;
void onOtherHoveredAddress(const std::optional<Targets::TargetMemoryAddress>& address);
void onOtherSelectionChanged(
const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>& selectedByteItemsByAddress
);
};
}

View File

@@ -0,0 +1,83 @@
#include "DifferentialItemGraphicsView.hpp"
namespace Bloom::Widgets
{
DifferentialItemGraphicsView::DifferentialItemGraphicsView(
DifferentialHexViewerSharedState& state,
const SnapshotDiffSettings& snapshotDiffSettings,
const Targets::TargetMemoryDescriptor& targetMemoryDescriptor,
const std::optional<Targets::TargetMemoryBuffer>& data,
const std::vector<FocusedMemoryRegion>& focusedMemoryRegions,
const std::vector<ExcludedMemoryRegion>& excludedMemoryRegions,
HexViewerWidgetSettings& settings,
QWidget* parent
)
: ItemGraphicsView(
targetMemoryDescriptor,
data,
focusedMemoryRegions,
excludedMemoryRegions,
settings,
parent
)
, state(state)
, snapshotDiffSettings(snapshotDiffSettings)
{}
void DifferentialItemGraphicsView::initScene() {
this->differentialScene = new DifferentialItemGraphicsScene(
this->state,
this->snapshotDiffSettings,
this->targetMemoryDescriptor,
this->data,
this->focusedMemoryRegions,
this->excludedMemoryRegions,
this->settings,
this
);
this->scene = this->differentialScene;
this->setScene(this->scene);
QObject::connect(
this->scene,
&ItemGraphicsScene::ready,
this,
[this] {
this->scene->setEnabled(this->isEnabled());
emit this->sceneReady();
}
);
this->scene->init();
}
void DifferentialItemGraphicsView::setOther(DifferentialItemGraphicsView* other) {
this->other = other;
this->differentialScene->setOther(this->other->differentialScene);
}
void DifferentialItemGraphicsView::alignScroll(
Targets::TargetMemoryAddress otherByteItemAddress,
int otherByteItemYOffset
) {
const auto byteItemPosition = this->differentialScene->getByteItemPositionByAddress(otherByteItemAddress);
this->verticalScrollBar()->setValue(
std::max(static_cast<int>(byteItemPosition.y() - otherByteItemYOffset), 0)
);
}
void DifferentialItemGraphicsView::scrollContentsBy(int dx, int dy) {
ItemGraphicsView::scrollContentsBy(dx, dy);
if (!this->snapshotDiffSettings.syncHexViewerScroll || this->state.syncingScroll) {
return;
}
auto* byteItem = this->differentialScene->byteItemAtViewportVerticalCenter();
this->state.syncingScroll = true;
this->other->alignScroll(byteItem->startAddress, byteItem->position().y() - this->verticalScrollBar()->value());
this->state.syncingScroll = false;
}
}

View File

@@ -0,0 +1,42 @@
#pragma once
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ItemGraphicsView.hpp"
#include "DifferentialHexViewerSharedState.hpp"
#include "DifferentialItemGraphicsScene.hpp"
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/SnapshotDiffSettings.hpp"
namespace Bloom::Widgets
{
class DifferentialItemGraphicsView final: public ItemGraphicsView
{
Q_OBJECT
public:
DifferentialItemGraphicsView(
DifferentialHexViewerSharedState& state,
const SnapshotDiffSettings& snapshotDiffSettings,
const Targets::TargetMemoryDescriptor& targetMemoryDescriptor,
const std::optional<Targets::TargetMemoryBuffer>& data,
const std::vector<FocusedMemoryRegion>& focusedMemoryRegions,
const std::vector<ExcludedMemoryRegion>& excludedMemoryRegions,
HexViewerWidgetSettings& settings,
QWidget* parent
);
void initScene() override;
void setOther(DifferentialItemGraphicsView* other);
void alignScroll(Targets::TargetMemoryAddress otherByteItemAddress, int otherByteItemYOffset);
protected:
DifferentialHexViewerSharedState& state;
const SnapshotDiffSettings& snapshotDiffSettings;
DifferentialItemGraphicsScene* differentialScene = nullptr;
DifferentialItemGraphicsView* other = nullptr;
void scrollContentsBy(int dx, int dy) override;
};
}

View File

@@ -0,0 +1,209 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="27"
height="22"
viewBox="0 0 7.1437502 5.8208333"
version="1.1"
id="svg974"
sodipodi:docname="sync-hex-viewer-hover.svg"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs
id="defs968" />
<sodipodi:namedview
id="base"
pagecolor="#343532"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="22.455047"
inkscape:cx="13.604959"
inkscape:cy="2.137604"
inkscape:document-units="px"
inkscape:current-layer="layer2"
inkscape:document-rotation="0"
showgrid="false"
units="px"
inkscape:snap-page="true"
inkscape:window-width="3440"
inkscape:window-height="1353"
inkscape:window-x="2560"
inkscape:window-y="34"
inkscape:window-maximized="1"
inkscape:snap-smooth-nodes="true"
inkscape:snap-bbox="true"
inkscape:snap-bbox-midpoints="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:pagecheckerboard="0" />
<metadata
id="metadata971">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Main"
style="display:inline">
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-0"
width="0.79374999"
height="0.79374993"
x="0.5291664"
y="2.9104168" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-7-7"
width="0.79374999"
height="0.79374999"
x="1.5875001"
y="2.9104168" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-4-8"
width="0.79374999"
height="0.79374999"
x="2.6458333"
y="2.9104168" />
<rect
style="display:inline;fill:#838382;fill-opacity:0.204609;stroke-width:0.0552426"
id="rect1537-3-6-0-3"
width="0.79374999"
height="0.79374993"
x="0.5291664"
y="1.8520832" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-7-7-6"
width="0.79374999"
height="0.79374999"
x="1.5875001"
y="1.8520832" />
<rect
style="display:inline;fill:#838382;fill-opacity:0.204609;stroke-width:0.0552426"
id="rect1537-3-6-4-8-7"
width="0.79374999"
height="0.79374999"
x="2.6458333"
y="1.8520832" />
<rect
style="display:inline;fill:#838382;fill-opacity:0.204609;stroke-width:0.0552426"
id="rect1537-3-6-8"
width="0.79374999"
height="0.79374993"
x="0.5291664"
y="3.96875" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-7-8"
width="0.79374999"
height="0.79374999"
x="1.5875001"
y="3.96875" />
<rect
style="display:inline;fill:#838382;fill-opacity:0.204609;stroke-width:0.0552426"
id="rect1537-3-6-4-4"
width="0.79374999"
height="0.79374999"
x="2.645833"
y="3.96875" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-4-8-3"
width="0.79374999"
height="0.79374999"
x="3.7041671"
y="2.9104168" />
<rect
style="display:inline;fill:#838382;fill-opacity:0.204609;stroke-width:0.0552426"
id="rect1537-3-6-4-8-7-6"
width="0.79374999"
height="0.79374999"
x="3.7041671"
y="1.8520832" />
<rect
style="display:inline;fill:#838382;fill-opacity:0.204609;stroke-width:0.0552426"
id="rect1537-3-6-4-4-7"
width="0.79374999"
height="0.79374999"
x="3.7041667"
y="3.96875" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-4-8-5"
width="0.79374999"
height="0.79374999"
x="4.7624998"
y="2.9104168" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-4-8-7-3"
width="0.79374999"
height="0.79374999"
x="4.7624998"
y="1.8520833" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-4-4-5"
width="0.79374999"
height="0.79374999"
x="4.7624998"
y="3.9687502" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-4-8-3-6"
width="0.79374999"
height="0.79374999"
x="5.8208337"
y="2.9104168" />
<rect
style="display:inline;fill:#838382;fill-opacity:0.204609;stroke-width:0.0552426"
id="rect1537-3-6-4-8-7-6-2"
width="0.79374999"
height="0.79374999"
x="5.8208337"
y="1.8520833" />
<rect
style="display:inline;fill:#838382;fill-opacity:0.204609;stroke-width:0.0552426"
id="rect1537-3-6-4-4-7-9"
width="0.79374999"
height="0.79374999"
x="5.8208332"
y="3.9687502" />
<rect
style="display:inline;fill:#6c6c6c;fill-opacity:1;stroke-width:0.30862"
id="rect858"
width="3.4395833"
height="0.26458335"
x="1.8520834"
y="1.0583333" />
<rect
style="display:inline;fill:#6c6c6c;fill-opacity:1;stroke-width:0.148256"
id="rect858-3"
width="0.26458335"
height="0.79374999"
x="1.8520834"
y="1.0583333" />
<rect
style="display:inline;fill:#6c6c6c;fill-opacity:1;stroke-width:0.148256"
id="rect858-3-6"
width="0.26458335"
height="0.79374999"
x="5.0270829"
y="1.0583334" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@@ -0,0 +1,195 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="29"
height="22"
viewBox="0 0 7.6729169 5.8208333"
version="1.1"
id="svg974"
sodipodi:docname="sync-hex-viewer-scroll.svg"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs
id="defs968" />
<sodipodi:namedview
id="base"
pagecolor="#343532"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="5.6634687"
inkscape:cx="9.9762183"
inkscape:cy="-0.70628094"
inkscape:document-units="px"
inkscape:current-layer="layer2"
inkscape:document-rotation="0"
showgrid="false"
units="px"
inkscape:snap-page="true"
inkscape:window-width="3440"
inkscape:window-height="1353"
inkscape:window-x="2560"
inkscape:window-y="34"
inkscape:window-maximized="1"
inkscape:snap-smooth-nodes="true"
inkscape:snap-bbox="true"
inkscape:snap-bbox-midpoints="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:pagecheckerboard="0" />
<metadata
id="metadata971">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Main"
style="display:inline">
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-0"
width="0.79374999"
height="0.79374993"
x="1.8520836"
y="2.9104168" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-7-7"
width="0.79374999"
height="0.79374999"
x="2.9104171"
y="2.9104168" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-4-8"
width="0.79374999"
height="0.79374999"
x="3.9687505"
y="2.9104168" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-0-3"
width="0.79374999"
height="0.79374993"
x="1.8520836"
y="1.8520831" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-7-7-6"
width="0.79374999"
height="0.79374999"
x="2.9104171"
y="1.8520831" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-4-8-7"
width="0.79374999"
height="0.79374999"
x="3.9687505"
y="1.8520831" />
<rect
style="display:inline;fill:#316065;fill-opacity:1;stroke-width:0.0781248"
id="rect1537-3-6-3-6-5"
width="0.5291667"
height="2.3812501"
x="6.0854168"
y="2.1166668" />
<rect
style="display:inline;fill:#316065;fill-opacity:1;stroke-width:0.0781248"
id="rect1537-3-6-3-6-5-2"
width="0.5291667"
height="2.3812501"
x="1.0583335"
y="2.1166666" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-8"
width="0.79374999"
height="0.79374993"
x="1.8520836"
y="3.9687502" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-7-8"
width="0.79374999"
height="0.79374999"
x="2.9104171"
y="3.9687502" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-4-4"
width="0.79374999"
height="0.79374999"
x="3.9687502"
y="3.9687502" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-4-8-9"
width="0.79374999"
height="0.79374999"
x="5.0270839"
y="2.9104168" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-4-8-7-1"
width="0.79374999"
height="0.79374999"
x="5.0270839"
y="1.8520832" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-4-4-2"
width="0.79374999"
height="0.79374999"
x="5.0270834"
y="3.9687502" />
<rect
style="display:inline;fill:#6c6c6c;fill-opacity:1;stroke-width:0.444769"
id="rect858"
width="7.1437502"
height="0.26458335"
x="0.26458329"
y="1.0583333" />
<rect
style="display:inline;fill:#6c6c6c;fill-opacity:1;stroke-width:0.256788"
id="rect858-3"
width="0.26458335"
height="2.3812501"
x="0.26458329"
y="1.0583333" />
<rect
style="display:inline;fill:#6c6c6c;fill-opacity:1;stroke-width:0.256788"
id="rect858-3-6"
width="0.26458335"
height="2.3812501"
x="7.1437497"
y="1.0583334" />
<rect
style="display:inline;fill:#6c6c6c;fill-opacity:1;stroke-width:0.148257"
id="rect858-3-6-7"
width="0.79374999"
height="0.26458335"
x="6.6145835"
y="3.1750002" />
<rect
style="display:inline;fill:#6c6c6c;fill-opacity:1;stroke-width:0.148257"
id="rect858-3-6-7-5"
width="0.79374999"
height="0.26458335"
x="0.26458329"
y="3.175" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

@@ -0,0 +1,209 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="27"
height="22"
viewBox="0 0 7.1437502 5.8208333"
version="1.1"
id="svg974"
sodipodi:docname="sync-hex-viewer-selection.svg"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs
id="defs968" />
<sodipodi:namedview
id="base"
pagecolor="#343532"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="31.72939"
inkscape:cx="3.0098278"
inkscape:cy="6.2717877"
inkscape:document-units="px"
inkscape:current-layer="layer2"
inkscape:document-rotation="0"
showgrid="false"
units="px"
inkscape:snap-page="true"
inkscape:window-width="3440"
inkscape:window-height="1353"
inkscape:window-x="2560"
inkscape:window-y="34"
inkscape:window-maximized="1"
inkscape:snap-smooth-nodes="true"
inkscape:snap-bbox="true"
inkscape:snap-bbox-midpoints="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:pagecheckerboard="0" />
<metadata
id="metadata971">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Main"
style="display:inline">
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-0"
width="0.79374999"
height="0.79374993"
x="0.52916634"
y="2.1166666" />
<rect
style="display:inline;fill:#316065;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-7-7"
width="0.79374999"
height="0.79374999"
x="1.5875001"
y="2.1166666" />
<rect
style="display:inline;fill:#316065;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-4-8"
width="0.79374999"
height="0.79374999"
x="2.6458333"
y="2.1166666" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-0-3"
width="0.79374999"
height="0.79374993"
x="0.52916634"
y="1.058333" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-7-7-6"
width="0.79374999"
height="0.79374999"
x="1.5875001"
y="1.058333" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-4-8-7"
width="0.79374999"
height="0.79374999"
x="2.6458333"
y="1.058333" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-8"
width="0.79374999"
height="0.79374993"
x="0.52916634"
y="3.175" />
<rect
style="display:inline;fill:#316065;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-7-8"
width="0.79374999"
height="0.79374999"
x="1.5875001"
y="3.175" />
<rect
style="display:inline;fill:#316065;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-4-4"
width="0.79374999"
height="0.79374999"
x="2.645833"
y="3.175" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-4-8-3"
width="0.79374999"
height="0.79374999"
x="3.7041671"
y="2.1166666" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-4-8-7-6"
width="0.79374999"
height="0.79374999"
x="3.7041671"
y="1.058333" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-4-4-7"
width="0.79374999"
height="0.79374999"
x="3.7041667"
y="3.175" />
<rect
style="display:inline;fill:#316065;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-4-8-5"
width="0.79374999"
height="0.79374999"
x="4.7624998"
y="2.1166668" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-4-8-7-3"
width="0.79374999"
height="0.79374999"
x="4.7624998"
y="1.0583332" />
<rect
style="display:inline;fill:#316065;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-4-4-5"
width="0.79374999"
height="0.79374999"
x="4.7624998"
y="3.1750002" />
<rect
style="display:inline;fill:#316065;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-4-8-3-6"
width="0.79374999"
height="0.79374999"
x="5.8208337"
y="2.1166668" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-4-8-7-6-2"
width="0.79374999"
height="0.79374999"
x="5.8208337"
y="1.0583332" />
<rect
style="display:inline;fill:#316065;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-4-4-7-9"
width="0.79374999"
height="0.79374999"
x="5.8208332"
y="3.1750002" />
<rect
style="display:inline;fill:#6c6c6c;fill-opacity:1;stroke-width:0.30862"
id="rect858"
width="3.4395833"
height="0.26458335"
x="2.3812501"
y="4.4979167" />
<rect
style="display:inline;fill:#6c6c6c;fill-opacity:1;stroke-width:0.148256"
id="rect858-3"
width="0.26458335"
height="0.79374999"
x="2.3812501"
y="3.96875" />
<rect
style="display:inline;fill:#6c6c6c;fill-opacity:1;stroke-width:0.148256"
id="rect858-3-3"
width="0.26458335"
height="0.79374999"
x="5.5562491"
y="3.9687505" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@@ -0,0 +1,181 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="27"
height="21"
viewBox="0 0 7.1437502 5.5562499"
version="1.1"
id="svg974"
sodipodi:docname="sync-hex-viewer-settings.svg"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs
id="defs968" />
<sodipodi:namedview
id="base"
pagecolor="#343532"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="13.972733"
inkscape:cx="8.6955075"
inkscape:cy="8.5165876"
inkscape:document-units="px"
inkscape:current-layer="layer2"
inkscape:document-rotation="0"
showgrid="false"
units="px"
inkscape:snap-page="true"
inkscape:window-width="3440"
inkscape:window-height="1353"
inkscape:window-x="2560"
inkscape:window-y="34"
inkscape:window-maximized="1"
inkscape:snap-smooth-nodes="true"
inkscape:snap-bbox="true"
inkscape:snap-bbox-midpoints="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:pagecheckerboard="0" />
<metadata
id="metadata971">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Main"
style="display:inline">
<rect
style="fill:#316065;fill-opacity:1;stroke-width:0.0863703"
id="rect1537-3-6"
width="2.9104166"
height="0.5291667"
x="0.52916664"
y="1.8520831" />
<rect
style="display:inline;fill:#316065;fill-opacity:1;stroke-width:0.0863703"
id="rect1537-3-6-3"
width="2.9104166"
height="0.5291667"
x="3.7041667"
y="1.8520832" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-0"
width="0.79374999"
height="0.79374993"
x="0.52916664"
y="2.6458337" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-7-7"
width="0.79374999"
height="0.79374999"
x="1.5875003"
y="2.6458337" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-4-8"
width="0.79374999"
height="0.79374999"
x="2.6458335"
y="2.6458337" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-3-6"
width="0.79374999"
height="0.79374999"
x="3.7041667"
y="2.6458337" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-8"
width="0.79374999"
height="0.79374993"
x="0.52916664"
y="3.7041669" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-7-8"
width="0.79374999"
height="0.79374999"
x="1.5875003"
y="3.7041669" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-4-4"
width="0.79374999"
height="0.79374999"
x="2.6458333"
y="3.7041669" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-3-3"
width="0.79374999"
height="0.79374999"
x="3.7041667"
y="3.7041669" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-4-8-5"
width="0.79374999"
height="0.79374999"
x="4.7625003"
y="2.6458337" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-3-6-3"
width="0.79374999"
height="0.79374999"
x="5.8208337"
y="2.6458337" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-4-4-5"
width="0.79374999"
height="0.79374999"
x="4.7625003"
y="3.7041669" />
<rect
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0552426"
id="rect1537-3-6-3-3-6"
width="0.79374999"
height="0.79374999"
x="5.8208337"
y="3.7041669" />
<rect
style="fill:#6c6c6c;fill-opacity:1;stroke-width:0.331511"
id="rect858"
width="3.96875"
height="0.26458335"
x="1.5875"
y="1.0583333" />
<rect
style="display:inline;fill:#6c6c6c;fill-opacity:1;stroke-width:0.148256"
id="rect858-3"
width="0.26458335"
height="0.79374999"
x="1.5875"
y="1.0583333" />
<rect
style="display:inline;fill:#6c6c6c;fill-opacity:1;stroke-width:0.148256"
id="rect858-3-6"
width="0.26458335"
height="0.79374999"
x="5.291667"
y="1.0583333" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

@@ -0,0 +1,241 @@
#include "SnapshotDiff.hpp"
#include <QFile>
#include <QVBoxLayout>
#include <algorithm>
#include "src/Insight/InsightWorker/Tasks/WriteTargetMemory.hpp"
#include "src/Insight/InsightWorker/InsightWorker.hpp"
#include "src/Insight/UserInterfaces/InsightWindow/UiLoader.hpp"
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/ConfirmationDialog.hpp"
#include "src/Services/PathService.hpp"
#include "src/Exceptions/Exception.hpp"
#include "src/Insight/InsightSignals.hpp"
#include "src/Logger/Logger.hpp"
namespace Bloom::Widgets
{
using Bloom::Exceptions::Exception;
SnapshotDiff::SnapshotDiff(
MemorySnapshot& snapshotA,
MemorySnapshot& snapshotB,
const Targets::TargetMemoryDescriptor& memoryDescriptor,
QWidget* parent
)
: QWidget(parent)
, memoryDescriptor(memoryDescriptor)
, hexViewerDataA(snapshotA.data)
, focusedRegionsA(snapshotA.focusedRegions)
, excludedRegionsA(snapshotA.excludedRegions)
, stackPointerA(snapshotA.stackPointer)
, hexViewerDataB(snapshotB.data)
, focusedRegionsB(snapshotB.focusedRegions)
, excludedRegionsB(snapshotB.excludedRegions)
, stackPointerB(snapshotB.stackPointer)
{
this->init();
this->setWindowTitle(
"Comparing snapshot \"" + snapshotA.name + "\" with snapshot \"" + snapshotB.name + "\""
);
this->dataAPrimaryLabel->setText(snapshotA.name);
this->dataBPrimaryLabel->setText(snapshotB.name);
this->dataASecondaryLabel->setText(snapshotA.createdDate.toString("dd/MM/yyyy hh:mm"));
this->dataBSecondaryLabel->setText(snapshotB.createdDate.toString("dd/MM/yyyy hh:mm"));
}
void SnapshotDiff::showEvent(QShowEvent* event) {
QWidget::showEvent(event);
}
void SnapshotDiff::resizeEvent(QResizeEvent* event) {
this->container->setFixedSize(this->size());
QWidget::resizeEvent(event);
}
void SnapshotDiff::init() {
this->setWindowFlag(Qt::Window);
this->setObjectName("snapshot-diff");
auto windowUiFile = QFile(
QString::fromStdString(Services::PathService::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane"
+ "/SnapshotManager/SnapshotDiff/UiFiles/SnapshotDiff.ui"
)
);
auto stylesheetFile = QFile(
QString::fromStdString(Services::PathService::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane"
+ "/SnapshotManager/SnapshotDiff/Stylesheets/SnapshotDiff.qss"
)
);
if (!windowUiFile.open(QFile::ReadOnly)) {
throw Exception("Failed to open SnapshotDiff UI file");
}
if (!stylesheetFile.open(QFile::ReadOnly)) {
throw Exception("Failed to open SnapshotDiff stylesheet file");
}
// Set ideal window size
this->setFixedSize(1600, 910);
this->setMinimumSize(700, 600);
this->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
auto uiLoader = UiLoader(this);
const auto styleSheet = stylesheetFile.readAll();
this->container = uiLoader.load(&windowUiFile, this);
this->container->setStyleSheet(styleSheet);
auto* toolBar = this->container->findChild<QWidget*>("tool-bar");
this->syncHexViewerSettingsButton = toolBar->findChild<SvgToolButton*>("sync-settings-btn");
this->syncHexViewerScrollButton = toolBar->findChild<SvgToolButton*>("sync-scroll-btn");
this->syncHexViewerHoverButton = toolBar->findChild<SvgToolButton*>("sync-hover-btn");
this->syncHexViewerSelectionButton = toolBar->findChild<SvgToolButton*>("sync-selection-btn");
this->restoreBytesAction = new ContextMenuAction("Restore Selection", std::nullopt, this);
this->dataAContainer = this->container->findChild<QWidget*>("data-a-container");
this->dataBContainer = this->container->findChild<QWidget*>("data-b-container");
this->dataAPrimaryLabel = this->dataAContainer->findChild<Label*>("primary-label");
this->dataASecondaryLabel = this->dataAContainer->findChild<Label*>("secondary-label");
this->dataBPrimaryLabel = this->dataBContainer->findChild<Label*>("primary-label");
this->dataBSecondaryLabel = this->dataBContainer->findChild<Label*>("secondary-label");
auto snapshotAContainerLayout = this->dataAContainer->findChild<QVBoxLayout*>();
auto snapshotBContainerLayout = this->dataBContainer->findChild<QVBoxLayout*>();
this->hexViewerWidgetA = new DifferentialHexViewerWidget(
DifferentialHexViewerWidgetType::PRIMARY,
this->differentialHexViewerSharedState,
this->settings,
this->memoryDescriptor,
this->hexViewerDataA,
this->hexViewerWidgetSettingsA,
this->focusedRegionsA,
this->excludedRegionsA,
this
);
this->hexViewerWidgetB = new DifferentialHexViewerWidget(
DifferentialHexViewerWidgetType::SECONDARY,
this->differentialHexViewerSharedState,
this->settings,
this->memoryDescriptor,
this->hexViewerDataB,
this->hexViewerWidgetSettingsB,
this->focusedRegionsB,
this->excludedRegionsB,
this
);
this->hexViewerWidgetA->setObjectName("differential-hex-viewer-widget-a");
this->hexViewerWidgetB->setObjectName("differential-hex-viewer-widget-b");
this->hexViewerWidgetA->setStyleSheet(this->hexViewerWidgetA->styleSheet() + styleSheet);
this->hexViewerWidgetB->setStyleSheet(this->hexViewerWidgetB->styleSheet() + styleSheet);
snapshotAContainerLayout->addWidget(this->hexViewerWidgetA);
snapshotBContainerLayout->addWidget(this->hexViewerWidgetB);
this->bottomBar = this->container->findChild<QWidget*>("bottom-bar");
this->bottomBarLayout = this->bottomBar->findChild<QHBoxLayout*>();
this->setSyncHexViewerSettingsEnabled(this->settings.syncHexViewerSettings);
this->setSyncHexViewerScrollEnabled(this->settings.syncHexViewerScroll);
this->setSyncHexViewerHoverEnabled(this->settings.syncHexViewerHover);
this->setSyncHexViewerSelectionEnabled(this->settings.syncHexViewerSelection);
QObject::connect(
this->syncHexViewerSettingsButton,
&QToolButton::clicked,
this,
[this] {
this->setSyncHexViewerSettingsEnabled(!this->settings.syncHexViewerSettings);
}
);
QObject::connect(
this->syncHexViewerScrollButton,
&QToolButton::clicked,
this,
[this] {
this->setSyncHexViewerScrollEnabled(!this->settings.syncHexViewerScroll);
}
);
QObject::connect(
this->syncHexViewerHoverButton,
&QToolButton::clicked,
this,
[this] {
this->setSyncHexViewerHoverEnabled(!this->settings.syncHexViewerHover);
}
);
QObject::connect(
this->syncHexViewerSelectionButton,
&QToolButton::clicked,
this,
[this] {
this->setSyncHexViewerSelectionEnabled(!this->settings.syncHexViewerSelection);
}
);
QObject::connect(this->hexViewerWidgetA, &HexViewerWidget::ready, this, &SnapshotDiff::onHexViewerAReady);
QObject::connect(this->hexViewerWidgetB, &HexViewerWidget::ready, this, &SnapshotDiff::onHexViewerBReady);
this->hexViewerWidgetA->init();
this->hexViewerWidgetB->init();
this->move(this->parentWidget()->window()->geometry().center() - this->rect().center());
}
void SnapshotDiff::onHexViewerAReady() {
this->hexViewerWidgetB->setOther(this->hexViewerWidgetA);
// this->hexViewerWidgetA->addExternalContextMenuAction(this->restoreBytesAction);
if (this->stackPointerA.has_value()) {
this->hexViewerWidgetA->setStackPointer(*(this->stackPointerA));
}
}
void SnapshotDiff::onHexViewerBReady() {
this->hexViewerWidgetA->setOther(this->hexViewerWidgetB);
if (this->stackPointerB.has_value()) {
this->hexViewerWidgetB->setStackPointer(*(this->stackPointerB));
}
}
void SnapshotDiff::setSyncHexViewerSettingsEnabled(bool enabled) {
this->settings.syncHexViewerSettings = enabled;
this->syncHexViewerSettingsButton->setChecked(this->settings.syncHexViewerSettings);
}
void SnapshotDiff::setSyncHexViewerScrollEnabled(bool enabled) {
this->settings.syncHexViewerScroll = enabled;
this->syncHexViewerScrollButton->setChecked(this->settings.syncHexViewerScroll);
}
void SnapshotDiff::setSyncHexViewerHoverEnabled(bool enabled) {
this->settings.syncHexViewerHover = enabled;
this->syncHexViewerHoverButton->setChecked(this->settings.syncHexViewerHover);
}
void SnapshotDiff::setSyncHexViewerSelectionEnabled(bool enabled) {
this->settings.syncHexViewerSelection = enabled;
this->syncHexViewerSelectionButton->setChecked(this->settings.syncHexViewerSelection);
}
}

View File

@@ -0,0 +1,94 @@
#pragma once
#include <QWidget>
#include <QShowEvent>
#include <QResizeEvent>
#include <QHBoxLayout>
#include <QPlainTextEdit>
#include <optional>
#include "SnapshotDiffSettings.hpp"
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/SvgToolButton.hpp"
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/Label.hpp"
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/PushButton.hpp"
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/ListView/ListView.hpp"
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TaskProgressIndicator/TaskProgressIndicator.hpp"
#include "src/Targets/TargetMemory.hpp"
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemorySnapshot.hpp"
#include "DifferentialHexViewerWidget/DifferentialHexViewerWidget.hpp"
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ContextMenuAction.hpp"
namespace Bloom::Widgets
{
class SnapshotDiff: public QWidget
{
Q_OBJECT
public:
SnapshotDiff(
MemorySnapshot& snapshotA,
MemorySnapshot& snapshotB,
const Targets::TargetMemoryDescriptor& memoryDescriptor,
QWidget* parent = nullptr
);
protected:
void showEvent(QShowEvent* event) override;
void resizeEvent(QResizeEvent* event) override;
private:
SnapshotDiffSettings settings;
const Targets::TargetMemoryDescriptor& memoryDescriptor;
QWidget* container = nullptr;
SvgToolButton* syncHexViewerSettingsButton = nullptr;
SvgToolButton* syncHexViewerScrollButton = nullptr;
SvgToolButton* syncHexViewerHoverButton = nullptr;
SvgToolButton* syncHexViewerSelectionButton = nullptr;
QWidget* dataAContainer = nullptr;
QWidget* dataBContainer = nullptr;
Label* dataAPrimaryLabel = nullptr;
Label* dataBPrimaryLabel = nullptr;
Label* dataASecondaryLabel = nullptr;
Label* dataBSecondaryLabel = nullptr;
DifferentialHexViewerSharedState differentialHexViewerSharedState;
std::optional<Targets::TargetMemoryBuffer> hexViewerDataA;
std::vector<FocusedMemoryRegion> focusedRegionsA;
std::vector<ExcludedMemoryRegion> excludedRegionsA;
std::optional<Targets::TargetStackPointer> stackPointerA;
DifferentialHexViewerWidget* hexViewerWidgetA = nullptr;
HexViewerWidgetSettings hexViewerWidgetSettingsA = HexViewerWidgetSettings();
std::optional<Targets::TargetMemoryBuffer> hexViewerDataB;
std::vector<FocusedMemoryRegion> focusedRegionsB;
std::vector<ExcludedMemoryRegion> excludedRegionsB;
std::optional<Targets::TargetStackPointer> stackPointerB;
DifferentialHexViewerWidget* hexViewerWidgetB = nullptr;
HexViewerWidgetSettings hexViewerWidgetSettingsB = HexViewerWidgetSettings();
ContextMenuAction* restoreBytesAction = nullptr;
QWidget* bottomBar = nullptr;
QHBoxLayout* bottomBarLayout = nullptr;
void init();
void onHexViewerAReady();
void onHexViewerBReady();
void setSyncHexViewerSettingsEnabled(bool enabled);
void setSyncHexViewerScrollEnabled(bool enabled);
void setSyncHexViewerHoverEnabled(bool enabled);
void setSyncHexViewerSelectionEnabled(bool enabled);
};
}

View File

@@ -0,0 +1,12 @@
#pragma once
namespace Bloom::Widgets
{
struct SnapshotDiffSettings
{
bool syncHexViewerSettings = true;
bool syncHexViewerScroll = true;
bool syncHexViewerHover = true;
bool syncHexViewerSelection = false;
};
}

View File

@@ -0,0 +1,72 @@
#snapshot-diff,
#snapshot-diff #container {
background-color: #373835;
border: none;
}
#snapshot-diff #tool-bar {
border-bottom: 1px solid #41423f;
border-left: none;
}
#snapshot-diff #tool-bar #sync-label {
color: #8a8a8d;
font-size: 12px;
}
#snapshot-diff #tool-bar QToolButton {
background-color: transparent;
border: none;
padding: 0;
qproperty-buttonWidth: 33;
qproperty-buttonHeight: 23;
}
#snapshot-diff #tool-bar QToolButton:hover {
background-color: #41413b;
border: none;
padding: 0;
}
#snapshot-diff #tool-bar QToolButton:checked {
background-color: #43433d;
border: none;
}
#snapshot-diff #tool-bar #separator {
background-color: transparent;
border-right: 1px solid #41423f;
padding: 0;
min-width: 1px;
max-width: 1px;
}
#snapshot-diff #data-details-container {
border-bottom: 1px solid #41423f;
color: #8a8a8d;
}
#snapshot-diff #data-details-container #primary-label,
#snapshot-diff #data-details-container #secondary-label {
color: #8a8a8d;
}
#snapshot-diff #snapshot-a-container,
#snapshot-diff #snapshot-a-container #data-details-container {
border-right: 1px solid #41423f;
}
#snapshot-diff #differential-hex-viewer-widget-a #hex-viewer-container QScrollBar {
margin: 0;
padding: 1px 3px 0 3px;
width: 15px;
border-right: 1px solid #41423f;
}
#snapshot-diff #differential-hex-viewer-widget-a #hex-viewer-container #graphics-view {
border-right: 1px solid #41423f;
}
#snapshot-diff #bottom-bar {
border-top: 1px solid #41423f;
}

View File

@@ -0,0 +1,309 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<widget class="QWidget" name="container">
<layout class="QVBoxLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="tool-bar">
<property name="minimumHeight">
<number>32</number>
</property>
<property name="maximumHeight">
<number>32</number>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"/>
</property>
<layout class="QHBoxLayout">
<property name="spacing">
<number>3</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<spacer name="horizontal-spacer">
<property name="sizeHint">
<size>
<width>6</width>
</size>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
</spacer>
</item>
<item>
<widget class="Label" name="sync-label">
<property name="text">
<string>Synchronisation:</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontal-spacer">
<property name="sizeHint">
<size>
<width>3</width>
</size>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
</spacer>
</item>
<item>
<widget class="SvgToolButton" name="sync-settings-btn">
<property name="checkable">
<bool>true</bool>
</property>
<property name="svgFilePath">
<string>:/compiled/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Images/sync-hex-viewer-settings.svg</string>
</property>
<property name="toolTip">
<string>Sync settings</string>
</property>
</widget>
</item>
<item>
<widget class="SvgToolButton" name="sync-scroll-btn">
<property name="checkable">
<bool>true</bool>
</property>
<property name="svgFilePath">
<string>:/compiled/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Images/sync-hex-viewer-scroll.svg</string>
</property>
<property name="toolTip">
<string>Sync scrolling</string>
</property>
</widget>
</item>
<item>
<widget class="SvgToolButton" name="sync-hover-btn">
<property name="checkable">
<bool>true</bool>
</property>
<property name="svgFilePath">
<string>:/compiled/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Images/sync-hex-viewer-hover.svg</string>
</property>
<property name="toolTip">
<string>Sync hover</string>
</property>
</widget>
</item>
<item>
<widget class="SvgToolButton" name="sync-selection-btn">
<property name="checkable">
<bool>true</bool>
</property>
<property name="svgFilePath">
<string>:/compiled/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotDiff/Images/sync-hex-viewer-selection.svg</string>
</property>
<property name="toolTip">
<string>Sync selection</string>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="separator"/>
</item>
<item>
<spacer name="horizontal-spacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="sub-container">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"/>
</property>
<layout class="QHBoxLayout" name="sub-layout">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="data-a-container">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"/>
</property>
<layout class="QVBoxLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="data-details-container">
<property name="minimumHeight">
<number>28</number>
</property>
<property name="maximumHeight">
<number>28</number>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"/>
</property>
<layout class="QHBoxLayout">
<property name="spacing">
<number>3</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<spacer name="horizontal-spacer">
<property name="sizeHint">
<size>
<width>6</width>
</size>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
</spacer>
</item>
<item>
<widget class="Label" name="primary-label"/>
</item>
<item>
<spacer name="horizontal-spacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</spacer>
</item>
<item>
<widget class="Label" name="secondary-label"/>
</item>
<item>
<spacer name="horizontal-spacer">
<property name="sizeHint">
<size>
<width>6</width>
</size>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="data-b-container">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"/>
</property>
<layout class="QVBoxLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="data-details-container">
<property name="minimumHeight">
<number>28</number>
</property>
<property name="maximumHeight">
<number>28</number>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"/>
</property>
<layout class="QHBoxLayout">
<property name="spacing">
<number>3</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<spacer name="horizontal-spacer">
<property name="sizeHint">
<size>
<width>6</width>
</size>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
</spacer>
</item>
<item>
<widget class="Label" name="primary-label"/>
</item>
<item>
<spacer name="horizontal-spacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</spacer>
</item>
<item>
<widget class="Label" name="secondary-label"/>
</item>
<item>
<spacer name="horizontal-spacer">
<property name="sizeHint">
<size>
<width>6</width>
</size>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="bottom-bar">
<property name="minimumHeight">
<number>28</number>
</property>
<property name="maximumHeight">
<number>28</number>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"/>
</property>
<layout class="QHBoxLayout">
<property name="spacing">
<number>3</number>
</property>
<property name="margin">
<number>0</number>
</property>
</layout>
</widget>
</item>
</layout>
</widget>
</ui>

View File

@@ -100,6 +100,13 @@ namespace Bloom::Widgets
}
);
QObject::connect(
this->snapshotListScene,
&ListScene::selectionChanged,
this,
&SnapshotManager::onSnapshotItemSelectionChanged
);
QObject::connect(
this->snapshotListScene,
&ListScene::itemDoubleClicked,
@@ -125,9 +132,29 @@ namespace Bloom::Widgets
&QAction::triggered,
this,
[this] {
if (this->contextMenuSnapshotItem != nullptr) {
this->openSnapshotViewer(this->contextMenuSnapshotItem->memorySnapshot.id);
if (this->selectedSnapshotItems.size() != 1) {
return;
}
this->openSnapshotViewer(this->selectedSnapshotItems.front()->memorySnapshot.id);
}
);
QObject::connect(
this->openSnapshotDiffAction,
&QAction::triggered,
this,
[this] {
if (this->selectedSnapshotItems.size() != 2) {
return;
}
const auto& first = this->selectedSnapshotItems.front()->memorySnapshot;
const auto& second = this->selectedSnapshotItems.back()->memorySnapshot;
this->openSnapshotDiff(
first.createdDate < second.createdDate ? first.id : second.id,
first.createdDate < second.createdDate ? second.id : first.id
);
}
);
@@ -136,9 +163,11 @@ namespace Bloom::Widgets
&QAction::triggered,
this,
[this] {
if (this->contextMenuSnapshotItem != nullptr) {
this->deleteSnapshot(this->contextMenuSnapshotItem->memorySnapshot.id, true);
if (this->selectedSnapshotItems.size() != 1) {
return;
}
this->deleteSnapshot(this->selectedSnapshotItems.front()->memorySnapshot.id, true);
}
);
@@ -147,9 +176,11 @@ namespace Bloom::Widgets
&QAction::triggered,
this,
[this] {
if (this->contextMenuSnapshotItem != nullptr) {
this->restoreSnapshot(this->contextMenuSnapshotItem->memorySnapshot.id, true);
if (this->selectedSnapshotItems.size() != 1) {
return;
}
this->restoreSnapshot(this->selectedSnapshotItems.front()->memorySnapshot.id, true);
}
);
@@ -247,12 +278,17 @@ namespace Bloom::Widgets
this->snapshotListScene->addListItem(snapshotItem);
}
void SnapshotManager::onSnapshotItemSelected(MemorySnapshotItem* item) {
if (this->selectedItem != nullptr && this->selectedItem != item) {
this->selectedItem->setSelected(false);
}
void SnapshotManager::onSnapshotItemSelectionChanged(const std::list<ListItem*>& selectedItems) {
this->selectedSnapshotItems.clear();
this->selectedItem = item;
std::transform(
selectedItems.begin(),
selectedItems.end(),
std::inserter(this->selectedSnapshotItems, this->selectedSnapshotItems.end()),
[] (ListItem* item) {
return dynamic_cast<MemorySnapshotItem*>(item);
}
);
}
void SnapshotManager::openSnapshotViewer(const QString& snapshotId) {
@@ -276,6 +312,34 @@ namespace Bloom::Widgets
snapshotViewer->activateWindow();
}
void SnapshotManager::openSnapshotDiff(const QString& snapshotIdA, const QString& snapshotIdB) {
const auto diffKey = snapshotIdA + snapshotIdB;
auto snapshotDiffIt = this->snapshotDiffs.find(diffKey);
if (snapshotDiffIt == this->snapshotDiffs.end()) {
const auto& snapshotItA = this->snapshotsById.find(snapshotIdA);
const auto& snapshotItB = this->snapshotsById.find(snapshotIdB);
if (snapshotItA == this->snapshotsById.end() || snapshotItB == this->snapshotsById.end()) {
return;
}
snapshotDiffIt = this->snapshotDiffs.insert(
diffKey,
new SnapshotDiff(
snapshotItA.value(),
snapshotItB.value(),
this->memoryDescriptor,
this
)
);
}
auto* snapshotDiff = snapshotDiffIt.value();
snapshotDiff->show();
snapshotDiff->activateWindow();
}
void SnapshotManager::deleteSnapshot(const QString& snapshotId, bool confirmationPromptEnabled) {
const auto& snapshotIt = this->snapshotsById.find(snapshotId);
@@ -460,17 +524,22 @@ namespace Bloom::Widgets
return;
}
this->contextMenuSnapshotItem = snapshotItem;
auto* menu = new QMenu(this);
menu->addAction(this->openSnapshotViewerAction);
menu->addAction(this->deleteSnapshotAction);
menu->addSeparator();
menu->addAction(this->restoreSnapshotAction);
this->restoreSnapshotAction->setEnabled(this->targetState == Targets::TargetState::STOPPED);
this->openSnapshotViewerAction->setEnabled(this->selectedSnapshotItems.size() == 1);
this->deleteSnapshotAction->setEnabled(this->selectedSnapshotItems.size() == 1);
this->restoreSnapshotAction->setEnabled(
this->selectedSnapshotItems.size() == 1 && this->targetState == Targets::TargetState::STOPPED
);
if (this->selectedSnapshotItems.size() == 2) {
menu->addAction(this->openSnapshotDiffAction);
}
menu->exec(sourcePosition);
}

View File

@@ -23,6 +23,7 @@
#include "./CreateSnapshotWindow/CreateSnapshotWindow.hpp"
#include "MemorySnapshotItem.hpp"
#include "SnapshotViewer/SnapshotViewer.hpp"
#include "SnapshotDiff/SnapshotDiff.hpp"
namespace Bloom::Widgets
{
@@ -65,6 +66,7 @@ namespace Bloom::Widgets
QMap<QString, MemorySnapshot> snapshotsById;
QMap<QString, MemorySnapshotItem*> snapshotItemsById;
QMap<QString, SnapshotViewer*> snapshotViewersById;
QMap<QString, SnapshotDiff*> snapshotDiffs;
QWidget* container = nullptr;
QWidget* toolBar = nullptr;
@@ -75,9 +77,10 @@ namespace Bloom::Widgets
ListView* snapshotListView = nullptr;
ListScene* snapshotListScene = nullptr;
MemorySnapshotItem* contextMenuSnapshotItem = nullptr;
std::list<MemorySnapshotItem*> selectedSnapshotItems;
QAction* openSnapshotViewerAction = new QAction("Open", this);
QAction* openSnapshotDiffAction = new QAction("Compare Selection", this);
QAction* deleteSnapshotAction = new QAction("Delete", this);
QAction* restoreSnapshotAction = new QAction("Restore", this);
@@ -89,8 +92,9 @@ namespace Bloom::Widgets
);
void addSnapshot(MemorySnapshot&& snapshotTmp);
void onSnapshotItemSelected(MemorySnapshotItem* item);
void onSnapshotItemSelectionChanged(const std::list<ListItem*>& selectedItems);
void openSnapshotViewer(const QString& snapshotId);
void openSnapshotDiff(const QString& snapshotIdA, const QString& snapshotIdB);
void deleteSnapshot(const QString& snapshotId, bool confirmationPromptEnabled);
void restoreSnapshot(const QString& snapshotId, bool confirmationPromptEnabled);
void onSnapshotItemDoubleClick(MemorySnapshotItem* item);