From 3e9418f8960f101c5a358474bdac5d3e6ee2ba96 Mon Sep 17 00:00:00 2001 From: Archon0ne Date: Sat, 25 May 2024 02:56:12 +0200 Subject: [PATCH] Initial commit. First working version. --- .gitignore | 5 + CMakeLists.txt | 79 +++++++++ CMakeLists.txt.user | 424 ++++++++++++++++++++++++++++++++++++++++++++ LoEDD.ico | Bin 0 -> 4376 bytes LoEDD.rc | 2 + LoEDD_en_US.ts | 3 + main.cpp | 9 + mainwindow.cpp | 227 ++++++++++++++++++++++++ mainwindow.h | 49 +++++ mainwindow.ui | 33 ++++ 10 files changed, 831 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 CMakeLists.txt.user create mode 100644 LoEDD.ico create mode 100644 LoEDD.rc create mode 100644 LoEDD_en_US.ts create mode 100644 main.cpp create mode 100644 mainwindow.cpp create mode 100644 mainwindow.h create mode 100644 mainwindow.ui diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..910937a --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# Ignore the build directory +/build/ + +# Ignore the config.ini file +/config.ini diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..dc94e4e --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,79 @@ +cmake_minimum_required(VERSION 3.5) + +project(LoEDD VERSION 0.1 LANGUAGES CXX) + +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets LinguistTools) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets LinguistTools) + +set(TS_FILES LoEDD_en_US.ts) + +set(PROJECT_SOURCES + main.cpp + mainwindow.cpp + mainwindow.h + mainwindow.ui + ${TS_FILES} +) + +if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) + set(app_icon_resource_windows "${CMAKE_CURRENT_SOURCE_DIR}/LoEDD.rc") + qt_add_executable(LoEDD + MANUAL_FINALIZATION + ${PROJECT_SOURCES} + ${app_icon_resource_windows} + ) +# Define target properties for Android with Qt 6 as: +# set_property(TARGET LoEDD APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR +# ${CMAKE_CURRENT_SOURCE_DIR}/android) +# For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation + + qt_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES}) +else() + if(ANDROID) + add_library(LoEDD SHARED + ${PROJECT_SOURCES} + ) +# Define properties for Android with Qt 5 after find_package() calls as: +# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android") + else() + add_executable(LoEDD + ${PROJECT_SOURCES} ${RESOURCES} + ) + endif() + + qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES}) +endif() + +target_link_libraries(LoEDD PRIVATE Qt${QT_VERSION_MAJOR}::Widgets) + +# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1. +# If you are developing for iOS or macOS you should consider setting an +# explicit, fixed bundle identifier manually though. +if(${QT_VERSION} VERSION_LESS 6.1.0) + set(BUNDLE_ID_OPTION MACOSX_BUNDLE_GUI_IDENTIFIER com.example.LoEDD) +endif() +set_target_properties(LoEDD PROPERTIES + ${BUNDLE_ID_OPTION} + MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION} + MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} + MACOSX_BUNDLE TRUE + WIN32_EXECUTABLE TRUE +) + +include(GNUInstallDirs) +install(TARGETS LoEDD + BUNDLE DESTINATION . + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +) + +if(QT_VERSION_MAJOR EQUAL 6) + qt_finalize_executable(LoEDD) +endif() diff --git a/CMakeLists.txt.user b/CMakeLists.txt.user new file mode 100644 index 0000000..5e242d1 --- /dev/null +++ b/CMakeLists.txt.user @@ -0,0 +1,424 @@ + + + + + + EnvironmentId + {3fed9806-2578-4ea3-8cbd-ed697a0dae04} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + false + 80 + true + true + 1 + 0 + false + true + false + 2 + true + true + 0 + 8 + true + false + 1 + true + true + true + *.md, *.MD, Makefile + false + true + true + + + + ProjectExplorer.Project.PluginSettings + + + true + false + true + true + true + true + + + 0 + true + + true + true + Builtin.DefaultTidyAndClazy + 4 + true + + + + true + + + + + ProjectExplorer.Project.Target.0 + + Desktop + Desktop Qt 6.7.0 MinGW 64-bit + Desktop Qt 6.7.0 MinGW 64-bit + qt.qt6.670.win64_mingw_kit + 1 + 0 + 0 + + Debug + 2 + false + + -DCMAKE_GENERATOR:STRING=Ninja +-DCMAKE_BUILD_TYPE:STRING=Debug +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} + 0 + G:\Games\SPT - Modding\loadordereditor\Qt6\LoEDD\build\Desktop_Qt_6_7_0_MinGW_64_bit-Debug + + + + + all + + false + + true + Build + CMakeProjectManager.MakeStep + + 1 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + Build + CMakeProjectManager.MakeStep + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Debug + CMakeProjectManager.CMakeBuildConfiguration + + + Release + 2 + false + + -DCMAKE_GENERATOR:STRING=Ninja +-DCMAKE_BUILD_TYPE:STRING=Release +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} + G:\Games\SPTModding\loadordereditor\Qt6\LoEDD\build\Desktop_Qt_6_7_0_MinGW_64_bit-Release + + + + + all + + false + + true + Build + CMakeProjectManager.MakeStep + + 1 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + Build + CMakeProjectManager.MakeStep + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Release + CMakeProjectManager.CMakeBuildConfiguration + + + RelWithDebInfo + 2 + false + + -DCMAKE_GENERATOR:STRING=Ninja +-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} + G:\Games\SPT - Modding\loadordereditor\Qt6\LoEDD\build\Desktop_Qt_6_7_0_MinGW_64_bit-RelWithDebInfo + + + + + all + + false + + true + CMakeProjectManager.MakeStep + + 1 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + CMakeProjectManager.MakeStep + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Udgivelse med fejlretinformation + CMakeProjectManager.CMakeBuildConfiguration + + + RelWithDebInfo + 2 + false + + -DCMAKE_GENERATOR:STRING=Ninja +-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} + 0 + G:\Games\SPT - Modding\loadordereditor\Qt6\LoEDD\build\Desktop_Qt_6_7_0_MinGW_64_bit-Profile + + + + + all + + false + + true + Build + CMakeProjectManager.MakeStep + + 1 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + Build + CMakeProjectManager.MakeStep + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Profile + CMakeProjectManager.CMakeBuildConfiguration + + + MinSizeRel + 2 + false + + -DCMAKE_GENERATOR:STRING=Ninja +-DCMAKE_BUILD_TYPE:STRING=MinSizeRel +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} + G:\Games\SPT - Modding\loadordereditor\Qt6\LoEDD\build\Desktop_Qt_6_7_0_MinGW_64_bit-MinSizeRel + + + + + all + + false + + true + CMakeProjectManager.MakeStep + + 1 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + CMakeProjectManager.MakeStep + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Minimum størrelse udgivelse + CMakeProjectManager.CMakeBuildConfiguration + + 5 + + + 0 + Deploy + Deploy + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + 1 + + true + true + 0 + true + + 2 + + false + -e cpu-cycles --call-graph "dwarf,4096" -F 250 + LoEDD + CMakeProjectManager.CMakeRunConfiguration.LoEDD + LoEDD + false + true + true + true + G:/Games/SPTModding/loadordereditor/Qt6/LoEDD/build/Desktop_Qt_6_7_0_MinGW_64_bit-Release + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 22 + + + Version + 22 + + diff --git a/LoEDD.ico b/LoEDD.ico new file mode 100644 index 0000000000000000000000000000000000000000..26dc5acc37bc8ca4226547525a160053e7b74259 GIT binary patch literal 4376 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 literal 0 HcmV?d00001 diff --git a/LoEDD.rc b/LoEDD.rc new file mode 100644 index 0000000..1174437 --- /dev/null +++ b/LoEDD.rc @@ -0,0 +1,2 @@ +#include +IDI_ICON1 ICON "LoEDD.ico" \ No newline at end of file diff --git a/LoEDD_en_US.ts b/LoEDD_en_US.ts new file mode 100644 index 0000000..edd0d34 --- /dev/null +++ b/LoEDD_en_US.ts @@ -0,0 +1,3 @@ + + + diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..efdc28a --- /dev/null +++ b/main.cpp @@ -0,0 +1,9 @@ +#include +#include "mainwindow.h" + +int main(int argc, char *argv[]) { + QApplication app(argc, argv); + MainWindow window; + window.show(); + return app.exec(); +} diff --git a/mainwindow.cpp b/mainwindow.cpp new file mode 100644 index 0000000..3d50f52 --- /dev/null +++ b/mainwindow.cpp @@ -0,0 +1,227 @@ +#include "mainwindow.h" +#include "ui_mainwindow.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent), ui(new Ui::MainWindow) { + ui->setupUi(this); + initWindowSize(); + initUI(); +} + +MainWindow::~MainWindow() { + delete ui; +} + +void MainWindow::initWindowSize() { + settings = new QSettings("config.ini", QSettings::IniFormat); + width = settings->value("width", 400).toInt(); + height = settings->value("height", 300).toInt(); + resize(width, height); +} + +void MainWindow::initUI() { + loadConfig(); + + if (!checkModsDirectory()) + return; + + initialOrder.clear(); + loadData(modDir); + + ui->listWidget->setDragDropMode(QListWidget::InternalMove); + ui->listWidget->setAlternatingRowColors(true); + + connect(ui->saveButton, &QPushButton::clicked, this, &MainWindow::saveOrder); +} + +void MainWindow::loadConfig() { + sptBaseDir = settings->value("sptBaseDir", "").toString(); + addNewItemsTo = settings->value("addNewItemsTo", "end").toString(); + reminder = settings->value("reminder", true).toBool(); +} + +bool MainWindow::checkModsDirectory() { + modDir = QDir::currentPath(); + QDir modsDir(modDir + "/../user/mods"); + if (!modsDir.exists()) { + QMessageBox::critical(this, "Error", "Could not find path '../user/mods'\nPlace the LoEDD folder in the spt-aki base directory: 'SPT-AKI/LoEDD!'\nCurrent Directory: " + modDir); + return false; + } + return true; +} + +void MainWindow::loadData(const QString &modDir) { + QFile orderFile(modDir + "/../user/mods/order.json"); + QStringList orderedMods; + + if (orderFile.exists()) { + orderFile.open(QIODevice::ReadOnly | QIODevice::Text); + QJsonDocument doc = QJsonDocument::fromJson(orderFile.readAll()); + QJsonObject obj = doc.object(); + orderedMods = obj["order"].toVariant().toStringList(); + orderFile.close(); + } + + QDir modsDir(modDir + "/../user/mods"); + QStringList modFolders; + QStringList validMods; + QHash modTooltips; + + // Clear the list widget before populating it + ui->listWidget->clear(); + + // List all folders in the mods directory and verify they have a package.json + foreach (QFileInfo entry, modsDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot)) { + QString folderName = entry.fileName(); + QString packageFilePath = entry.filePath() + "/package.json"; + QFile packageFile(packageFilePath); + + if (packageFile.exists()) { + qDebug() << "Found package.json for mod:" << folderName; + packageFile.open(QIODevice::ReadOnly | QIODevice::Text); + QJsonDocument doc = QJsonDocument::fromJson(packageFile.readAll()); + QJsonObject obj = doc.object(); + QString modName = obj["name"].toString(); + QString version = obj["version"].toString(); + QString author = obj["author"].toString(); + QString akiVersion = obj["akiVersion"].toString(); + QString tooltipText = QString("Name: %1\nFolder Name: %2\nVersion: %3\nAuthor: %4\nakiVersion: %5") + .arg(modName).arg(folderName).arg(version).arg(author).arg(akiVersion); + qDebug() << "Mod Info:" << modName << version << author << akiVersion; + + modFolders.append(folderName); + validMods.append(folderName); + modTooltips.insert(folderName, tooltipText); + packageFile.close(); + } else { + qDebug() << "No package.json found for mod folder:" << folderName; + } + } + + // Create a list to keep track of mods that have been added to the final list + QStringList modsAdded; + + // Temporary list to hold the reordered mods + QStringList finalModList; + + // Reorder listWidget items based on order.json + foreach (QString modName, orderedMods) { + if (validMods.contains(modName)) { + finalModList.append(modName); + modsAdded.append(modName); + } + } + + // Sort remaining mods that were not in the order.json alphabetically + QStringList remainingMods; + foreach (QString modName, validMods) { + if (!modsAdded.contains(modName)) { + remainingMods.append(modName); + } + } + remainingMods.sort(Qt::CaseInsensitive); + + // Add remaining mods that were not in the order.json to the beginning of the list + finalModList = remainingMods + finalModList; + + // Add the final ordered mods to the list widget + for (int i = 0; i < finalModList.size(); ++i) { + QString modName = finalModList[i]; + QString tooltipText = modTooltips.value(modName); + bool isNew = remainingMods.contains(modName); + addListItem(modName, tooltipText, -1, isNew); + } + + initialOrder = QStringList(); + for (int i = 0; i < ui->listWidget->count(); ++i) { + initialOrder.append(ui->listWidget->item(i)->text()); + } +} + +void MainWindow::addListItem(const QString &modName, const QString &tooltipText, int position, bool isNew) { + QListWidgetItem *listItem = new QListWidgetItem(modName); + listItem->setToolTip(tooltipText); + + // Set background color for new items + if (isNew) { + listItem->setBackground(QBrush(QColor(255, 255, 200))); // Light yellow + } + + // Set position indicator + if (position == -1) { + position = ui->listWidget->count(); + } + QString itemText = QString("%1. %2").arg(position + 1).arg(modName); + listItem->setText(itemText); + + if (position == -1) { + ui->listWidget->addItem(listItem); + } else { + ui->listWidget->insertItem(position, listItem); + } +} + +void MainWindow::saveOrder() { + QStringList newOrder; + for (int i = 0; i < ui->listWidget->count(); ++i) { + newOrder.append(ui->listWidget->item(i)->text()); + } + + QString orderFilePath = modDir + "/../user/mods/order.json"; + QString backupFilePath = modDir + "/../user/mods/order.bkp"; + + if (QFile::exists(orderFilePath)) { + QFile::copy(orderFilePath, backupFilePath); + } + + QFile orderFile(orderFilePath); + if (orderFile.open(QIODevice::WriteOnly | QIODevice::Text)) { + QJsonObject obj; + obj["order"] = QJsonArray::fromStringList(newOrder); + QJsonDocument doc(obj); + orderFile.write(doc.toJson()); + orderFile.close(); + } + + initialOrder = newOrder; + + settings->setValue("width", width); + settings->setValue("height", height); +} + +void MainWindow::resizeEvent(QResizeEvent *event) { + QMainWindow::resizeEvent(event); + width = size().width(); + height = size().height(); +} + +void MainWindow::closeEvent(QCloseEvent *event) { + if (reminder && ui->listWidget->count() > 0) { + QStringList currentOrder; + for (int i = 0; i < ui->listWidget->count(); ++i) { + currentOrder.append(ui->listWidget->item(i)->text()); + } + if (currentOrder != initialOrder) { + QMessageBox::StandardButton reply; + reply = QMessageBox::question(this, "Reminder", "You have unsaved changes.\nSave before closing?", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + if (reply == QMessageBox::Yes) { + saveOrder(); + } + } + } + + settings->setValue("width", width); + settings->setValue("height", height); + event->accept(); +} diff --git a/mainwindow.h b/mainwindow.h new file mode 100644 index 0000000..cee8c85 --- /dev/null +++ b/mainwindow.h @@ -0,0 +1,49 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow { + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +protected: + void resizeEvent(QResizeEvent *event) override; + void closeEvent(QCloseEvent *event) override; + +private slots: + void saveOrder(); + +private: + void initUI(); + void initWindowSize(); + void loadConfig(); + bool checkModsDirectory(); + void loadData(const QString &modDir); + void addListItem(const QString &modName, const QString &tooltipText, int position = -1, bool isNew = false); + + Ui::MainWindow *ui; + QListWidget *listWidget; + QPushButton *saveButton; + QSettings *settings; + QString modDir; + QStringList initialOrder; + QString sptBaseDir; + QString addNewItemsTo; + bool reminder; + int width; + int height; +}; + +#endif // MAINWINDOW_H diff --git a/mainwindow.ui b/mainwindow.ui new file mode 100644 index 0000000..fe7ca76 --- /dev/null +++ b/mainwindow.ui @@ -0,0 +1,33 @@ + + + MainWindow + + + + 0 + 0 + 400 + 300 + + + + Drag and Drop JSON Reorder + + + + + + + + + + Save Order + + + + + + + + +