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:
11
.clang-format
Normal file
11
.clang-format
Normal file
@@ -0,0 +1,11 @@
|
||||
BasedOnStyle: Microsoft
|
||||
IndentWidth: 4
|
||||
ColumnLimit: 120
|
||||
PointerAlignment: Left
|
||||
ReferenceAlignment: Left
|
||||
BreakBeforeBraces: Allman
|
||||
AllowShortFunctionsOnASingleLine: Inline
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
SortIncludes: CaseInsensitive
|
||||
IncludeBlocks: Regroup
|
||||
11
.gitignore
vendored
Normal file
11
.gitignore
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
build/
|
||||
.vs/
|
||||
.vscode/
|
||||
*.user
|
||||
*.suo
|
||||
*.sdf
|
||||
*.opensdf
|
||||
*.db
|
||||
*.opendb
|
||||
CMakeUserPresets.json
|
||||
compile_commands.json
|
||||
28
CMakeLists.txt
Normal file
28
CMakeLists.txt
Normal file
@@ -0,0 +1,28 @@
|
||||
cmake_minimum_required(VERSION 3.25)
|
||||
project(SetecPartitionWizard VERSION 1.0.0 LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
|
||||
# Find Qt6
|
||||
find_package(Qt6 REQUIRED COMPONENTS Widgets Core)
|
||||
|
||||
# CMake helpers
|
||||
include(cmake/CompilerWarnings.cmake)
|
||||
include(cmake/Version.cmake)
|
||||
|
||||
# Third-party dependencies
|
||||
add_subdirectory(third_party)
|
||||
|
||||
# Main source tree
|
||||
add_subdirectory(src)
|
||||
|
||||
# Tests
|
||||
option(SPW_BUILD_TESTS "Build tests" ON)
|
||||
if(SPW_BUILD_TESTS)
|
||||
enable_testing()
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
54
CMakePresets.json
Normal file
54
CMakePresets.json
Normal file
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"version": 6,
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "default",
|
||||
"displayName": "Default (Debug)",
|
||||
"generator": "Ninja",
|
||||
"binaryDir": "${sourceDir}/build/${presetName}",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "release",
|
||||
"displayName": "Release",
|
||||
"generator": "Ninja",
|
||||
"binaryDir": "${sourceDir}/build/${presetName}",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "relwithdebinfo",
|
||||
"displayName": "Release with Debug Info",
|
||||
"generator": "Ninja",
|
||||
"binaryDir": "${sourceDir}/build/${presetName}",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "RelWithDebInfo"
|
||||
}
|
||||
}
|
||||
],
|
||||
"buildPresets": [
|
||||
{
|
||||
"name": "default",
|
||||
"configurePreset": "default"
|
||||
},
|
||||
{
|
||||
"name": "release",
|
||||
"configurePreset": "release"
|
||||
}
|
||||
],
|
||||
"testPresets": [
|
||||
{
|
||||
"name": "default",
|
||||
"configurePreset": "default",
|
||||
"output": { "outputOnFailure": true }
|
||||
},
|
||||
{
|
||||
"name": "release",
|
||||
"configurePreset": "release",
|
||||
"output": { "outputOnFailure": true }
|
||||
}
|
||||
]
|
||||
}
|
||||
13
cmake/CompilerWarnings.cmake
Normal file
13
cmake/CompilerWarnings.cmake
Normal file
@@ -0,0 +1,13 @@
|
||||
# Compiler warning settings for MSVC and GCC/Clang
|
||||
if(MSVC)
|
||||
add_compile_options(/W4 /permissive- /utf-8)
|
||||
# Treat warnings as errors in CI builds
|
||||
if(DEFINED ENV{CI})
|
||||
add_compile_options(/WX)
|
||||
endif()
|
||||
else()
|
||||
add_compile_options(-Wall -Wextra -Wpedantic)
|
||||
if(DEFINED ENV{CI})
|
||||
add_compile_options(-Werror)
|
||||
endif()
|
||||
endif()
|
||||
9
cmake/FindWMI.cmake
Normal file
9
cmake/FindWMI.cmake
Normal file
@@ -0,0 +1,9 @@
|
||||
# FindWMI.cmake - Locate WMI libraries on Windows
|
||||
# Sets WMI_FOUND, WMI_LIBRARIES
|
||||
|
||||
if(WIN32)
|
||||
set(WMI_LIBRARIES wbemuuid ole32 oleaut32 setupapi)
|
||||
set(WMI_FOUND TRUE)
|
||||
else()
|
||||
set(WMI_FOUND FALSE)
|
||||
endif()
|
||||
16
cmake/QtDeployHelper.cmake
Normal file
16
cmake/QtDeployHelper.cmake
Normal file
@@ -0,0 +1,16 @@
|
||||
# QtDeployHelper.cmake - Post-build windeployqt integration
|
||||
function(spw_deploy_qt TARGET)
|
||||
if(WIN32)
|
||||
find_program(WINDEPLOYQT windeployqt HINTS "${Qt6_DIR}/../../../bin")
|
||||
if(WINDEPLOYQT)
|
||||
add_custom_command(TARGET ${TARGET} POST_BUILD
|
||||
COMMAND "${WINDEPLOYQT}"
|
||||
--no-translations
|
||||
--no-system-d3d-compiler
|
||||
--no-opengl-sw
|
||||
"$<TARGET_FILE:${TARGET}>"
|
||||
COMMENT "Running windeployqt on ${TARGET}..."
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
12
cmake/Version.cmake
Normal file
12
cmake/Version.cmake
Normal file
@@ -0,0 +1,12 @@
|
||||
# Version.cmake - Parse version from project and expose as definitions
|
||||
set(SPW_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
|
||||
set(SPW_VERSION_MINOR ${PROJECT_VERSION_MINOR})
|
||||
set(SPW_VERSION_PATCH ${PROJECT_VERSION_PATCH})
|
||||
set(SPW_VERSION_STRING "${PROJECT_VERSION}")
|
||||
|
||||
add_compile_definitions(
|
||||
SPW_VERSION_MAJOR=${SPW_VERSION_MAJOR}
|
||||
SPW_VERSION_MINOR=${SPW_VERSION_MINOR}
|
||||
SPW_VERSION_PATCH=${SPW_VERSION_PATCH}
|
||||
SPW_VERSION_STRING="${SPW_VERSION_STRING}"
|
||||
)
|
||||
29
resources/setec.manifest
Normal file
29
resources/setec.manifest
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<assemblyIdentity
|
||||
version="1.0.0.0"
|
||||
processorArchitecture="amd64"
|
||||
name="Setec.PartitionWizard"
|
||||
type="win32"
|
||||
/>
|
||||
<description>Setec Partition Wizard</description>
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<security>
|
||||
<requestedPrivileges>
|
||||
<requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- Windows 10 and 11 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||
</application>
|
||||
</compatibility>
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
</assembly>
|
||||
37
resources/setec.rc
Normal file
37
resources/setec.rc
Normal file
@@ -0,0 +1,37 @@
|
||||
#include <winver.h>
|
||||
|
||||
// Application icon
|
||||
IDI_APPICON ICON "icons\\app.ico"
|
||||
|
||||
// UAC manifest
|
||||
1 RT_MANIFEST "setec.manifest"
|
||||
|
||||
// Version info
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,0,0,0
|
||||
PRODUCTVERSION 1,0,0,0
|
||||
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
|
||||
FILEFLAGS 0x0L
|
||||
FILEOS VOS_NT_WINDOWS32
|
||||
FILETYPE VFT_APP
|
||||
FILESUBTYPE VFT2_UNKNOWN
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904B0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Setec"
|
||||
VALUE "FileDescription", "Setec Partition Wizard"
|
||||
VALUE "FileVersion", "1.0.0.0"
|
||||
VALUE "InternalName", "SetecPartitionWizard"
|
||||
VALUE "OriginalFilename", "SetecPartitionWizard.exe"
|
||||
VALUE "ProductName", "Setec Partition Wizard"
|
||||
VALUE "ProductVersion", "1.0.0.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2026 Setec"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x0409, 1200
|
||||
END
|
||||
END
|
||||
165
resources/styles/default.qss
Normal file
165
resources/styles/default.qss
Normal file
@@ -0,0 +1,165 @@
|
||||
/* Setec Partition Wizard - Default Stylesheet */
|
||||
|
||||
QMainWindow {
|
||||
background-color: #1e1e2e;
|
||||
color: #cdd6f4;
|
||||
}
|
||||
|
||||
QTabWidget::pane {
|
||||
border: 1px solid #45475a;
|
||||
background-color: #1e1e2e;
|
||||
}
|
||||
|
||||
QTabBar::tab {
|
||||
background-color: #313244;
|
||||
color: #bac2de;
|
||||
padding: 8px 20px;
|
||||
margin-right: 2px;
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
QTabBar::tab:selected {
|
||||
background-color: #45475a;
|
||||
color: #cdd6f4;
|
||||
border-bottom: 2px solid #89b4fa;
|
||||
}
|
||||
|
||||
QTabBar::tab:hover:!selected {
|
||||
background-color: #3b3d52;
|
||||
}
|
||||
|
||||
QTreeView, QTableView, QListView {
|
||||
background-color: #181825;
|
||||
color: #cdd6f4;
|
||||
border: 1px solid #45475a;
|
||||
selection-background-color: #45475a;
|
||||
selection-color: #cdd6f4;
|
||||
alternate-background-color: #1e1e2e;
|
||||
}
|
||||
|
||||
QTreeView::item:hover, QTableView::item:hover {
|
||||
background-color: #313244;
|
||||
}
|
||||
|
||||
QHeaderView::section {
|
||||
background-color: #313244;
|
||||
color: #bac2de;
|
||||
padding: 4px 8px;
|
||||
border: 1px solid #45475a;
|
||||
}
|
||||
|
||||
QPushButton {
|
||||
background-color: #45475a;
|
||||
color: #cdd6f4;
|
||||
border: 1px solid #585b70;
|
||||
border-radius: 4px;
|
||||
padding: 6px 16px;
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
QPushButton:hover {
|
||||
background-color: #585b70;
|
||||
}
|
||||
|
||||
QPushButton:pressed {
|
||||
background-color: #313244;
|
||||
}
|
||||
|
||||
QPushButton#applyButton {
|
||||
background-color: #a6e3a1;
|
||||
color: #1e1e2e;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
QPushButton#applyButton:hover {
|
||||
background-color: #94e2d5;
|
||||
}
|
||||
|
||||
QPushButton#cancelButton {
|
||||
background-color: #f38ba8;
|
||||
color: #1e1e2e;
|
||||
}
|
||||
|
||||
QProgressBar {
|
||||
background-color: #313244;
|
||||
border: 1px solid #45475a;
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
color: #cdd6f4;
|
||||
}
|
||||
|
||||
QProgressBar::chunk {
|
||||
background-color: #89b4fa;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
QMenuBar {
|
||||
background-color: #181825;
|
||||
color: #cdd6f4;
|
||||
}
|
||||
|
||||
QMenuBar::item:selected {
|
||||
background-color: #45475a;
|
||||
}
|
||||
|
||||
QMenu {
|
||||
background-color: #1e1e2e;
|
||||
color: #cdd6f4;
|
||||
border: 1px solid #45475a;
|
||||
}
|
||||
|
||||
QMenu::item:selected {
|
||||
background-color: #45475a;
|
||||
}
|
||||
|
||||
QToolBar {
|
||||
background-color: #181825;
|
||||
border-bottom: 1px solid #45475a;
|
||||
spacing: 4px;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
QStatusBar {
|
||||
background-color: #181825;
|
||||
color: #a6adc8;
|
||||
border-top: 1px solid #45475a;
|
||||
}
|
||||
|
||||
QSplitter::handle {
|
||||
background-color: #45475a;
|
||||
}
|
||||
|
||||
QGroupBox {
|
||||
border: 1px solid #45475a;
|
||||
border-radius: 4px;
|
||||
margin-top: 8px;
|
||||
padding-top: 8px;
|
||||
color: #cdd6f4;
|
||||
}
|
||||
|
||||
QGroupBox::title {
|
||||
subcontrol-origin: margin;
|
||||
left: 10px;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
QLineEdit, QSpinBox, QComboBox {
|
||||
background-color: #313244;
|
||||
color: #cdd6f4;
|
||||
border: 1px solid #45475a;
|
||||
border-radius: 4px;
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
QLineEdit:focus, QSpinBox:focus, QComboBox:focus {
|
||||
border-color: #89b4fa;
|
||||
}
|
||||
|
||||
QToolTip {
|
||||
background-color: #313244;
|
||||
color: #cdd6f4;
|
||||
border: 1px solid #45475a;
|
||||
padding: 4px;
|
||||
}
|
||||
3
src/CMakeLists.txt
Normal file
3
src/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
add_subdirectory(core)
|
||||
add_subdirectory(ui)
|
||||
add_subdirectory(app)
|
||||
26
src/app/CMakeLists.txt
Normal file
26
src/app/CMakeLists.txt
Normal file
@@ -0,0 +1,26 @@
|
||||
set(APP_SOURCES
|
||||
main.cpp
|
||||
SingleInstance.cpp
|
||||
)
|
||||
|
||||
set(APP_HEADERS
|
||||
SingleInstance.h
|
||||
)
|
||||
|
||||
add_executable(SetecPartitionWizard WIN32
|
||||
${APP_SOURCES}
|
||||
${APP_HEADERS}
|
||||
${CMAKE_SOURCE_DIR}/resources/setec.rc
|
||||
)
|
||||
|
||||
target_include_directories(SetecPartitionWizard PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
)
|
||||
|
||||
target_link_libraries(SetecPartitionWizard PRIVATE
|
||||
spw_ui
|
||||
)
|
||||
|
||||
# Deploy Qt DLLs on install
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/QtDeployHelper.cmake)
|
||||
spw_deploy_qt(SetecPartitionWizard)
|
||||
36
src/app/SingleInstance.cpp
Normal file
36
src/app/SingleInstance.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#include "SingleInstance.h"
|
||||
|
||||
namespace spw
|
||||
{
|
||||
|
||||
SingleInstance::SingleInstance(const QString& mutexName)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
m_mutex = CreateMutexW(nullptr, FALSE, mutexName.toStdWString().c_str());
|
||||
if (m_mutex != nullptr)
|
||||
{
|
||||
m_isUnique = (GetLastError() != ERROR_ALREADY_EXISTS);
|
||||
}
|
||||
#else
|
||||
Q_UNUSED(mutexName);
|
||||
m_isUnique = true; // Non-Windows: always unique (placeholder)
|
||||
#endif
|
||||
}
|
||||
|
||||
SingleInstance::~SingleInstance()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (m_mutex != nullptr)
|
||||
{
|
||||
ReleaseMutex(m_mutex);
|
||||
CloseHandle(m_mutex);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool SingleInstance::isUnique() const
|
||||
{
|
||||
return m_isUnique;
|
||||
}
|
||||
|
||||
} // namespace spw
|
||||
32
src/app/SingleInstance.h
Normal file
32
src/app/SingleInstance.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
namespace spw
|
||||
{
|
||||
|
||||
// Prevents multiple instances of the application using a named kernel mutex.
|
||||
class SingleInstance
|
||||
{
|
||||
public:
|
||||
explicit SingleInstance(const QString& mutexName);
|
||||
~SingleInstance();
|
||||
|
||||
SingleInstance(const SingleInstance&) = delete;
|
||||
SingleInstance& operator=(const SingleInstance&) = delete;
|
||||
|
||||
// Returns true if this is the only running instance.
|
||||
bool isUnique() const;
|
||||
|
||||
private:
|
||||
#ifdef _WIN32
|
||||
HANDLE m_mutex = nullptr;
|
||||
#endif
|
||||
bool m_isUnique = false;
|
||||
};
|
||||
|
||||
} // namespace spw
|
||||
124
src/app/main.cpp
Normal file
124
src/app/main.cpp
Normal file
@@ -0,0 +1,124 @@
|
||||
#include "SingleInstance.h"
|
||||
#include "core/common/Logging.h"
|
||||
#include "core/common/Version.h"
|
||||
#include "ui/MainWindow.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QFile>
|
||||
#include <QMessageBox>
|
||||
#include <QStandardPaths>
|
||||
#include <QString>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
static bool isRunningAsAdmin()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
BOOL isAdmin = FALSE;
|
||||
PSID adminGroup = nullptr;
|
||||
SID_IDENTIFIER_AUTHORITY ntAuthority = SECURITY_NT_AUTHORITY;
|
||||
|
||||
if (AllocateAndInitializeSid(&ntAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID,
|
||||
DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &adminGroup))
|
||||
{
|
||||
CheckTokenMembership(nullptr, adminGroup, &isAdmin);
|
||||
FreeSid(adminGroup);
|
||||
}
|
||||
return isAdmin != FALSE;
|
||||
#else
|
||||
return geteuid() == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool relaunchAsAdmin()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
wchar_t path[MAX_PATH];
|
||||
GetModuleFileNameW(nullptr, path, MAX_PATH);
|
||||
|
||||
SHELLEXECUTEINFOW sei = {};
|
||||
sei.cbSize = sizeof(sei);
|
||||
sei.lpVerb = L"runas";
|
||||
sei.lpFile = path;
|
||||
sei.nShow = SW_SHOWNORMAL;
|
||||
|
||||
return ShellExecuteExW(&sei) != FALSE;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void applyStylesheet(QApplication& app)
|
||||
{
|
||||
QFile styleFile(":/styles/default.qss");
|
||||
if (styleFile.open(QFile::ReadOnly | QFile::Text))
|
||||
{
|
||||
app.setStyleSheet(styleFile.readAll());
|
||||
styleFile.close();
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
QApplication app(argc, argv);
|
||||
app.setApplicationName(spw::AppName);
|
||||
app.setApplicationVersion(spw::VersionString);
|
||||
app.setOrganizationName("Setec");
|
||||
|
||||
// Single instance check
|
||||
spw::SingleInstance instance(QStringLiteral("SetecPartitionWizard_SingleInstance_Mutex"));
|
||||
if (!instance.isUnique())
|
||||
{
|
||||
QMessageBox::warning(nullptr, spw::AppName,
|
||||
"Setec Partition Wizard is already running.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Initialize logging
|
||||
auto logDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
||||
QDir().mkpath(logDir);
|
||||
auto logPath = logDir + "/setec_partition_wizard.log";
|
||||
spw::log::init(logPath.toStdString());
|
||||
|
||||
spw::log::info(QString("Starting %1 v%2").arg(spw::AppName, spw::VersionString));
|
||||
|
||||
// Admin privilege check (defense-in-depth; manifest should handle UAC)
|
||||
if (!isRunningAsAdmin())
|
||||
{
|
||||
spw::log::warn("Not running as administrator — attempting elevation");
|
||||
auto answer = QMessageBox::question(
|
||||
nullptr, spw::AppName,
|
||||
"Setec Partition Wizard requires administrator privileges for disk operations.\n\n"
|
||||
"Restart as administrator?",
|
||||
QMessageBox::Yes | QMessageBox::No);
|
||||
|
||||
if (answer == QMessageBox::Yes)
|
||||
{
|
||||
if (relaunchAsAdmin())
|
||||
{
|
||||
return 0; // Exit this instance; elevated one will start
|
||||
}
|
||||
QMessageBox::critical(nullptr, spw::AppName, "Failed to elevate privileges.");
|
||||
}
|
||||
// Continue anyway — some read-only features may still work
|
||||
spw::log::warn("Continuing without admin privileges — write operations will fail");
|
||||
}
|
||||
|
||||
// Apply stylesheet
|
||||
applyStylesheet(app);
|
||||
|
||||
// Show main window
|
||||
spw::MainWindow mainWindow;
|
||||
mainWindow.show();
|
||||
|
||||
spw::log::info("Application ready");
|
||||
|
||||
int result = app.exec();
|
||||
|
||||
spw::log::info("Application shutting down");
|
||||
spw::log::shutdown();
|
||||
|
||||
return result;
|
||||
}
|
||||
51
src/core/CMakeLists.txt
Normal file
51
src/core/CMakeLists.txt
Normal file
@@ -0,0 +1,51 @@
|
||||
set(CORE_SOURCES
|
||||
# Common
|
||||
common/Logging.cpp
|
||||
|
||||
# Disk (stubs — will be implemented in Phase 2)
|
||||
# disk/RawDiskHandle.cpp
|
||||
# disk/VolumeHandle.cpp
|
||||
# disk/DiskEnumerator.cpp
|
||||
# disk/PartitionTable.cpp
|
||||
# disk/DiskGeometry.cpp
|
||||
# disk/VolumeManager.cpp
|
||||
|
||||
# Filesystem (stubs — will be implemented in Phase 4)
|
||||
# filesystem/FilesystemFactory.cpp
|
||||
# filesystem/FilesystemDetector.cpp
|
||||
# filesystem/NtfsDriver.cpp
|
||||
# filesystem/Fat32Driver.cpp
|
||||
|
||||
# Operations (stubs — will be implemented in Phase 3)
|
||||
# operations/OperationQueue.cpp
|
||||
# operations/OperationRunner.cpp
|
||||
)
|
||||
|
||||
set(CORE_HEADERS
|
||||
common/Types.h
|
||||
common/Result.h
|
||||
common/Error.h
|
||||
common/Constants.h
|
||||
common/Logging.h
|
||||
common/Version.h
|
||||
)
|
||||
|
||||
add_library(spw_core STATIC ${CORE_SOURCES} ${CORE_HEADERS})
|
||||
|
||||
target_include_directories(spw_core PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/..
|
||||
)
|
||||
|
||||
target_link_libraries(spw_core PUBLIC
|
||||
Qt6::Core
|
||||
)
|
||||
|
||||
# Windows system libraries
|
||||
if(WIN32)
|
||||
target_link_libraries(spw_core PUBLIC
|
||||
setupapi
|
||||
wbemuuid
|
||||
ole32
|
||||
oleaut32
|
||||
)
|
||||
endif()
|
||||
71
src/core/common/Constants.h
Normal file
71
src/core/common/Constants.h
Normal file
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace spw
|
||||
{
|
||||
|
||||
// Sector sizes
|
||||
constexpr uint32_t SECTOR_SIZE_512 = 512;
|
||||
constexpr uint32_t SECTOR_SIZE_4K = 4096;
|
||||
constexpr uint32_t DEFAULT_SECTOR_SIZE = SECTOR_SIZE_512;
|
||||
|
||||
// Partition alignment (1 MiB default — optimal for SSDs and 4K drives)
|
||||
constexpr uint64_t DEFAULT_ALIGNMENT_BYTES = 1048576; // 1 MiB
|
||||
constexpr uint64_t DEFAULT_ALIGNMENT_SECTORS_512 = DEFAULT_ALIGNMENT_BYTES / SECTOR_SIZE_512;
|
||||
|
||||
// MBR constants
|
||||
constexpr uint16_t MBR_SIGNATURE = 0xAA55;
|
||||
constexpr uint32_t MBR_SIZE = 512;
|
||||
constexpr int MBR_MAX_PRIMARY_PARTITIONS = 4;
|
||||
constexpr uint8_t MBR_PARTITION_ENTRY_OFFSET = 446;
|
||||
constexpr uint8_t MBR_PARTITION_ENTRY_SIZE = 16;
|
||||
|
||||
// GPT constants
|
||||
constexpr uint64_t GPT_HEADER_SIGNATURE = 0x5452415020494645ULL; // "EFI PART"
|
||||
constexpr uint32_t GPT_HEADER_SIZE = 92;
|
||||
constexpr uint32_t GPT_ENTRY_SIZE = 128;
|
||||
constexpr int GPT_MAX_PARTITIONS = 128;
|
||||
constexpr uint64_t GPT_HEADER_LBA = 1;
|
||||
|
||||
// Apple Partition Map constants
|
||||
constexpr uint16_t APM_SIGNATURE = 0x504D; // "PM"
|
||||
constexpr uint16_t APM_DDM_SIGNATURE = 0x4552; // "ER"
|
||||
|
||||
// Filesystem magic bytes
|
||||
constexpr uint32_t NTFS_MAGIC_OFFSET = 3; // "NTFS " at byte 3
|
||||
constexpr uint16_t EXT_SUPER_MAGIC = 0xEF53; // ext2/3/4 superblock magic at offset 1080
|
||||
constexpr uint32_t BTRFS_MAGIC_OFFSET = 0x10040; // "_BHRfS_M" at 64K + 64
|
||||
constexpr uint32_t XFS_MAGIC = 0x58465342; // "XFSB"
|
||||
constexpr uint16_t HFS_PLUS_MAGIC = 0x482B; // "H+"
|
||||
constexpr uint16_t HFSX_MAGIC = 0x4858; // "HX"
|
||||
constexpr uint32_t APFS_MAGIC = 0x4253584E; // "NXSB" (little-endian)
|
||||
constexpr uint16_t FAT_SIGNATURE = 0xAA55;
|
||||
constexpr uint32_t REFS_MAGIC = 0x53465265; // "ReFS"
|
||||
constexpr uint16_t HPFS_SUPER_MAGIC = 0xF995E849;
|
||||
constexpr uint16_t MINIX_SUPER_MAGIC = 0x137F;
|
||||
constexpr uint16_t MINIX2_SUPER_MAGIC = 0x2468;
|
||||
constexpr uint32_t UFS_MAGIC = 0x00011954;
|
||||
constexpr uint32_t UDF_MAGIC_BEA = 0x00424541; // BEA01
|
||||
constexpr uint32_t ISO9660_MAGIC = 0x30304443; // "CD001" identifier
|
||||
constexpr uint32_t BEOS_SUPER_MAGIC = 0x42465331; // BFS1
|
||||
constexpr uint16_t QNX4_SUPER_MAGIC = 0x002F;
|
||||
constexpr uint32_t REISERFS_MAGIC_OFFSET = 0x10034;
|
||||
constexpr uint32_t JFS_MAGIC = 0x3153464A; // "JFS1"
|
||||
|
||||
// Imaging
|
||||
constexpr uint32_t IMAGE_CHUNK_SIZE = 4 * 1024 * 1024; // 4 MiB chunks for compressed images
|
||||
constexpr char SPW_IMAGE_MAGIC[] = "SPWIMG01";
|
||||
|
||||
// Benchmark
|
||||
constexpr uint32_t BENCH_BLOCK_SEQ = 1024 * 1024; // 1 MiB sequential
|
||||
constexpr uint32_t BENCH_BLOCK_RND = 4096; // 4 KiB random
|
||||
constexpr int BENCH_DEFAULT_DURATION_SEC = 5;
|
||||
|
||||
// Secure erase
|
||||
constexpr int ERASE_PASS_ZERO = 1;
|
||||
constexpr int ERASE_PASS_DOD_3 = 3; // DoD 5220.22-M (3-pass)
|
||||
constexpr int ERASE_PASS_DOD_7 = 7; // DoD 5220.22-M ECE (7-pass)
|
||||
constexpr int ERASE_PASS_GUTMANN = 35; // Gutmann method
|
||||
|
||||
} // namespace spw
|
||||
114
src/core/common/Error.h
Normal file
114
src/core/common/Error.h
Normal file
@@ -0,0 +1,114 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace spw
|
||||
{
|
||||
|
||||
enum class ErrorCode
|
||||
{
|
||||
Success = 0,
|
||||
|
||||
// General
|
||||
Unknown,
|
||||
InvalidArgument,
|
||||
NotImplemented,
|
||||
OperationCanceled,
|
||||
InsufficientPrivileges,
|
||||
|
||||
// Disk I/O
|
||||
DiskNotFound,
|
||||
DiskAccessDenied,
|
||||
DiskReadError,
|
||||
DiskWriteError,
|
||||
DiskLockFailed,
|
||||
DiskDismountFailed,
|
||||
DiskBusy,
|
||||
SectorOutOfRange,
|
||||
|
||||
// Partition
|
||||
PartitionNotFound,
|
||||
PartitionTableCorrupt,
|
||||
PartitionTableFull,
|
||||
PartitionOverlap,
|
||||
PartitionTooSmall,
|
||||
PartitionTooLarge,
|
||||
InvalidPartitionType,
|
||||
AlignmentError,
|
||||
|
||||
// Filesystem
|
||||
FilesystemNotSupported,
|
||||
FilesystemCorrupt,
|
||||
FormatFailed,
|
||||
ResizeFailed,
|
||||
FilesystemTooSmallToShrink,
|
||||
FilesystemCheckFailed,
|
||||
|
||||
// Recovery
|
||||
NoPartitionsFound,
|
||||
NoFilesRecovered,
|
||||
RecoveryScanFailed,
|
||||
|
||||
// Imaging
|
||||
ImageCorrupt,
|
||||
ImageChecksumMismatch,
|
||||
ImageReadError,
|
||||
ImageWriteError,
|
||||
IsoParseError,
|
||||
InsufficientDiskSpace,
|
||||
|
||||
// Security
|
||||
Fido2DeviceNotFound,
|
||||
Fido2PinRequired,
|
||||
Fido2AuthFailed,
|
||||
EncryptionFailed,
|
||||
DecryptionFailed,
|
||||
KeyGenerationFailed,
|
||||
|
||||
// SMART / Diagnostics
|
||||
SmartNotSupported,
|
||||
SmartReadFailed,
|
||||
BenchmarkFailed,
|
||||
|
||||
// Boot
|
||||
BootRepairFailed,
|
||||
BcdNotFound,
|
||||
MbrRepairFailed,
|
||||
|
||||
// System
|
||||
OutOfMemory,
|
||||
FileNotFound,
|
||||
FileCreateFailed,
|
||||
WmiQueryFailed,
|
||||
};
|
||||
|
||||
struct ErrorInfo
|
||||
{
|
||||
ErrorCode code = ErrorCode::Success;
|
||||
std::string message;
|
||||
uint32_t win32Error = 0; // GetLastError() value
|
||||
long hresult = 0; // COM HRESULT if applicable
|
||||
|
||||
bool isOk() const { return code == ErrorCode::Success; }
|
||||
bool isError() const { return code != ErrorCode::Success; }
|
||||
|
||||
static ErrorInfo ok() { return {}; }
|
||||
|
||||
static ErrorInfo fromCode(ErrorCode code, const std::string& msg = "")
|
||||
{
|
||||
return {code, msg, 0, 0};
|
||||
}
|
||||
|
||||
static ErrorInfo fromWin32(ErrorCode code, uint32_t win32Err, const std::string& msg = "")
|
||||
{
|
||||
return {code, msg, win32Err, 0};
|
||||
}
|
||||
|
||||
static ErrorInfo fromHResult(ErrorCode code, long hr, const std::string& msg = "")
|
||||
{
|
||||
return {code, msg, 0, hr};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace spw
|
||||
97
src/core/common/Logging.cpp
Normal file
97
src/core/common/Logging.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
#include "Logging.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QDateTime>
|
||||
#include <QMutex>
|
||||
#include <QTextStream>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
namespace spw
|
||||
{
|
||||
namespace log
|
||||
{
|
||||
|
||||
static Level s_minLevel = Level::Info;
|
||||
static std::ofstream s_logFile;
|
||||
static QMutex s_mutex;
|
||||
|
||||
static const char* levelToString(Level level)
|
||||
{
|
||||
switch (level)
|
||||
{
|
||||
case Level::Trace:
|
||||
return "TRACE";
|
||||
case Level::Debug:
|
||||
return "DEBUG";
|
||||
case Level::Info:
|
||||
return "INFO ";
|
||||
case Level::Warn:
|
||||
return "WARN ";
|
||||
case Level::Error:
|
||||
return "ERROR";
|
||||
case Level::Critical:
|
||||
return "CRIT ";
|
||||
}
|
||||
return "?????";
|
||||
}
|
||||
|
||||
static void logImpl(Level level, const char* msg)
|
||||
{
|
||||
if (level < s_minLevel)
|
||||
return;
|
||||
|
||||
QMutexLocker lock(&s_mutex);
|
||||
|
||||
auto timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz");
|
||||
auto line = QString("[%1] [%2] %3").arg(timestamp, levelToString(level), msg);
|
||||
|
||||
// Write to debug output
|
||||
qDebug().noquote() << line;
|
||||
|
||||
// Write to file if open
|
||||
if (s_logFile.is_open())
|
||||
{
|
||||
s_logFile << line.toStdString() << "\n";
|
||||
s_logFile.flush();
|
||||
}
|
||||
}
|
||||
|
||||
void init(const std::string& logFilePath)
|
||||
{
|
||||
if (!logFilePath.empty())
|
||||
{
|
||||
s_logFile.open(logFilePath, std::ios::app);
|
||||
}
|
||||
info("Setec Partition Wizard logging initialized");
|
||||
}
|
||||
|
||||
void shutdown()
|
||||
{
|
||||
info("Logging shutdown");
|
||||
if (s_logFile.is_open())
|
||||
{
|
||||
s_logFile.close();
|
||||
}
|
||||
}
|
||||
|
||||
void setLevel(Level level) { s_minLevel = level; }
|
||||
|
||||
void trace(const char* msg) { logImpl(Level::Trace, msg); }
|
||||
void debug(const char* msg) { logImpl(Level::Debug, msg); }
|
||||
void info(const char* msg) { logImpl(Level::Info, msg); }
|
||||
void warn(const char* msg) { logImpl(Level::Warn, msg); }
|
||||
void error(const char* msg) { logImpl(Level::Error, msg); }
|
||||
void critical(const char* msg) { logImpl(Level::Critical, msg); }
|
||||
|
||||
void trace(const QString& msg) { logImpl(Level::Trace, msg.toUtf8().constData()); }
|
||||
void debug(const QString& msg) { logImpl(Level::Debug, msg.toUtf8().constData()); }
|
||||
void info(const QString& msg) { logImpl(Level::Info, msg.toUtf8().constData()); }
|
||||
void warn(const QString& msg) { logImpl(Level::Warn, msg.toUtf8().constData()); }
|
||||
void error(const QString& msg) { logImpl(Level::Error, msg.toUtf8().constData()); }
|
||||
void critical(const QString& msg) { logImpl(Level::Critical, msg.toUtf8().constData()); }
|
||||
|
||||
} // namespace log
|
||||
} // namespace spw
|
||||
47
src/core/common/Logging.h
Normal file
47
src/core/common/Logging.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <string>
|
||||
|
||||
namespace spw
|
||||
{
|
||||
namespace log
|
||||
{
|
||||
|
||||
enum class Level
|
||||
{
|
||||
Trace,
|
||||
Debug,
|
||||
Info,
|
||||
Warn,
|
||||
Error,
|
||||
Critical,
|
||||
};
|
||||
|
||||
// Initialize logging (call once from main)
|
||||
void init(const std::string& logFilePath = "");
|
||||
|
||||
// Shutdown logging
|
||||
void shutdown();
|
||||
|
||||
// Set minimum log level
|
||||
void setLevel(Level level);
|
||||
|
||||
// Log functions
|
||||
void trace(const char* msg);
|
||||
void debug(const char* msg);
|
||||
void info(const char* msg);
|
||||
void warn(const char* msg);
|
||||
void error(const char* msg);
|
||||
void critical(const char* msg);
|
||||
|
||||
// QString overloads
|
||||
void trace(const QString& msg);
|
||||
void debug(const QString& msg);
|
||||
void info(const QString& msg);
|
||||
void warn(const QString& msg);
|
||||
void error(const QString& msg);
|
||||
void critical(const QString& msg);
|
||||
|
||||
} // namespace log
|
||||
} // namespace spw
|
||||
121
src/core/common/Result.h
Normal file
121
src/core/common/Result.h
Normal file
@@ -0,0 +1,121 @@
|
||||
#pragma once
|
||||
|
||||
#include "Error.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <optional>
|
||||
#include <variant>
|
||||
|
||||
namespace spw
|
||||
{
|
||||
|
||||
// Monadic result type for disk operations.
|
||||
// Forces callers to handle errors — no exceptions can skip past a partial write.
|
||||
template <typename T>
|
||||
class Result
|
||||
{
|
||||
public:
|
||||
// Construct success
|
||||
Result(const T& value) : m_data(value) {}
|
||||
Result(T&& value) : m_data(std::move(value)) {}
|
||||
|
||||
// Construct failure
|
||||
Result(const ErrorInfo& error) : m_data(error)
|
||||
{
|
||||
assert(error.isError());
|
||||
}
|
||||
|
||||
Result(ErrorInfo&& error) : m_data(std::move(error))
|
||||
{
|
||||
assert(std::get<ErrorInfo>(m_data).isError());
|
||||
}
|
||||
|
||||
bool isOk() const { return std::holds_alternative<T>(m_data); }
|
||||
bool isError() const { return std::holds_alternative<ErrorInfo>(m_data); }
|
||||
|
||||
const T& value() const&
|
||||
{
|
||||
assert(isOk());
|
||||
return std::get<T>(m_data);
|
||||
}
|
||||
|
||||
T& value() &
|
||||
{
|
||||
assert(isOk());
|
||||
return std::get<T>(m_data);
|
||||
}
|
||||
|
||||
T&& value() &&
|
||||
{
|
||||
assert(isOk());
|
||||
return std::get<T>(std::move(m_data));
|
||||
}
|
||||
|
||||
const ErrorInfo& error() const
|
||||
{
|
||||
assert(isError());
|
||||
return std::get<ErrorInfo>(m_data);
|
||||
}
|
||||
|
||||
// Value or default
|
||||
T valueOr(const T& defaultVal) const
|
||||
{
|
||||
if (isOk())
|
||||
return value();
|
||||
return defaultVal;
|
||||
}
|
||||
|
||||
// Monadic map
|
||||
template <typename Func>
|
||||
auto map(Func&& func) const -> Result<decltype(func(std::declval<T>()))>
|
||||
{
|
||||
if (isOk())
|
||||
return func(value());
|
||||
return error();
|
||||
}
|
||||
|
||||
// Monadic flatMap / andThen
|
||||
template <typename Func>
|
||||
auto andThen(Func&& func) const -> decltype(func(std::declval<T>()))
|
||||
{
|
||||
if (isOk())
|
||||
return func(value());
|
||||
return error();
|
||||
}
|
||||
|
||||
explicit operator bool() const { return isOk(); }
|
||||
|
||||
private:
|
||||
std::variant<T, ErrorInfo> m_data;
|
||||
};
|
||||
|
||||
// Specialization for void results
|
||||
template <>
|
||||
class Result<void>
|
||||
{
|
||||
public:
|
||||
Result() : m_error(std::nullopt) {}
|
||||
|
||||
Result(const ErrorInfo& error) : m_error(error)
|
||||
{
|
||||
assert(error.isError());
|
||||
}
|
||||
|
||||
bool isOk() const { return !m_error.has_value(); }
|
||||
bool isError() const { return m_error.has_value(); }
|
||||
|
||||
const ErrorInfo& error() const
|
||||
{
|
||||
assert(isError());
|
||||
return m_error.value();
|
||||
}
|
||||
|
||||
explicit operator bool() const { return isOk(); }
|
||||
|
||||
static Result ok() { return {}; }
|
||||
|
||||
private:
|
||||
std::optional<ErrorInfo> m_error;
|
||||
};
|
||||
|
||||
} // namespace spw
|
||||
140
src/core/common/Types.h
Normal file
140
src/core/common/Types.h
Normal file
@@ -0,0 +1,140 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace spw
|
||||
{
|
||||
|
||||
// Sector-level addressing (LBA)
|
||||
using SectorOffset = uint64_t;
|
||||
using SectorCount = uint64_t;
|
||||
|
||||
// Byte-level sizes
|
||||
using ByteCount = uint64_t;
|
||||
|
||||
// Identifiers
|
||||
using DiskId = int; // Physical drive index (0, 1, 2, ...)
|
||||
using PartitionId = int; // Partition index within a disk
|
||||
|
||||
// GUID as 16-byte array
|
||||
struct Guid
|
||||
{
|
||||
uint8_t data[16] = {};
|
||||
|
||||
bool operator==(const Guid& other) const;
|
||||
bool operator!=(const Guid& other) const;
|
||||
bool isZero() const;
|
||||
std::string toString() const;
|
||||
static Guid fromString(const std::string& str);
|
||||
static Guid generate();
|
||||
};
|
||||
|
||||
// Partition table type
|
||||
enum class PartitionTableType
|
||||
{
|
||||
Unknown,
|
||||
MBR,
|
||||
GPT,
|
||||
APM, // Apple Partition Map
|
||||
};
|
||||
|
||||
// Filesystem types — comprehensive list including legacy systems
|
||||
enum class FilesystemType
|
||||
{
|
||||
Unknown,
|
||||
|
||||
// Modern Windows
|
||||
NTFS,
|
||||
FAT32,
|
||||
FAT16,
|
||||
FAT12,
|
||||
ExFAT,
|
||||
ReFS,
|
||||
|
||||
// Linux
|
||||
Ext2,
|
||||
Ext3,
|
||||
Ext4,
|
||||
Btrfs,
|
||||
XFS,
|
||||
ZFS,
|
||||
JFS,
|
||||
ReiserFS,
|
||||
Reiser4,
|
||||
|
||||
// Apple
|
||||
HFSPlus,
|
||||
APFS,
|
||||
HFS, // Classic Mac OS (legacy)
|
||||
MFS, // Macintosh File System (1984)
|
||||
|
||||
// Legacy / retro (1980s-1990s)
|
||||
FAT8, // CP/M-86 era
|
||||
HPFS, // OS/2 High Performance File System
|
||||
UFS, // Unix File System (BSD)
|
||||
FFS, // Berkeley Fast File System
|
||||
Minix, // MINIX filesystem
|
||||
Xiafs, // Linux early filesystem
|
||||
ADFS, // Acorn Disc Filing System
|
||||
AfFS, // Amiga Fast File System
|
||||
OFS, // Amiga Old File System
|
||||
BFS_BeOS, // BeOS File System
|
||||
QNX4, // QNX4 filesystem
|
||||
QNX6, // QNX6 filesystem
|
||||
SysV, // System V filesystem
|
||||
Coherent, // Coherent filesystem
|
||||
Xenix, // Xenix filesystem
|
||||
VxFS, // Veritas File System
|
||||
UDF, // Universal Disk Format (optical media)
|
||||
ISO9660, // CD-ROM filesystem
|
||||
RomFS, // Read-only filesystem
|
||||
CramFS, // Compressed ROM filesystem
|
||||
SquashFS, // Compressed read-only filesystem
|
||||
VFAT, // Virtual FAT (long filename extension)
|
||||
UMSDOS, // Unix on MS-DOS filesystem
|
||||
|
||||
// Network / special (read-only detection)
|
||||
NFS,
|
||||
SMB,
|
||||
SWAP_LINUX,
|
||||
SWAP_SOLARIS,
|
||||
|
||||
// Raw / unformatted
|
||||
Raw,
|
||||
Unallocated,
|
||||
};
|
||||
|
||||
// Disk interface types
|
||||
enum class DiskInterfaceType
|
||||
{
|
||||
Unknown,
|
||||
SATA,
|
||||
NVMe,
|
||||
USB,
|
||||
SCSI,
|
||||
SAS,
|
||||
IDE,
|
||||
MMC, // SD cards, eMMC
|
||||
Firewire,
|
||||
Thunderbolt,
|
||||
Virtual, // VHD, VHDX, etc.
|
||||
};
|
||||
|
||||
// Media types
|
||||
enum class MediaType
|
||||
{
|
||||
Unknown,
|
||||
HDD,
|
||||
SSD,
|
||||
NVMe,
|
||||
USBFlash,
|
||||
SDCard,
|
||||
CompactFlash,
|
||||
OpticalDrive,
|
||||
FloppyDisk,
|
||||
MemoryStick,
|
||||
Virtual,
|
||||
};
|
||||
|
||||
} // namespace spw
|
||||
28
src/core/common/Version.h
Normal file
28
src/core/common/Version.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef SPW_VERSION_MAJOR
|
||||
#define SPW_VERSION_MAJOR 1
|
||||
#endif
|
||||
|
||||
#ifndef SPW_VERSION_MINOR
|
||||
#define SPW_VERSION_MINOR 0
|
||||
#endif
|
||||
|
||||
#ifndef SPW_VERSION_PATCH
|
||||
#define SPW_VERSION_PATCH 0
|
||||
#endif
|
||||
|
||||
#ifndef SPW_VERSION_STRING
|
||||
#define SPW_VERSION_STRING "1.0.0"
|
||||
#endif
|
||||
|
||||
namespace spw
|
||||
{
|
||||
|
||||
constexpr int VersionMajor = SPW_VERSION_MAJOR;
|
||||
constexpr int VersionMinor = SPW_VERSION_MINOR;
|
||||
constexpr int VersionPatch = SPW_VERSION_PATCH;
|
||||
constexpr const char* VersionString = SPW_VERSION_STRING;
|
||||
constexpr const char* AppName = "Setec Partition Wizard";
|
||||
|
||||
} // namespace spw
|
||||
30
src/ui/CMakeLists.txt
Normal file
30
src/ui/CMakeLists.txt
Normal 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
149
src/ui/MainWindow.cpp
Normal 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 © 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
51
src/ui/MainWindow.h
Normal 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
|
||||
90
src/ui/tabs/DiagnosticsTab.cpp
Normal file
90
src/ui/tabs/DiagnosticsTab.cpp
Normal 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
|
||||
20
src/ui/tabs/DiagnosticsTab.h
Normal file
20
src/ui/tabs/DiagnosticsTab.h
Normal 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
|
||||
107
src/ui/tabs/DiskPartitionTab.cpp
Normal file
107
src/ui/tabs/DiskPartitionTab.cpp
Normal 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
|
||||
39
src/ui/tabs/DiskPartitionTab.h
Normal file
39
src/ui/tabs/DiskPartitionTab.h
Normal 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
|
||||
96
src/ui/tabs/ImagingTab.cpp
Normal file
96
src/ui/tabs/ImagingTab.cpp
Normal 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
20
src/ui/tabs/ImagingTab.h
Normal 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
|
||||
106
src/ui/tabs/MaintenanceTab.cpp
Normal file
106
src/ui/tabs/MaintenanceTab.cpp
Normal 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
|
||||
20
src/ui/tabs/MaintenanceTab.h
Normal file
20
src/ui/tabs/MaintenanceTab.h
Normal 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
|
||||
91
src/ui/tabs/RecoveryTab.cpp
Normal file
91
src/ui/tabs/RecoveryTab.cpp
Normal 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
20
src/ui/tabs/RecoveryTab.h
Normal 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
154
src/ui/tabs/SecurityTab.cpp
Normal 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
20
src/ui/tabs/SecurityTab.h
Normal 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
|
||||
16
tests/CMakeLists.txt
Normal file
16
tests/CMakeLists.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
include(GoogleTest)
|
||||
|
||||
add_executable(spw_tests
|
||||
test_result.cpp
|
||||
)
|
||||
|
||||
target_include_directories(spw_tests PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
)
|
||||
|
||||
target_link_libraries(spw_tests PRIVATE
|
||||
spw_core
|
||||
GTest::gtest_main
|
||||
)
|
||||
|
||||
gtest_discover_tests(spw_tests)
|
||||
72
tests/test_result.cpp
Normal file
72
tests/test_result.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#include "core/common/Result.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <string>
|
||||
|
||||
using namespace spw;
|
||||
|
||||
TEST(ResultTest, SuccessValue)
|
||||
{
|
||||
Result<int> r(42);
|
||||
EXPECT_TRUE(r.isOk());
|
||||
EXPECT_FALSE(r.isError());
|
||||
EXPECT_EQ(r.value(), 42);
|
||||
}
|
||||
|
||||
TEST(ResultTest, ErrorValue)
|
||||
{
|
||||
Result<int> r(ErrorInfo::fromCode(ErrorCode::DiskNotFound, "disk missing"));
|
||||
EXPECT_FALSE(r.isOk());
|
||||
EXPECT_TRUE(r.isError());
|
||||
EXPECT_EQ(r.error().code, ErrorCode::DiskNotFound);
|
||||
EXPECT_EQ(r.error().message, "disk missing");
|
||||
}
|
||||
|
||||
TEST(ResultTest, ValueOr)
|
||||
{
|
||||
Result<int> ok(10);
|
||||
EXPECT_EQ(ok.valueOr(99), 10);
|
||||
|
||||
Result<int> err(ErrorInfo::fromCode(ErrorCode::Unknown));
|
||||
EXPECT_EQ(err.valueOr(99), 99);
|
||||
}
|
||||
|
||||
TEST(ResultTest, BoolConversion)
|
||||
{
|
||||
Result<std::string> ok(std::string("hello"));
|
||||
EXPECT_TRUE(static_cast<bool>(ok));
|
||||
|
||||
Result<std::string> err(ErrorInfo::fromCode(ErrorCode::Unknown));
|
||||
EXPECT_FALSE(static_cast<bool>(err));
|
||||
}
|
||||
|
||||
TEST(ResultTest, Map)
|
||||
{
|
||||
Result<int> r(5);
|
||||
auto mapped = r.map([](int v) { return v * 2; });
|
||||
EXPECT_TRUE(mapped.isOk());
|
||||
EXPECT_EQ(mapped.value(), 10);
|
||||
}
|
||||
|
||||
TEST(ResultTest, MapOnError)
|
||||
{
|
||||
Result<int> r(ErrorInfo::fromCode(ErrorCode::DiskReadError));
|
||||
auto mapped = r.map([](int v) { return v * 2; });
|
||||
EXPECT_TRUE(mapped.isError());
|
||||
EXPECT_EQ(mapped.error().code, ErrorCode::DiskReadError);
|
||||
}
|
||||
|
||||
TEST(ResultVoidTest, Success)
|
||||
{
|
||||
Result<void> r = Result<void>::ok();
|
||||
EXPECT_TRUE(r.isOk());
|
||||
EXPECT_FALSE(r.isError());
|
||||
}
|
||||
|
||||
TEST(ResultVoidTest, Error)
|
||||
{
|
||||
Result<void> r(ErrorInfo::fromCode(ErrorCode::DiskWriteError, "write failed"));
|
||||
EXPECT_FALSE(r.isOk());
|
||||
EXPECT_TRUE(r.isError());
|
||||
EXPECT_EQ(r.error().code, ErrorCode::DiskWriteError);
|
||||
}
|
||||
23
third_party/CMakeLists.txt
vendored
Normal file
23
third_party/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
# Third-party dependencies managed via FetchContent
|
||||
include(FetchContent)
|
||||
|
||||
# Google Test (for testing only)
|
||||
if(SPW_BUILD_TESTS)
|
||||
FetchContent_Declare(
|
||||
googletest
|
||||
GIT_REPOSITORY https://github.com/google/googletest.git
|
||||
GIT_TAG v1.14.0
|
||||
)
|
||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||
FetchContent_MakeAvailable(googletest)
|
||||
endif()
|
||||
|
||||
# zstd (compression for disk images)
|
||||
# For now, we'll use system zstd or fetch it later
|
||||
# FetchContent_Declare(
|
||||
# zstd
|
||||
# GIT_REPOSITORY https://github.com/facebook/zstd.git
|
||||
# GIT_TAG v1.5.5
|
||||
# SOURCE_SUBDIR build/cmake
|
||||
# )
|
||||
# FetchContent_MakeAvailable(zstd)
|
||||
Reference in New Issue
Block a user