SnapshotViewer window
This commit is contained in:
@@ -106,6 +106,7 @@ target_sources(
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/MemorySnapshotItem.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/MemorySnapshotItem.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/CreateSnapshotWindow/CreateSnapshotWindow.cpp
|
${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/SnapshotViewer.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotViewer/MemoryRegionItem.cpp
|
||||||
|
|
||||||
# Memory region manager window
|
# Memory region manager window
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/MemoryRegionManagerWindow.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/MemoryRegionManagerWindow.cpp
|
||||||
@@ -188,6 +189,8 @@ qt_add_resources(
|
|||||||
# Memory snapshots
|
# Memory snapshots
|
||||||
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/UiFiles/SnapshotManager.ui"
|
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/UiFiles/SnapshotManager.ui"
|
||||||
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/CreateSnapshotWindow/UiFiles/CreateSnapshotWindow.ui"
|
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/CreateSnapshotWindow/UiFiles/CreateSnapshotWindow.ui"
|
||||||
|
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotViewer/UiFiles/SnapshotViewer.ui"
|
||||||
|
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/SnapshotManager/SnapshotViewer/Stylesheets/SnapshotViewer.qss"
|
||||||
|
|
||||||
# Memory region manager window
|
# Memory region manager window
|
||||||
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/UiFiles/MemoryRegionManagerWindow.ui"
|
"./UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/UiFiles/MemoryRegionManagerWindow.ui"
|
||||||
|
|||||||
@@ -100,6 +100,18 @@ namespace Bloom::Widgets
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
QObject::connect(
|
||||||
|
this->snapshotListScene,
|
||||||
|
&ListScene::itemDoubleClicked,
|
||||||
|
this,
|
||||||
|
[this] (ListItem* item) {
|
||||||
|
auto* snapshotItem = dynamic_cast<MemorySnapshotItem*>(item);
|
||||||
|
|
||||||
|
if (snapshotItem != nullptr) {
|
||||||
|
this->onSnapshotItemDoubleClick(snapshotItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
QObject::connect(
|
QObject::connect(
|
||||||
this->snapshotListScene,
|
this->snapshotListScene,
|
||||||
@@ -108,6 +120,17 @@ namespace Bloom::Widgets
|
|||||||
&SnapshotManager::onSnapshotItemContextMenu
|
&SnapshotManager::onSnapshotItemContextMenu
|
||||||
);
|
);
|
||||||
|
|
||||||
|
QObject::connect(
|
||||||
|
this->openSnapshotViewerAction,
|
||||||
|
&QAction::triggered,
|
||||||
|
this,
|
||||||
|
[this] {
|
||||||
|
if (this->contextMenuSnapshotItem != nullptr) {
|
||||||
|
this->openSnapshotViewer(this->contextMenuSnapshotItem->memorySnapshot.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
QObject::connect(
|
QObject::connect(
|
||||||
this->restoreSnapshotAction,
|
this->restoreSnapshotAction,
|
||||||
&QAction::triggered,
|
&QAction::triggered,
|
||||||
@@ -223,6 +246,25 @@ namespace Bloom::Widgets
|
|||||||
this->selectedItem = item;
|
this->selectedItem = item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SnapshotManager::openSnapshotViewer(const QString& snapshotId) {
|
||||||
|
auto snapshotViewerIt = this->snapshotViewersById.find(snapshotId);
|
||||||
|
|
||||||
|
if (snapshotViewerIt == this->snapshotViewersById.end()) {
|
||||||
|
const auto& snapshotIt = this->snapshotsById.find(snapshotId);
|
||||||
|
|
||||||
|
assert(snapshotIt != this->snapshotsById.end());
|
||||||
|
|
||||||
|
snapshotViewerIt = this->snapshotViewersById.insert(
|
||||||
|
snapshotId,
|
||||||
|
new SnapshotViewer(snapshotIt.value(), this->memoryDescriptor, this)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* snapshotViewer = snapshotViewerIt.value();
|
||||||
|
snapshotViewer->show();
|
||||||
|
snapshotViewer->activateWindow();
|
||||||
|
}
|
||||||
|
|
||||||
void SnapshotManager::restoreSnapshot(const QString& snapshotId, bool confirmationPromptEnabled) {
|
void SnapshotManager::restoreSnapshot(const QString& snapshotId, bool confirmationPromptEnabled) {
|
||||||
const auto& snapshotIt = this->snapshotsById.find(snapshotId);
|
const auto& snapshotIt = this->snapshotsById.find(snapshotId);
|
||||||
assert(snapshotIt != this->snapshotsById.end());
|
assert(snapshotIt != this->snapshotsById.end());
|
||||||
@@ -316,6 +358,10 @@ namespace Bloom::Widgets
|
|||||||
InsightWorker::queueTask(writeMemoryTask);
|
InsightWorker::queueTask(writeMemoryTask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SnapshotManager::onSnapshotItemDoubleClick(MemorySnapshotItem* item) {
|
||||||
|
this->openSnapshotViewer(item->memorySnapshot.id);
|
||||||
|
}
|
||||||
|
|
||||||
void SnapshotManager::onSnapshotItemContextMenu(ListItem *item, QPoint sourcePosition) {
|
void SnapshotManager::onSnapshotItemContextMenu(ListItem *item, QPoint sourcePosition) {
|
||||||
auto* snapshotItem = dynamic_cast<MemorySnapshotItem*>(item);
|
auto* snapshotItem = dynamic_cast<MemorySnapshotItem*>(item);
|
||||||
|
|
||||||
@@ -326,6 +372,7 @@ namespace Bloom::Widgets
|
|||||||
this->contextMenuSnapshotItem = snapshotItem;
|
this->contextMenuSnapshotItem = snapshotItem;
|
||||||
|
|
||||||
auto* menu = new QMenu(this);
|
auto* menu = new QMenu(this);
|
||||||
|
menu->addAction(this->openSnapshotViewerAction);
|
||||||
menu->addAction(this->deleteSnapshotAction);
|
menu->addAction(this->deleteSnapshotAction);
|
||||||
|
|
||||||
menu->addSeparator();
|
menu->addSeparator();
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include "./CreateSnapshotWindow/CreateSnapshotWindow.hpp"
|
#include "./CreateSnapshotWindow/CreateSnapshotWindow.hpp"
|
||||||
#include "MemorySnapshotItem.hpp"
|
#include "MemorySnapshotItem.hpp"
|
||||||
|
#include "SnapshotViewer/SnapshotViewer.hpp"
|
||||||
|
|
||||||
namespace Bloom::Widgets
|
namespace Bloom::Widgets
|
||||||
{
|
{
|
||||||
@@ -63,6 +64,7 @@ namespace Bloom::Widgets
|
|||||||
|
|
||||||
QMap<QString, MemorySnapshot> snapshotsById;
|
QMap<QString, MemorySnapshot> snapshotsById;
|
||||||
QMap<QString, MemorySnapshotItem*> snapshotItemsById;
|
QMap<QString, MemorySnapshotItem*> snapshotItemsById;
|
||||||
|
QMap<QString, SnapshotViewer*> snapshotViewersById;
|
||||||
|
|
||||||
QWidget* container = nullptr;
|
QWidget* container = nullptr;
|
||||||
QWidget* toolBar = nullptr;
|
QWidget* toolBar = nullptr;
|
||||||
@@ -75,6 +77,7 @@ namespace Bloom::Widgets
|
|||||||
|
|
||||||
MemorySnapshotItem* contextMenuSnapshotItem = nullptr;
|
MemorySnapshotItem* contextMenuSnapshotItem = nullptr;
|
||||||
|
|
||||||
|
QAction* openSnapshotViewerAction = new QAction("Open", this);
|
||||||
QAction* deleteSnapshotAction = new QAction("Delete", this);
|
QAction* deleteSnapshotAction = new QAction("Delete", this);
|
||||||
QAction* restoreSnapshotAction = new QAction("Restore", this);
|
QAction* restoreSnapshotAction = new QAction("Restore", this);
|
||||||
|
|
||||||
@@ -84,9 +87,12 @@ namespace Bloom::Widgets
|
|||||||
bool captureFocusedRegions,
|
bool captureFocusedRegions,
|
||||||
bool captureDirectlyFromTarget
|
bool captureDirectlyFromTarget
|
||||||
);
|
);
|
||||||
|
|
||||||
void addSnapshot(MemorySnapshot&& snapshotTmp);
|
void addSnapshot(MemorySnapshot&& snapshotTmp);
|
||||||
void onSnapshotItemSelected(MemorySnapshotItem* item);
|
void onSnapshotItemSelected(MemorySnapshotItem* item);
|
||||||
|
void openSnapshotViewer(const QString& snapshotId);
|
||||||
void restoreSnapshot(const QString& snapshotId, bool confirmationPromptEnabled);
|
void restoreSnapshot(const QString& snapshotId, bool confirmationPromptEnabled);
|
||||||
|
void onSnapshotItemDoubleClick(MemorySnapshotItem* item);
|
||||||
void onSnapshotItemContextMenu(ListItem* item, QPoint sourcePosition);
|
void onSnapshotItemContextMenu(ListItem* item, QPoint sourcePosition);
|
||||||
void onTargetStateChanged(Targets::TargetState newState);
|
void onTargetStateChanged(Targets::TargetState newState);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
#include "MemoryRegionItem.hpp"
|
||||||
|
|
||||||
|
#include "src/Services/DateTimeService.hpp"
|
||||||
|
|
||||||
|
namespace Bloom::Widgets
|
||||||
|
{
|
||||||
|
MemoryRegionItem::MemoryRegionItem(const MemoryRegion& memoryRegion)
|
||||||
|
: memoryRegion(memoryRegion)
|
||||||
|
{
|
||||||
|
this->size = QSize(0, MemoryRegionItem::HEIGHT);
|
||||||
|
|
||||||
|
this->nameText = memoryRegion.name;
|
||||||
|
this->addressRangeText = "0x" + QString::number(this->memoryRegion.addressRange.startAddress, 16).toUpper()
|
||||||
|
+ QString(" -> ") + "0x" + QString::number(this->memoryRegion.addressRange.endAddress, 16).toUpper();
|
||||||
|
this->regionTypeText = this->memoryRegion.type == MemoryRegionType::EXCLUDED ? "Excluded" : "Focused";
|
||||||
|
this->createdDateText = memoryRegion.createdDate.toString(
|
||||||
|
memoryRegion.createdDate.date() == Services::DateTimeService::currentDate()
|
||||||
|
? "hh:mm"
|
||||||
|
: "dd/MM/yyyy hh:mm"
|
||||||
|
);
|
||||||
|
|
||||||
|
this->setToolTip(this->nameText);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MemoryRegionItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
|
||||||
|
static constexpr auto margins = QMargins(10, 5, 10, 0);
|
||||||
|
|
||||||
|
painter->setOpacity(0.7);
|
||||||
|
static auto font = QFont("'Ubuntu', sans-serif");
|
||||||
|
font.setPixelSize(14);
|
||||||
|
static auto secondaryFont = QFont("'Ubuntu', sans-serif");
|
||||||
|
secondaryFont.setPixelSize(13);
|
||||||
|
|
||||||
|
static constexpr auto fontColor = QColor(0xAF, 0xB1, 0xB3);
|
||||||
|
static constexpr auto secondaryFontColor = QColor(0x8A, 0x8A, 0x8D);
|
||||||
|
|
||||||
|
painter->setFont(font);
|
||||||
|
painter->setPen(fontColor);
|
||||||
|
|
||||||
|
const auto fontMetrics = painter->fontMetrics();
|
||||||
|
|
||||||
|
const auto addressRangeTextSize = fontMetrics.size(Qt::TextSingleLine, this->addressRangeText);
|
||||||
|
const auto regionTypeTextSize = fontMetrics.size(Qt::TextSingleLine, this->regionTypeText);
|
||||||
|
const auto createdDateTextSize = fontMetrics.size(Qt::TextSingleLine, this->createdDateText);
|
||||||
|
|
||||||
|
constexpr auto nameTextRightMargin = 10;
|
||||||
|
const auto availableNameTextWidth = this->size.width() - margins.left() - margins.right()
|
||||||
|
- regionTypeTextSize.width() - nameTextRightMargin;
|
||||||
|
|
||||||
|
const auto nameText = fontMetrics.elidedText(
|
||||||
|
this->nameText,
|
||||||
|
Qt::TextElideMode::ElideRight,
|
||||||
|
availableNameTextWidth
|
||||||
|
);
|
||||||
|
|
||||||
|
const auto nameTextSize = fontMetrics.size(Qt::TextSingleLine, nameText);
|
||||||
|
const auto nameTextRect = QRect(
|
||||||
|
margins.left(),
|
||||||
|
margins.top(),
|
||||||
|
nameTextSize.width(),
|
||||||
|
nameTextSize.height()
|
||||||
|
);
|
||||||
|
|
||||||
|
painter->drawText(nameTextRect, Qt::AlignLeft, nameText);
|
||||||
|
|
||||||
|
painter->setFont(secondaryFont);
|
||||||
|
painter->setPen(secondaryFontColor);
|
||||||
|
|
||||||
|
const auto addressRangeTextRect = QRect(
|
||||||
|
margins.left(),
|
||||||
|
nameTextRect.bottom() + 5,
|
||||||
|
addressRangeTextSize.width(),
|
||||||
|
addressRangeTextSize.height()
|
||||||
|
);
|
||||||
|
|
||||||
|
painter->drawText(addressRangeTextRect, Qt::AlignLeft, this->addressRangeText);
|
||||||
|
|
||||||
|
const auto regionTypeTextRect = QRect(
|
||||||
|
this->size.width() - margins.right() - regionTypeTextSize.width(),
|
||||||
|
margins.top(),
|
||||||
|
regionTypeTextSize.width(),
|
||||||
|
regionTypeTextSize.height()
|
||||||
|
);
|
||||||
|
|
||||||
|
painter->drawText(regionTypeTextRect, Qt::AlignRight, this->regionTypeText);
|
||||||
|
|
||||||
|
const auto createdDateTextRect = QRect(
|
||||||
|
this->size.width() - margins.right() - createdDateTextSize.width(),
|
||||||
|
nameTextRect.bottom() + 5,
|
||||||
|
createdDateTextSize.width(),
|
||||||
|
createdDateTextSize.height()
|
||||||
|
);
|
||||||
|
|
||||||
|
painter->drawText(createdDateTextRect, Qt::AlignRight, this->createdDateText);
|
||||||
|
|
||||||
|
static constexpr auto borderColor = QColor(0x41, 0x42, 0x3F);
|
||||||
|
painter->setPen(borderColor);
|
||||||
|
painter->drawLine(0, this->size.height() - 1, this->size.width(), this->size.height() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/ListView/ListItem.hpp"
|
||||||
|
|
||||||
|
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegion.hpp"
|
||||||
|
#include "src/Targets/TargetMemory.hpp"
|
||||||
|
|
||||||
|
namespace Bloom::Widgets
|
||||||
|
{
|
||||||
|
class MemoryRegionItem: public ListItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
const MemoryRegion& memoryRegion;
|
||||||
|
|
||||||
|
MemoryRegionItem(const MemoryRegion& memoryRegion);
|
||||||
|
|
||||||
|
bool operator < (const ListItem& rhs) const override {
|
||||||
|
const auto& rhsRegionItem = dynamic_cast<const MemoryRegionItem&>(rhs);
|
||||||
|
return this->memoryRegion.createdDate < rhsRegionItem.memoryRegion.createdDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr int HEIGHT = 50;
|
||||||
|
|
||||||
|
QString nameText;
|
||||||
|
QString addressRangeText;
|
||||||
|
QString regionTypeText;
|
||||||
|
QString createdDateText;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,284 @@
|
|||||||
|
#include "SnapshotViewer.hpp"
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <QSize>
|
||||||
|
#include <QDesktopServices>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#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/Label.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;
|
||||||
|
|
||||||
|
SnapshotViewer::SnapshotViewer(
|
||||||
|
MemorySnapshot& snapshot,
|
||||||
|
const Targets::TargetMemoryDescriptor& memoryDescriptor,
|
||||||
|
QWidget* parent
|
||||||
|
)
|
||||||
|
: QWidget(parent)
|
||||||
|
, snapshot(snapshot)
|
||||||
|
, memoryDescriptor(memoryDescriptor)
|
||||||
|
, hexViewerData(snapshot.data)
|
||||||
|
{
|
||||||
|
this->setWindowFlag(Qt::Window);
|
||||||
|
this->setObjectName("snapshot-viewer");
|
||||||
|
this->setWindowTitle(this->snapshot.name + " (" + this->snapshot.id + ")");
|
||||||
|
|
||||||
|
auto windowUiFile = QFile(
|
||||||
|
QString::fromStdString(Services::PathService::compiledResourcesPath()
|
||||||
|
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane"
|
||||||
|
+ "/SnapshotManager/SnapshotViewer/UiFiles/SnapshotViewer.ui"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
auto stylesheetFile = QFile(
|
||||||
|
QString::fromStdString(Services::PathService::compiledResourcesPath()
|
||||||
|
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane"
|
||||||
|
+ "/SnapshotManager/SnapshotViewer/Stylesheets/SnapshotViewer.qss"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!windowUiFile.open(QFile::ReadOnly)) {
|
||||||
|
throw Exception("Failed to open SnapshotViewer UI file");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!stylesheetFile.open(QFile::ReadOnly)) {
|
||||||
|
throw Exception("Failed to open SnapshotViewer stylesheet file");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set ideal window size
|
||||||
|
this->setFixedSize(1200, 850);
|
||||||
|
this->setMinimumSize(700, 600);
|
||||||
|
this->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
|
||||||
|
|
||||||
|
auto uiLoader = UiLoader(this);
|
||||||
|
this->setStyleSheet(stylesheetFile.readAll());
|
||||||
|
this->container = uiLoader.load(&windowUiFile, this);
|
||||||
|
|
||||||
|
auto* containerLayout = this->container->findChild<QVBoxLayout*>();
|
||||||
|
this->detailsContainer = this->container->findChild<QWidget*>("details-container");
|
||||||
|
|
||||||
|
this->nameInput = this->detailsContainer->findChild<TextInput*>("name-input");
|
||||||
|
this->descriptionInput = this->detailsContainer->findChild<QPlainTextEdit*>("description-input");
|
||||||
|
|
||||||
|
auto* detailsContainerLayout = this->detailsContainer->findChild<QHBoxLayout*>();
|
||||||
|
detailsContainerLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
|
||||||
|
auto* attributesLayout = this->detailsContainer->findChild<QVBoxLayout*>("attributes-layout");
|
||||||
|
attributesLayout->setContentsMargins(10, 10, 10, 0);
|
||||||
|
|
||||||
|
auto* rightPanelLayout = this->detailsContainer->findChild<QVBoxLayout*>("right-panel-layout");
|
||||||
|
rightPanelLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
|
||||||
|
if (!this->snapshot.excludedRegions.empty() || !this->snapshot.focusedRegions.empty()) {
|
||||||
|
auto* memoryRegionsContainer = this->detailsContainer->findChild<QWidget*>("memory-regions-container");
|
||||||
|
auto* memoryRegionsLayout = memoryRegionsContainer->findChild<QVBoxLayout*>();
|
||||||
|
auto* noMemoryRegionsLabel = memoryRegionsContainer->findChild<Label*>("no-regions-label");
|
||||||
|
|
||||||
|
std::transform(
|
||||||
|
this->snapshot.focusedRegions.begin(),
|
||||||
|
this->snapshot.focusedRegions.end(),
|
||||||
|
std::back_inserter(this->memoryRegionItems),
|
||||||
|
[] (const MemoryRegion& focusedRegion) {
|
||||||
|
return new MemoryRegionItem(focusedRegion);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
std::transform(
|
||||||
|
this->snapshot.excludedRegions.begin(),
|
||||||
|
this->snapshot.excludedRegions.end(),
|
||||||
|
std::back_inserter(this->memoryRegionItems),
|
||||||
|
[] (const MemoryRegion& excludedRegion) {
|
||||||
|
return new MemoryRegionItem(excludedRegion);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this->memoryRegionListView = new ListView(
|
||||||
|
std::vector<ListItem*>(this->memoryRegionItems.begin(), this->memoryRegionItems.end()),
|
||||||
|
this
|
||||||
|
);
|
||||||
|
this->memoryRegionListView->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded);
|
||||||
|
|
||||||
|
this->memoryRegionListScene = this->memoryRegionListView->listScene();
|
||||||
|
this->memoryRegionListScene->margins = QMargins(0, 5, 0, 5);
|
||||||
|
this->memoryRegionListScene->setSelectionLimit(2);
|
||||||
|
|
||||||
|
noMemoryRegionsLabel->hide();
|
||||||
|
memoryRegionsLayout->insertWidget(0, this->memoryRegionListView);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->restoreBytesAction = new ContextMenuAction("Restore Selected", std::nullopt, this);
|
||||||
|
|
||||||
|
this->hexViewerWidget = new HexViewerWidget(
|
||||||
|
this->memoryDescriptor,
|
||||||
|
this->hexViewerData,
|
||||||
|
this->hexViewerWidgetSettings,
|
||||||
|
this->snapshot.focusedRegions,
|
||||||
|
this->snapshot.excludedRegions,
|
||||||
|
this
|
||||||
|
);
|
||||||
|
|
||||||
|
containerLayout->insertWidget(1, this->hexViewerWidget);
|
||||||
|
|
||||||
|
this->bottomBar = this->container->findChild<QWidget*>("bottom-bar");
|
||||||
|
this->bottomBarLayout = this->bottomBar->findChild<QHBoxLayout*>();
|
||||||
|
|
||||||
|
auto* memoryCapacityLabel = this->bottomBar->findChild<Label*>("memory-capacity-label");
|
||||||
|
auto* snapshotIdLabel = this->bottomBar->findChild<Label*>("id-label");
|
||||||
|
auto* programCounterLabel = this->bottomBar->findChild<Label*>("program-counter-label");
|
||||||
|
auto* dateLabel = this->bottomBar->findChild<Label*>("date-label");
|
||||||
|
|
||||||
|
memoryCapacityLabel->setText(QLocale(QLocale::English).toString(this->memoryDescriptor.size()) + " Bytes");
|
||||||
|
snapshotIdLabel->setText(this->snapshot.id);
|
||||||
|
programCounterLabel->setText(
|
||||||
|
"0x" + QString::number(this->snapshot.programCounter, 16).rightJustified(8, '0').toUpper()
|
||||||
|
);
|
||||||
|
dateLabel->setText(this->snapshot.createdDate.toString("dd/MM/yyyy hh:mm"));
|
||||||
|
|
||||||
|
this->nameInput->setText(this->snapshot.name);
|
||||||
|
this->descriptionInput->setPlainText(this->snapshot.description);
|
||||||
|
|
||||||
|
this->taskProgressIndicator = new TaskProgressIndicator(this);
|
||||||
|
this->bottomBarLayout->insertWidget(2, this->taskProgressIndicator);
|
||||||
|
|
||||||
|
auto* insightSignals = InsightSignals::instance();
|
||||||
|
|
||||||
|
QObject::connect(
|
||||||
|
this->restoreBytesAction,
|
||||||
|
&ContextMenuAction::invoked,
|
||||||
|
this,
|
||||||
|
[this] (const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>& selectedByteItemsByAddress) {
|
||||||
|
this->restoreSelectedBytes(selectedByteItemsByAddress, true);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
QObject::connect(this->hexViewerWidget, &HexViewerWidget::ready, this, &SnapshotViewer::onHexViewerReady);
|
||||||
|
|
||||||
|
this->hexViewerWidget->init();
|
||||||
|
this->move(this->parentWidget()->window()->geometry().center() - this->rect().center());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SnapshotViewer::showEvent(QShowEvent* event) {
|
||||||
|
QWidget::showEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SnapshotViewer::resizeEvent(QResizeEvent* event) {
|
||||||
|
this->container->setFixedSize(this->size());
|
||||||
|
|
||||||
|
QWidget::resizeEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SnapshotViewer::onHexViewerReady() {
|
||||||
|
this->hexViewerWidget->addExternalContextMenuAction(this->restoreBytesAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SnapshotViewer::restoreSelectedBytes(
|
||||||
|
const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>& selectedByteItemsByAddress,
|
||||||
|
bool confirmationPromptEnabled
|
||||||
|
) {
|
||||||
|
auto sortedByteItemsByAddress = std::map<Targets::TargetMemoryAddress, ByteItem*>();
|
||||||
|
|
||||||
|
// Ideally, we'd use std::transform here, but that would require an additional pass to remove excluded bytes
|
||||||
|
for (const auto& pair : selectedByteItemsByAddress) {
|
||||||
|
if (pair.second->excluded) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
sortedByteItemsByAddress.insert(pair);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sortedByteItemsByAddress.empty()) {
|
||||||
|
// The user has only selected bytes that are within an excluded region - nothing to do here
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (confirmationPromptEnabled) {
|
||||||
|
auto* confirmationDialog = new ConfirmationDialog(
|
||||||
|
"Restore selected bytes",
|
||||||
|
"This operation will write " + QString::number(sortedByteItemsByAddress.size())
|
||||||
|
+ " byte(s) to the target's "
|
||||||
|
+ QString(this->memoryDescriptor.type == Targets::TargetMemoryType::EEPROM ? "EEPROM" : "RAM")
|
||||||
|
+ ".<br/><br/>Do you wish to proceed?",
|
||||||
|
"Proceed",
|
||||||
|
std::nullopt,
|
||||||
|
this
|
||||||
|
);
|
||||||
|
|
||||||
|
QObject::connect(
|
||||||
|
confirmationDialog,
|
||||||
|
&ConfirmationDialog::confirmed,
|
||||||
|
this,
|
||||||
|
[this, selectedByteItemsByAddress] {
|
||||||
|
this->restoreSelectedBytes(selectedByteItemsByAddress, false);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
confirmationDialog->show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto writeBlocks = std::vector<WriteTargetMemory::Block>();
|
||||||
|
|
||||||
|
Targets::TargetMemoryAddress blockStartAddress = sortedByteItemsByAddress.begin()->first;
|
||||||
|
Targets::TargetMemoryAddress blockEndAddress = blockStartAddress;
|
||||||
|
|
||||||
|
for (const auto& [address, byteItem] : sortedByteItemsByAddress) {
|
||||||
|
if (address > (blockEndAddress + 1)) {
|
||||||
|
// Commit the block
|
||||||
|
const auto dataBeginOffset = blockStartAddress - this->memoryDescriptor.addressRange.startAddress;
|
||||||
|
const auto dataEndOffset = blockEndAddress - this->memoryDescriptor.addressRange.startAddress + 1;
|
||||||
|
|
||||||
|
writeBlocks.emplace_back(
|
||||||
|
blockStartAddress,
|
||||||
|
Targets::TargetMemoryBuffer(
|
||||||
|
this->snapshot.data.begin() + dataBeginOffset,
|
||||||
|
this->snapshot.data.begin() + dataEndOffset
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
blockStartAddress = address;
|
||||||
|
blockEndAddress = address;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockEndAddress = address;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const auto dataBeginOffset = blockStartAddress - this->memoryDescriptor.addressRange.startAddress;
|
||||||
|
const auto dataEndOffset = blockEndAddress - this->memoryDescriptor.addressRange.startAddress + 1;
|
||||||
|
|
||||||
|
writeBlocks.emplace_back(
|
||||||
|
blockStartAddress,
|
||||||
|
Targets::TargetMemoryBuffer(
|
||||||
|
this->snapshot.data.begin() + dataBeginOffset,
|
||||||
|
this->snapshot.data.begin() + dataEndOffset
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto writeMemoryTask = QSharedPointer<WriteTargetMemory>(
|
||||||
|
new WriteTargetMemory(this->memoryDescriptor, std::move(writeBlocks)),
|
||||||
|
&QObject::deleteLater
|
||||||
|
);
|
||||||
|
|
||||||
|
this->taskProgressIndicator->addTask(writeMemoryTask);
|
||||||
|
InsightWorker::queueTask(writeMemoryTask);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QShowEvent>
|
||||||
|
#include <QResizeEvent>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QPlainTextEdit>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TextInput.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 "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/HexViewerWidget.hpp"
|
||||||
|
#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ContextMenuAction.hpp"
|
||||||
|
#include "MemoryRegionItem.hpp"
|
||||||
|
|
||||||
|
namespace Bloom::Widgets
|
||||||
|
{
|
||||||
|
class SnapshotViewer: public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
SnapshotViewer(
|
||||||
|
MemorySnapshot& snapshot,
|
||||||
|
const Targets::TargetMemoryDescriptor& memoryDescriptor,
|
||||||
|
QWidget* parent = nullptr
|
||||||
|
);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void showEvent(QShowEvent* event) override;
|
||||||
|
void resizeEvent(QResizeEvent* event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
MemorySnapshot& snapshot;
|
||||||
|
const Targets::TargetMemoryDescriptor& memoryDescriptor;
|
||||||
|
|
||||||
|
QWidget* container = nullptr;
|
||||||
|
|
||||||
|
QWidget* detailsContainer = nullptr;
|
||||||
|
TextInput* nameInput = nullptr;
|
||||||
|
QPlainTextEdit* descriptionInput = nullptr;
|
||||||
|
|
||||||
|
ListView* memoryRegionListView = nullptr;
|
||||||
|
ListScene* memoryRegionListScene = nullptr;
|
||||||
|
std::vector<MemoryRegionItem*> memoryRegionItems;
|
||||||
|
|
||||||
|
std::optional<Targets::TargetMemoryBuffer> hexViewerData;
|
||||||
|
HexViewerWidget* hexViewerWidget = nullptr;
|
||||||
|
HexViewerWidgetSettings hexViewerWidgetSettings = HexViewerWidgetSettings();
|
||||||
|
|
||||||
|
ContextMenuAction* restoreBytesAction = nullptr;
|
||||||
|
|
||||||
|
QWidget* bottomBar = nullptr;
|
||||||
|
QHBoxLayout* bottomBarLayout = nullptr;
|
||||||
|
|
||||||
|
TaskProgressIndicator* taskProgressIndicator = nullptr;
|
||||||
|
|
||||||
|
PushButton* closeButton = nullptr;
|
||||||
|
|
||||||
|
void onHexViewerReady();
|
||||||
|
void restoreSelectedBytes(
|
||||||
|
const std::unordered_map<Targets::TargetMemoryAddress, ByteItem*>& selectedByteItemsByAddress,
|
||||||
|
bool confirmationPromptEnabled
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
#snapshot-viewer,
|
||||||
|
#snapshot-viewer #container {
|
||||||
|
background-color: #373835;
|
||||||
|
}
|
||||||
|
|
||||||
|
#snapshot-viewer #top-bar {
|
||||||
|
border-bottom: 1px solid #41423f;
|
||||||
|
}
|
||||||
|
|
||||||
|
#snapshot-viewer #bottom-bar {
|
||||||
|
border-top: 1px solid #41423f;
|
||||||
|
}
|
||||||
|
|
||||||
|
#snapshot-viewer #details-container {
|
||||||
|
border-bottom: 1px solid #41423f;
|
||||||
|
}
|
||||||
|
|
||||||
|
#snapshot-viewer #right-panel-container {
|
||||||
|
border-left: 1px solid #41423f;
|
||||||
|
}
|
||||||
|
|
||||||
|
#snapshot-viewer #memory-regions-container #no-regions-label {
|
||||||
|
color: #838386;
|
||||||
|
}
|
||||||
|
|
||||||
|
#snapshot-viewer #bottom-bar #separator {
|
||||||
|
background-color: transparent;
|
||||||
|
border-right: 1px solid #41423f;
|
||||||
|
padding: 0;
|
||||||
|
min-width: 1px;
|
||||||
|
max-width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#snapshot-viewer #memory-capacity-label,
|
||||||
|
#snapshot-viewer #id-label,
|
||||||
|
#snapshot-viewer #program-counter-label,
|
||||||
|
#snapshot-viewer #date-label {
|
||||||
|
color: #8a8a8d;
|
||||||
|
}
|
||||||
|
|
||||||
|
#snapshot-viewer #program-counter-label,
|
||||||
|
#snapshot-viewer #date-label {
|
||||||
|
padding: 1px 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#snapshot-viewer #memory-capacity-label,
|
||||||
|
#snapshot-viewer #id-label {
|
||||||
|
padding: 1px 5px;
|
||||||
|
}
|
||||||
@@ -0,0 +1,227 @@
|
|||||||
|
<?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="details-container">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"/>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="margin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="attributes-layout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="margin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="LabeledSeparator" name="details-separator">
|
||||||
|
<property name="title">
|
||||||
|
<string>Details</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</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 alignment="Qt::AlignVCenter">
|
||||||
|
<widget class="TextInput" name="name-input">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"/>
|
||||||
|
</property>
|
||||||
|
<property name="maximumWidth">
|
||||||
|
<number>800</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</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>
|
||||||
|
<widget class="QPlainTextEdit" name="description-input">
|
||||||
|
<property name="minimumHeight">
|
||||||
|
<number>100</number>
|
||||||
|
</property>
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</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>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QWidget" name="right-panel-container">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="MinimumExpanding"/>
|
||||||
|
</property>
|
||||||
|
<property name="minimumWidth">
|
||||||
|
<number>400</number>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="right-panel-layout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="margin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QWidget" name="memory-regions-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 alignment="Qt::AlignHCenter">
|
||||||
|
<widget class="Label" name="no-regions-label">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"/>
|
||||||
|
</property>
|
||||||
|
<property name="visible">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<enum>Qt::AlignCenter</enum>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>No memory regions were captured in this snapshot</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</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>
|
||||||
|
<item>
|
||||||
|
<widget class="Label" name="memory-capacity-label">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Memory capacity</string>
|
||||||
|
</property>
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed"/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="separator"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="separator"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="Label" name="program-counter-label">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Program counter at point of capture</string>
|
||||||
|
</property>
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed"/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="separator"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="Label" name="date-label">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Date of capture</string>
|
||||||
|
</property>
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed"/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="separator"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="Label" name="id-label">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Snapshot ID</string>
|
||||||
|
</property>
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed"/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</ui>
|
||||||
Reference in New Issue
Block a user