aboutsummaryrefslogtreecommitdiff
path: root/libs/installer_fomod_plus/patchwizard/FomodPlusPatchWizard.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/installer_fomod_plus/patchwizard/FomodPlusPatchWizard.cpp')
-rw-r--r--libs/installer_fomod_plus/patchwizard/FomodPlusPatchWizard.cpp181
1 files changed, 181 insertions, 0 deletions
diff --git a/libs/installer_fomod_plus/patchwizard/FomodPlusPatchWizard.cpp b/libs/installer_fomod_plus/patchwizard/FomodPlusPatchWizard.cpp
new file mode 100644
index 0000000..00be8d5
--- /dev/null
+++ b/libs/installer_fomod_plus/patchwizard/FomodPlusPatchWizard.cpp
@@ -0,0 +1,181 @@
+#include "FomodPlusPatchWizard.h"
+
+#include <QApplication>
+#include <QDialog>
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QMessageBox>
+#include <QProgressDialog>
+#include <QPushButton>
+#include <QVBoxLayout>
+
+#include "lib/PatchFinder.h"
+#include <FomodRescan.h>
+
+bool FomodPlusPatchWizard::init(IOrganizer* organizer)
+{
+ mOrganizer = organizer;
+ mDialog = new QDialog();
+ mDialog->setWindowTitle(tr("Patch Wizard"));
+ mDialog->setMinimumSize(400, 200);
+ log.setLogFilePath(QDir::currentPath().toStdString() + "/logs/fomodplus-patchwizard.log");
+
+ mOrganizer->onUserInterfaceInitialized([this](QMainWindow*) {
+ logMessage(DEBUG, "patches populated.");
+ mPatchFinder = std::make_unique<PatchFinder>(mOrganizer);
+ mPatchFinder->populateInstalledPlugins();
+ mAvailablePatches = mPatchFinder->getAvailablePatchesForModList();
+ logMessage(DEBUG, "Available Patches: " + std::to_string(mAvailablePatches.size()));
+ });
+
+ return true;
+}
+
+void FomodPlusPatchWizard::display() const
+{
+ // Clear any existing layout
+ if (mDialog->layout() != nullptr) {
+ QLayoutItem* item;
+ while ((item = mDialog->layout()->takeAt(0)) != nullptr) {
+ delete item->widget();
+ delete item;
+ }
+ delete mDialog->layout();
+ }
+
+ if (mAvailablePatches.empty()) {
+ setupEmptyState();
+ } else {
+ setupPatchList();
+ }
+
+ mDialog->exec();
+}
+
+void FomodPlusPatchWizard::setupEmptyState() const
+{
+ auto* mainLayout = new QVBoxLayout(mDialog);
+ mainLayout->setAlignment(Qt::AlignCenter);
+
+ auto* contentWidget = new QWidget(mDialog);
+ auto* contentLayout = new QHBoxLayout(contentWidget);
+ contentLayout->setAlignment(Qt::AlignCenter);
+ contentLayout->setSpacing(16);
+
+ auto* imageLabel = new QLabel(contentWidget);
+ imageLabel->setPixmap(QPixmap(":/fomod/infoscroll").scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation));
+
+ auto* textLabel = new QLabel(
+ tr("Nothing of interest yet. The wizard gets wiser as you\ninstall FOMODs, so check back later!"),
+ contentWidget
+ );
+ textLabel->setAlignment(Qt::AlignVCenter | Qt::AlignLeft);
+
+ contentLayout->addWidget(imageLabel);
+ contentLayout->addWidget(textLabel);
+
+ mainLayout->addWidget(contentWidget);
+
+ auto* rescanButton = new QPushButton(tr("Rescan Load Order"), mDialog);
+ // Use const_cast since setupEmptyState is const but onRescanClicked modifies state
+ connect(rescanButton, &QPushButton::clicked, const_cast<FomodPlusPatchWizard*>(this),
+ &FomodPlusPatchWizard::onRescanClicked);
+ mainLayout->addWidget(rescanButton, 0, Qt::AlignCenter);
+}
+
+void FomodPlusPatchWizard::onRescanClicked()
+{
+ const auto confirmResult = QMessageBox::question(
+ mDialog,
+ tr("Rescan Load Order"),
+ tr("Rescanning will populate as many existing choices and options as we can, "
+ "but if some downloads are deleted it may be missing things! "
+ "It may take a few minutes depending on the size of your load order and all that."),
+ QMessageBox::Ok | QMessageBox::Cancel,
+ QMessageBox::Cancel
+ );
+
+ if (confirmResult != QMessageBox::Ok) {
+ return;
+ }
+
+ logMessage(DEBUG, "Rescan requested by user");
+
+ // Create progress dialog
+ QProgressDialog progress(tr("Scanning mods..."), tr("Cancel"), 0, 100, mDialog);
+ progress.setWindowModality(Qt::WindowModal);
+ progress.setMinimumDuration(0);
+ progress.setValue(0);
+
+ bool cancelled = false;
+
+ // Perform the rescan
+ FomodRescan rescan(mOrganizer, mPatchFinder->mFomodDb.get());
+ auto result = rescan.scanAllModsWithChoices([&](int current, int total, const QString& modName) {
+ if (progress.wasCanceled()) {
+ cancelled = true;
+ return;
+ }
+ const int percent = total > 0 ? (current * 100 / total) : 0;
+ progress.setValue(percent);
+ progress.setLabelText(tr("Scanning: %1 (%2/%3)").arg(modName).arg(current).arg(total));
+ QApplication::processEvents();
+ });
+
+ progress.setValue(100);
+
+ if (cancelled) {
+ QMessageBox::information(
+ mDialog,
+ tr("Rescan Cancelled"),
+ tr("The rescan was cancelled. Partial results may have been saved.")
+ );
+ logMessage(INFO, "Rescan cancelled by user");
+ } else {
+ // Show result summary
+ QString summary = tr("Rescan complete!\n\n"
+ "Mods processed: %1\n"
+ "Successfully scanned: %2\n"
+ "Missing archives: %3\n"
+ "Parse errors: %4")
+ .arg(result.totalModsProcessed)
+ .arg(result.successfullyScanned)
+ .arg(result.missingArchives)
+ .arg(result.parseErrors);
+
+ if (!result.failedMods.empty() && result.failedMods.size() <= 10) {
+ summary += tr("\n\nFailed mods:");
+ for (const auto& mod : result.failedMods) {
+ summary += QString("\n- %1").arg(QString::fromStdString(mod));
+ }
+ } else if (result.failedMods.size() > 10) {
+ summary += tr("\n\n%1 mods failed (see log for details)").arg(result.failedMods.size());
+ for (const auto& mod : result.failedMods) {
+ logMessage(INFO, "Failed mod: " + mod);
+ }
+ }
+
+ QMessageBox::information(mDialog, tr("Rescan Complete"), summary);
+
+ logMessage(INFO, "Rescan complete: " + std::to_string(result.successfullyScanned) +
+ "/" + std::to_string(result.totalModsProcessed) + " successful");
+ }
+
+ // Refresh available patches
+ mPatchFinder->populateInstalledPlugins();
+ mAvailablePatches = mPatchFinder->getAvailablePatchesForModList();
+ logMessage(DEBUG, "Available Patches after rescan: " + std::to_string(mAvailablePatches.size()));
+
+ // Refresh the UI
+ display();
+}
+
+void FomodPlusPatchWizard::setupPatchList() const
+{
+ auto* mainLayout = new QVBoxLayout(mDialog);
+
+ auto* label = new QLabel(tr("Available patches: %1").arg(mAvailablePatches.size()), mDialog);
+ mainLayout->addWidget(label);
+
+ // TODO: Implement actual patch list UI
+} \ No newline at end of file