Memory snapshot management via memory inspection pane
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <mutex>
|
||||
#include <QDateTime>
|
||||
#include <QDate>
|
||||
|
||||
namespace Bloom
|
||||
{
|
||||
@@ -23,6 +24,16 @@ namespace Bloom
|
||||
return QDateTime::currentDateTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* This function calls QDateTime::currentDateTime(). See comment for DateTime::currentDateTime().
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
static QDate currentDate() {
|
||||
const auto lock = std::unique_lock(DateTime::systemClockMutex);
|
||||
return QDateTime::currentDateTime().date();
|
||||
}
|
||||
|
||||
/**
|
||||
* The QDateTime::timeZoneAbbreviation() is a non-static member function but it may still interface with the
|
||||
* system clock. This can result in race conditions when called simultaneously to QDateTime::currentDateTime(),
|
||||
|
||||
@@ -82,6 +82,9 @@ target_sources(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/FocusedMemoryRegion.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/ExcludedMemoryRegion.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemorySnapshot.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotManager.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/MemorySnapshotItem.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/CreateSnapshotWindow/CreateSnapshotWindow.cpp
|
||||
|
||||
# Memory region manager window
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/MemoryRegionManagerWindow.cpp
|
||||
@@ -105,6 +108,8 @@ qt_add_resources(
|
||||
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/UiFiles/TargetMemoryInspectionPane.ui"
|
||||
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/Stylesheets/TargetMemoryInspectionPane.qss"
|
||||
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/UiFiles/HexViewerWidget.ui"
|
||||
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/UiFiles/SnapshotManager.ui"
|
||||
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/CreateSnapshotWindow/UiFiles/CreateSnapshotWindow.ui"
|
||||
"./UserInterfaces/InsightWindow/Images/bloom-icon.svg"
|
||||
"./UserInterfaces/InsightWindow/Images/RAM.svg"
|
||||
"./UserInterfaces/InsightWindow/Images/refresh.svg"
|
||||
@@ -126,6 +131,7 @@ qt_add_resources(
|
||||
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/Images/memory-inspection-icon-disabled.svg"
|
||||
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/Images/manage-focused-regions.svg"
|
||||
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/Images/stale-data-icon.svg"
|
||||
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/Images/manage-snapshots-icon.svg"
|
||||
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/Images/highlight-stack-memory.svg"
|
||||
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/Images/highlight-stack-memory-disabled.svg"
|
||||
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/Images/highlight-focused-memory.svg"
|
||||
@@ -136,6 +142,7 @@ 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"
|
||||
|
||||
# Target package widgets
|
||||
"./UserInterfaces/InsightWindow/Widgets/TargetWidgets/DIP/Stylesheets/DualInlinePackage.qss"
|
||||
|
||||
@@ -201,6 +201,22 @@ Bloom--Widgets--SvgToolButton QMenu::item {
|
||||
}
|
||||
|
||||
Bloom--Widgets--PushButton[styleName="primary"] {
|
||||
background-color: #353C41;
|
||||
border: 1px solid #454C52;
|
||||
background-color: rgba(53, 60, 65, 1);
|
||||
border: 1px solid rgba(69, 76, 82, 1);
|
||||
}
|
||||
|
||||
Bloom--Widgets--PushButton[styleName="primary"]:disabled {
|
||||
background-color: rgba(53, 60, 65, 0.25);
|
||||
border: 1px solid rgba(69, 76, 82, 0.5);
|
||||
|
||||
}
|
||||
|
||||
QCheckBox::indicator:checked {
|
||||
margin-top: 2px;
|
||||
image: url(":/compiled/src/Insight/UserInterfaces/InsightWindow/Images/checked-action-icon.svg");
|
||||
}
|
||||
|
||||
QCheckBox::indicator:unchecked {
|
||||
margin-top: 2px;
|
||||
image: url(":/compiled/src/Insight/UserInterfaces/InsightWindow/Images/unchecked-action-icon.svg");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="15"
|
||||
height="15"
|
||||
viewBox="0 0 3.9687501 3.96875"
|
||||
version="1.1"
|
||||
id="svg974"
|
||||
sodipodi:docname="manage-snapshots-icon.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="39.687885"
|
||||
inkscape:cx="8.6802307"
|
||||
inkscape:cy="7.5337852"
|
||||
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">
|
||||
<path
|
||||
id="rect6137"
|
||||
style="fill:#838382;fill-opacity:1;stroke:none;stroke-width:1.327;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
d="m 1,1 v 13 h 3 v -4 h 7 v 4 h 3 V 1 Z m 2,2 h 9 V 8 H 3 Z"
|
||||
transform="scale(0.26458334)" />
|
||||
<rect
|
||||
style="fill:#838382;fill-opacity:1;stroke:none;stroke-width:0.568665;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
id="rect22557"
|
||||
width="1.3229166"
|
||||
height="0.5291667"
|
||||
x="1.3229166"
|
||||
y="2.9104166" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1,140 @@
|
||||
#include "CreateSnapshotWindow.hpp"
|
||||
|
||||
#include <QFile>
|
||||
#include <QSize>
|
||||
#include <QDesktopServices>
|
||||
|
||||
#include "src/Insight/UserInterfaces/InsightWindow/UiLoader.hpp"
|
||||
|
||||
#include "src/Insight/InsightSignals.hpp"
|
||||
|
||||
#include "src/Helpers/Paths.hpp"
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
namespace Bloom::Widgets
|
||||
{
|
||||
using Bloom::Exceptions::Exception;
|
||||
|
||||
CreateSnapshotWindow::CreateSnapshotWindow(
|
||||
Targets::TargetMemoryType memoryType,
|
||||
const std::optional<Targets::TargetMemoryBuffer>& data,
|
||||
const bool& staleData,
|
||||
QWidget* parent
|
||||
)
|
||||
: QWidget(parent)
|
||||
, data(data)
|
||||
, staleData(staleData)
|
||||
{
|
||||
this->setWindowFlag(Qt::Window);
|
||||
this->setObjectName("create-snapshot-window");
|
||||
this->setWindowTitle(
|
||||
"New Snapshot - " + QString(memoryType == Targets::TargetMemoryType::EEPROM ? "EEPROM" : "RAM")
|
||||
);
|
||||
|
||||
auto windowUiFile = QFile(
|
||||
QString::fromStdString(Paths::compiledResourcesPath()
|
||||
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane"
|
||||
+ "/SnapshotManager/CreateSnapshotWindow/UiFiles/CreateSnapshotWindow.ui"
|
||||
)
|
||||
);
|
||||
|
||||
if (!windowUiFile.open(QFile::ReadOnly)) {
|
||||
throw Exception("Failed to open CreateSnapshotWindow UI file");
|
||||
}
|
||||
|
||||
this->setFixedSize(QSize(500, 300));
|
||||
|
||||
auto uiLoader = UiLoader(this);
|
||||
this->container = uiLoader.load(&windowUiFile, this);
|
||||
|
||||
this->container->setFixedSize(this->size());
|
||||
this->container->setContentsMargins(15, 15, 15, 15);
|
||||
|
||||
auto* formContainer = this->container->findChild<QWidget*>("form-container");
|
||||
|
||||
this->nameInput = formContainer->findChild<TextInput*>("name-input");
|
||||
this->descriptionInput = formContainer->findChild<QPlainTextEdit*>("description-input");
|
||||
this->includeFocusedRegionsInput = formContainer->findChild<QCheckBox*>("include-focus-regions-input");
|
||||
this->captureDirectlyFromTargetInput = formContainer->findChild<QCheckBox*>("capture-directly-from-target-input");
|
||||
|
||||
this->staleDataWarning = formContainer->findChild<QWidget*>("stale-data-warning");
|
||||
|
||||
this->captureButton = this->container->findChild<PushButton*>("capture-btn");
|
||||
this->closeButton = this->container->findChild<PushButton*>("close-btn");
|
||||
|
||||
QObject::connect(this->nameInput, &QLineEdit::textEdited, this, &CreateSnapshotWindow::refreshForm);
|
||||
QObject::connect(
|
||||
this->captureDirectlyFromTargetInput,
|
||||
&QCheckBox::stateChanged,
|
||||
this,
|
||||
&CreateSnapshotWindow::refreshForm
|
||||
);
|
||||
|
||||
QObject::connect(this->captureButton, &QPushButton::clicked, this, &CreateSnapshotWindow::issueCaptureRequest);
|
||||
QObject::connect(this->closeButton, &QPushButton::clicked, this, &QWidget::close);
|
||||
|
||||
auto* insightSignals = InsightSignals::instance();
|
||||
|
||||
QObject::connect(
|
||||
insightSignals,
|
||||
&InsightSignals::targetStateUpdated,
|
||||
this,
|
||||
[this] (Targets::TargetState newState) {
|
||||
this->targetState = newState;
|
||||
this->refreshForm();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void CreateSnapshotWindow::refreshForm() {
|
||||
this->captureButton->setEnabled(this->captureEnabled());
|
||||
this->staleDataWarning->setVisible(this->staleData && !this->captureDirectlyFromTargetInput->isChecked());
|
||||
}
|
||||
|
||||
void CreateSnapshotWindow::showEvent(QShowEvent* event) {
|
||||
this->move(this->parentWidget()->window()->geometry().center() - this->rect().center());
|
||||
this->resetForm();
|
||||
this->refreshForm();
|
||||
QWidget::showEvent(event);
|
||||
}
|
||||
|
||||
bool CreateSnapshotWindow::captureEnabled() {
|
||||
if (this->targetState != Targets::TargetState::STOPPED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this->data.has_value()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this->nameInput->text().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CreateSnapshotWindow::resetForm() {
|
||||
this->nameInput->setText("Untitled Snapshot");
|
||||
this->descriptionInput->setPlainText("");
|
||||
this->includeFocusedRegionsInput->setChecked(true);
|
||||
this->captureDirectlyFromTargetInput->setChecked(false);
|
||||
}
|
||||
|
||||
void CreateSnapshotWindow::issueCaptureRequest() {
|
||||
if (!this->captureEnabled()) {
|
||||
// Sanity check
|
||||
this->refreshForm();
|
||||
return;
|
||||
}
|
||||
|
||||
emit this->snapshotCaptureRequested(
|
||||
this->nameInput->text(),
|
||||
this->descriptionInput->toPlainText(),
|
||||
this->includeFocusedRegionsInput->isChecked(),
|
||||
this->captureDirectlyFromTargetInput->isChecked()
|
||||
);
|
||||
|
||||
this->close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include <QShowEvent>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QCheckBox>
|
||||
#include <optional>
|
||||
|
||||
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TextInput.hpp"
|
||||
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/PushButton.hpp"
|
||||
|
||||
#include "src/Targets/TargetMemory.hpp"
|
||||
#include "src/Targets/TargetState.hpp"
|
||||
|
||||
namespace Bloom::Widgets
|
||||
{
|
||||
class CreateSnapshotWindow: public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit CreateSnapshotWindow(
|
||||
Targets::TargetMemoryType memoryType,
|
||||
const std::optional<Targets::TargetMemoryBuffer>& data,
|
||||
const bool& staleData,
|
||||
QWidget* parent = nullptr
|
||||
);
|
||||
|
||||
void refreshForm();
|
||||
|
||||
signals:
|
||||
void snapshotCaptureRequested(
|
||||
const QString& name,
|
||||
const QString& description,
|
||||
bool captureFocusedRegions,
|
||||
bool refreshBeforeCapture
|
||||
);
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent* event) override;
|
||||
|
||||
private:
|
||||
QWidget* container = nullptr;
|
||||
TextInput* nameInput = nullptr;
|
||||
QPlainTextEdit* descriptionInput = nullptr;
|
||||
|
||||
QCheckBox* includeFocusedRegionsInput = nullptr;
|
||||
QCheckBox* captureDirectlyFromTargetInput = nullptr;
|
||||
|
||||
QWidget* staleDataWarning = nullptr;
|
||||
|
||||
PushButton* captureButton = nullptr;
|
||||
PushButton* closeButton = nullptr;
|
||||
|
||||
const std::optional<Targets::TargetMemoryBuffer>& data;
|
||||
const bool& staleData;
|
||||
Targets::TargetState targetState = Targets::TargetState::UNKNOWN;
|
||||
|
||||
bool captureEnabled();
|
||||
void resetForm();
|
||||
|
||||
void issueCaptureRequest();
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
<?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="form-container">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding"/>
|
||||
</property>
|
||||
<layout class="QVBoxLayout">
|
||||
<property name="spacing">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="name-row">
|
||||
<item alignment="Qt::AlignVCenter">
|
||||
<widget class="Label" name="name-label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed"/>
|
||||
</property>
|
||||
<property name="minimumWidth">
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Name:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item alignment="Qt::AlignVCenter">
|
||||
<widget class="TextInput" name="name-input">
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="description-row">
|
||||
<item alignment="Qt::AlignTop">
|
||||
<widget class="Label" name="register-details-description-label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed"/>
|
||||
</property>
|
||||
<property name="minimumWidth">
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Description:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="description-input">
|
||||
<property name="maximumHeight">
|
||||
<number>100</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="vertical-spacer">
|
||||
<property name="sizeHint">
|
||||
<size>
|
||||
<height>10</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="include-focus-regions-row">
|
||||
<item alignment="Qt::AlignLeft">
|
||||
<widget class="QCheckBox" name="include-focus-regions-input">
|
||||
<property name="text">
|
||||
<string>Include focused regions</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Captures any focused regions currently defined in the memory inspection pane.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontal-spacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="capture-directly-from-target-row">
|
||||
<item alignment="Qt::AlignLeft">
|
||||
<widget class="QCheckBox" name="capture-directly-from-target-input">
|
||||
<property name="text">
|
||||
<string>Capture directly from target</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Captures the memory directly from the target, as opposed to what's currently loaded in the memory inspection pane.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontal-spacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="stale-data-warning">
|
||||
<layout class="QHBoxLayout">
|
||||
<property name="spacing">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<spacer name="horizontal-spacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="SvgWidget" name="icon">
|
||||
<property name="containerHeight">
|
||||
<number>15</number>
|
||||
</property>
|
||||
<property name="containerWidth">
|
||||
<number>14</number>
|
||||
</property>
|
||||
<property name="svgFilePath">
|
||||
<string>:/compiled/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/Images/stale-data-icon.svg</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Label">
|
||||
<property name="text">
|
||||
<string>Captured data may be stale</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Use the 'Capture directly from target' option to avoid capturing stale data.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="vertical-spacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="actions">
|
||||
<property name="minimumHeight">
|
||||
<number>25</number>
|
||||
</property>
|
||||
<property name="maximumHeight">
|
||||
<number>25</number>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed"/>
|
||||
</property>
|
||||
<layout class="QHBoxLayout">
|
||||
<property name="spacing">
|
||||
<number>15</number>
|
||||
</property>
|
||||
<item>
|
||||
<spacer name="horizontal-spacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="PushButton" name="close-btn">
|
||||
<property name="text">
|
||||
<string>Cancel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="PushButton" name="capture-btn">
|
||||
<property name="styleName">
|
||||
<string>primary</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Capture</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</ui>
|
||||
@@ -0,0 +1,93 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 4.2333334 4.2333333"
|
||||
version="1.1"
|
||||
id="svg974"
|
||||
sodipodi:docname="new-manage-snapshot-icon.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="29.367338"
|
||||
inkscape:cx="1.8217518"
|
||||
inkscape:cy="4.7672009"
|
||||
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"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="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"
|
||||
transform="translate(-0.26458334,-0.26458334)">
|
||||
<path
|
||||
id="rect6137"
|
||||
style="fill:#838382;fill-opacity:0.5;stroke:none;stroke-width:1.327;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
d="m 1,1 v 13 h 3 v -4 h 7 v 4 h 3 V 1 Z m 2,2 h 9 V 8 H 3 Z"
|
||||
transform="scale(0.26458334)" />
|
||||
<rect
|
||||
style="fill:#838382;fill-opacity:0.5;stroke:none;stroke-width:0.568665;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
id="rect22557"
|
||||
width="1.3229166"
|
||||
height="0.5291667"
|
||||
x="1.3229166"
|
||||
y="2.9104166" />
|
||||
<rect
|
||||
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0520833"
|
||||
id="rect1537-3-6-3-7"
|
||||
width="0.5291667"
|
||||
height="2.1166666"
|
||||
x="3.175"
|
||||
y="2.3812501" />
|
||||
<rect
|
||||
style="display:inline;fill:#838382;fill-opacity:1;stroke-width:0.0823502"
|
||||
id="rect1537-3-6-3-6-5"
|
||||
width="2.1166666"
|
||||
height="0.5291667"
|
||||
x="2.3812501"
|
||||
y="3.1750002" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
@@ -0,0 +1,69 @@
|
||||
#include "MemorySnapshotItem.hpp"
|
||||
|
||||
#include "src/Helpers/DateTime.hpp"
|
||||
|
||||
namespace Bloom::Widgets
|
||||
{
|
||||
MemorySnapshotItem::MemorySnapshotItem(
|
||||
const MemorySnapshot& memorySnapshot,
|
||||
QWidget *parent
|
||||
)
|
||||
: memorySnapshot(memorySnapshot)
|
||||
, ClickableWidget(parent)
|
||||
{
|
||||
this->setObjectName("snapshot-item");
|
||||
this->setFixedHeight(50);
|
||||
this->layout->setContentsMargins(5, 5, 5, 0);
|
||||
|
||||
this->nameLabel->setText(memorySnapshot.name);
|
||||
this->nameLabel->setObjectName("name-label");
|
||||
|
||||
this->programCounterLabel->setText("0x" + QString::number(this->memorySnapshot.programCounter, 16).toUpper());
|
||||
this->programCounterLabel->setObjectName("program-counter-label");
|
||||
|
||||
this->createdDateLabel->setText(
|
||||
memorySnapshot.createdDate.toString(
|
||||
memorySnapshot.createdDate.date() == DateTime::currentDate()
|
||||
? "hh:mm"
|
||||
: "dd/MM/yyyy hh:mm"
|
||||
)
|
||||
);
|
||||
this->createdDateLabel->setObjectName("created-date-label");
|
||||
|
||||
auto* topLabelLayout = new QHBoxLayout();
|
||||
topLabelLayout->setSpacing(0);
|
||||
topLabelLayout->setContentsMargins(0, 0, 0, 0);
|
||||
topLabelLayout->addWidget(this->nameLabel, 0, Qt::AlignmentFlag::AlignLeft);
|
||||
topLabelLayout->addStretch(1);
|
||||
topLabelLayout->addWidget(this->programCounterLabel, 0, Qt::AlignmentFlag::AlignRight);
|
||||
|
||||
auto* bottomLabelLayout = new QHBoxLayout();
|
||||
bottomLabelLayout->setSpacing(0);
|
||||
bottomLabelLayout->setContentsMargins(0, 0, 0, 0);
|
||||
bottomLabelLayout->addWidget(this->createdDateLabel, 0, Qt::AlignmentFlag::AlignLeft);
|
||||
|
||||
this->layout->setSpacing(5);
|
||||
this->layout->addLayout(topLabelLayout);
|
||||
this->layout->addLayout(bottomLabelLayout);
|
||||
this->layout->addStretch(1);
|
||||
|
||||
auto onClick = [this] {
|
||||
this->setSelected(true);
|
||||
};
|
||||
|
||||
QObject::connect(this, &ClickableWidget::clicked, this, onClick);
|
||||
QObject::connect(this, &ClickableWidget::rightClicked, this, onClick);
|
||||
|
||||
this->setSelected(false);
|
||||
}
|
||||
|
||||
void MemorySnapshotItem::setSelected(bool selected) {
|
||||
this->setProperty("selected", selected);
|
||||
this->style()->unpolish(this);
|
||||
this->style()->polish(this);
|
||||
|
||||
if (selected) {
|
||||
emit this->selected(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QComboBox>
|
||||
#include <map>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/ClickableWidget.hpp"
|
||||
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/Label.hpp"
|
||||
|
||||
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemorySnapshot.hpp"
|
||||
#include "src/Targets/TargetMemory.hpp"
|
||||
|
||||
namespace Bloom::Widgets
|
||||
{
|
||||
class MemorySnapshotItem: public ClickableWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
const MemorySnapshot& memorySnapshot;
|
||||
|
||||
MemorySnapshotItem(
|
||||
const MemorySnapshot& memorySnapshot,
|
||||
QWidget *parent
|
||||
);
|
||||
|
||||
void setSelected(bool selected);
|
||||
|
||||
signals:
|
||||
void selected(MemorySnapshotItem*);
|
||||
|
||||
private:
|
||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||
Label* nameLabel = new Label(this);
|
||||
Label* programCounterLabel = new Label(this);
|
||||
Label* createdDateLabel = new Label(this);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
#include "SnapshotManager.hpp"
|
||||
|
||||
#include <QDesktopServices>
|
||||
|
||||
#include "src/Insight/UserInterfaces/InsightWindow/UiLoader.hpp"
|
||||
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/ErrorDialogue/ErrorDialogue.hpp"
|
||||
|
||||
#include "src/Insight/InsightWorker/Tasks/RetrieveMemorySnapshots.hpp"
|
||||
#include "src/Insight/InsightWorker/Tasks/CaptureMemorySnapshot.hpp"
|
||||
#include "src/Insight/InsightWorker/InsightWorker.hpp"
|
||||
|
||||
#include "src/Helpers/Paths.hpp"
|
||||
#include "src/Helpers/EnumToStringMappings.hpp"
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
#include "src/Logger/Logger.hpp"
|
||||
|
||||
namespace Bloom::Widgets
|
||||
{
|
||||
using Bloom::Exceptions::Exception;
|
||||
|
||||
SnapshotManager::SnapshotManager(
|
||||
const Targets::TargetMemoryDescriptor& memoryDescriptor,
|
||||
const std::optional<Targets::TargetMemoryBuffer>& data,
|
||||
const bool& staleData,
|
||||
PaneState& state,
|
||||
PanelWidget* parent
|
||||
)
|
||||
: PaneWidget(state, parent)
|
||||
, memoryDescriptor(memoryDescriptor)
|
||||
, data(data)
|
||||
, staleData(staleData)
|
||||
{
|
||||
this->setObjectName("snapshot-manager");
|
||||
this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
|
||||
|
||||
auto widgetUiFile = QFile(
|
||||
QString::fromStdString(Paths::compiledResourcesPath()
|
||||
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane"
|
||||
+ "/SnapshotManager/UiFiles/SnapshotManager.ui"
|
||||
)
|
||||
);
|
||||
|
||||
if (!widgetUiFile.open(QFile::ReadOnly)) {
|
||||
throw Exception("Failed to open SnapshotManager UI file");
|
||||
}
|
||||
|
||||
auto uiLoader = UiLoader(this);
|
||||
this->container = uiLoader.load(&widgetUiFile, this);
|
||||
|
||||
this->container->setFixedSize(this->size());
|
||||
this->container->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
this->toolBar = this->container->findChild<QWidget*>("tool-bar");
|
||||
this->createSnapshotButton = this->toolBar->findChild<SvgToolButton*>("create-snapshot-btn");
|
||||
this->deleteSnapshotButton = this->toolBar->findChild<SvgToolButton*>("delete-snapshot-btn");
|
||||
|
||||
this->itemScrollArea = this->container->findChild<QScrollArea*>("snapshot-item-scroll-area");
|
||||
this->itemScrollAreaViewport = this->itemScrollArea->findChild<QWidget*>("item-container");
|
||||
this->itemLayout = this->itemScrollAreaViewport->findChild<QVBoxLayout*>(
|
||||
"item-container-layout"
|
||||
);
|
||||
|
||||
this->itemScrollArea->setContentsMargins(0, 0, 0, 0);
|
||||
this->itemScrollAreaViewport->setContentsMargins(0, 0, 0, 0);
|
||||
this->itemLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
this->createSnapshotWindow = new CreateSnapshotWindow(
|
||||
this->memoryDescriptor.type,
|
||||
this->data,
|
||||
this->staleData,
|
||||
this
|
||||
);
|
||||
|
||||
QObject::connect(
|
||||
this->createSnapshotWindow,
|
||||
&CreateSnapshotWindow::snapshotCaptureRequested,
|
||||
this,
|
||||
&SnapshotManager::createSnapshot
|
||||
);
|
||||
|
||||
QObject::connect(
|
||||
this->createSnapshotButton,
|
||||
&QToolButton::clicked,
|
||||
this,
|
||||
[this] {
|
||||
if (!this->createSnapshotWindow->isVisible()) {
|
||||
this->createSnapshotWindow->show();
|
||||
return;
|
||||
}
|
||||
|
||||
this->createSnapshotWindow->activateWindow();
|
||||
}
|
||||
);
|
||||
|
||||
auto* retrieveSnapshotsTask = new RetrieveMemorySnapshots(this->memoryDescriptor.type);
|
||||
|
||||
QObject::connect(
|
||||
retrieveSnapshotsTask,
|
||||
&RetrieveMemorySnapshots::memorySnapshotsRetrieved,
|
||||
this,
|
||||
[this] (std::vector<MemorySnapshot> snapshots) {
|
||||
for (auto& snapshot : snapshots) {
|
||||
if (!snapshot.isCompatible(this->memoryDescriptor)) {
|
||||
Logger::warning(
|
||||
"Ignoring snapshot " + snapshot.id.toStdString()
|
||||
+ " - snapshot incompatible with current memory descriptor"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
this->addSnapshot(std::move(snapshot));
|
||||
}
|
||||
this->sortSnapshotItems();
|
||||
}
|
||||
);
|
||||
|
||||
InsightWorker::queueTask(retrieveSnapshotsTask);
|
||||
|
||||
this->show();
|
||||
}
|
||||
|
||||
void SnapshotManager::resizeEvent(QResizeEvent* event) {
|
||||
const auto size = this->size();
|
||||
this->container->setFixedSize(size.width(), size.height());
|
||||
|
||||
PaneWidget::resizeEvent(event);
|
||||
}
|
||||
|
||||
void SnapshotManager::showEvent(QShowEvent* event) {
|
||||
PaneWidget::showEvent(event);
|
||||
}
|
||||
|
||||
void SnapshotManager::createSnapshot(
|
||||
const QString& name,
|
||||
const QString& description,
|
||||
bool captureFocusedRegions,
|
||||
bool captureDirectlyFromTarget
|
||||
) {
|
||||
auto* captureTask = new CaptureMemorySnapshot(
|
||||
std::move(name),
|
||||
std::move(description),
|
||||
this->memoryDescriptor.type,
|
||||
{},
|
||||
{},
|
||||
captureDirectlyFromTarget ? std::nullopt : this->data
|
||||
);
|
||||
|
||||
QObject::connect(
|
||||
captureTask,
|
||||
&CaptureMemorySnapshot::memorySnapshotCaptured,
|
||||
this,
|
||||
[this] (MemorySnapshot snapshot) {
|
||||
this->addSnapshot(std::move(snapshot));
|
||||
this->sortSnapshotItems();
|
||||
}
|
||||
);
|
||||
|
||||
InsightWorker::queueTask(captureTask);
|
||||
}
|
||||
|
||||
void SnapshotManager::addSnapshot(MemorySnapshot&& snapshotTmp) {
|
||||
const auto snapshotIt = this->snapshotsById.insert(std::pair(snapshotTmp.id, std::move(snapshotTmp)));
|
||||
const auto& snapshot = snapshotIt.first->second;
|
||||
|
||||
auto* snapshotItem = new MemorySnapshotItem(snapshot, this);
|
||||
this->itemLayout->addWidget(snapshotItem);
|
||||
|
||||
QObject::connect(
|
||||
snapshotItem,
|
||||
&MemorySnapshotItem::selected,
|
||||
this,
|
||||
&SnapshotManager::onSnapshotItemSelected
|
||||
);
|
||||
}
|
||||
|
||||
void SnapshotManager::sortSnapshotItems() {
|
||||
const auto snapshotItemCompare = [] (MemorySnapshotItem* itemA, MemorySnapshotItem* itemB) {
|
||||
return itemA->memorySnapshot.createdDate > itemB->memorySnapshot.createdDate;
|
||||
};
|
||||
|
||||
auto sortedSnapshotItems = std::set<MemorySnapshotItem*, decltype(snapshotItemCompare)>(snapshotItemCompare);
|
||||
|
||||
QLayoutItem* layoutItem = nullptr;
|
||||
while ((layoutItem = this->itemLayout->takeAt(0)) != nullptr) {
|
||||
auto* snapshotItem = qobject_cast<MemorySnapshotItem*>(layoutItem->widget());
|
||||
if (snapshotItem != nullptr) {
|
||||
sortedSnapshotItems.insert(snapshotItem);
|
||||
}
|
||||
|
||||
delete layoutItem;
|
||||
}
|
||||
|
||||
for (auto* regionItem : sortedSnapshotItems) {
|
||||
this->itemLayout->addWidget(regionItem);
|
||||
}
|
||||
}
|
||||
|
||||
void SnapshotManager::onSnapshotItemSelected(MemorySnapshotItem* item) {
|
||||
if (this->selectedItem != nullptr && this->selectedItem != item) {
|
||||
this->selectedItem->setSelected(false);
|
||||
}
|
||||
|
||||
this->selectedItem = item;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include <optional>
|
||||
#include <QResizeEvent>
|
||||
#include <QShowEvent>
|
||||
#include <QScrollArea>
|
||||
#include <QVBoxLayout>
|
||||
#include <map>
|
||||
|
||||
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/PaneWidget.hpp"
|
||||
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/SvgToolButton.hpp"
|
||||
|
||||
#include "src/Targets/TargetMemory.hpp"
|
||||
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemorySnapshot.hpp"
|
||||
|
||||
#include "./CreateSnapshotWindow/CreateSnapshotWindow.hpp"
|
||||
#include "MemorySnapshotItem.hpp"
|
||||
|
||||
namespace Bloom::Widgets
|
||||
{
|
||||
class SnapshotManager: public PaneWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
CreateSnapshotWindow* createSnapshotWindow = nullptr;
|
||||
MemorySnapshotItem* selectedItem = nullptr;
|
||||
|
||||
explicit SnapshotManager(
|
||||
const Targets::TargetMemoryDescriptor& memoryDescriptor,
|
||||
const std::optional<Targets::TargetMemoryBuffer>& data,
|
||||
const bool& staleData,
|
||||
PaneState& state,
|
||||
PanelWidget* parent = nullptr
|
||||
);
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent* event) override;
|
||||
void showEvent(QShowEvent* event) override;
|
||||
|
||||
private:
|
||||
const Targets::TargetMemoryDescriptor& memoryDescriptor;
|
||||
const std::optional<Targets::TargetMemoryBuffer>& data;
|
||||
const bool& staleData;
|
||||
|
||||
std::map<QString, MemorySnapshot> snapshotsById;
|
||||
|
||||
QWidget* container = nullptr;
|
||||
QWidget* toolBar = nullptr;
|
||||
|
||||
SvgToolButton* createSnapshotButton = nullptr;
|
||||
SvgToolButton* deleteSnapshotButton = nullptr;
|
||||
|
||||
QScrollArea* itemScrollArea = nullptr;
|
||||
QWidget* itemScrollAreaViewport = nullptr;
|
||||
QVBoxLayout* itemLayout = nullptr;
|
||||
|
||||
void createSnapshot(
|
||||
const QString& name,
|
||||
const QString& description,
|
||||
bool captureFocusedRegions,
|
||||
bool captureDirectlyFromTarget
|
||||
);
|
||||
void addSnapshot(MemorySnapshot&& snapshotTmp);
|
||||
void sortSnapshotItems();
|
||||
void onSnapshotItemSelected(MemorySnapshotItem* item);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<widget class="QWidget" name="container">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding"/>
|
||||
</property>
|
||||
<layout class="QVBoxLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QWidget" name="title-bar">
|
||||
<property name="minimumHeight">
|
||||
<number>28</number>
|
||||
</property>
|
||||
<property name="maximumHeight">
|
||||
<number>28</number>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" 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>5</width>
|
||||
</size>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Label" name="snapshot-title-label">
|
||||
<property name="text">
|
||||
<string>Memory Snapshots</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item alignment="Qt::AlignLeft">
|
||||
<spacer name="horizontal-spacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="tool-bar">
|
||||
<property name="minimumHeight">
|
||||
<number>28</number>
|
||||
</property>
|
||||
<property name="maximumHeight">
|
||||
<number>28</number>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" 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>5</width>
|
||||
</size>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="SvgToolButton" name="create-snapshot-btn">
|
||||
<property name="checkable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="svgFilePath">
|
||||
<string>:/compiled/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/Images/new-snapshot-icon.svg</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Create Snapshot</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item alignment="Qt::AlignLeft">
|
||||
<spacer name="horizontal-spacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="snapshot-item-scroll-area">
|
||||
<property name="widgetResizable"><bool>true</bool></property>
|
||||
<property name="verticalScrollBarPolicy"><enum>Qt::ScrollBarAsNeeded</enum></property>
|
||||
<property name="sizeAdjustPolicy"><enum>QAbstractScrollArea::AdjustToContents</enum></property>
|
||||
<property name="horizontalScrollBarPolicy"><enum>Qt::ScrollBarAlwaysOff</enum></property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"/>
|
||||
</property>
|
||||
<widget class="QWidget" name="item-container">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"/>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="item-container-layout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetMinAndMaxSize</enum>
|
||||
</property>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
</layout>
|
||||
</widget>
|
||||
</ui>
|
||||
@@ -208,3 +208,83 @@
|
||||
padding-left: 10px;
|
||||
min-width: 150px;
|
||||
}
|
||||
|
||||
/* Snapshot Manager */
|
||||
#target-memory-inspection-pane #rh-side-bar #manage-memory-snapshots-btn {
|
||||
border-bottom: 1px solid #41423f;
|
||||
qproperty-buttonWidth: 31;
|
||||
qproperty-buttonHeight: 28;
|
||||
}
|
||||
|
||||
#target-memory-inspection-pane #rh-side-bar #manage-memory-snapshots-btn:hover {
|
||||
background-color: #2F2F2D;
|
||||
}
|
||||
|
||||
#target-memory-inspection-pane #rh-side-bar #manage-memory-snapshots-btn:checked {
|
||||
background-color: #2F2F2D;
|
||||
}
|
||||
|
||||
#snapshot-manager #container {
|
||||
background-color: #373835;
|
||||
border-left: 1px solid #41423f;
|
||||
}
|
||||
|
||||
#snapshot-manager #title-bar {
|
||||
background-color: transparent;
|
||||
border-bottom: 1px solid #41423f;
|
||||
}
|
||||
|
||||
#snapshot-manager #title-bar #snapshot-title-label {
|
||||
color: #8a8a8d;
|
||||
}
|
||||
|
||||
#snapshot-manager #tool-bar {
|
||||
border-bottom: 1px solid #41423f;
|
||||
}
|
||||
|
||||
#snapshot-manager #tool-bar QToolButton {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
padding: 0;
|
||||
qproperty-buttonWidth: 20;
|
||||
qproperty-buttonHeight: 18;
|
||||
}
|
||||
|
||||
#snapshot-manager #snapshot-item-scroll-area {
|
||||
border: none;
|
||||
}
|
||||
|
||||
#snapshot-manager #snapshot-item {
|
||||
border: none;
|
||||
border-bottom: 1px solid #41423f;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
#snapshot-manager #snapshot-item:hover {
|
||||
border-left: none;
|
||||
background-color: rgba(142, 139, 131, 0.1);
|
||||
}
|
||||
|
||||
#snapshot-manager #snapshot-item[selected=true] {
|
||||
background-color: #355A80;
|
||||
}
|
||||
|
||||
#snapshot-manager #snapshot-item #name-label {
|
||||
font-size: 14px
|
||||
}
|
||||
|
||||
#snapshot-manager #snapshot-item #program-counter-label,
|
||||
#snapshot-manager #snapshot-item #created-date-label {
|
||||
font-size: 13px;
|
||||
color: #8a8a8d;
|
||||
}
|
||||
|
||||
/* Create Snapshot Window */
|
||||
#create-snapshot-window #stale-data-warning QLabel {
|
||||
margin-top: 1px;
|
||||
color: #9a9a9d;
|
||||
}
|
||||
|
||||
#create-snapshot-window #stale-data-warning #icon {
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "src/Helpers/Paths.hpp"
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
#include "src/Logger/Logger.hpp"
|
||||
|
||||
namespace Bloom::Widgets
|
||||
{
|
||||
@@ -78,8 +79,9 @@ namespace Bloom::Widgets
|
||||
// Quick sanity check to ensure the validity of persisted settings.
|
||||
this->sanitiseSettings();
|
||||
|
||||
auto* containerLayout = this->container->findChild<QHBoxLayout*>("container-sub-layout");
|
||||
this->subContainerLayout = this->container->findChild<QHBoxLayout*>("container-sub-layout");
|
||||
this->manageMemoryRegionsButton = this->container->findChild<SvgToolButton*>("manage-memory-regions-btn");
|
||||
this->manageMemorySnapshotsButton = this->container->findChild<SvgToolButton*>("manage-memory-snapshots-btn");
|
||||
|
||||
this->refreshButton = this->container->findChild<SvgToolButton*>("refresh-memory-btn");
|
||||
this->refreshOnTargetStopAction = this->refreshButton->findChild<QAction*>("refresh-target-stopped");
|
||||
@@ -102,7 +104,7 @@ namespace Bloom::Widgets
|
||||
);
|
||||
this->hexViewerWidget->setDisabled(true);
|
||||
|
||||
containerLayout->insertWidget(1, this->hexViewerWidget);
|
||||
this->subContainerLayout->insertWidget(1, this->hexViewerWidget);
|
||||
|
||||
QObject::connect(
|
||||
this->hexViewerWidget,
|
||||
@@ -117,6 +119,21 @@ namespace Bloom::Widgets
|
||||
|
||||
this->hexViewerWidget->init();
|
||||
|
||||
this->rightPanel = new PanelWidget(PanelWidgetType::RIGHT, this->settings.rightPanelState, this);
|
||||
this->rightPanel->setObjectName("right-panel");
|
||||
this->rightPanel->setMinimumResize(200);
|
||||
this->rightPanel->setHandleSize(5);
|
||||
this->subContainerLayout->insertWidget(2, this->rightPanel);
|
||||
|
||||
this->snapshotManager = new SnapshotManager(
|
||||
this->targetMemoryDescriptor,
|
||||
this->data,
|
||||
this->staleData,
|
||||
this->settings.snapshotManagerState,
|
||||
this->rightPanel
|
||||
);
|
||||
this->rightPanel->layout()->addWidget(this->snapshotManager);
|
||||
|
||||
this->setRefreshOnTargetStopEnabled(this->settings.refreshOnTargetStop);
|
||||
this->setRefreshOnActivationEnabled(this->settings.refreshOnActivation);
|
||||
|
||||
@@ -155,6 +172,31 @@ namespace Bloom::Widgets
|
||||
&TargetMemoryInspectionPane::openMemoryRegionManagerWindow
|
||||
);
|
||||
|
||||
QObject::connect(
|
||||
this->manageMemorySnapshotsButton,
|
||||
&QToolButton::clicked,
|
||||
this,
|
||||
&TargetMemoryInspectionPane::toggleMemorySnapshotManagerPane
|
||||
);
|
||||
|
||||
QObject::connect(
|
||||
this->snapshotManager,
|
||||
&PaneWidget::paneActivated,
|
||||
this,
|
||||
[this] {
|
||||
this->manageMemorySnapshotsButton->setChecked(true);
|
||||
}
|
||||
);
|
||||
|
||||
QObject::connect(
|
||||
this->snapshotManager,
|
||||
&PaneWidget::paneDeactivated,
|
||||
this,
|
||||
[this] {
|
||||
this->manageMemorySnapshotsButton->setChecked(false);
|
||||
}
|
||||
);
|
||||
|
||||
QObject::connect(
|
||||
this->refreshButton,
|
||||
&QToolButton::clicked,
|
||||
@@ -247,6 +289,8 @@ namespace Bloom::Widgets
|
||||
} else {
|
||||
this->deactivate();
|
||||
}
|
||||
|
||||
this->snapshotManager->deactivate();
|
||||
}
|
||||
|
||||
void TargetMemoryInspectionPane::refreshMemoryValues(std::optional<std::function<void(void)>> callback) {
|
||||
@@ -362,6 +406,8 @@ namespace Bloom::Widgets
|
||||
const auto size = this->size();
|
||||
this->container->setFixedSize(size.width(), size.height());
|
||||
|
||||
this->rightPanel->setMaximumResize(static_cast<int>(size.width() * 0.4));
|
||||
|
||||
PaneWidget::resizeEvent(event);
|
||||
}
|
||||
|
||||
@@ -486,6 +532,8 @@ namespace Bloom::Widgets
|
||||
this->data = data;
|
||||
this->hexViewerWidget->updateValues(this->data.value());
|
||||
this->setStaleData(false);
|
||||
|
||||
this->snapshotManager->createSnapshotWindow->refreshForm();
|
||||
}
|
||||
|
||||
void TargetMemoryInspectionPane::openMemoryRegionManagerWindow() {
|
||||
@@ -515,6 +563,15 @@ namespace Bloom::Widgets
|
||||
}
|
||||
}
|
||||
|
||||
void TargetMemoryInspectionPane::toggleMemorySnapshotManagerPane() {
|
||||
if (!this->snapshotManager->state.activated) {
|
||||
this->snapshotManager->activate();
|
||||
return;
|
||||
}
|
||||
|
||||
this->snapshotManager->deactivate();
|
||||
}
|
||||
|
||||
void TargetMemoryInspectionPane::onMemoryRegionsChange() {
|
||||
this->hexViewerWidget->refreshRegions();
|
||||
}
|
||||
@@ -546,6 +603,7 @@ namespace Bloom::Widgets
|
||||
) {
|
||||
if (memoryType == this->targetMemoryDescriptor.type && this->data.has_value()) {
|
||||
this->setStaleData(true);
|
||||
this->snapshotManager->createSnapshotWindow->refreshForm();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include <QResizeEvent>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
#include <QResizeEvent>
|
||||
#include <QHBoxLayout>
|
||||
|
||||
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/PaneWidget.hpp"
|
||||
|
||||
@@ -15,6 +17,7 @@
|
||||
|
||||
#include "HexViewerWidget/HexViewerWidget.hpp"
|
||||
#include "MemoryRegionManager/MemoryRegionManagerWindow.hpp"
|
||||
#include "SnapshotManager/SnapshotManager.hpp"
|
||||
|
||||
#include "TargetMemoryInspectionPaneSettings.hpp"
|
||||
|
||||
@@ -50,9 +53,11 @@ namespace Bloom::Widgets
|
||||
std::optional<Targets::TargetMemoryBuffer> data;
|
||||
|
||||
QWidget* container = nullptr;
|
||||
QHBoxLayout* subContainerLayout = nullptr;
|
||||
|
||||
QWidget* titleBar = nullptr;
|
||||
SvgToolButton* manageMemoryRegionsButton = nullptr;
|
||||
SvgToolButton* manageMemorySnapshotsButton = nullptr;
|
||||
|
||||
SvgToolButton* refreshButton = nullptr;
|
||||
QAction* refreshOnTargetStopAction = nullptr;
|
||||
@@ -62,6 +67,9 @@ namespace Bloom::Widgets
|
||||
SvgToolButton* attachPaneButton = nullptr;
|
||||
HexViewerWidget* hexViewerWidget = nullptr;
|
||||
|
||||
PanelWidget* rightPanel = nullptr;
|
||||
SnapshotManager* snapshotManager = nullptr;
|
||||
|
||||
QWidget* staleDataLabelContainer = nullptr;
|
||||
|
||||
Targets::TargetState targetState = Targets::TargetState::UNKNOWN;
|
||||
@@ -76,6 +84,7 @@ namespace Bloom::Widgets
|
||||
void setRefreshOnActivationEnabled(bool enabled);
|
||||
void onMemoryRead(const Targets::TargetMemoryBuffer& data);
|
||||
void openMemoryRegionManagerWindow();
|
||||
void toggleMemorySnapshotManagerPane();
|
||||
void onMemoryRegionsChange();
|
||||
void onTargetReset();
|
||||
void onProgrammingModeEnabled();
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
|
||||
#include "HexViewerWidget/HexViewerWidgetSettings.hpp"
|
||||
|
||||
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/PanelState.hpp"
|
||||
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/PaneState.hpp"
|
||||
|
||||
namespace Bloom::Widgets
|
||||
{
|
||||
struct TargetMemoryInspectionPaneSettings
|
||||
@@ -18,5 +21,8 @@ namespace Bloom::Widgets
|
||||
|
||||
std::vector<FocusedMemoryRegion> focusedMemoryRegions;
|
||||
std::vector<ExcludedMemoryRegion> excludedMemoryRegions;
|
||||
|
||||
PanelState rightPanelState = PanelState(300, true);
|
||||
PaneState snapshotManagerState = PaneState(true, true, std::nullopt);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -236,16 +236,20 @@
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<spacer name="vertical-spacer">
|
||||
<property name="sizeHint">
|
||||
<size>
|
||||
<height>3</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<!-- Button items here -->
|
||||
<item alignment="Qt::AlignRight">
|
||||
<widget class="SvgToolButton" name="manage-memory-snapshots-btn">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="svgFilePath">
|
||||
<string>:/compiled/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/Images/manage-snapshots-icon.svg</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Manage Memory Snapshots</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="vertical-spacer">
|
||||
<property name="orientation">
|
||||
|
||||
Reference in New Issue
Block a user