From 5c97fb76aa00f84d3c56e57f2ec4d8471dd257b3 Mon Sep 17 00:00:00 2001 From: Nav Date: Thu, 23 Dec 2021 01:26:58 +0000 Subject: [PATCH] New memory region manager window --- CMakeLists.txt | 6 + .../ExcludedRegionItem.cpp | 40 ++ .../ExcludedRegionItem.hpp | 24 + .../MemoryRegionManager/FocusedRegionItem.cpp | 70 +++ .../MemoryRegionManager/FocusedRegionItem.hpp | 45 ++ .../MemoryRegionManager/Images/add-region.svg | 83 ++++ .../MemoryRegionManager/Images/icon.svg | 105 ++++ .../Images/remove-region.svg | 69 +++ .../MemoryRegionManagerWindow.cpp | 391 +++++++++++++++ .../MemoryRegionManagerWindow.hpp | 82 ++++ .../MemoryRegionManager/RegionItem.cpp | 202 ++++++++ .../MemoryRegionManager/RegionItem.hpp | 79 +++ .../Stylesheets/MemoryRegionManagerWindow.qss | 122 +++++ .../UiFiles/ExcludedMemoryRegionForm.ui | 351 ++++++++++++++ .../UiFiles/FocusedMemoryRegionForm.ui | 458 ++++++++++++++++++ .../UiFiles/MemoryRegionManagerWindow.ui | 233 +++++++++ .../TargetMemoryInspectionPane.cpp | 35 +- .../TargetMemoryInspectionPane.hpp | 31 +- src/resources.qrc | 9 + 19 files changed, 2417 insertions(+), 18 deletions(-) create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/ExcludedRegionItem.cpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/ExcludedRegionItem.hpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/FocusedRegionItem.cpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/FocusedRegionItem.hpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/Images/add-region.svg create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/Images/icon.svg create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/Images/remove-region.svg create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/MemoryRegionManagerWindow.cpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/MemoryRegionManagerWindow.hpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/RegionItem.cpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/RegionItem.hpp create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/Stylesheets/MemoryRegionManagerWindow.qss create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/UiFiles/ExcludedMemoryRegionForm.ui create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/UiFiles/FocusedMemoryRegionForm.ui create mode 100644 src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/UiFiles/MemoryRegionManagerWindow.ui diff --git a/CMakeLists.txt b/CMakeLists.txt index b0bdc113..c7ce9147 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -198,6 +198,12 @@ add_executable(Bloom src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteItem.cpp src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteAddressContainer.cpp src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget/ByteAddressItem.cpp + + # Focused memory region manager window + src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/MemoryRegionManagerWindow.cpp + src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/RegionItem.cpp + src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/FocusedRegionItem.cpp + src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/ExcludedRegionItem.cpp ) set_target_properties(Bloom PROPERTIES OUTPUT_NAME bloom) diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/ExcludedRegionItem.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/ExcludedRegionItem.cpp new file mode 100644 index 00000000..98ea573c --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/ExcludedRegionItem.cpp @@ -0,0 +1,40 @@ +#include "ExcludedRegionItem.hpp" + +#include + +#include "src/Helpers/Paths.hpp" +#include "src/Exceptions/Exception.hpp" +#include "src/Insight/UserInterfaces/InsightWindow/UiLoader.hpp" + +using namespace Bloom; +using namespace Bloom::Widgets; + +ExcludedRegionItem::ExcludedRegionItem( + const ExcludedMemoryRegion& region, + QWidget* parent +): memoryRegion(region), RegionItem(region, parent) { + auto formUiFile = QFile( + QString::fromStdString(Paths::compiledResourcesPath() + + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane" + + "/MemoryRegionManager/UiFiles/ExcludedMemoryRegionForm.ui" + ) + ); + + if (!formUiFile.open(QFile::ReadOnly)) { + throw Bloom::Exceptions::Exception("Failed to open excluded region item form UI file"); + } + + auto uiLoader = UiLoader(this); + this->formWidget = uiLoader.load(&formUiFile, this); + + this->initFormInputs(); +} + +ExcludedMemoryRegion ExcludedRegionItem::generateExcludedMemoryRegionFromInput() { + this->memoryRegion.name = this->nameInput->text(); + this->memoryRegion.addressRange.startAddress = this->startAddressInput->text().toUInt(nullptr, 16); + this->memoryRegion.addressRange.endAddress = this->endAddressInput->text().toUInt(nullptr, 16); + this->memoryRegion.addressRangeType = this->getSelectedAddressType(); + + return this->memoryRegion; +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/ExcludedRegionItem.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/ExcludedRegionItem.hpp new file mode 100644 index 00000000..6cabca7f --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/ExcludedRegionItem.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include "RegionItem.hpp" +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/ExcludedMemoryRegion.hpp" + +namespace Bloom::Widgets +{ + class ExcludedRegionItem: public RegionItem + { + Q_OBJECT + + public: + ExcludedRegionItem(const ExcludedMemoryRegion& region, QWidget *parent); + + [[nodiscard]] const MemoryRegion& getMemoryRegion() const override { + return this->memoryRegion; + }; + + [[nodiscard]] virtual ExcludedMemoryRegion generateExcludedMemoryRegionFromInput(); + + private: + ExcludedMemoryRegion memoryRegion; + }; +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/FocusedRegionItem.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/FocusedRegionItem.cpp new file mode 100644 index 00000000..bb9ea40f --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/FocusedRegionItem.cpp @@ -0,0 +1,70 @@ +#include "FocusedRegionItem.hpp" + +#include + +#include "src/Helpers/Paths.hpp" +#include "src/Exceptions/Exception.hpp" +#include "src/Insight/UserInterfaces/InsightWindow/UiLoader.hpp" + +using namespace Bloom; +using namespace Bloom::Widgets; + +FocusedRegionItem::FocusedRegionItem( + const FocusedMemoryRegion& region, + QWidget* parent +): memoryRegion(region), RegionItem(region, parent) { + auto formUiFile = QFile( + QString::fromStdString(Paths::compiledResourcesPath() + + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane" + + "/MemoryRegionManager/UiFiles/FocusedMemoryRegionForm.ui" + ) + ); + + if (!formUiFile.open(QFile::ReadOnly)) { + throw Bloom::Exceptions::Exception("Failed to open focused region item form UI file"); + } + + auto uiLoader = UiLoader(this); + this->formWidget = uiLoader.load(&formUiFile, this); + + this->initFormInputs(); +} + +FocusedMemoryRegion FocusedRegionItem::generateFocusedMemoryRegionFromInput() { + this->memoryRegion.name = this->nameInput->text(); + this->memoryRegion.addressRange.startAddress = this->startAddressInput->text().toUInt(nullptr, 16); + this->memoryRegion.addressRange.endAddress = this->endAddressInput->text().toUInt(nullptr, 16); + this->memoryRegion.addressRangeType = this->getSelectedAddressType(); + + auto selectedDataTypeOptionName = this->dataTypeInput->currentData().toString(); + if (FocusedRegionItem::dataTypeOptionsByName.contains(selectedDataTypeOptionName)) { + this->memoryRegion.dataType = FocusedRegionItem::dataTypeOptionsByName.at(selectedDataTypeOptionName).dataType; + } + + return this->memoryRegion; +} + +void FocusedRegionItem::initFormInputs() { + RegionItem::initFormInputs(); + const auto& region = this->memoryRegion; + + this->dataTypeInput = this->formWidget->findChild("data-type-input"); + + for (const auto& [optionName, option] : FocusedRegionItem::dataTypeOptionsByName) { + this->dataTypeInput->addItem(option.text, optionName); + } + + switch (region.dataType) { + case MemoryRegionDataType::INTEGER: { + this->dataTypeInput->setCurrentText(FocusedRegionItem::dataTypeOptionsByName.at("integer").text); + break; + } + case MemoryRegionDataType::ASCII_STRING: { + this->dataTypeInput->setCurrentText(FocusedRegionItem::dataTypeOptionsByName.at("ascii").text); + break; + } + default: { + this->dataTypeInput->setCurrentText(FocusedRegionItem::dataTypeOptionsByName.at("other").text); + } + } +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/FocusedRegionItem.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/FocusedRegionItem.hpp new file mode 100644 index 00000000..05601f9e --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/FocusedRegionItem.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include "RegionItem.hpp" +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/FocusedMemoryRegion.hpp" + +namespace Bloom::Widgets +{ + struct DataTypeOption + { + QString text; + MemoryRegionDataType dataType = MemoryRegionDataType::UNKNOWN; + + DataTypeOption(const QString& text, MemoryRegionDataType dataType) + : text(text), dataType(dataType) {}; + }; + + class FocusedRegionItem: public RegionItem + { + Q_OBJECT + + public: + FocusedRegionItem(const FocusedMemoryRegion& region, QWidget *parent); + + [[nodiscard]] const MemoryRegion& getMemoryRegion() const override { + return this->memoryRegion; + }; + + [[nodiscard]] virtual FocusedMemoryRegion generateFocusedMemoryRegionFromInput(); + + protected: + void initFormInputs() override; + + private: + FocusedMemoryRegion memoryRegion; + QComboBox* dataTypeInput = nullptr; + + static inline const std::map dataTypeOptionsByName = std::map< + QString, DataTypeOption + >({ + {"other", DataTypeOption("Other", MemoryRegionDataType::UNKNOWN)}, + {"integer", DataTypeOption("Integer", MemoryRegionDataType::INTEGER)}, + {"ascii", DataTypeOption("ASCII String", MemoryRegionDataType::ASCII_STRING)}, + }); + }; +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/Images/add-region.svg b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/Images/add-region.svg new file mode 100644 index 00000000..804f13f6 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/Images/add-region.svg @@ -0,0 +1,83 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/Images/icon.svg b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/Images/icon.svg new file mode 100644 index 00000000..8ce74833 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/Images/icon.svg @@ -0,0 +1,105 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/Images/remove-region.svg b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/Images/remove-region.svg new file mode 100644 index 00000000..75ca36e0 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/Images/remove-region.svg @@ -0,0 +1,69 @@ + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/MemoryRegionManagerWindow.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/MemoryRegionManagerWindow.cpp new file mode 100644 index 00000000..bce21054 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/MemoryRegionManagerWindow.cpp @@ -0,0 +1,391 @@ +#include "MemoryRegionManagerWindow.hpp" + +#include +#include +#include + +#include "src/Insight/UserInterfaces/InsightWindow/UiLoader.hpp" +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/ErrorDialogue/ErrorDialogue.hpp" + +#include "src/Helpers/Paths.hpp" +#include "src/Exceptions/Exception.hpp" + +using namespace Bloom; +using namespace Bloom::Widgets; + +using Bloom::Exceptions::Exception; + +MemoryRegionManagerWindow::MemoryRegionManagerWindow( + const Targets::TargetMemoryDescriptor& memoryDescriptor, + std::vector& focusedMemoryRegions, + std::vector& excludedMemoryRegions, + QWidget* parent +): + QWidget(parent), + memoryDescriptor(memoryDescriptor), + focusedMemoryRegions(focusedMemoryRegions), + excludedMemoryRegions(excludedMemoryRegions) +{ + this->setWindowFlag(Qt::Window); + this->setObjectName("memory-region-manager-window"); + this->setWindowTitle( + "Memory Regions - " + + QString(this->memoryDescriptor.type == Targets::TargetMemoryType::EEPROM ? "EEPROM" : "RAM") + ); + + auto windowUiFile = QFile( + QString::fromStdString(Paths::compiledResourcesPath() + + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane" + + "/MemoryRegionManager/UiFiles/MemoryRegionManagerWindow.ui" + ) + ); + + auto windowStylesheet = QFile( + QString::fromStdString(Paths::compiledResourcesPath() + + "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane" + + "/MemoryRegionManager/Stylesheets/MemoryRegionManagerWindow.qss" + ) + ); + + if (!windowUiFile.open(QFile::ReadOnly)) { + throw Exception("Failed to open MemoryRegionManagerWindow UI file"); + } + + if (!windowStylesheet.open(QFile::ReadOnly)) { + throw Exception("Failed to open MemoryRegionManagerWindow stylesheet file"); + } + + this->setStyleSheet(windowStylesheet.readAll()); + this->setFixedSize(QSize(860, 470)); + + auto uiLoader = UiLoader(this); + this->container = uiLoader.load(&windowUiFile, this); + + this->container->setFixedSize(this->size()); + this->container->setContentsMargins(0, 0, 0, 0); + + this->regionSelector = this->container->findChild("region-selector"); + auto* regionSelectorToolBar = this->regionSelector->findChild("region-selector-tool-bar"); + this->addRegionButton = this->regionSelector->findChild("add-region-btn"); + this->removeRegionButton = this->regionSelector->findChild("remove-region-btn"); + this->addFocusedRegionMenuAction = this->addRegionButton->findChild("add-focused-region"); + this->addExcludedRegionMenuAction = this->addRegionButton->findChild("add-excluded-region"); + this->regionItemScrollArea = this->regionSelector->findChild("region-item-scroll-area"); + this->regionItemScrollAreaViewport = this->regionItemScrollArea->findChild("item-container"); + this->regionItemScrollAreaViewportLayout = this->regionItemScrollAreaViewport->findChild( + "item-container-layout" + ); + + this->stackedFormLayout = this->container->findChild("stacked-form-layout"); + + this->applyButton = this->container->findChild("apply-btn"); + this->helpButton = this->container->findChild("help-btn"); + this->closeButton = this->container->findChild("close-btn"); + + regionSelectorToolBar->setContentsMargins(0, 0, 0, 0); + this->regionItemScrollArea->setContentsMargins(0, 0, 0, 0); + this->regionItemScrollAreaViewport->setContentsMargins(0, 0, 0, 0); + this->regionItemScrollAreaViewportLayout->setContentsMargins(0, 0, 0, 0); + this->regionItemScrollAreaViewportLayout->setDirection(QBoxLayout::Direction::TopToBottom); + + QObject::connect( + this->addFocusedRegionMenuAction, + &QAction::triggered, + this, + &MemoryRegionManagerWindow::onNewFocusedRegionTrigger + ); + + QObject::connect( + this->addExcludedRegionMenuAction, + &QAction::triggered, + this, + &MemoryRegionManagerWindow::onNewExcludedRegionTrigger + ); + + QObject::connect( + this->removeRegionButton, + &QToolButton::clicked, + this, + &MemoryRegionManagerWindow::onDeleteRegionTrigger + ); + + QObject::connect(this->closeButton, &QPushButton::clicked, this, &QWidget::close); + QObject::connect(this->applyButton, &QPushButton::clicked, this, &MemoryRegionManagerWindow::applyChanges); + QObject::connect(this->helpButton, &QPushButton::clicked, this, &MemoryRegionManagerWindow::openHelpPage); + + this->refreshRegions(); + + // Position the inspection window at the center of the main Insight window + this->move(parent->window()->geometry().center() - this->rect().center()); + this->show(); +} + +void MemoryRegionManagerWindow::refreshRegions() { + this->clearRegions(); + + for (const auto& focusedRegion: this->focusedMemoryRegions) { + this->addFocusedRegion(focusedRegion); + } + + for (const auto& excludedRegion: this->excludedMemoryRegions) { + this->addExcludedRegion(excludedRegion); + } + + this->sortRegionItems(); +} + +void MemoryRegionManagerWindow::showEvent(QShowEvent* event) { + if (this->selectedRegion == nullptr && this->regionItemScrollAreaViewportLayout->count() > 0) { + auto* firstRegionItem = qobject_cast( + this->regionItemScrollAreaViewportLayout->itemAt(0)->widget() + ); + + if (firstRegionItem != nullptr) { + firstRegionItem->setSelected(true); + } + } +} + +void MemoryRegionManagerWindow::clearRegions() { + this->selectedRegion = nullptr; + + for (auto* focusedRegionItem: this->focusedRegionItems) { + this->regionItemScrollAreaViewportLayout->removeWidget(focusedRegionItem); + + focusedRegionItem->getFormWidget()->deleteLater(); + focusedRegionItem->deleteLater(); + } + + for (auto* excludedRegionItem: this->excludedRegionItems) { + this->regionItemScrollAreaViewportLayout->removeWidget(excludedRegionItem); + + excludedRegionItem->getFormWidget()->deleteLater(); + excludedRegionItem->deleteLater(); + } + + this->focusedRegionItems.clear(); + this->excludedRegionItems.clear(); +} + +void MemoryRegionManagerWindow::sortRegionItems() { + /* + * This isn't very pretty. + * + * Because the insertion order is persisted in QBoxLayouts, sorting the items requires removing them from the + * layout, and then re-inserting them in the correct order. + */ + auto regionItemCompare = [] (RegionItem* itemA, RegionItem* itemB) { + return itemA->getMemoryRegion().id < itemB->getMemoryRegion().id; + }; + auto sortedRegionItems = std::set(regionItemCompare); + + QLayoutItem* layoutItem = nullptr; + while ((layoutItem = this->regionItemScrollAreaViewportLayout->takeAt(0)) != nullptr) { + auto* regionItem = qobject_cast(layoutItem->widget()); + if (regionItem != nullptr) { + sortedRegionItems.insert(regionItem); + } + + delete layoutItem; + } + + for (auto* regionItem: sortedRegionItems) { + this->regionItemScrollAreaViewportLayout->addWidget(regionItem); + } +} + +FocusedRegionItem* MemoryRegionManagerWindow::addFocusedRegion(const FocusedMemoryRegion& region) { + auto* focusedRegionItem = new FocusedRegionItem(region, this->regionItemScrollAreaViewport); + this->focusedRegionItems.insert(focusedRegionItem); + + this->regionItemScrollAreaViewportLayout->addWidget(focusedRegionItem); + this->stackedFormLayout->addWidget(focusedRegionItem->getFormWidget()); + + QObject::connect(focusedRegionItem, &RegionItem::selected, this, &MemoryRegionManagerWindow::onRegionSelected); + + return focusedRegionItem; +} + +ExcludedRegionItem* MemoryRegionManagerWindow::addExcludedRegion(const ExcludedMemoryRegion& region) { + auto* excludedRegionItem = new ExcludedRegionItem(region, this->regionItemScrollAreaViewport); + this->excludedRegionItems.insert(excludedRegionItem); + + this->regionItemScrollAreaViewportLayout->addWidget(excludedRegionItem); + this->stackedFormLayout->addWidget(excludedRegionItem->getFormWidget()); + + QObject::connect(excludedRegionItem, &RegionItem::selected, this, &MemoryRegionManagerWindow::onRegionSelected); + + return excludedRegionItem; +} + +void MemoryRegionManagerWindow::onRegionSelected(RegionItem* selectedRegion) { + if (this->selectedRegion != nullptr && this->selectedRegion != selectedRegion) { + this->selectedRegion->setSelected(false); + } + + this->selectedRegion = selectedRegion; + this->stackedFormLayout->setCurrentWidget(this->selectedRegion->getFormWidget()); +} + +void MemoryRegionManagerWindow::onNewFocusedRegionTrigger() { + using Targets::TargetMemoryAddressRange; + + auto* region = this->addFocusedRegion(FocusedMemoryRegion( + "Untitled Region", + this->memoryDescriptor, + TargetMemoryAddressRange( + this->memoryDescriptor.addressRange.startAddress, + this->memoryDescriptor.addressRange.startAddress + 10 + ) + )); + + region->setSelected(true); +} + +void MemoryRegionManagerWindow::onNewExcludedRegionTrigger() { + using Targets::TargetMemoryAddressRange; + + auto* region = this->addExcludedRegion(ExcludedMemoryRegion( + "Untitled Region", + this->memoryDescriptor, + TargetMemoryAddressRange( + this->memoryDescriptor.addressRange.startAddress, + this->memoryDescriptor.addressRange.startAddress + 10 + ) + )); + + region->setSelected(true); +} + +void MemoryRegionManagerWindow::onDeleteRegionTrigger() { + if (this->selectedRegion == nullptr) { + return; + } + + auto* regionItem = this->selectedRegion; + const auto& region = regionItem->getMemoryRegion(); + + if (region.type == MemoryRegionType::FOCUSED) { + auto* focusedRegionItem = qobject_cast(regionItem); + + if (focusedRegionItem != nullptr) { + this->focusedRegionItems.erase(focusedRegionItem); + } + + } else { + auto* excludedRegionItem = qobject_cast(regionItem); + + if (excludedRegionItem != nullptr) { + this->excludedRegionItems.erase(excludedRegionItem); + } + } + + regionItem->getFormWidget()->deleteLater(); + this->regionItemScrollAreaViewportLayout->removeWidget(regionItem); + regionItem->deleteLater(); + + this->selectedRegion = nullptr; + + if (this->regionItemScrollAreaViewportLayout->count() > 0) { + auto* regionItem = qobject_cast( + this->regionItemScrollAreaViewportLayout->itemAt(0)->widget() + ); + + if (regionItem != nullptr) { + regionItem->setSelected(true); + } + } +} + +void MemoryRegionManagerWindow::applyChanges() { + auto processedFocusedMemoryRegions = std::vector(); + auto processedExcludedMemoryRegions = std::vector(); + + for (auto* focusedRegionItem : this->focusedRegionItems) { + const auto validationFailures = focusedRegionItem->getValidationFailures(); + + if (!validationFailures.empty()) { + auto* errorDialogue = new ErrorDialogue( + "Invalid memory region", + "Invalid memory region \"" + focusedRegionItem->getRegionNameInputValue() + "\"" + + "

- " + validationFailures.join("
- "), + this + ); + errorDialogue->show(); + return; + } + + auto focusedRegion = focusedRegionItem->generateFocusedMemoryRegionFromInput(); + for (const auto& processedFocusedRegion : processedFocusedMemoryRegions) { + if (processedFocusedRegion.intersectsWith(focusedRegion)) { + auto* errorDialogue = new ErrorDialogue( + "Intersection region found", + "Region \"" + focusedRegionItem->getRegionNameInputValue() + + "\" intersects with region \"" + processedFocusedRegion.name + "\". " + + "Regions cannot intersect. Please review the relevant address ranges.", + this + ); + errorDialogue->show(); + return; + } + } + + processedFocusedMemoryRegions.emplace_back(focusedRegion); + } + + for (auto* excludedRegionItem : this->excludedRegionItems) { + const auto validationFailures = excludedRegionItem->getValidationFailures(); + + if (!validationFailures.empty()) { + auto* errorDialogue = new ErrorDialogue( + "Invalid memory region", + "Invalid memory region \"" + excludedRegionItem->getRegionNameInputValue() + "\"" + + "

- " + validationFailures.join("
- "), + this + ); + errorDialogue->show(); + return; + } + + auto excludedRegion = excludedRegionItem->generateExcludedMemoryRegionFromInput(); + for (const auto& processedFocusedRegion : processedFocusedMemoryRegions) { + if (processedFocusedRegion.intersectsWith(excludedRegion)) { + auto* errorDialogue = new ErrorDialogue( + "Intersecting region found", + "Region \"" + excludedRegionItem->getRegionNameInputValue() + + "\" intersects with region \"" + processedFocusedRegion.name + "\". " + + "Regions cannot intersect. Please review the relevant address ranges.", + this + ); + errorDialogue->show(); + return; + } + } + + for (const auto& processedExcludedRegion : processedExcludedMemoryRegions) { + if (processedExcludedRegion.intersectsWith(excludedRegion)) { + auto* errorDialogue = new ErrorDialogue( + "Intersecting region found", + "Region \"" + excludedRegionItem->getRegionNameInputValue() + + "\" intersects with region \"" + processedExcludedRegion.name + "\". " + + "Regions cannot intersect. Please review the relevant address ranges.", + this + ); + errorDialogue->show(); + return; + } + } + + processedExcludedMemoryRegions.emplace_back(excludedRegion); + } + + this->focusedMemoryRegions = std::move(processedFocusedMemoryRegions); + this->excludedMemoryRegions = std::move(processedExcludedMemoryRegions); + this->close(); +} + +void MemoryRegionManagerWindow::openHelpPage() { + QDesktopServices::openUrl( + QUrl(QString::fromStdString(Paths::homeDomainName() + "/docs/manage-memory-regions")) + ); +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/MemoryRegionManagerWindow.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/MemoryRegionManagerWindow.hpp new file mode 100644 index 00000000..e02aa757 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/MemoryRegionManagerWindow.hpp @@ -0,0 +1,82 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "src/Targets/TargetMemory.hpp" + +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegion.hpp" +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/FocusedMemoryRegion.hpp" +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/ExcludedMemoryRegion.hpp" + +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/SvgToolButton.hpp" +#include "RegionItem.hpp" +#include "FocusedRegionItem.hpp" +#include "ExcludedRegionItem.hpp" + +namespace Bloom::Widgets +{ + class MemoryRegionManagerWindow: public QWidget + { + Q_OBJECT + + public: + explicit MemoryRegionManagerWindow( + const Targets::TargetMemoryDescriptor& memoryDescriptor, + std::vector& focusedMemoryRegions, + std::vector& excludedMemoryRegions, + QWidget* parent = nullptr + ); + + void refreshRegions(); + + protected: + void showEvent(QShowEvent* event) override; + + private: + const Targets::TargetMemoryDescriptor& memoryDescriptor; + std::vector& focusedMemoryRegions; + std::vector& excludedMemoryRegions; + + QWidget* container = nullptr; + QWidget* regionSelector = nullptr; + + QPushButton* applyButton = nullptr; + QPushButton* helpButton = nullptr; + QPushButton* closeButton = nullptr; + + SvgToolButton* addRegionButton = nullptr; + QAction* addFocusedRegionMenuAction = nullptr; + QAction* addExcludedRegionMenuAction = nullptr; + SvgToolButton* removeRegionButton = nullptr; + + QScrollArea* regionItemScrollArea = nullptr; + QWidget* regionItemScrollAreaViewport = nullptr; + QVBoxLayout* regionItemScrollAreaViewportLayout = nullptr; + + QStackedLayout* stackedFormLayout = nullptr; + + std::set focusedRegionItems; + std::set excludedRegionItems; + + RegionItem* selectedRegion = nullptr; + + void clearRegions(); + void sortRegionItems(); + FocusedRegionItem* addFocusedRegion(const FocusedMemoryRegion& region); + ExcludedRegionItem* addExcludedRegion(const ExcludedMemoryRegion& region); + void onRegionSelected(RegionItem* selectedRegion); + void onNewFocusedRegionTrigger(); + void onNewExcludedRegionTrigger(); + void onDeleteRegionTrigger(); + void applyChanges(); + void openHelpPage(); + }; +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/RegionItem.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/RegionItem.cpp new file mode 100644 index 00000000..bb823234 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/RegionItem.cpp @@ -0,0 +1,202 @@ +#include "RegionItem.hpp" + +#include +#include + +using namespace Bloom; +using namespace Bloom::Widgets; + +RegionItem::RegionItem( + const MemoryRegion& region, + QWidget* parent +): ClickableWidget(parent) { + this->setObjectName("region-item"); + this->setFixedHeight(50); + this->layout->setContentsMargins(5, 5, 5, 0); + + this->timeLabel->setText(region.createdDate.toString("hh:mm")); + this->timeLabel->setObjectName("time-label"); + + auto regionName = region.name; + regionName.truncate(RegionItem::NAME_LABEL_MAX_LENGTH); + this->nameLabel->setText(regionName); + this->nameLabel->setObjectName("name-label"); + + this->typeLabel->setText(region.type == MemoryRegionType::FOCUSED ? "Focused" : "Excluded"); + this->typeLabel->setObjectName("type-label"); + + auto addressRange = region.getAbsoluteAddressRange(); + this->addressRangeLabel->setText( + "0x" + QString::number(addressRange.startAddress, 16).toUpper() + QString(" -> ") + + "0x" + QString::number(addressRange.endAddress, 16).toUpper() + ); + this->addressRangeLabel->setObjectName("address-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->typeLabel, 0, Qt::AlignmentFlag::AlignRight); + + auto* bottomLabelLayout = new QHBoxLayout(); + bottomLabelLayout->setSpacing(0); + bottomLabelLayout->setContentsMargins(0, 0, 0, 0); + bottomLabelLayout->addWidget(this->addressRangeLabel, 0, Qt::AlignmentFlag::AlignLeft); + bottomLabelLayout->addStretch(1); + bottomLabelLayout->addWidget(this->timeLabel, 0, Qt::AlignmentFlag::AlignRight); + + 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 RegionItem::setSelected(bool selected) { + this->setProperty("selected", selected); + this->style()->unpolish(this); + this->style()->polish(this); + + if (selected) { + emit this->selected(this); + } +} + +QStringList RegionItem::getValidationFailures() const { + auto validationFailures = QStringList(); + + const auto& memoryDescriptor = this->getMemoryRegion().memoryDescriptor; + + if (this->nameInput->text().isEmpty()) { + validationFailures.emplace_back("Missing region name."); + } + + bool conversionOk = false; + + std::uint32_t startAddress = this->startAddressInput->text().toUInt(&conversionOk, 16); + if (!conversionOk) { + validationFailures.emplace_back("Invalid start address."); + } + + std::uint32_t endAddress = this->endAddressInput->text().toUInt(&conversionOk, 16); + if (!conversionOk) { + validationFailures.emplace_back("Invalid end address."); + } + + if (startAddress > endAddress) { + validationFailures.emplace_back("The start address exceeds the end address."); + } + + auto addressType = this->getSelectedAddressType(); + const auto memoryAddressRange = memoryDescriptor.addressRange; + + const auto memoryAddressRangeStr = QString( + "0x" + QString::number(memoryAddressRange.startAddress, 16).toUpper() + QString(" -> ") + + "0x" + QString::number(memoryAddressRange.endAddress, 16).toUpper() + ); + + std::uint32_t absoluteStartAddress = addressType == MemoryRegionAddressType::RELATIVE ? + memoryAddressRange.startAddress + startAddress : startAddress; + + std::uint32_t absoluteEndAddress = addressType == MemoryRegionAddressType::RELATIVE ? + memoryAddressRange.startAddress + endAddress : endAddress; + + if (absoluteStartAddress < memoryAddressRange.startAddress + || absoluteStartAddress > memoryAddressRange.endAddress + ) { + validationFailures.emplace_back( + "The start address is not within the absolute memory address range (" + memoryAddressRangeStr + ")." + ); + } + + if (absoluteEndAddress < memoryAddressRange.startAddress || absoluteEndAddress > memoryAddressRange.endAddress) { + validationFailures.emplace_back( + "The end address not within the absolute memory address range (" + memoryAddressRangeStr + ")." + ); + } + + return validationFailures; +} + +void RegionItem::initFormInputs() { + const auto& region = this->getMemoryRegion(); + + this->nameInput = this->formWidget->findChild("name-input"); + this->addressTypeInput = this->formWidget->findChild("address-type-input"); + this->startAddressInput = this->formWidget->findChild("start-address-input"); + this->endAddressInput = this->formWidget->findChild("end-address-input"); + this->sizeInput = this->formWidget->findChild("size-input"); + + this->nameInput->setText(region.name); + this->sizeInput->setText(QString::number((region.addressRange.endAddress - region.addressRange.startAddress) + 1)); + + for (const auto& [optionName, option] : RegionItem::addressRangeTypeOptionsByName) { + this->addressTypeInput->addItem(option.text, optionName); + } + + if (region.addressRangeType == MemoryRegionAddressType::RELATIVE) { + this->addressTypeInput->setCurrentText(RegionItem::addressRangeTypeOptionsByName.at("relative").text); + + auto addressRange = region.getRelativeAddressRange(); + this->startAddressInput->setText("0x" + QString::number(addressRange.startAddress, 16).toUpper()); + this->endAddressInput->setText("0x" + QString::number(addressRange.endAddress, 16).toUpper()); + + } else { + this->addressTypeInput->setCurrentText(RegionItem::addressRangeTypeOptionsByName.at("absolute").text); + + auto addressRange = region.getAbsoluteAddressRange(); + this->startAddressInput->setText("0x" + QString::number(addressRange.startAddress, 16).toUpper()); + this->endAddressInput->setText("0x" + QString::number(addressRange.endAddress, 16).toUpper()); + } + + QObject::connect(this->startAddressInput, &QLineEdit::textEdited, this, &RegionItem::onAddressRangeInputChange); + QObject::connect(this->endAddressInput, &QLineEdit::textEdited, this, &RegionItem::onAddressRangeInputChange); + QObject::connect(this->sizeInput, &QLineEdit::textEdited, this, &RegionItem::onSizeInputChange); + QObject::connect(this->nameInput, &QLineEdit::textEdited, this, &RegionItem::onNameInputChange); +} + +MemoryRegionAddressType RegionItem::getSelectedAddressType() const { + auto selectedAddressTypeOptionName = this->addressTypeInput->currentData().toString(); + if (RegionItem::addressRangeTypeOptionsByName.contains(selectedAddressTypeOptionName)) { + return RegionItem::addressRangeTypeOptionsByName.at(selectedAddressTypeOptionName).addressType; + } + + return MemoryRegionAddressType::ABSOLUTE; +} + +void RegionItem::onAddressRangeInputChange() { + bool startAddressConversionOk = false; + bool endAddressConversionOk = false; + std::uint32_t startAddress = this->startAddressInput->text().toUInt(&startAddressConversionOk, 16); + std::uint32_t endAddress = this->endAddressInput->text().toUInt(&endAddressConversionOk, 16); + + if (startAddressConversionOk && endAddressConversionOk && startAddress <= endAddress) { + this->sizeInput->setText(QString::number((endAddress - startAddress) + 1)); + } +} + +void RegionItem::onSizeInputChange() { + bool startAddressConversionOk = false; + bool sizeConversionOk = false; + std::uint32_t startAddress = this->startAddressInput->text().toUInt(&startAddressConversionOk, 16); + std::uint32_t size = this->sizeInput->text().toUInt(&sizeConversionOk, 10); + + if (startAddressConversionOk && sizeConversionOk && size > 0) { + this->endAddressInput->setText("0x" + QString::number((startAddress + size) - 1, 16).toUpper()); + } +} + +void RegionItem::onNameInputChange() { + auto newName = this->nameInput->text(); + newName.truncate(RegionItem::NAME_LABEL_MAX_LENGTH); + this->nameLabel->setText(newName); +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/RegionItem.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/RegionItem.hpp new file mode 100644 index 00000000..c0768c41 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/RegionItem.hpp @@ -0,0 +1,79 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/ClickableWidget.hpp" +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TextInput.hpp" + +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegion.hpp" + +namespace Bloom::Widgets +{ + struct AddressRangeTypeOption + { + QString text; + MemoryRegionAddressType addressType; + + AddressRangeTypeOption(const QString& text, MemoryRegionAddressType addressType) + : text(text), addressType(addressType) {}; + }; + + class RegionItem: public ClickableWidget + { + Q_OBJECT + + public: + RegionItem(const MemoryRegion& region, QWidget *parent); + void setSelected(bool selected); + + [[nodiscard]] QWidget* getFormWidget() const { + return this->formWidget; + } + + [[nodiscard]] QString getRegionNameInputValue() const { + return this->nameInput->text(); + } + + [[nodiscard]] virtual const MemoryRegion& getMemoryRegion() const = 0; + [[nodiscard]] virtual QStringList getValidationFailures() const; + + signals: + void selected(RegionItem*); + + protected: + static constexpr int NAME_LABEL_MAX_LENGTH = 34; + + QWidget* formWidget = nullptr; + TextInput* nameInput = nullptr; + QComboBox* addressTypeInput = nullptr; + TextInput* startAddressInput = nullptr; + TextInput* endAddressInput = nullptr; + TextInput* sizeInput = nullptr; + + virtual void initFormInputs(); + [[nodiscard]] MemoryRegionAddressType getSelectedAddressType() const; + + private: + QVBoxLayout* layout = new QVBoxLayout(this); + QLabel* nameLabel = new QLabel(this); + QLabel* typeLabel = new QLabel(this); + QLabel* addressRangeLabel = new QLabel(this); + QLabel* timeLabel = new QLabel(this); + + static inline const std::map addressRangeTypeOptionsByName = std::map< + QString, AddressRangeTypeOption + >({ + {"absolute", AddressRangeTypeOption("Absolute", MemoryRegionAddressType::ABSOLUTE)}, + {"relative", AddressRangeTypeOption("Relative", MemoryRegionAddressType::RELATIVE)}, + }); + + void onAddressRangeInputChange(); + void onSizeInputChange(); + void onNameInputChange(); + }; +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/Stylesheets/MemoryRegionManagerWindow.qss b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/Stylesheets/MemoryRegionManagerWindow.qss new file mode 100644 index 00000000..b3c13b8f --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/Stylesheets/MemoryRegionManagerWindow.qss @@ -0,0 +1,122 @@ +#memory-region-manager-window { + background-color: #373835; +} + +#title-label { + font-size: 15px; +} + +#region-selector { + border-right: 1px solid #2F2F2D; +} + +#region-selector #region-item-scroll-area { + border-top: none; + border-bottom: none; + border-left: none; +} + +#region-selector-tool-bar { + border-bottom: 1px solid #2F2F2D; + margin: 0; +} + +#region-selector-tool-bar QToolButton { + background-color: transparent; + border: none; + padding: 0; + qproperty-buttonWidth: 20; + qproperty-buttonHeight: 18; +} + +#region-selector-tool-bar QToolButton::menu-indicator { + image: none; +} + +#region-selector-tool-bar QToolButton:hover { + background-color: #41413b; + border: none; + padding: 0; +} + +#region-selector-tool-bar QToolButton:checked { + background-color: #43433d; + border: none; +} + +#region-selector-tool-bar QToolButton:checked:disabled { + background-color: #3d3d39; + border: none; +} + +#region-selector-tool-bar #separator { + background-color: transparent; + border-right: 1px solid #41423f; + padding: 0; + min-width: 1px; + max-width: 1px; +} + +#region-selector #region-item-scroll-area, +#region-selector #region-item-scroll-area #item-container { + background-color: transparent; +} + +#region-selector #region-item { + border-bottom: 1px solid #2e2e2e; + background-color: transparent; +} + +#region-selector #region-item[selected=true] { + background-color: #355A80; +} + +#region-selector #region-item #name-label { + font-size: 14px +} + +#region-selector #region-item #address-label { + font-size: 13px; + font-style: italic; + color: #8a8a8d; +} + +#region-selector #region-item #type-label, +#region-selector #region-item #time-label { + font-size: 13px; + color: #8a8a8d; +} + +/* Region form */ +#form-container #name-label { + min-width: 60px; + max-width: 60px; +} + +#form-container #address-type-label, +#form-container #start-address-label, +#form-container #end-address-label { + min-width: 130px; + max-width: 130px; +} + +#form-container #size-label { + min-width: 30px; + max-width: 30px; +} + +#form-container #address-range-description-label, +#form-container #value-annotations-description-label { + color: #8a8a8d; +} + +/* Actions */ +#actions { + border-top: 1px solid #2F2F2D; + min-height: 50px; +} + +#apply-btn { + background-color: #353C41; + border: 1px solid #454C52; +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/UiFiles/ExcludedMemoryRegionForm.ui b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/UiFiles/ExcludedMemoryRegionForm.ui new file mode 100644 index 00000000..2cb974e6 --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/UiFiles/ExcludedMemoryRegionForm.ui @@ -0,0 +1,351 @@ + + + + + + + + + 0 + + + + + + 10 + + + + QSizePolicy::Fixed + + + + + + + + + + + + Name: + + + + + + + + 20 + + + + QSizePolicy::Fixed + + + + + + + 300 + + + + + + + + 300 + + + + QSizePolicy::Fixed + + + + + + + + + + 15 + + + + QSizePolicy::Fixed + + + + + + + Address Range + + + + + + + + 15 + + + + QSizePolicy::Fixed + + + + + + + + + + true + + + The region's address range can be specified in relative or absolute form. Absolute addresses must account for any relevant offsets. Address ranges displayed in the region selector will always be in absolute form. + + + + + + + + 15 + + + + QSizePolicy::Fixed + + + + + + + + + + + + Address type: + + + + + + + + 20 + + + + QSizePolicy::Fixed + + + + + + + + + + 350 + + + false + + + + + + + + 10 + + + + QSizePolicy::Fixed + + + + + + + + + + 10 + + + + QSizePolicy::Fixed + + + + + + + + + + + + Start address: + + + + + + + + 20 + + + + QSizePolicy::Fixed + + + + + + + + + + 350 + + + + + + + + 10 + + + + QSizePolicy::Fixed + + + + + + + + + + 10 + + + + QSizePolicy::Fixed + + + + + + + + + + + + End address: + + + + + + + + 20 + + + + QSizePolicy::Fixed + + + + + + + + + + 150 + + + + + + + + 10 + + + + QSizePolicy::Fixed + + + + + + + + + + Size: + + + + + + + + 5 + + + + QSizePolicy::Fixed + + + + + + + + + + 60 + + + 60 + + + + + + + + 10 + + + + QSizePolicy::Fixed + + + + + + + + + Qt::Vertical + + + + + + diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/UiFiles/FocusedMemoryRegionForm.ui b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/UiFiles/FocusedMemoryRegionForm.ui new file mode 100644 index 00000000..fc30ea7f --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/UiFiles/FocusedMemoryRegionForm.ui @@ -0,0 +1,458 @@ + + + + + + + + + 0 + + + + + + 10 + + + + QSizePolicy::Fixed + + + + + + + + + + + + Name: + + + + + + + + 20 + + + + QSizePolicy::Fixed + + + + + + + 300 + + + + + + + + 300 + + + + QSizePolicy::Fixed + + + + + + + + + + 15 + + + + QSizePolicy::Fixed + + + + + + + Address Range + + + + + + + + 15 + + + + QSizePolicy::Fixed + + + + + + + + + + true + + + The region's address range can be specified in relative or absolute form. Absolute addresses must account for any relevant offsets. Address ranges displayed in the region selector will always be in absolute form. + + + + + + + + 15 + + + + QSizePolicy::Fixed + + + + + + + + + + + + Address type: + + + + + + + + 20 + + + + QSizePolicy::Fixed + + + + + + + + + + 350 + + + false + + + + + + + + 10 + + + + QSizePolicy::Fixed + + + + + + + + + + 10 + + + + QSizePolicy::Fixed + + + + + + + + + + + + Start address: + + + + + + + + 20 + + + + QSizePolicy::Fixed + + + + + + + + + + 350 + + + + + + + + 10 + + + + QSizePolicy::Fixed + + + + + + + + + + 10 + + + + QSizePolicy::Fixed + + + + + + + + + + + + End address: + + + + + + + + 20 + + + + QSizePolicy::Fixed + + + + + + + + + + 150 + + + + + + + + 10 + + + + QSizePolicy::Fixed + + + + + + + + + + Size: + + + + + + + + 5 + + + + QSizePolicy::Fixed + + + + + + + + + + 60 + + + 60 + + + + + + + + 10 + + + + QSizePolicy::Fixed + + + + + + + + + + 20 + + + + QSizePolicy::Fixed + + + + + + + Value Annotation + + + + + + + + 15 + + + + QSizePolicy::Fixed + + + + + + + + + + true + + + Specifying the type of data stored in this region will allow Bloom to present the current value of the data, in the form of a value annotation. + + + + + + + + 15 + + + + QSizePolicy::Fixed + + + + + + + + + + + + Data type: + + + + + + + + 20 + + + + QSizePolicy::Fixed + + + + + + + + + + 150 + + + false + + + + + + + + 10 + + + + QSizePolicy::Fixed + + + + + + + + + Qt::Vertical + + + + + + diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/UiFiles/MemoryRegionManagerWindow.ui b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/UiFiles/MemoryRegionManagerWindow.ui new file mode 100644 index 00000000..f9b5bebc --- /dev/null +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/UiFiles/MemoryRegionManagerWindow.ui @@ -0,0 +1,233 @@ + + + + + + 0 + + + 0 + + + + + 0 + + + 0 + + + + + 300 + + + 300 + + + + + + + 0 + + + 0 + + + + + 30 + + + + + + + 3 + + + 0 + + + + + + 5 + + + + QSizePolicy::Fixed + + + + + + + false + + + QToolButton::InstantPopup + + + :/compiled/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/Images/add-region.svg + + + :/compiled/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/Images/add-region.svg + + + Add Memory Region + + + + + + + Focused Region + + + + + Excluded Region + + + + + + + + + false + + + :/compiled/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/Images/remove-region.svg + + + :/compiled/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/Images/remove-region.svg + + + Remove Memory Region + + + + + + + Qt::Horizontal + + + + + + + + + true + Qt::ScrollBarAsNeeded + QAbstractScrollArea::AdjustToContents + Qt::ScrollBarAlwaysOff + + + + + + + + + + 0 + + + 0 + + + QLayout::SetMinAndMaxSize + + + + + + + + + + + + + 10 + + + + QSizePolicy::Fixed + + + + + + + + + + + + + 15 + + + + + + 10 + + + + QSizePolicy::Fixed + + + + + + + Help + + + + + + + Qt::Horizontal + + + + + + + Close + + + + + + + OK + + + + + + + + 10 + + + + QSizePolicy::Fixed + + + + + + + + + diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/TargetMemoryInspectionPane.cpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/TargetMemoryInspectionPane.cpp index 159531d0..e0ec70a9 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/TargetMemoryInspectionPane.cpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/TargetMemoryInspectionPane.cpp @@ -1,9 +1,8 @@ #include "TargetMemoryInspectionPane.hpp" -#include +#include #include #include -#include #include "src/Insight/UserInterfaces/InsightWindow/UiLoader.hpp" @@ -12,7 +11,6 @@ #include "src/Helpers/Paths.hpp" #include "src/Exceptions/Exception.hpp" -#include "src/Logger/Logger.hpp" using namespace Bloom::Widgets; using namespace Bloom::Exceptions; @@ -44,17 +42,25 @@ TargetMemoryInspectionPane::TargetMemoryInspectionPane( this->titleBar = this->container->findChild("title-bar"); this->titleBar->layout()->setContentsMargins(7, 0, 7, 0); - auto titleLabel = this->titleBar->findChild("title"); + auto* titleLabel = this->titleBar->findChild("title"); titleLabel->setText( this->targetMemoryDescriptor.type == TargetMemoryType::EEPROM ? "Internal EEPROM" : "Internal RAM" ); - auto subContainerLayout = this->container->findChild("sub-container-layout"); + auto* subContainerLayout = this->container->findChild("sub-container-layout"); + this->manageMemoryRegionsButton = this->container->findChild("manage-memory-regions-btn"); this->hexViewerWidget = new HexViewerWidget(this->targetMemoryDescriptor, this->insightWorker, this); this->hexViewerWidget->setDisabled(true); subContainerLayout->addWidget(this->hexViewerWidget); + QObject::connect( + this->manageMemoryRegionsButton, + &QToolButton::clicked, + this, + &TargetMemoryInspectionPane::openMemoryRegionManagerWindow + ); + QObject::connect( &insightWorker, &InsightWorker::targetStateUpdated, @@ -172,3 +178,22 @@ void TargetMemoryInspectionPane::onTargetStateChanged(Targets::TargetState newSt void TargetMemoryInspectionPane::onMemoryRead(const Targets::TargetMemoryBuffer& buffer) { this->hexViewerWidget->updateValues(buffer); } + +void TargetMemoryInspectionPane::openMemoryRegionManagerWindow() { + if (this->memoryRegionManagerWindow == nullptr) { + this->memoryRegionManagerWindow = new MemoryRegionManagerWindow( + this->targetMemoryDescriptor, + this->focusedMemoryRegions, + this->excludedMemoryRegions, + this + ); + } + + if (!this->memoryRegionManagerWindow->isVisible()) { + this->memoryRegionManagerWindow->refreshRegions(); + this->memoryRegionManagerWindow->show(); + + } else { + this->memoryRegionManagerWindow->activateWindow(); + } +} diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/TargetMemoryInspectionPane.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/TargetMemoryInspectionPane.hpp index b489f481..604fe6e8 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/TargetMemoryInspectionPane.hpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/TargetMemoryInspectionPane.hpp @@ -1,23 +1,24 @@ #pragma once #include -#include -#include -#include -#include -#include -#include -#include +#include +#include -#include "src/Insight/UserInterfaces/InsightWindow/Widgets/PanelWidget.hpp" -#include "src/Insight/UserInterfaces/InsightWindow/Widgets/SvgToolButton.hpp" #include "src/Insight/InsightWorker/InsightWorker.hpp" -#include "HexViewerWidget/HexViewerWidget.hpp" - #include "src/Targets/TargetMemory.hpp" #include "src/Targets/TargetState.hpp" +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/PanelWidget.hpp" +#include "src/Insight/UserInterfaces/InsightWindow/Widgets/SvgToolButton.hpp" + +#include "HexViewerWidget/HexViewerWidget.hpp" + +#include "MemoryRegionManager/MemoryRegionManagerWindow.hpp" +#include "MemoryRegion.hpp" +#include "FocusedMemoryRegion.hpp" +#include "ExcludedMemoryRegion.hpp" + namespace Bloom::Widgets { class TargetMemoryInspectionPane: public QWidget @@ -34,7 +35,6 @@ namespace Bloom::Widgets ); void refreshMemoryValues(std::optional> callback = std::nullopt); - void activate(); void deactivate(); @@ -52,12 +52,17 @@ namespace Bloom::Widgets QWidget* container = nullptr; QWidget* titleBar = nullptr; + SvgToolButton* manageMemoryRegionsButton = nullptr; HexViewerWidget* hexViewerWidget = nullptr; Targets::TargetState targetState = Targets::TargetState::UNKNOWN; - private slots: + std::vector focusedMemoryRegions; + std::vector excludedMemoryRegions; + MemoryRegionManagerWindow* memoryRegionManagerWindow = nullptr; + void onTargetStateChanged(Targets::TargetState newState); void onMemoryRead(const Targets::TargetMemoryBuffer& buffer); + void openMemoryRegionManagerWindow(); }; } diff --git a/src/resources.qrc b/src/resources.qrc index d33224a5..ed2aaeae 100644 --- a/src/resources.qrc +++ b/src/resources.qrc @@ -56,6 +56,15 @@ ./Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/RegisterHistoryWidget/UiFiles/RegisterHistoryWidget.ui ./Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/Images/icon.svg + + ./Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/UiFiles/MemoryRegionManagerWindow.ui + ./Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/Stylesheets/MemoryRegionManagerWindow.qss + ./Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/UiFiles/FocusedMemoryRegionForm.ui + ./Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/UiFiles/ExcludedMemoryRegionForm.ui + ./Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/Images/icon.svg + ./Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/Images/add-region.svg + ./Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/MemoryRegionManager/Images/remove-region.svg + ./Insight/UserInterfaces/InsightWindow/UiFiles/AboutWindow.ui ./Insight/UserInterfaces/InsightWindow/Stylesheets/AboutWindow.qss