summaryrefslogtreecommitdiff
path: root/meshmc/launcher/ui/dialogs/BlockedModsDialog.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'meshmc/launcher/ui/dialogs/BlockedModsDialog.cpp')
-rw-r--r--meshmc/launcher/ui/dialogs/BlockedModsDialog.cpp178
1 files changed, 178 insertions, 0 deletions
diff --git a/meshmc/launcher/ui/dialogs/BlockedModsDialog.cpp b/meshmc/launcher/ui/dialogs/BlockedModsDialog.cpp
new file mode 100644
index 0000000000..884c1d186d
--- /dev/null
+++ b/meshmc/launcher/ui/dialogs/BlockedModsDialog.cpp
@@ -0,0 +1,178 @@
+/* SPDX-FileCopyrightText: 2026 Project Tick
+ * SPDX-FileContributor: Project Tick
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ * MeshMC - A Custom Launcher for Minecraft
+ * Copyright (C) 2026 Project Tick
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "BlockedModsDialog.h"
+
+#include <QDesktopServices>
+#include <QDir>
+#include <QFont>
+#include <QGridLayout>
+#include <QScrollArea>
+#include <QStandardPaths>
+#include <QUrl>
+
+BlockedModsDialog::BlockedModsDialog(QWidget* parent, const QString& title,
+ const QString& text,
+ QList<BlockedMod>& mods)
+ : QDialog(parent), m_mods(mods)
+{
+ setWindowTitle(title);
+ setMinimumSize(550, 300);
+ resize(620, 420);
+ setWindowModality(Qt::WindowModal);
+
+ auto* mainLayout = new QVBoxLayout(this);
+
+ // Description label at top
+ auto* descLabel = new QLabel(text, this);
+ descLabel->setWordWrap(true);
+ mainLayout->addWidget(descLabel);
+
+ // Scrollable area for mod list
+ auto* scrollArea = new QScrollArea(this);
+ scrollArea->setWidgetResizable(true);
+
+ auto* scrollWidget = new QWidget();
+ auto* grid = new QGridLayout(scrollWidget);
+ grid->setColumnStretch(0, 3); // mod name
+ grid->setColumnStretch(1, 1); // status
+ grid->setColumnStretch(2, 0); // button
+
+ // Header row
+ auto* headerName = new QLabel(tr("<b>Mod</b>"), scrollWidget);
+ auto* headerStatus = new QLabel(tr("<b>Status</b>"), scrollWidget);
+ grid->addWidget(headerName, 0, 0);
+ grid->addWidget(headerStatus, 0, 1);
+
+ for (int i = 0; i < m_mods.size(); i++) {
+ int row = i + 1;
+
+ auto* nameLabel = new QLabel(m_mods[i].fileName, scrollWidget);
+ nameLabel->setTextInteractionFlags(Qt::TextSelectableByMouse);
+
+ auto* statusLabel = new QLabel(tr("Missing"), scrollWidget);
+ statusLabel->setStyleSheet("color: #cc3333; font-weight: bold;");
+
+ auto* downloadBtn = new QPushButton(tr("Download"), scrollWidget);
+ connect(downloadBtn, &QPushButton::clicked, this,
+ [this, i]() { openModDownload(i); });
+
+ grid->addWidget(nameLabel, row, 0);
+ grid->addWidget(statusLabel, row, 1);
+ grid->addWidget(downloadBtn, row, 2);
+
+ m_rows.append({nameLabel, statusLabel, downloadBtn});
+ }
+
+ // Add stretch at bottom of grid
+ grid->setRowStretch(m_mods.size() + 1, 1);
+
+ scrollWidget->setLayout(grid);
+ scrollArea->setWidget(scrollWidget);
+ mainLayout->addWidget(scrollArea, 1);
+
+ // Button box at bottom
+ m_buttons = new QDialogButtonBox(
+ QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
+ m_buttons->button(QDialogButtonBox::Ok)->setText(tr("Continue"));
+ m_buttons->button(QDialogButtonBox::Ok)->setEnabled(false);
+ connect(m_buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
+ connect(m_buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
+ mainLayout->addWidget(m_buttons);
+
+ setLayout(mainLayout);
+
+ // Set up Downloads folder watching
+ setupWatch();
+
+ // Initial scan
+ scanDownloadsFolder();
+}
+
+void BlockedModsDialog::setupWatch()
+{
+ m_downloadDir =
+ QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
+ if (!m_downloadDir.isEmpty() && QDir(m_downloadDir).exists()) {
+ m_watcher.addPath(m_downloadDir);
+ connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this,
+ &BlockedModsDialog::onDownloadDirChanged);
+ }
+}
+
+void BlockedModsDialog::onDownloadDirChanged(const QString& path)
+{
+ Q_UNUSED(path);
+ scanDownloadsFolder();
+}
+
+void BlockedModsDialog::scanDownloadsFolder()
+{
+ if (m_downloadDir.isEmpty())
+ return;
+
+ QDir dir(m_downloadDir);
+ QStringList files = dir.entryList(QDir::Files);
+
+ for (int i = 0; i < m_mods.size(); i++) {
+ if (!m_mods[i].found && files.contains(m_mods[i].fileName)) {
+ m_mods[i].found = true;
+ }
+ }
+
+ updateModStatus();
+}
+
+void BlockedModsDialog::updateModStatus()
+{
+ bool allFound = true;
+
+ for (int i = 0; i < m_mods.size(); i++) {
+ if (m_mods[i].found) {
+ m_rows[i].statusLabel->setText(QString::fromUtf8("\u2714 ") +
+ tr("Found"));
+ m_rows[i].statusLabel->setStyleSheet(
+ "color: #33aa33; font-weight: bold;");
+ m_rows[i].downloadButton->setEnabled(false);
+ } else {
+ m_rows[i].statusLabel->setText(tr("Missing"));
+ m_rows[i].statusLabel->setStyleSheet(
+ "color: #cc3333; font-weight: bold;");
+ m_rows[i].downloadButton->setEnabled(true);
+ allFound = false;
+ }
+ }
+
+ m_buttons->button(QDialogButtonBox::Ok)->setEnabled(allFound);
+}
+
+void BlockedModsDialog::openModDownload(int index)
+{
+ if (index < 0 || index >= m_mods.size())
+ return;
+
+ const auto& mod = m_mods[index];
+ QString url =
+ QString("https://www.curseforge.com/api/v1/mods/%1/files/%2/download")
+ .arg(mod.projectId)
+ .arg(mod.fileId);
+ QDesktopServices::openUrl(QUrl(url));
+}