Initial project scaffold for Setec Partition Wizard

C++17/Qt6 application for disk recovery, repair, flashing, formatting,
and USB security key creation on Windows. Includes CMake build system,
tabbed UI shell with 6 main tabs, core type system with Result<T>
monadic error handling, admin elevation, and dark Catppuccin theme.
This commit is contained in:
DigiJ
2026-03-11 19:11:25 -07:00
commit 179bb85c4f
42 changed files with 2399 additions and 0 deletions

30
src/ui/CMakeLists.txt Normal file
View File

@@ -0,0 +1,30 @@
set(UI_SOURCES
MainWindow.cpp
tabs/DiskPartitionTab.cpp
tabs/RecoveryTab.cpp
tabs/ImagingTab.cpp
tabs/DiagnosticsTab.cpp
tabs/SecurityTab.cpp
tabs/MaintenanceTab.cpp
)
set(UI_HEADERS
MainWindow.h
tabs/DiskPartitionTab.h
tabs/RecoveryTab.h
tabs/ImagingTab.h
tabs/DiagnosticsTab.h
tabs/SecurityTab.h
tabs/MaintenanceTab.h
)
add_library(spw_ui STATIC ${UI_SOURCES} ${UI_HEADERS})
target_include_directories(spw_ui PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/..
)
target_link_libraries(spw_ui PUBLIC
spw_core
Qt6::Widgets
)

149
src/ui/MainWindow.cpp Normal file
View File

@@ -0,0 +1,149 @@
#include "MainWindow.h"
#include "tabs/DiskPartitionTab.h"
#include "tabs/RecoveryTab.h"
#include "tabs/ImagingTab.h"
#include "tabs/DiagnosticsTab.h"
#include "tabs/SecurityTab.h"
#include "tabs/MaintenanceTab.h"
#include "core/common/Version.h"
#include <QAction>
#include <QApplication>
#include <QMenuBar>
#include <QMessageBox>
#include <QStatusBar>
#include <QTabWidget>
#include <QToolBar>
namespace spw
{
MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent)
{
setWindowTitle(QString("%1 v%2").arg(AppName, VersionString));
resize(1280, 800);
setMinimumSize(1024, 600);
setupMenuBar();
setupToolBar();
setupTabs();
setupStatusBar();
}
MainWindow::~MainWindow() = default;
void MainWindow::setupMenuBar()
{
auto* fileMenu = menuBar()->addMenu(tr("&File"));
fileMenu->addAction(tr("&Refresh Disks"), this, &MainWindow::onRefreshDisks, QKeySequence::Refresh);
fileMenu->addSeparator();
fileMenu->addAction(tr("E&xit"), qApp, &QApplication::quit, QKeySequence::Quit);
auto* diskMenu = menuBar()->addMenu(tr("&Disk"));
diskMenu->addAction(tr("&Clone Disk..."));
diskMenu->addAction(tr("Create &Image..."));
diskMenu->addAction(tr("&Restore Image..."));
diskMenu->addAction(tr("&Flash ISO/IMG..."));
diskMenu->addSeparator();
diskMenu->addAction(tr("&Secure Erase..."));
auto* partitionMenu = menuBar()->addMenu(tr("&Partition"));
partitionMenu->addAction(tr("&Create..."));
partitionMenu->addAction(tr("&Delete"));
partitionMenu->addAction(tr("&Resize/Move..."));
partitionMenu->addAction(tr("&Format..."));
partitionMenu->addSeparator();
partitionMenu->addAction(tr("&Merge..."));
partitionMenu->addAction(tr("&Split..."));
auto* toolsMenu = menuBar()->addMenu(tr("&Tools"));
toolsMenu->addAction(tr("&S.M.A.R.T. Info..."));
toolsMenu->addAction(tr("&Benchmark..."));
toolsMenu->addAction(tr("S&urface Scan..."));
toolsMenu->addSeparator();
toolsMenu->addAction(tr("&Boot Repair..."));
auto* helpMenu = menuBar()->addMenu(tr("&Help"));
helpMenu->addAction(tr("&About..."), this, &MainWindow::onAbout);
}
void MainWindow::setupToolBar()
{
m_toolBar = addToolBar(tr("Main Toolbar"));
m_toolBar->setMovable(false);
m_toolBar->setIconSize(QSize(24, 24));
m_toolBar->addAction(tr("Refresh"));
m_toolBar->addSeparator();
m_toolBar->addAction(tr("Create"));
m_toolBar->addAction(tr("Delete"));
m_toolBar->addAction(tr("Resize"));
m_toolBar->addAction(tr("Format"));
m_toolBar->addSeparator();
m_toolBar->addAction(tr("Clone"));
m_toolBar->addAction(tr("Flash"));
m_toolBar->addSeparator();
// Apply button (prominent)
auto* applyAction = m_toolBar->addAction(tr("Apply"));
if (auto* widget = m_toolBar->widgetForAction(applyAction))
{
widget->setObjectName("applyButton");
}
auto* cancelAction = m_toolBar->addAction(tr("Undo All"));
if (auto* widget = m_toolBar->widgetForAction(cancelAction))
{
widget->setObjectName("cancelButton");
}
}
void MainWindow::setupTabs()
{
m_tabWidget = new QTabWidget(this);
m_tabWidget->setTabPosition(QTabWidget::North);
m_tabWidget->setDocumentMode(true);
m_diskPartitionTab = new DiskPartitionTab(this);
m_recoveryTab = new RecoveryTab(this);
m_imagingTab = new ImagingTab(this);
m_diagnosticsTab = new DiagnosticsTab(this);
m_securityTab = new SecurityTab(this);
m_maintenanceTab = new MaintenanceTab(this);
m_tabWidget->addTab(m_diskPartitionTab, tr("Disks && Partitions"));
m_tabWidget->addTab(m_recoveryTab, tr("Recovery"));
m_tabWidget->addTab(m_imagingTab, tr("Imaging && Flashing"));
m_tabWidget->addTab(m_diagnosticsTab, tr("Diagnostics"));
m_tabWidget->addTab(m_securityTab, tr("Security Keys"));
m_tabWidget->addTab(m_maintenanceTab, tr("Maintenance"));
setCentralWidget(m_tabWidget);
}
void MainWindow::setupStatusBar()
{
statusBar()->showMessage(tr("Ready — No pending operations"));
}
void MainWindow::onAbout()
{
QMessageBox::about(this, tr("About %1").arg(AppName),
tr("<h2>%1</h2>"
"<p>Version %2</p>"
"<p>A comprehensive disk recovery, repair, flashing, and formatting tool "
"for Windows.</p>"
"<p>Supports all major and legacy filesystems, partition table formats "
"(MBR, GPT, APM), USB security key creation, and media imaging.</p>"
"<p>Copyright &copy; 2026 Setec</p>")
.arg(AppName, VersionString));
}
void MainWindow::onRefreshDisks()
{
statusBar()->showMessage(tr("Refreshing disk list..."), 2000);
// TODO: Call DiskController::refresh()
}
} // namespace spw

51
src/ui/MainWindow.h Normal file
View File

@@ -0,0 +1,51 @@
#pragma once
#include <QMainWindow>
class QTabWidget;
class QMenuBar;
class QToolBar;
class QStatusBar;
namespace spw
{
class DiskPartitionTab;
class RecoveryTab;
class ImagingTab;
class DiagnosticsTab;
class SecurityTab;
class MaintenanceTab;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget* parent = nullptr);
~MainWindow() override;
private:
void setupMenuBar();
void setupToolBar();
void setupTabs();
void setupStatusBar();
private slots:
void onAbout();
void onRefreshDisks();
private:
QTabWidget* m_tabWidget = nullptr;
QToolBar* m_toolBar = nullptr;
// Tabs
DiskPartitionTab* m_diskPartitionTab = nullptr;
RecoveryTab* m_recoveryTab = nullptr;
ImagingTab* m_imagingTab = nullptr;
DiagnosticsTab* m_diagnosticsTab = nullptr;
SecurityTab* m_securityTab = nullptr;
MaintenanceTab* m_maintenanceTab = nullptr;
};
} // namespace spw

View File

@@ -0,0 +1,90 @@
#include "DiagnosticsTab.h"
#include <QComboBox>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QProgressBar>
#include <QPushButton>
#include <QSplitter>
#include <QTableWidget>
#include <QVBoxLayout>
namespace spw
{
DiagnosticsTab::DiagnosticsTab(QWidget* parent)
: QWidget(parent)
{
setupUi();
}
DiagnosticsTab::~DiagnosticsTab() = default;
void DiagnosticsTab::setupUi()
{
auto* layout = new QVBoxLayout(this);
// Disk selector
auto* selectorLayout = new QHBoxLayout();
selectorLayout->addWidget(new QLabel(tr("Select Disk:")));
auto* diskCombo = new QComboBox();
selectorLayout->addWidget(diskCombo, 1);
auto* refreshBtn = new QPushButton(tr("Refresh"));
selectorLayout->addWidget(refreshBtn);
layout->addLayout(selectorLayout);
auto* splitter = new QSplitter(Qt::Horizontal);
// S.M.A.R.T. panel
auto* smartGroup = new QGroupBox(tr("S.M.A.R.T. Health"));
auto* smartLayout = new QVBoxLayout(smartGroup);
auto* healthLabel = new QLabel(tr("Overall Health: —"));
healthLabel->setStyleSheet("font-size: 16px; font-weight: bold; padding: 8px;");
smartLayout->addWidget(healthLabel);
auto* smartTable = new QTableWidget(0, 5);
smartTable->setHorizontalHeaderLabels(
{tr("ID"), tr("Attribute"), tr("Value"), tr("Worst"), tr("Threshold")});
smartTable->setAlternatingRowColors(true);
smartLayout->addWidget(smartTable);
splitter->addWidget(smartGroup);
// Benchmark & Surface Scan panel
auto* rightPanel = new QWidget();
auto* rightLayout = new QVBoxLayout(rightPanel);
auto* benchGroup = new QGroupBox(tr("Benchmark"));
auto* benchLayout = new QVBoxLayout(benchGroup);
auto* benchResults = new QLabel(
tr("Sequential Read: — MB/s\n"
"Sequential Write: — MB/s\n"
"Random 4K Read: — IOPS\n"
"Random 4K Write: — IOPS"));
benchResults->setStyleSheet("font-family: monospace; padding: 8px;");
benchLayout->addWidget(benchResults);
auto* benchBtn = new QPushButton(tr("Run Benchmark"));
benchBtn->setObjectName("applyButton");
benchLayout->addWidget(benchBtn);
rightLayout->addWidget(benchGroup);
auto* scanGroup = new QGroupBox(tr("Surface Scan"));
auto* scanLayout = new QVBoxLayout(scanGroup);
auto* scanInfo = new QLabel(tr("Sectors: — total, — bad, — pending"));
scanLayout->addWidget(scanInfo);
auto* scanProgress = new QProgressBar();
scanLayout->addWidget(scanProgress);
auto* scanBtn = new QPushButton(tr("Start Surface Scan"));
scanBtn->setObjectName("applyButton");
scanLayout->addWidget(scanBtn);
rightLayout->addWidget(scanGroup);
rightLayout->addStretch();
splitter->addWidget(rightPanel);
layout->addWidget(splitter);
}
} // namespace spw

View File

@@ -0,0 +1,20 @@
#pragma once
#include <QWidget>
namespace spw
{
class DiagnosticsTab : public QWidget
{
Q_OBJECT
public:
explicit DiagnosticsTab(QWidget* parent = nullptr);
~DiagnosticsTab() override;
private:
void setupUi();
};
} // namespace spw

View File

@@ -0,0 +1,107 @@
#include "DiskPartitionTab.h"
#include <QHBoxLayout>
#include <QHeaderView>
#include <QLabel>
#include <QListWidget>
#include <QPushButton>
#include <QSplitter>
#include <QTableView>
#include <QTreeView>
#include <QVBoxLayout>
namespace spw
{
DiskPartitionTab::DiskPartitionTab(QWidget* parent)
: QWidget(parent)
{
setupUi();
}
DiskPartitionTab::~DiskPartitionTab() = default;
void DiskPartitionTab::setupUi()
{
auto* layout = new QHBoxLayout(this);
layout->setContentsMargins(4, 4, 4, 4);
m_mainSplitter = new QSplitter(Qt::Horizontal, this);
// Left panel: Disk tree view
auto* leftPanel = new QWidget();
auto* leftLayout = new QVBoxLayout(leftPanel);
leftLayout->setContentsMargins(0, 0, 0, 0);
auto* diskLabel = new QLabel(tr("Physical Disks"));
diskLabel->setStyleSheet("font-weight: bold; padding: 4px;");
leftLayout->addWidget(diskLabel);
m_diskTree = new QTreeView();
m_diskTree->setHeaderHidden(false);
m_diskTree->setAlternatingRowColors(true);
m_diskTree->setMinimumWidth(250);
leftLayout->addWidget(m_diskTree);
m_mainSplitter->addWidget(leftPanel);
// Center + Bottom: partition map and table
m_rightSplitter = new QSplitter(Qt::Vertical);
// Disk map placeholder (will be replaced by DiskMapWidget)
m_diskMapPlaceholder = new QWidget();
m_diskMapPlaceholder->setMinimumHeight(120);
auto* mapLayout = new QVBoxLayout(m_diskMapPlaceholder);
auto* mapLabel = new QLabel(tr("Partition Map"));
mapLabel->setAlignment(Qt::AlignCenter);
mapLabel->setStyleSheet("color: #6c7086; font-size: 14px;");
mapLayout->addWidget(mapLabel);
m_rightSplitter->addWidget(m_diskMapPlaceholder);
// Partition detail table
m_partitionTable = new QTableView();
m_partitionTable->setAlternatingRowColors(true);
m_partitionTable->setSelectionBehavior(QAbstractItemView::SelectRows);
m_partitionTable->setSelectionMode(QAbstractItemView::SingleSelection);
m_partitionTable->horizontalHeader()->setStretchLastSection(true);
m_rightSplitter->addWidget(m_partitionTable);
m_rightSplitter->setStretchFactor(0, 2);
m_rightSplitter->setStretchFactor(1, 3);
m_mainSplitter->addWidget(m_rightSplitter);
// Right panel: pending operations list
m_operationList = new QWidget();
auto* opLayout = new QVBoxLayout(m_operationList);
opLayout->setContentsMargins(0, 0, 0, 0);
auto* opLabel = new QLabel(tr("Pending Operations"));
opLabel->setStyleSheet("font-weight: bold; padding: 4px;");
opLayout->addWidget(opLabel);
auto* opListWidget = new QListWidget();
opListWidget->setMinimumWidth(220);
opLayout->addWidget(opListWidget);
auto* buttonLayout = new QHBoxLayout();
auto* applyBtn = new QPushButton(tr("Apply"));
applyBtn->setObjectName("applyButton");
auto* undoBtn = new QPushButton(tr("Undo"));
auto* clearBtn = new QPushButton(tr("Clear"));
buttonLayout->addWidget(applyBtn);
buttonLayout->addWidget(undoBtn);
buttonLayout->addWidget(clearBtn);
opLayout->addLayout(buttonLayout);
m_mainSplitter->addWidget(m_operationList);
// Set splitter proportions
m_mainSplitter->setStretchFactor(0, 1); // Disk tree
m_mainSplitter->setStretchFactor(1, 3); // Center content
m_mainSplitter->setStretchFactor(2, 1); // Operation list
layout->addWidget(m_mainSplitter);
}
} // namespace spw

View File

@@ -0,0 +1,39 @@
#pragma once
#include <QWidget>
class QSplitter;
class QTreeView;
class QTableView;
namespace spw
{
class DiskPartitionTab : public QWidget
{
Q_OBJECT
public:
explicit DiskPartitionTab(QWidget* parent = nullptr);
~DiskPartitionTab() override;
private:
void setupUi();
QSplitter* m_mainSplitter = nullptr;
QSplitter* m_rightSplitter = nullptr;
// Left panel: disk tree
QTreeView* m_diskTree = nullptr;
// Center: partition map (placeholder for DiskMapWidget)
QWidget* m_diskMapPlaceholder = nullptr;
// Bottom: partition detail table
QTableView* m_partitionTable = nullptr;
// Right: operation list
QWidget* m_operationList = nullptr;
};
} // namespace spw

View File

@@ -0,0 +1,96 @@
#include "ImagingTab.h"
#include <QComboBox>
#include <QFileDialog>
#include <QGridLayout>
#include <QGroupBox>
#include <QLabel>
#include <QLineEdit>
#include <QProgressBar>
#include <QPushButton>
#include <QSpinBox>
#include <QVBoxLayout>
namespace spw
{
ImagingTab::ImagingTab(QWidget* parent)
: QWidget(parent)
{
setupUi();
}
ImagingTab::~ImagingTab() = default;
void ImagingTab::setupUi()
{
auto* layout = new QVBoxLayout(this);
// Clone Disk section
auto* cloneGroup = new QGroupBox(tr("Clone Disk"));
auto* cloneLayout = new QGridLayout(cloneGroup);
cloneLayout->addWidget(new QLabel(tr("Source:")), 0, 0);
cloneLayout->addWidget(new QComboBox(), 0, 1);
cloneLayout->addWidget(new QLabel(tr("Target:")), 1, 0);
cloneLayout->addWidget(new QComboBox(), 1, 1);
auto* cloneBtn = new QPushButton(tr("Clone"));
cloneBtn->setObjectName("applyButton");
cloneLayout->addWidget(cloneBtn, 2, 1, Qt::AlignRight);
layout->addWidget(cloneGroup);
// Create Image section
auto* imageGroup = new QGroupBox(tr("Create Disk/Media Image"));
auto* imageLayout = new QGridLayout(imageGroup);
imageLayout->addWidget(new QLabel(tr("Source:")), 0, 0);
auto* sourceCombo = new QComboBox();
sourceCombo->setToolTip(tr("Select disk, USB drive, SD card, or other media"));
imageLayout->addWidget(sourceCombo, 0, 1);
imageLayout->addWidget(new QLabel(tr("Output File:")), 1, 0);
auto* outputLine = new QLineEdit();
imageLayout->addWidget(outputLine, 1, 1);
auto* browseBtn = new QPushButton(tr("Browse..."));
imageLayout->addWidget(browseBtn, 1, 2);
imageLayout->addWidget(new QLabel(tr("Compression:")), 2, 0);
auto* compCombo = new QComboBox();
compCombo->addItems({tr("None"), tr("Fast (zstd-1)"), tr("Default (zstd-3)"), tr("Best (zstd-9)")});
imageLayout->addWidget(compCombo, 2, 1);
auto* createImgBtn = new QPushButton(tr("Create Image"));
createImgBtn->setObjectName("applyButton");
imageLayout->addWidget(createImgBtn, 3, 1, Qt::AlignRight);
layout->addWidget(imageGroup);
// Restore Image section
auto* restoreGroup = new QGroupBox(tr("Restore Image"));
auto* restoreLayout = new QGridLayout(restoreGroup);
restoreLayout->addWidget(new QLabel(tr("Image File:")), 0, 0);
restoreLayout->addWidget(new QLineEdit(), 0, 1);
restoreLayout->addWidget(new QPushButton(tr("Browse...")), 0, 2);
restoreLayout->addWidget(new QLabel(tr("Target:")), 1, 0);
restoreLayout->addWidget(new QComboBox(), 1, 1);
auto* restoreBtn = new QPushButton(tr("Restore"));
restoreBtn->setObjectName("applyButton");
restoreLayout->addWidget(restoreBtn, 2, 1, Qt::AlignRight);
layout->addWidget(restoreGroup);
// Flash ISO/IMG section
auto* flashGroup = new QGroupBox(tr("Flash ISO/IMG to USB"));
auto* flashLayout = new QGridLayout(flashGroup);
flashLayout->addWidget(new QLabel(tr("Image:")), 0, 0);
flashLayout->addWidget(new QLineEdit(), 0, 1);
flashLayout->addWidget(new QPushButton(tr("Browse...")), 0, 2);
flashLayout->addWidget(new QLabel(tr("Target USB:")), 1, 0);
flashLayout->addWidget(new QComboBox(), 1, 1);
auto* flashBtn = new QPushButton(tr("Flash"));
flashBtn->setObjectName("applyButton");
flashLayout->addWidget(flashBtn, 2, 1, Qt::AlignRight);
layout->addWidget(flashGroup);
// Progress
auto* progressBar = new QProgressBar();
progressBar->setVisible(false);
layout->addWidget(progressBar);
layout->addStretch();
}
} // namespace spw

20
src/ui/tabs/ImagingTab.h Normal file
View File

@@ -0,0 +1,20 @@
#pragma once
#include <QWidget>
namespace spw
{
class ImagingTab : public QWidget
{
Q_OBJECT
public:
explicit ImagingTab(QWidget* parent = nullptr);
~ImagingTab() override;
private:
void setupUi();
};
} // namespace spw

View File

@@ -0,0 +1,106 @@
#include "MaintenanceTab.h"
#include <QCheckBox>
#include <QComboBox>
#include <QGridLayout>
#include <QGroupBox>
#include <QLabel>
#include <QProgressBar>
#include <QPushButton>
#include <QRadioButton>
#include <QSpinBox>
#include <QVBoxLayout>
namespace spw
{
MaintenanceTab::MaintenanceTab(QWidget* parent)
: QWidget(parent)
{
setupUi();
}
MaintenanceTab::~MaintenanceTab() = default;
void MaintenanceTab::setupUi()
{
auto* layout = new QVBoxLayout(this);
// Secure Erase section
auto* eraseGroup = new QGroupBox(tr("Secure Erase"));
auto* eraseLayout = new QGridLayout(eraseGroup);
eraseLayout->addWidget(new QLabel(tr("Target Disk:")), 0, 0);
eraseLayout->addWidget(new QComboBox(), 0, 1);
eraseLayout->addWidget(new QLabel(tr("Erase Method:")), 1, 0);
auto* methodWidget = new QWidget();
auto* methodLayout = new QVBoxLayout(methodWidget);
methodLayout->setContentsMargins(0, 0, 0, 0);
auto* zeroPass = new QRadioButton(tr("Zero fill (1 pass) — Fast"));
zeroPass->setChecked(true);
auto* dod3Pass = new QRadioButton(tr("DoD 5220.22-M (3 passes) — Standard"));
auto* dod7Pass = new QRadioButton(tr("DoD 5220.22-M ECE (7 passes) — Enhanced"));
auto* gutmann = new QRadioButton(tr("Gutmann method (35 passes) — Maximum"));
auto* customPass = new QRadioButton(tr("Custom:"));
auto* customSpin = new QSpinBox();
customSpin->setRange(1, 99);
customSpin->setValue(3);
customSpin->setEnabled(false);
auto* customRow = new QHBoxLayout();
customRow->addWidget(customPass);
customRow->addWidget(customSpin);
customRow->addWidget(new QLabel(tr("passes")));
customRow->addStretch();
methodLayout->addWidget(zeroPass);
methodLayout->addWidget(dod3Pass);
methodLayout->addWidget(dod7Pass);
methodLayout->addWidget(gutmann);
methodLayout->addLayout(customRow);
eraseLayout->addWidget(methodWidget, 1, 1);
auto* verifyCheck = new QCheckBox(tr("Verify after erase"));
verifyCheck->setChecked(true);
eraseLayout->addWidget(verifyCheck, 2, 1);
auto* eraseBtn = new QPushButton(tr("Secure Erase"));
eraseBtn->setObjectName("cancelButton");
eraseBtn->setToolTip(tr("WARNING: This permanently destroys all data on the selected disk!"));
eraseLayout->addWidget(eraseBtn, 3, 1, Qt::AlignRight);
layout->addWidget(eraseGroup);
// Boot Repair section
auto* bootGroup = new QGroupBox(tr("Boot Repair"));
auto* bootLayout = new QVBoxLayout(bootGroup);
auto* bootInfo = new QLabel(
tr("Repair boot configuration for Windows and other operating systems."));
bootLayout->addWidget(bootInfo);
auto* mbrRepairBtn = new QPushButton(tr("Repair MBR"));
mbrRepairBtn->setToolTip(tr("Rewrite the Master Boot Record with a standard boot loader"));
auto* gptRepairBtn = new QPushButton(tr("Repair GPT"));
gptRepairBtn->setToolTip(tr("Rebuild GPT headers and verify partition entries"));
auto* bcdRepairBtn = new QPushButton(tr("Repair Windows BCD"));
bcdRepairBtn->setToolTip(tr("Rebuild the Windows Boot Configuration Data store"));
auto* bootloaderBtn = new QPushButton(tr("Reinstall Bootloader"));
bootloaderBtn->setToolTip(tr("Reinstall the bootloader to the selected disk's boot sector"));
bootLayout->addWidget(mbrRepairBtn);
bootLayout->addWidget(gptRepairBtn);
bootLayout->addWidget(bcdRepairBtn);
bootLayout->addWidget(bootloaderBtn);
layout->addWidget(bootGroup);
// Progress
auto* progressBar = new QProgressBar();
progressBar->setVisible(false);
layout->addWidget(progressBar);
layout->addStretch();
}
} // namespace spw

View File

@@ -0,0 +1,20 @@
#pragma once
#include <QWidget>
namespace spw
{
class MaintenanceTab : public QWidget
{
Q_OBJECT
public:
explicit MaintenanceTab(QWidget* parent = nullptr);
~MaintenanceTab() override;
private:
void setupUi();
};
} // namespace spw

View File

@@ -0,0 +1,91 @@
#include "RecoveryTab.h"
#include <QComboBox>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QListWidget>
#include <QProgressBar>
#include <QPushButton>
#include <QSplitter>
#include <QTableWidget>
#include <QVBoxLayout>
namespace spw
{
RecoveryTab::RecoveryTab(QWidget* parent)
: QWidget(parent)
{
setupUi();
}
RecoveryTab::~RecoveryTab() = default;
void RecoveryTab::setupUi()
{
auto* layout = new QHBoxLayout(this);
auto* splitter = new QSplitter(Qt::Horizontal);
// Left: recovery options
auto* optionsPanel = new QWidget();
auto* optLayout = new QVBoxLayout(optionsPanel);
auto* typeGroup = new QGroupBox(tr("Recovery Type"));
auto* typeLayout = new QVBoxLayout(typeGroup);
auto* partRecoveryBtn = new QPushButton(tr("Partition Recovery"));
partRecoveryBtn->setToolTip(tr("Scan for lost or deleted partitions"));
auto* fileRecoveryBtn = new QPushButton(tr("File Recovery"));
fileRecoveryBtn->setToolTip(tr("Recover files from damaged or formatted drives"));
auto* mbrRepairBtn = new QPushButton(tr("MBR/GPT Repair"));
mbrRepairBtn->setToolTip(tr("Rebuild partition table from filesystem superblocks"));
typeLayout->addWidget(partRecoveryBtn);
typeLayout->addWidget(fileRecoveryBtn);
typeLayout->addWidget(mbrRepairBtn);
optLayout->addWidget(typeGroup);
auto* targetGroup = new QGroupBox(tr("Target Disk"));
auto* targetLayout = new QVBoxLayout(targetGroup);
auto* diskCombo = new QComboBox();
diskCombo->addItem(tr("Select a disk..."));
targetLayout->addWidget(diskCombo);
optLayout->addWidget(targetGroup);
auto* scanBtn = new QPushButton(tr("Start Scan"));
scanBtn->setObjectName("applyButton");
optLayout->addWidget(scanBtn);
auto* progressBar = new QProgressBar();
progressBar->setVisible(false);
optLayout->addWidget(progressBar);
optLayout->addStretch();
splitter->addWidget(optionsPanel);
// Right: results
auto* resultsPanel = new QWidget();
auto* resLayout = new QVBoxLayout(resultsPanel);
auto* resLabel = new QLabel(tr("Recovery Results"));
resLabel->setStyleSheet("font-weight: bold; padding: 4px;");
resLayout->addWidget(resLabel);
auto* resultsTable = new QTableWidget(0, 5);
resultsTable->setHorizontalHeaderLabels(
{tr("Type"), tr("Name/Label"), tr("Size"), tr("Filesystem"), tr("Confidence")});
resultsTable->setAlternatingRowColors(true);
resultsTable->setSelectionBehavior(QAbstractItemView::SelectRows);
resLayout->addWidget(resultsTable);
auto* recoverBtn = new QPushButton(tr("Recover Selected"));
recoverBtn->setObjectName("applyButton");
resLayout->addWidget(recoverBtn);
splitter->addWidget(resultsPanel);
splitter->setStretchFactor(0, 1);
splitter->setStretchFactor(1, 2);
layout->addWidget(splitter);
}
} // namespace spw

20
src/ui/tabs/RecoveryTab.h Normal file
View File

@@ -0,0 +1,20 @@
#pragma once
#include <QWidget>
namespace spw
{
class RecoveryTab : public QWidget
{
Q_OBJECT
public:
explicit RecoveryTab(QWidget* parent = nullptr);
~RecoveryTab() override;
private:
void setupUi();
};
} // namespace spw

154
src/ui/tabs/SecurityTab.cpp Normal file
View File

@@ -0,0 +1,154 @@
#include "SecurityTab.h"
#include <QCheckBox>
#include <QComboBox>
#include <QGridLayout>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QListWidget>
#include <QProgressBar>
#include <QPushButton>
#include <QSpinBox>
#include <QTabWidget>
#include <QVBoxLayout>
namespace spw
{
SecurityTab::SecurityTab(QWidget* parent)
: QWidget(parent)
{
setupUi();
}
SecurityTab::~SecurityTab() = default;
void SecurityTab::setupUi()
{
auto* layout = new QVBoxLayout(this);
// Sub-tabs for the three security key types
auto* subTabs = new QTabWidget();
// --- FIDO2/WebAuthn Tab ---
auto* fido2Widget = new QWidget();
auto* fido2Layout = new QVBoxLayout(fido2Widget);
auto* fido2DevGroup = new QGroupBox(tr("FIDO2 Device"));
auto* fido2DevLayout = new QGridLayout(fido2DevGroup);
fido2DevLayout->addWidget(new QLabel(tr("Device:")), 0, 0);
auto* fido2DeviceCombo = new QComboBox();
fido2DeviceCombo->addItem(tr("No FIDO2 devices detected"));
fido2DevLayout->addWidget(fido2DeviceCombo, 0, 1);
auto* fido2RefreshBtn = new QPushButton(tr("Refresh"));
fido2DevLayout->addWidget(fido2RefreshBtn, 0, 2);
fido2DevLayout->addWidget(new QLabel(tr("Device Info:")), 1, 0);
fido2DevLayout->addWidget(new QLabel(tr("")), 1, 1);
fido2Layout->addWidget(fido2DevGroup);
auto* fido2OpsGroup = new QGroupBox(tr("Operations"));
auto* fido2OpsLayout = new QVBoxLayout(fido2OpsGroup);
auto* setPinBtn = new QPushButton(tr("Set/Change PIN"));
auto* genCredBtn = new QPushButton(tr("Generate Credential"));
auto* listCredsBtn = new QPushButton(tr("List Resident Keys"));
auto* resetBtn = new QPushButton(tr("Factory Reset Device"));
resetBtn->setObjectName("cancelButton");
fido2OpsLayout->addWidget(setPinBtn);
fido2OpsLayout->addWidget(genCredBtn);
fido2OpsLayout->addWidget(listCredsBtn);
fido2OpsLayout->addWidget(resetBtn);
fido2Layout->addWidget(fido2OpsGroup);
auto* fido2KeyList = new QListWidget();
fido2Layout->addWidget(new QLabel(tr("Resident Keys:")));
fido2Layout->addWidget(fido2KeyList);
subTabs->addTab(fido2Widget, tr("FIDO2 / WebAuthn"));
// --- Encrypted Vault Tab ---
auto* vaultWidget = new QWidget();
auto* vaultLayout = new QVBoxLayout(vaultWidget);
auto* vaultCreateGroup = new QGroupBox(tr("Create Encrypted Vault"));
auto* vaultCreateLayout = new QGridLayout(vaultCreateGroup);
vaultCreateLayout->addWidget(new QLabel(tr("USB Drive:")), 0, 0);
vaultCreateLayout->addWidget(new QComboBox(), 0, 1);
vaultCreateLayout->addWidget(new QLabel(tr("Vault Size:")), 1, 0);
auto* vaultSize = new QSpinBox();
vaultSize->setRange(1, 999999);
vaultSize->setSuffix(" MB");
vaultSize->setValue(256);
vaultCreateLayout->addWidget(vaultSize, 1, 1);
vaultCreateLayout->addWidget(new QLabel(tr("Encryption:")), 2, 0);
auto* encCombo = new QComboBox();
encCombo->addItems({tr("AES-256-XTS"), tr("AES-256-CBC"), tr("ChaCha20-Poly1305")});
vaultCreateLayout->addWidget(encCombo, 2, 1);
vaultCreateLayout->addWidget(new QLabel(tr("Password:")), 3, 0);
auto* passEdit = new QLineEdit();
passEdit->setEchoMode(QLineEdit::Password);
vaultCreateLayout->addWidget(passEdit, 3, 1);
vaultCreateLayout->addWidget(new QLabel(tr("Confirm:")), 4, 0);
auto* confirmEdit = new QLineEdit();
confirmEdit->setEchoMode(QLineEdit::Password);
vaultCreateLayout->addWidget(confirmEdit, 4, 1);
auto* keyFileCheck = new QCheckBox(tr("Also require key file"));
vaultCreateLayout->addWidget(keyFileCheck, 5, 1);
vaultLayout->addWidget(vaultCreateGroup);
auto* createVaultBtn = new QPushButton(tr("Create Vault"));
createVaultBtn->setObjectName("applyButton");
vaultLayout->addWidget(createVaultBtn);
auto* vaultManageGroup = new QGroupBox(tr("Manage Existing Vaults"));
auto* vaultManageLayout = new QVBoxLayout(vaultManageGroup);
auto* unlockBtn = new QPushButton(tr("Unlock Vault"));
auto* lockBtn = new QPushButton(tr("Lock Vault"));
auto* changePassBtn = new QPushButton(tr("Change Password"));
vaultManageLayout->addWidget(unlockBtn);
vaultManageLayout->addWidget(lockBtn);
vaultManageLayout->addWidget(changePassBtn);
vaultLayout->addWidget(vaultManageGroup);
vaultLayout->addStretch();
subTabs->addTab(vaultWidget, tr("Encrypted Vaults"));
// --- Boot Auth Key Tab ---
auto* bootAuthWidget = new QWidget();
auto* bootAuthLayout = new QVBoxLayout(bootAuthWidget);
auto* bootAuthGroup = new QGroupBox(tr("Boot Authentication Key"));
auto* bootAuthGridLayout = new QGridLayout(bootAuthGroup);
bootAuthGridLayout->addWidget(new QLabel(tr("USB Drive:")), 0, 0);
bootAuthGridLayout->addWidget(new QComboBox(), 0, 1);
bootAuthGridLayout->addWidget(new QLabel(tr("Target PC:")), 1, 0);
auto* pcIdLabel = new QLabel(tr("Current machine"));
bootAuthGridLayout->addWidget(pcIdLabel, 1, 1);
bootAuthGridLayout->addWidget(new QLabel(tr("Auth Method:")), 2, 0);
auto* authMethodCombo = new QComboBox();
authMethodCombo->addItems({tr("USB presence only"), tr("USB + PIN"), tr("USB + Password")});
bootAuthGridLayout->addWidget(authMethodCombo, 2, 1);
bootAuthLayout->addWidget(bootAuthGroup);
auto* createBootKeyBtn = new QPushButton(tr("Create Boot Auth Key"));
createBootKeyBtn->setObjectName("applyButton");
bootAuthLayout->addWidget(createBootKeyBtn);
auto* bootKeyInfoGroup = new QGroupBox(tr("Information"));
auto* bootKeyInfoLayout = new QVBoxLayout(bootKeyInfoGroup);
bootKeyInfoLayout->addWidget(new QLabel(
tr("A boot authentication key prevents your PC from booting\n"
"unless the USB key is inserted. The key material is paired\n"
"with your machine's hardware identity.\n\n"
"Warning: Keep a backup key! Losing the USB key may lock\n"
"you out of your system.")));
bootAuthLayout->addWidget(bootKeyInfoGroup);
bootAuthLayout->addStretch();
subTabs->addTab(bootAuthWidget, tr("Boot Authentication"));
layout->addWidget(subTabs);
}
} // namespace spw

20
src/ui/tabs/SecurityTab.h Normal file
View File

@@ -0,0 +1,20 @@
#pragma once
#include <QWidget>
namespace spw
{
class SecurityTab : public QWidget
{
Q_OBJECT
public:
explicit SecurityTab(QWidget* parent = nullptr);
~SecurityTab() override;
private:
void setupUi();
};
} // namespace spw