Merge branch 'develop' into chore/add-compiler-warnings
Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
This commit is contained in:
commit
520594e529
28
.github/workflows/update-flake.yml
vendored
Normal file
28
.github/workflows/update-flake.yml
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
name: Update Flake Lockfile
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
# run weekly on sunday
|
||||||
|
- cron: "0 0 * * 0"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
update-flake:
|
||||||
|
if: github.repository == 'PrismLauncher/PrismLauncher'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: cachix/install-nix-action@v22
|
||||||
|
|
||||||
|
- uses: DeterminateSystems/update-flake-lock@v19
|
||||||
|
with:
|
||||||
|
commit-msg: "chore(nix): update lockfile"
|
||||||
|
pr-title: "chore(nix): update lockfile"
|
||||||
|
pr-labels: |
|
||||||
|
Linux
|
||||||
|
simple change
|
48
flake.lock
generated
48
flake.lock
generated
@ -21,11 +21,11 @@
|
|||||||
"nixpkgs-lib": "nixpkgs-lib"
|
"nixpkgs-lib": "nixpkgs-lib"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1683560683,
|
"lastModified": 1688254665,
|
||||||
"narHash": "sha256-XAygPMN5Xnk/W2c1aW0jyEa6lfMDZWlQgiNtmHXytPc=",
|
"narHash": "sha256-8FHEgBrr7gYNiS/NzCxIO3m4hvtLRW9YY1nYo1ivm3o=",
|
||||||
"owner": "hercules-ci",
|
"owner": "hercules-ci",
|
||||||
"repo": "flake-parts",
|
"repo": "flake-parts",
|
||||||
"rev": "006c75898cf814ef9497252b022e91c946ba8e17",
|
"rev": "267149c58a14d15f7f81b4d737308421de9d7152",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -35,12 +35,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flake-utils": {
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1667395993,
|
"lastModified": 1685518550,
|
||||||
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
|
"narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "flake-utils",
|
"repo": "flake-utils",
|
||||||
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
|
"rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -88,11 +91,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1685012353,
|
"lastModified": 1688221086,
|
||||||
"narHash": "sha256-U3oOge4cHnav8OLGdRVhL45xoRj4Ppd+It6nPC9nNIU=",
|
"narHash": "sha256-cdW6qUL71cNWhHCpMPOJjlw0wzSRP0pVlRn2vqX/VVg=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "aeb75dba965e790de427b73315d5addf91a54955",
|
"rev": "cd99c2b3c9f160cd004318e0697f90bbd5960825",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -105,11 +108,11 @@
|
|||||||
"nixpkgs-lib": {
|
"nixpkgs-lib": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"dir": "lib",
|
"dir": "lib",
|
||||||
"lastModified": 1682879489,
|
"lastModified": 1688049487,
|
||||||
"narHash": "sha256-sASwo8gBt7JDnOOstnps90K1wxmVfyhsTPPNTGBPjjg=",
|
"narHash": "sha256-100g4iaKC9MalDjUW9iN6Jl/OocTDtXdeAj7pEGIRh4=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "da45bf6ec7bbcc5d1e14d3795c025199f28e0de0",
|
"rev": "4bc72cae107788bf3f24f30db2e2f685c9298dc9",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -135,11 +138,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1684842236,
|
"lastModified": 1688386108,
|
||||||
"narHash": "sha256-rYWsIXHvNhVQ15RQlBUv67W3YnM+Pd+DuXGMvCBq2IE=",
|
"narHash": "sha256-Vffto9QaVonzYAcPlAzd0soqWYpPpKk60dfNLSIXcFA=",
|
||||||
"owner": "cachix",
|
"owner": "cachix",
|
||||||
"repo": "pre-commit-hooks.nix",
|
"repo": "pre-commit-hooks.nix",
|
||||||
"rev": "61e567d6497bc9556f391faebe5e410e6623217f",
|
"rev": "42587d3414d1747999a5f71e92a83cf6547b62da",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -156,6 +159,21 @@
|
|||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs",
|
||||||
"pre-commit-hooks": "pre-commit-hooks"
|
"pre-commit-hooks": "pre-commit-hooks"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"root": "root",
|
"root": "root",
|
||||||
|
@ -568,6 +568,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
|
|
||||||
// Language
|
// Language
|
||||||
m_settings->registerSetting("Language", QString());
|
m_settings->registerSetting("Language", QString());
|
||||||
|
m_settings->registerSetting("UseSystemLocale", false);
|
||||||
|
|
||||||
// Console
|
// Console
|
||||||
m_settings->registerSetting("ShowConsole", false);
|
m_settings->registerSetting("ShowConsole", false);
|
||||||
@ -687,9 +688,17 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
m_settings->reset("PastebinCustomAPIBase");
|
m_settings->reset("PastebinCustomAPIBase");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// meta URL
|
{
|
||||||
|
// Meta URL
|
||||||
m_settings->registerSetting("MetaURLOverride", "");
|
m_settings->registerSetting("MetaURLOverride", "");
|
||||||
|
|
||||||
|
QUrl metaUrl(m_settings->get("MetaURLOverride").toString());
|
||||||
|
|
||||||
|
// get rid of invalid meta urls
|
||||||
|
if (!metaUrl.isValid() || metaUrl.scheme() != "http" || metaUrl.scheme() != "https")
|
||||||
|
m_settings->reset("MetaURLOverride");
|
||||||
|
}
|
||||||
|
|
||||||
m_settings->registerSetting("CloseAfterLaunch", false);
|
m_settings->registerSetting("CloseAfterLaunch", false);
|
||||||
m_settings->registerSetting("QuitAfterGameStop", false);
|
m_settings->registerSetting("QuitAfterGameStop", false);
|
||||||
|
|
||||||
@ -910,12 +919,7 @@ bool Application::createSetupWizard()
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}();
|
}();
|
||||||
bool languageRequired = [&]()
|
bool languageRequired = settings()->get("Language").toString().isEmpty();
|
||||||
{
|
|
||||||
if (settings()->get("Language").toString().isEmpty())
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}();
|
|
||||||
bool pasteInterventionRequired = settings()->get("PastebinURL") != "";
|
bool pasteInterventionRequired = settings()->get("PastebinURL") != "";
|
||||||
bool themeInterventionRequired = settings()->get("ApplicationTheme") == "";
|
bool themeInterventionRequired = settings()->get("ApplicationTheme") == "";
|
||||||
bool wizardRequired = javaRequired || languageRequired || pasteInterventionRequired || themeInterventionRequired;
|
bool wizardRequired = javaRequired || languageRequired || pasteInterventionRequired || themeInterventionRequired;
|
||||||
|
@ -514,6 +514,8 @@ set(FLAME_SOURCES
|
|||||||
modplatform/flame/FlameCheckUpdate.h
|
modplatform/flame/FlameCheckUpdate.h
|
||||||
modplatform/flame/FlameInstanceCreationTask.h
|
modplatform/flame/FlameInstanceCreationTask.h
|
||||||
modplatform/flame/FlameInstanceCreationTask.cpp
|
modplatform/flame/FlameInstanceCreationTask.cpp
|
||||||
|
modplatform/flame/FlamePackExportTask.h
|
||||||
|
modplatform/flame/FlamePackExportTask.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(MODRINTH_SOURCES
|
set(MODRINTH_SOURCES
|
||||||
@ -907,8 +909,8 @@ SET(LAUNCHER_SOURCES
|
|||||||
ui/dialogs/EditAccountDialog.h
|
ui/dialogs/EditAccountDialog.h
|
||||||
ui/dialogs/ExportInstanceDialog.cpp
|
ui/dialogs/ExportInstanceDialog.cpp
|
||||||
ui/dialogs/ExportInstanceDialog.h
|
ui/dialogs/ExportInstanceDialog.h
|
||||||
ui/dialogs/ExportMrPackDialog.cpp
|
ui/dialogs/ExportPackDialog.cpp
|
||||||
ui/dialogs/ExportMrPackDialog.h
|
ui/dialogs/ExportPackDialog.h
|
||||||
ui/dialogs/IconPickerDialog.cpp
|
ui/dialogs/IconPickerDialog.cpp
|
||||||
ui/dialogs/IconPickerDialog.h
|
ui/dialogs/IconPickerDialog.h
|
||||||
ui/dialogs/ImportResourceDialog.cpp
|
ui/dialogs/ImportResourceDialog.cpp
|
||||||
@ -1055,7 +1057,7 @@ qt_wrap_ui(LAUNCHER_UI
|
|||||||
ui/dialogs/ProfileSelectDialog.ui
|
ui/dialogs/ProfileSelectDialog.ui
|
||||||
ui/dialogs/SkinUploadDialog.ui
|
ui/dialogs/SkinUploadDialog.ui
|
||||||
ui/dialogs/ExportInstanceDialog.ui
|
ui/dialogs/ExportInstanceDialog.ui
|
||||||
ui/dialogs/ExportMrPackDialog.ui
|
ui/dialogs/ExportPackDialog.ui
|
||||||
ui/dialogs/IconPickerDialog.ui
|
ui/dialogs/IconPickerDialog.ui
|
||||||
ui/dialogs/ImportResourceDialog.ui
|
ui/dialogs/ImportResourceDialog.ui
|
||||||
ui/dialogs/MSALoginDialog.ui
|
ui/dialogs/MSALoginDialog.ui
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
|
#include <QPair>
|
||||||
|
|
||||||
#include "BuildConfig.h"
|
#include "BuildConfig.h"
|
||||||
|
|
||||||
@ -102,7 +103,7 @@ namespace fs = ghc::filesystem;
|
|||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#elif defined(Q_OS_MACOS) || defined(Q_OS_OPENBSD)
|
#elif defined(Q_OS_MACOS)
|
||||||
#include <sys/attr.h>
|
#include <sys/attr.h>
|
||||||
#include <sys/clonefile.h>
|
#include <sys/clonefile.h>
|
||||||
#elif defined(Q_OS_WIN)
|
#elif defined(Q_OS_WIN)
|
||||||
@ -246,6 +247,7 @@ bool copy::operator()(const QString& offset, bool dryRun)
|
|||||||
{
|
{
|
||||||
using copy_opts = fs::copy_options;
|
using copy_opts = fs::copy_options;
|
||||||
m_copied = 0; // reset counter
|
m_copied = 0; // reset counter
|
||||||
|
m_failedPaths.clear();
|
||||||
|
|
||||||
// NOTE always deep copy on windows. the alternatives are too messy.
|
// NOTE always deep copy on windows. the alternatives are too messy.
|
||||||
#if defined Q_OS_WIN32
|
#if defined Q_OS_WIN32
|
||||||
@ -277,6 +279,9 @@ bool copy::operator()(const QString& offset, bool dryRun)
|
|||||||
qWarning() << "Failed to copy files:" << QString::fromStdString(err.message());
|
qWarning() << "Failed to copy files:" << QString::fromStdString(err.message());
|
||||||
qDebug() << "Source file:" << src_path;
|
qDebug() << "Source file:" << src_path;
|
||||||
qDebug() << "Destination file:" << dst_path;
|
qDebug() << "Destination file:" << dst_path;
|
||||||
|
m_failedPaths.append(dst_path);
|
||||||
|
emit copyFailed(relative_dst_path);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
m_copied++;
|
m_copied++;
|
||||||
emit fileCopied(relative_dst_path);
|
emit fileCopied(relative_dst_path);
|
||||||
@ -773,9 +778,43 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri
|
|||||||
destination = PathCombine(getDesktopDir(), RemoveInvalidFilenameChars(name));
|
destination = PathCombine(getDesktopDir(), RemoveInvalidFilenameChars(name));
|
||||||
}
|
}
|
||||||
#if defined(Q_OS_MACOS)
|
#if defined(Q_OS_MACOS)
|
||||||
destination += ".command";
|
// Create the Application
|
||||||
|
QDir applicationDirectory = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation) + "/" + BuildConfig.LAUNCHER_NAME + " Instances/";
|
||||||
|
|
||||||
QFile f(destination);
|
if (!applicationDirectory.mkpath(".")) {
|
||||||
|
qWarning() << "Couldn't create application directory";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDir application = applicationDirectory.path() + "/" + name + ".app/";
|
||||||
|
|
||||||
|
if (application.exists()) {
|
||||||
|
qWarning() << "Application already exists!";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!application.mkpath(".")) {
|
||||||
|
qWarning() << "Couldn't create application";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDir content = application.path() + "/Contents/";
|
||||||
|
QDir resources = content.path() + "/Resources/";
|
||||||
|
QDir binaryDir = content.path() + "/MacOS/";
|
||||||
|
QFile info = content.path() + "/Info.plist";
|
||||||
|
|
||||||
|
if (!(content.mkpath(".") && resources.mkpath(".") && binaryDir.mkpath("."))) {
|
||||||
|
qWarning() << "Couldn't create directories within application";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
info.open(QIODevice::WriteOnly | QIODevice::Text);
|
||||||
|
|
||||||
|
QFile(icon).rename(resources.path() + "/Icon.icns");
|
||||||
|
|
||||||
|
// Create the Command file
|
||||||
|
QString exec = binaryDir.path() + "/Run.command";
|
||||||
|
|
||||||
|
QFile f(exec);
|
||||||
f.open(QIODevice::WriteOnly | QIODevice::Text);
|
f.open(QIODevice::WriteOnly | QIODevice::Text);
|
||||||
QTextStream stream(&f);
|
QTextStream stream(&f);
|
||||||
|
|
||||||
@ -792,6 +831,28 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri
|
|||||||
|
|
||||||
f.setPermissions(f.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeGroup | QFileDevice::ExeOther);
|
f.setPermissions(f.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeGroup | QFileDevice::ExeOther);
|
||||||
|
|
||||||
|
// Generate the Info.plist
|
||||||
|
QTextStream infoStream(&info);
|
||||||
|
infoStream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?> \n"
|
||||||
|
"<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
|
||||||
|
"\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">"
|
||||||
|
"<plist version=\"1.0\">\n"
|
||||||
|
"<dict>\n"
|
||||||
|
" <key>CFBundleExecutable</key>\n"
|
||||||
|
" <string>Run.command</string>\n" // The path to the executable
|
||||||
|
" <key>CFBundleIconFile</key>\n"
|
||||||
|
" <string>Icon.icns</string>\n"
|
||||||
|
" <key>CFBundleName</key>\n"
|
||||||
|
" <string>" << name << "</string>\n" // Name of the application
|
||||||
|
" <key>CFBundlePackageType</key>\n"
|
||||||
|
" <string>APPL</string>\n"
|
||||||
|
" <key>CFBundleShortVersionString</key>\n"
|
||||||
|
" <string>1.0</string>\n"
|
||||||
|
" <key>CFBundleVersion</key>\n"
|
||||||
|
" <string>1.0</string>\n"
|
||||||
|
"</dict>\n"
|
||||||
|
"</plist>";
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
#elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
#elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
||||||
if (!destination.endsWith(".desktop")) // in case of isFlatpak destination is already populated
|
if (!destination.endsWith(".desktop")) // in case of isFlatpak destination is already populated
|
||||||
@ -1077,6 +1138,7 @@ bool clone::operator()(const QString& offset, bool dryRun)
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_cloned = 0; // reset counter
|
m_cloned = 0; // reset counter
|
||||||
|
m_failedClones.clear();
|
||||||
|
|
||||||
auto src = PathCombine(m_src.absolutePath(), offset);
|
auto src = PathCombine(m_src.absolutePath(), offset);
|
||||||
auto dst = PathCombine(m_dst.absolutePath(), offset);
|
auto dst = PathCombine(m_dst.absolutePath(), offset);
|
||||||
@ -1097,6 +1159,9 @@ bool clone::operator()(const QString& offset, bool dryRun)
|
|||||||
qDebug() << "Failed to clone files: error" << err.value() << "message" << QString::fromStdString(err.message());
|
qDebug() << "Failed to clone files: error" << err.value() << "message" << QString::fromStdString(err.message());
|
||||||
qDebug() << "Source file:" << src_path;
|
qDebug() << "Source file:" << src_path;
|
||||||
qDebug() << "Destination file:" << dst_path;
|
qDebug() << "Destination file:" << dst_path;
|
||||||
|
m_failedClones.append(qMakePair(src_path, dst_path));
|
||||||
|
emit cloneFailed(src_path, dst_path);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
m_cloned++;
|
m_cloned++;
|
||||||
emit fileCloned(src_path, dst_path);
|
emit fileCloned(src_path, dst_path);
|
||||||
@ -1156,7 +1221,7 @@ bool clone_file(const QString& src, const QString& dst, std::error_code& ec)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#elif defined(Q_OS_MACOS) || defined(Q_OS_OPENBSD)
|
#elif defined(Q_OS_MACOS)
|
||||||
|
|
||||||
if (!macos_bsd_clonefile(src_path, dst_path, ec)) {
|
if (!macos_bsd_clonefile(src_path, dst_path, ec)) {
|
||||||
qDebug() << "failed macos_bsd_clonefile:";
|
qDebug() << "failed macos_bsd_clonefile:";
|
||||||
@ -1385,7 +1450,7 @@ bool linux_ficlone(const std::string& src_path, const std::string& dst_path, std
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#elif defined(Q_OS_MACOS) || defined(Q_OS_OPENBSD)
|
#elif defined(Q_OS_MACOS)
|
||||||
|
|
||||||
bool macos_bsd_clonefile(const std::string& src_path, const std::string& dst_path, std::error_code& ec)
|
bool macos_bsd_clonefile(const std::string& src_path, const std::string& dst_path, std::error_code& ec)
|
||||||
{
|
{
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
#include <system_error>
|
#include <system_error>
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
#include <QPair>
|
||||||
#include <QFlags>
|
#include <QFlags>
|
||||||
#include <QLocalServer>
|
#include <QLocalServer>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
@ -112,9 +113,12 @@ class copy : public QObject {
|
|||||||
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
|
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
|
||||||
|
|
||||||
int totalCopied() { return m_copied; }
|
int totalCopied() { return m_copied; }
|
||||||
|
int totalFailed() { return m_failedPaths.length(); }
|
||||||
|
QStringList failed() { return m_failedPaths; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void fileCopied(const QString& relativeName);
|
void fileCopied(const QString& relativeName);
|
||||||
|
void copyFailed(const QString& relativeName);
|
||||||
// TODO: maybe add a "shouldCopy" signal in the future?
|
// TODO: maybe add a "shouldCopy" signal in the future?
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -127,6 +131,7 @@ class copy : public QObject {
|
|||||||
QDir m_src;
|
QDir m_src;
|
||||||
QDir m_dst;
|
QDir m_dst;
|
||||||
int m_copied;
|
int m_copied;
|
||||||
|
QStringList m_failedPaths;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LinkPair {
|
struct LinkPair {
|
||||||
@ -471,6 +476,9 @@ class clone : public QObject {
|
|||||||
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
|
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
|
||||||
|
|
||||||
int totalCloned() { return m_cloned; }
|
int totalCloned() { return m_cloned; }
|
||||||
|
int totalFailed() { return m_failedClones.length(); }
|
||||||
|
|
||||||
|
QList<QPair<QString, QString>> failed() { return m_failedClones; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void fileCloned(const QString& src, const QString& dst);
|
void fileCloned(const QString& src, const QString& dst);
|
||||||
@ -485,6 +493,7 @@ class clone : public QObject {
|
|||||||
QDir m_src;
|
QDir m_src;
|
||||||
QDir m_dst;
|
QDir m_dst;
|
||||||
int m_cloned;
|
int m_cloned;
|
||||||
|
QList<QPair<QString, QString>> m_failedClones;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -187,8 +187,8 @@ void LaunchController::login() {
|
|||||||
switch(m_accountToUse->accountState()) {
|
switch(m_accountToUse->accountState()) {
|
||||||
case AccountState::Offline: {
|
case AccountState::Offline: {
|
||||||
m_session->wants_online = false;
|
m_session->wants_online = false;
|
||||||
// NOTE: fallthrough is intentional
|
|
||||||
}
|
}
|
||||||
|
/* fallthrough */
|
||||||
case AccountState::Online: {
|
case AccountState::Online: {
|
||||||
if(!m_session->wants_online) {
|
if(!m_session->wants_online) {
|
||||||
// we ask the user for a player name
|
// we ask the user for a player name
|
||||||
@ -267,8 +267,8 @@ void LaunchController::login() {
|
|||||||
// This means some sort of soft error that we can fix with a refresh ... so let's refresh.
|
// This means some sort of soft error that we can fix with a refresh ... so let's refresh.
|
||||||
case AccountState::Unchecked: {
|
case AccountState::Unchecked: {
|
||||||
m_accountToUse->refresh();
|
m_accountToUse->refresh();
|
||||||
// NOTE: fallthrough intentional
|
|
||||||
}
|
}
|
||||||
|
/* fallthrough */
|
||||||
case AccountState::Working: {
|
case AccountState::Working: {
|
||||||
// refresh is in progress, we need to wait for it to finish to proceed.
|
// refresh is in progress, we need to wait for it to finish to proceed.
|
||||||
ProgressDialog progDialog(m_parentWidget);
|
ProgressDialog progDialog(m_parentWidget);
|
||||||
|
@ -193,29 +193,23 @@ QVariant VersionProxyModel::data(const QModelIndex &index, int role) const
|
|||||||
}
|
}
|
||||||
case Qt::ToolTipRole:
|
case Qt::ToolTipRole:
|
||||||
{
|
{
|
||||||
switch(column)
|
if(column == Name && hasRecommended)
|
||||||
{
|
{
|
||||||
case Name:
|
auto value = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole);
|
||||||
|
if(value.toBool())
|
||||||
{
|
{
|
||||||
if(hasRecommended)
|
|
||||||
{
|
|
||||||
auto recommended = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole);
|
|
||||||
if (recommended.toBool()) {
|
|
||||||
return tr("Recommended");
|
return tr("Recommended");
|
||||||
} else if(hasLatest) {
|
} else if(hasLatest) {
|
||||||
auto latest = sourceModel()->data(parentIndex, BaseVersionList::LatestRole);
|
auto value = sourceModel()->data(parentIndex, BaseVersionList::LatestRole);
|
||||||
if (latest.toBool()) {
|
if(value.toBool())
|
||||||
|
{
|
||||||
return tr("Latest");
|
return tr("Latest");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
return sourceModel()->data(parentIndex, BaseVersionList::VersionIdRole);
|
return sourceModel()->data(parentIndex, BaseVersionList::VersionIdRole);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
case Qt::DecorationRole:
|
case Qt::DecorationRole:
|
||||||
{
|
{
|
||||||
switch(column)
|
switch(column)
|
||||||
|
@ -45,10 +45,10 @@ QVariant Index::data(const QModelIndex &index, int role) const
|
|||||||
switch (role)
|
switch (role)
|
||||||
{
|
{
|
||||||
case Qt::DisplayRole:
|
case Qt::DisplayRole:
|
||||||
switch (index.column())
|
if (index.column() == 0) {
|
||||||
{
|
return list->humanReadable();
|
||||||
case 0: return list->humanReadable();
|
} else {
|
||||||
default: break;
|
break;
|
||||||
}
|
}
|
||||||
case UidRole: return list->uid();
|
case UidRole: return list->uid();
|
||||||
case NameRole: return list->name();
|
case NameRole: return list->name();
|
||||||
|
@ -1112,36 +1112,27 @@ JavaVersion MinecraftInstance::getJavaVersion()
|
|||||||
|
|
||||||
std::shared_ptr<ModFolderModel> MinecraftInstance::loaderModList()
|
std::shared_ptr<ModFolderModel> MinecraftInstance::loaderModList()
|
||||||
{
|
{
|
||||||
if (!m_loader_mod_list)
|
if (!m_loader_mod_list) {
|
||||||
{
|
|
||||||
bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
|
bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
|
||||||
m_loader_mod_list.reset(new ModFolderModel(modsRoot(), this, is_indexed));
|
m_loader_mod_list.reset(new ModFolderModel(modsRoot(), this, is_indexed));
|
||||||
m_loader_mod_list->disableInteraction(isRunning());
|
|
||||||
connect(this, &BaseInstance::runningStatusChanged, m_loader_mod_list.get(), &ModFolderModel::disableInteraction);
|
|
||||||
}
|
}
|
||||||
return m_loader_mod_list;
|
return m_loader_mod_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<ModFolderModel> MinecraftInstance::coreModList()
|
std::shared_ptr<ModFolderModel> MinecraftInstance::coreModList()
|
||||||
{
|
{
|
||||||
if (!m_core_mod_list)
|
if (!m_core_mod_list) {
|
||||||
{
|
|
||||||
bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
|
bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
|
||||||
m_core_mod_list.reset(new ModFolderModel(coreModsDir(), this, is_indexed));
|
m_core_mod_list.reset(new ModFolderModel(coreModsDir(), this, is_indexed));
|
||||||
m_core_mod_list->disableInteraction(isRunning());
|
|
||||||
connect(this, &BaseInstance::runningStatusChanged, m_core_mod_list.get(), &ModFolderModel::disableInteraction);
|
|
||||||
}
|
}
|
||||||
return m_core_mod_list;
|
return m_core_mod_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<ModFolderModel> MinecraftInstance::nilModList()
|
std::shared_ptr<ModFolderModel> MinecraftInstance::nilModList()
|
||||||
{
|
{
|
||||||
if (!m_nil_mod_list)
|
if (!m_nil_mod_list) {
|
||||||
{
|
|
||||||
bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
|
bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
|
||||||
m_nil_mod_list.reset(new ModFolderModel(nilModsDir(), this, is_indexed, false));
|
m_nil_mod_list.reset(new ModFolderModel(nilModsDir(), this, is_indexed, false));
|
||||||
m_nil_mod_list->disableInteraction(isRunning());
|
|
||||||
connect(this, &BaseInstance::runningStatusChanged, m_nil_mod_list.get(), &ModFolderModel::disableInteraction);
|
|
||||||
}
|
}
|
||||||
return m_nil_mod_list;
|
return m_nil_mod_list;
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,8 @@
|
|||||||
static const QMap<QString, ResourceAPI::ModLoaderType> modloaderMapping{
|
static const QMap<QString, ResourceAPI::ModLoaderType> modloaderMapping{
|
||||||
{"net.minecraftforge", ResourceAPI::Forge},
|
{"net.minecraftforge", ResourceAPI::Forge},
|
||||||
{"net.fabricmc.fabric-loader", ResourceAPI::Fabric},
|
{"net.fabricmc.fabric-loader", ResourceAPI::Fabric},
|
||||||
{"org.quiltmc.quilt-loader", ResourceAPI::Quilt}
|
{"org.quiltmc.quilt-loader", ResourceAPI::Quilt},
|
||||||
|
{"com.mumfrey.liteloader", ResourceAPI::LiteLoader}
|
||||||
};
|
};
|
||||||
|
|
||||||
PackProfile::PackProfile(MinecraftInstance * instance)
|
PackProfile::PackProfile(MinecraftInstance * instance)
|
||||||
|
@ -328,6 +328,9 @@ QVariant AccountList::data(const QModelIndex &index, int role) const
|
|||||||
case AccountState::Gone: {
|
case AccountState::Gone: {
|
||||||
return tr("Gone", "Account status");
|
return tr("Gone", "Account status");
|
||||||
}
|
}
|
||||||
|
default: {
|
||||||
|
return tr("Unknown", "Account status");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -354,12 +357,13 @@ QVariant AccountList::data(const QModelIndex &index, int role) const
|
|||||||
return QVariant::fromValue(account);
|
return QVariant::fromValue(account);
|
||||||
|
|
||||||
case Qt::CheckStateRole:
|
case Qt::CheckStateRole:
|
||||||
switch (index.column())
|
if (index.column() == ProfileNameColumn) {
|
||||||
{
|
|
||||||
case ProfileNameColumn:
|
|
||||||
return account == m_defaultAccount ? Qt::Checked : Qt::Unchecked;
|
return account == m_defaultAccount ? Qt::Checked : Qt::Unchecked;
|
||||||
|
} else {
|
||||||
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
@ -273,6 +273,7 @@ void Yggdrasil::processReply() {
|
|||||||
AccountTaskState::STATE_FAILED_GONE,
|
AccountTaskState::STATE_FAILED_GONE,
|
||||||
tr("The Mojang account no longer exists. It may have been migrated to a Microsoft account.")
|
tr("The Mojang account no longer exists. It may have been migrated to a Microsoft account.")
|
||||||
);
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
changeState(
|
changeState(
|
||||||
|
@ -74,6 +74,7 @@ std::pair<int, bool> DataPack::compare(const Resource& other, SortType type) con
|
|||||||
auto res = Resource::compare(other, type);
|
auto res = Resource::compare(other, type);
|
||||||
if (res.first != 0)
|
if (res.first != 0)
|
||||||
return res;
|
return res;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case SortType::PACK_FORMAT: {
|
case SortType::PACK_FORMAT: {
|
||||||
auto this_ver = packFormat();
|
auto this_ver = packFormat();
|
||||||
@ -83,6 +84,7 @@ std::pair<int, bool> DataPack::compare(const Resource& other, SortType type) con
|
|||||||
return { 1, type == SortType::PACK_FORMAT };
|
return { 1, type == SortType::PACK_FORMAT };
|
||||||
if (this_ver < other_ver)
|
if (this_ver < other_ver)
|
||||||
return { -1, type == SortType::PACK_FORMAT };
|
return { -1, type == SortType::PACK_FORMAT };
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { 0, false };
|
return { 0, false };
|
||||||
|
@ -91,6 +91,7 @@ std::pair<int, bool> Mod::compare(const Resource& other, SortType type) const
|
|||||||
auto res = Resource::compare(other, type);
|
auto res = Resource::compare(other, type);
|
||||||
if (res.first != 0)
|
if (res.first != 0)
|
||||||
return res;
|
return res;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case SortType::VERSION: {
|
case SortType::VERSION: {
|
||||||
auto this_ver = Version(version());
|
auto this_ver = Version(version());
|
||||||
@ -99,11 +100,13 @@ std::pair<int, bool> Mod::compare(const Resource& other, SortType type) const
|
|||||||
return { 1, type == SortType::VERSION };
|
return { 1, type == SortType::VERSION };
|
||||||
if (this_ver < other_ver)
|
if (this_ver < other_ver)
|
||||||
return { -1, type == SortType::VERSION };
|
return { -1, type == SortType::VERSION };
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case SortType::PROVIDER: {
|
case SortType::PROVIDER: {
|
||||||
auto compare_result = QString::compare(provider().value_or("Unknown"), cast_other->provider().value_or("Unknown"), Qt::CaseInsensitive);
|
auto compare_result = QString::compare(provider().value_or("Unknown"), cast_other->provider().value_or("Unknown"), Qt::CaseInsensitive);
|
||||||
if (compare_result != 0)
|
if (compare_result != 0)
|
||||||
return { compare_result, type == SortType::PROVIDER };
|
return { compare_result, type == SortType::PROVIDER };
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { 0, false };
|
return { 0, false };
|
||||||
@ -123,7 +126,7 @@ bool Mod::applyFilter(QRegularExpression filter) const
|
|||||||
return Resource::applyFilter(filter);
|
return Resource::applyFilter(filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Mod::destroy(QDir& index_dir, bool preserve_metadata) -> bool
|
auto Mod::destroy(QDir& index_dir, bool preserve_metadata, bool attempt_trash) -> bool
|
||||||
{
|
{
|
||||||
if (!preserve_metadata) {
|
if (!preserve_metadata) {
|
||||||
qDebug() << QString("Destroying metadata for '%1' on purpose").arg(name());
|
qDebug() << QString("Destroying metadata for '%1' on purpose").arg(name());
|
||||||
@ -136,7 +139,7 @@ auto Mod::destroy(QDir& index_dir, bool preserve_metadata) -> bool
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Resource::destroy();
|
return Resource::destroy(attempt_trash);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Mod::details() const -> const ModDetails&
|
auto Mod::details() const -> const ModDetails&
|
||||||
@ -166,6 +169,13 @@ auto Mod::homeurl() const -> QString
|
|||||||
return details().homeurl;
|
return details().homeurl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto Mod::metaurl() const -> QString
|
||||||
|
{
|
||||||
|
if (metadata() == nullptr)
|
||||||
|
return homeurl();
|
||||||
|
return ModPlatform::getMetaURL(metadata()->provider, metadata()->project_id);
|
||||||
|
}
|
||||||
|
|
||||||
auto Mod::description() const -> QString
|
auto Mod::description() const -> QString
|
||||||
{
|
{
|
||||||
return details().description;
|
return details().description;
|
||||||
|
@ -70,6 +70,7 @@ public:
|
|||||||
auto provider() const -> std::optional<QString>;
|
auto provider() const -> std::optional<QString>;
|
||||||
auto licenses() const -> const QList<ModLicense>&;
|
auto licenses() const -> const QList<ModLicense>&;
|
||||||
auto issueTracker() const -> QString;
|
auto issueTracker() const -> QString;
|
||||||
|
auto metaurl() const -> QString;
|
||||||
|
|
||||||
/** Get the intneral path to the mod's icon file*/
|
/** Get the intneral path to the mod's icon file*/
|
||||||
QString iconPath() const { return m_local_details.icon_file; }
|
QString iconPath() const { return m_local_details.icon_file; }
|
||||||
@ -92,7 +93,7 @@ public:
|
|||||||
[[nodiscard]] bool applyFilter(QRegularExpression filter) const override;
|
[[nodiscard]] bool applyFilter(QRegularExpression filter) const override;
|
||||||
|
|
||||||
// Delete all the files of this mod
|
// Delete all the files of this mod
|
||||||
auto destroy(QDir& index_dir, bool preserve_metadata = false) -> bool;
|
auto destroy(QDir& index_dir, bool preserve_metadata = false, bool attempt_trash = true) -> bool;
|
||||||
|
|
||||||
void finishResolvingWithDetails(ModDetails&& details);
|
void finishResolvingWithDetails(ModDetails&& details);
|
||||||
|
|
||||||
|
@ -202,7 +202,7 @@ bool ModFolderModel::uninstallMod(const QString& filename, bool preserve_metadat
|
|||||||
for(auto mod : allMods()) {
|
for(auto mod : allMods()) {
|
||||||
if(mod->fileinfo().fileName() == filename) {
|
if(mod->fileinfo().fileName() == filename) {
|
||||||
auto index_dir = indexDir();
|
auto index_dir = indexDir();
|
||||||
mod->destroy(index_dir, preserve_metadata);
|
mod->destroy(index_dir, preserve_metadata, false);
|
||||||
|
|
||||||
update();
|
update();
|
||||||
|
|
||||||
@ -215,15 +215,10 @@ bool ModFolderModel::uninstallMod(const QString& filename, bool preserve_metadat
|
|||||||
|
|
||||||
bool ModFolderModel::deleteMods(const QModelIndexList& indexes)
|
bool ModFolderModel::deleteMods(const QModelIndexList& indexes)
|
||||||
{
|
{
|
||||||
if(!m_can_interact) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (indexes.isEmpty())
|
if (indexes.isEmpty())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
for (auto i: indexes)
|
for (auto i : indexes) {
|
||||||
{
|
|
||||||
if (i.column() != 0) {
|
if (i.column() != 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -71,6 +71,7 @@ std::pair<int, bool> Resource::compare(const Resource& other, SortType type) con
|
|||||||
return { 1, type == SortType::ENABLED };
|
return { 1, type == SortType::ENABLED };
|
||||||
if (!enabled() && other.enabled())
|
if (!enabled() && other.enabled())
|
||||||
return { -1, type == SortType::ENABLED };
|
return { -1, type == SortType::ENABLED };
|
||||||
|
break;
|
||||||
case SortType::NAME: {
|
case SortType::NAME: {
|
||||||
QString this_name{ name() };
|
QString this_name{ name() };
|
||||||
QString other_name{ other.name() };
|
QString other_name{ other.name() };
|
||||||
@ -81,12 +82,14 @@ std::pair<int, bool> Resource::compare(const Resource& other, SortType type) con
|
|||||||
auto compare_result = QString::compare(this_name, other_name, Qt::CaseInsensitive);
|
auto compare_result = QString::compare(this_name, other_name, Qt::CaseInsensitive);
|
||||||
if (compare_result != 0)
|
if (compare_result != 0)
|
||||||
return { compare_result, type == SortType::NAME };
|
return { compare_result, type == SortType::NAME };
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case SortType::DATE:
|
case SortType::DATE:
|
||||||
if (dateTimeChanged() > other.dateTimeChanged())
|
if (dateTimeChanged() > other.dateTimeChanged())
|
||||||
return { 1, type == SortType::DATE };
|
return { 1, type == SortType::DATE };
|
||||||
if (dateTimeChanged() < other.dateTimeChanged())
|
if (dateTimeChanged() < other.dateTimeChanged())
|
||||||
return { -1, type == SortType::DATE };
|
return { -1, type == SortType::DATE };
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { 0, false };
|
return { 0, false };
|
||||||
@ -145,14 +148,10 @@ bool Resource::enable(EnableAction action)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Resource::destroy()
|
bool Resource::destroy(bool attemptTrash)
|
||||||
{
|
{
|
||||||
m_type = ResourceType::UNKNOWN;
|
m_type = ResourceType::UNKNOWN;
|
||||||
|
return (attemptTrash && FS::trash(m_file_info.filePath())) || FS::deletePath(m_file_info.filePath());
|
||||||
if (FS::trash(m_file_info.filePath()))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return FS::deletePath(m_file_info.filePath());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Resource::isSymLinkUnder(const QString& instPath) const
|
bool Resource::isSymLinkUnder(const QString& instPath) const
|
||||||
|
@ -92,7 +92,7 @@ class Resource : public QObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delete all files of this resource.
|
// Delete all files of this resource.
|
||||||
bool destroy();
|
bool destroy(bool attemptTrash = true);
|
||||||
|
|
||||||
[[nodiscard]] auto isSymLink() const -> bool { return m_file_info.isSymLink(); }
|
[[nodiscard]] auto isSymLink() const -> bool { return m_file_info.isSymLink(); }
|
||||||
|
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
#include "ResourceFolderModel.h"
|
#include "ResourceFolderModel.h"
|
||||||
|
#include <QMessageBox>
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
|
#include <QMenu>
|
||||||
#include <QMimeData>
|
#include <QMimeData>
|
||||||
#include <QStyle>
|
#include <QStyle>
|
||||||
#include <QThreadPool>
|
#include <QThreadPool>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QMenu>
|
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
@ -18,6 +19,7 @@
|
|||||||
|
|
||||||
#include "settings/Setting.h"
|
#include "settings/Setting.h"
|
||||||
#include "tasks/Task.h"
|
#include "tasks/Task.h"
|
||||||
|
#include "ui/dialogs/CustomMessageBox.h"
|
||||||
|
|
||||||
ResourceFolderModel::ResourceFolderModel(QDir dir, BaseInstance* instance, QObject* parent, bool create_dir)
|
ResourceFolderModel::ResourceFolderModel(QDir dir, BaseInstance* instance, QObject* parent, bool create_dir)
|
||||||
: QAbstractListModel(parent), m_dir(dir), m_instance(instance), m_watcher(this)
|
: QAbstractListModel(parent), m_dir(dir), m_instance(instance), m_watcher(this)
|
||||||
@ -77,10 +79,6 @@ bool ResourceFolderModel::stopWatching(const QStringList paths)
|
|||||||
|
|
||||||
bool ResourceFolderModel::installResource(QString original_path)
|
bool ResourceFolderModel::installResource(QString original_path)
|
||||||
{
|
{
|
||||||
if (!m_can_interact) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: fix for GH-1178: remove trailing slash to avoid issues with using the empty result of QFileInfo::fileName
|
// NOTE: fix for GH-1178: remove trailing slash to avoid issues with using the empty result of QFileInfo::fileName
|
||||||
original_path = FS::NormalizePath(original_path);
|
original_path = FS::NormalizePath(original_path);
|
||||||
QFileInfo file_info(original_path);
|
QFileInfo file_info(original_path);
|
||||||
@ -159,7 +157,7 @@ bool ResourceFolderModel::uninstallResource(QString file_name)
|
|||||||
{
|
{
|
||||||
for (auto& resource : m_resources) {
|
for (auto& resource : m_resources) {
|
||||||
if (resource->fileinfo().fileName() == file_name) {
|
if (resource->fileinfo().fileName() == file_name) {
|
||||||
auto res = resource->destroy();
|
auto res = resource->destroy(false);
|
||||||
|
|
||||||
update();
|
update();
|
||||||
|
|
||||||
@ -171,9 +169,6 @@ bool ResourceFolderModel::uninstallResource(QString file_name)
|
|||||||
|
|
||||||
bool ResourceFolderModel::deleteResources(const QModelIndexList& indexes)
|
bool ResourceFolderModel::deleteResources(const QModelIndexList& indexes)
|
||||||
{
|
{
|
||||||
if (!m_can_interact)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (indexes.isEmpty())
|
if (indexes.isEmpty())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@ -194,9 +189,6 @@ bool ResourceFolderModel::deleteResources(const QModelIndexList& indexes)
|
|||||||
|
|
||||||
bool ResourceFolderModel::setResourceEnabled(const QModelIndexList& indexes, EnableAction action)
|
bool ResourceFolderModel::setResourceEnabled(const QModelIndexList& indexes, EnableAction action)
|
||||||
{
|
{
|
||||||
if (!m_can_interact)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (indexes.isEmpty())
|
if (indexes.isEmpty())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@ -249,7 +241,9 @@ bool ResourceFolderModel::update()
|
|||||||
connect(m_current_update_task.get(), &Task::succeeded, this, &ResourceFolderModel::onUpdateSucceeded,
|
connect(m_current_update_task.get(), &Task::succeeded, this, &ResourceFolderModel::onUpdateSucceeded,
|
||||||
Qt::ConnectionType::QueuedConnection);
|
Qt::ConnectionType::QueuedConnection);
|
||||||
connect(m_current_update_task.get(), &Task::failed, this, &ResourceFolderModel::onUpdateFailed, Qt::ConnectionType::QueuedConnection);
|
connect(m_current_update_task.get(), &Task::failed, this, &ResourceFolderModel::onUpdateFailed, Qt::ConnectionType::QueuedConnection);
|
||||||
connect(m_current_update_task.get(), &Task::finished, this, [=] {
|
connect(
|
||||||
|
m_current_update_task.get(), &Task::finished, this,
|
||||||
|
[=] {
|
||||||
m_current_update_task.reset();
|
m_current_update_task.reset();
|
||||||
if (m_scheduled_update) {
|
if (m_scheduled_update) {
|
||||||
m_scheduled_update = false;
|
m_scheduled_update = false;
|
||||||
@ -257,7 +251,8 @@ bool ResourceFolderModel::update()
|
|||||||
} else {
|
} else {
|
||||||
emit updateFinished();
|
emit updateFinished();
|
||||||
}
|
}
|
||||||
}, Qt::ConnectionType::QueuedConnection);
|
},
|
||||||
|
Qt::ConnectionType::QueuedConnection);
|
||||||
|
|
||||||
QThreadPool::globalInstance()->start(m_current_update_task.get());
|
QThreadPool::globalInstance()->start(m_current_update_task.get());
|
||||||
|
|
||||||
@ -347,15 +342,9 @@ Qt::DropActions ResourceFolderModel::supportedDropActions() const
|
|||||||
Qt::ItemFlags ResourceFolderModel::flags(const QModelIndex& index) const
|
Qt::ItemFlags ResourceFolderModel::flags(const QModelIndex& index) const
|
||||||
{
|
{
|
||||||
Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index);
|
Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index);
|
||||||
auto flags = defaultFlags;
|
auto flags = defaultFlags | Qt::ItemIsDropEnabled;
|
||||||
if (!m_can_interact) {
|
if (index.isValid())
|
||||||
flags &= ~Qt::ItemIsDropEnabled;
|
|
||||||
} else {
|
|
||||||
flags |= Qt::ItemIsDropEnabled;
|
|
||||||
if (index.isValid()) {
|
|
||||||
flags |= Qt::ItemIsUserCheckable;
|
flags |= Qt::ItemIsUserCheckable;
|
||||||
}
|
|
||||||
}
|
|
||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -430,7 +419,8 @@ QVariant ResourceFolderModel::data(const QModelIndex& index, int role) const
|
|||||||
return m_resources[row]->internal_id() +
|
return m_resources[row]->internal_id() +
|
||||||
tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original."
|
tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original."
|
||||||
"\nCanonical Path: %1")
|
"\nCanonical Path: %1")
|
||||||
.arg(at(row).fileinfo().canonicalFilePath());;
|
.arg(at(row).fileinfo().canonicalFilePath());
|
||||||
|
;
|
||||||
}
|
}
|
||||||
if (at(row).isMoreThanOneHardLink()) {
|
if (at(row).isMoreThanOneHardLink()) {
|
||||||
return m_resources[row]->internal_id() +
|
return m_resources[row]->internal_id() +
|
||||||
@ -463,8 +453,20 @@ bool ResourceFolderModel::setData(const QModelIndex& index, [[maybe_unused]] con
|
|||||||
if (row < 0 || row >= rowCount(index.parent()) || !index.isValid())
|
if (row < 0 || row >= rowCount(index.parent()) || !index.isValid())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (role == Qt::CheckStateRole)
|
if (role == Qt::CheckStateRole) {
|
||||||
|
if (m_instance != nullptr && m_instance->isRunning()) {
|
||||||
|
auto response =
|
||||||
|
CustomMessageBox::selectable(nullptr, "Confirm toggle",
|
||||||
|
"If you enable/disable this resource while the game is running it may crash your game.\n"
|
||||||
|
"Are you sure you want to do this?",
|
||||||
|
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
|
||||||
|
->exec();
|
||||||
|
|
||||||
|
if (response != QMessageBox::Yes)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return setResourceEnabled({ index }, EnableAction::TOGGLE);
|
return setResourceEnabled({ index }, EnableAction::TOGGLE);
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -583,16 +585,6 @@ SortType ResourceFolderModel::columnToSortKey(size_t column) const
|
|||||||
return m_column_sort_keys.at(column);
|
return m_column_sort_keys.at(column);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResourceFolderModel::enableInteraction(bool enabled)
|
|
||||||
{
|
|
||||||
if (m_can_interact == enabled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_can_interact = enabled;
|
|
||||||
if (size())
|
|
||||||
emit dataChanged(index(0), index(size() - 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Standard Proxy Model for createFilterProxyModel */
|
/* Standard Proxy Model for createFilterProxyModel */
|
||||||
[[nodiscard]] bool ResourceFolderModel::ProxyModel::filterAcceptsRow(int source_row, [[maybe_unused]] const QModelIndex& source_parent) const
|
[[nodiscard]] bool ResourceFolderModel::ProxyModel::filterAcceptsRow(int source_row, [[maybe_unused]] const QModelIndex& source_parent) const
|
||||||
{
|
{
|
||||||
@ -628,6 +620,7 @@ void ResourceFolderModel::enableInteraction(bool enabled)
|
|||||||
return (compare_result.first > 0);
|
return (compare_result.first > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ResourceFolderModel::instDirPath() const {
|
QString ResourceFolderModel::instDirPath() const
|
||||||
|
{
|
||||||
return QFileInfo(m_instance->instanceRoot()).absoluteFilePath();
|
return QFileInfo(m_instance->instanceRoot()).absoluteFilePath();
|
||||||
}
|
}
|
||||||
|
@ -14,8 +14,8 @@
|
|||||||
|
|
||||||
#include "BaseInstance.h"
|
#include "BaseInstance.h"
|
||||||
|
|
||||||
#include "tasks/Task.h"
|
|
||||||
#include "tasks/ConcurrentTask.h"
|
#include "tasks/ConcurrentTask.h"
|
||||||
|
#include "tasks/Task.h"
|
||||||
|
|
||||||
class QSortFilterProxyModel;
|
class QSortFilterProxyModel;
|
||||||
|
|
||||||
@ -141,10 +141,6 @@ class ResourceFolderModel : public QAbstractListModel {
|
|||||||
|
|
||||||
QString instDirPath() const;
|
QString instDirPath() const;
|
||||||
|
|
||||||
public slots:
|
|
||||||
void enableInteraction(bool enabled);
|
|
||||||
void disableInteraction(bool disabled) { enableInteraction(!disabled); }
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void updateFinished();
|
void updateFinished();
|
||||||
|
|
||||||
@ -193,7 +189,11 @@ class ResourceFolderModel : public QAbstractListModel {
|
|||||||
* if the resource is complex and has more stuff to parse.
|
* if the resource is complex and has more stuff to parse.
|
||||||
*/
|
*/
|
||||||
virtual void onParseSucceeded(int ticket, QString resource_id);
|
virtual void onParseSucceeded(int ticket, QString resource_id);
|
||||||
virtual void onParseFailed(int ticket, QString resource_id) { Q_UNUSED(ticket); Q_UNUSED(resource_id); }
|
virtual void onParseFailed(int ticket, QString resource_id)
|
||||||
|
{
|
||||||
|
Q_UNUSED(ticket);
|
||||||
|
Q_UNUSED(resource_id);
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Represents the relationship between a column's index (represented by the list index), and it's sorting key.
|
// Represents the relationship between a column's index (represented by the list index), and it's sorting key.
|
||||||
@ -203,8 +203,6 @@ class ResourceFolderModel : public QAbstractListModel {
|
|||||||
QStringList m_column_names_translated = {tr("Enable"), tr("Name"), tr("Last Modified")};
|
QStringList m_column_names_translated = {tr("Enable"), tr("Name"), tr("Last Modified")};
|
||||||
QList<QHeaderView::ResizeMode> m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Stretch, QHeaderView::ResizeToContents };
|
QList<QHeaderView::ResizeMode> m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Stretch, QHeaderView::ResizeToContents };
|
||||||
|
|
||||||
bool m_can_interact = true;
|
|
||||||
|
|
||||||
QDir m_dir;
|
QDir m_dir;
|
||||||
BaseInstance* m_instance;
|
BaseInstance* m_instance;
|
||||||
QFileSystemWatcher m_watcher;
|
QFileSystemWatcher m_watcher;
|
||||||
|
@ -102,6 +102,7 @@ std::pair<int, bool> ResourcePack::compare(const Resource& other, SortType type)
|
|||||||
auto res = Resource::compare(other, type);
|
auto res = Resource::compare(other, type);
|
||||||
if (res.first != 0)
|
if (res.first != 0)
|
||||||
return res;
|
return res;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case SortType::PACK_FORMAT: {
|
case SortType::PACK_FORMAT: {
|
||||||
auto this_ver = packFormat();
|
auto this_ver = packFormat();
|
||||||
@ -111,6 +112,7 @@ std::pair<int, bool> ResourcePack::compare(const Resource& other, SortType type)
|
|||||||
return { 1, type == SortType::PACK_FORMAT };
|
return { 1, type == SortType::PACK_FORMAT };
|
||||||
if (this_ver < other_ver)
|
if (this_ver < other_ver)
|
||||||
return { -1, type == SortType::PACK_FORMAT };
|
return { -1, type == SortType::PACK_FORMAT };
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { 0, false };
|
return { 0, false };
|
||||||
|
@ -103,7 +103,7 @@ void ModFolderLoadTask::executeTask()
|
|||||||
while (iter.hasNext()) {
|
while (iter.hasNext()) {
|
||||||
auto mod = iter.next().value();
|
auto mod = iter.next().value();
|
||||||
if (mod->status() == ModStatus::NotInstalled) {
|
if (mod->status() == ModStatus::NotInstalled) {
|
||||||
mod->destroy(m_index_dir, false);
|
mod->destroy(m_index_dir, false, false);
|
||||||
iter.remove();
|
iter.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,4 +77,10 @@ auto ProviderCapabilities::hash(ResourceProvider p, QIODevice* device, QString t
|
|||||||
return { hash.result().toHex() };
|
return { hash.result().toHex() };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString getMetaURL(ResourceProvider provider, QVariant projectID)
|
||||||
|
{
|
||||||
|
return ((provider == ModPlatform::ResourceProvider::FLAME) ? "https://www.curseforge.com/projects/" : "https://modrinth.com/mod/") +
|
||||||
|
projectID.toString();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ModPlatform
|
} // namespace ModPlatform
|
||||||
|
@ -128,6 +128,7 @@ struct IndexedPack {
|
|||||||
return std::any_of(versions.constBegin(), versions.constEnd(), [](auto const& v) { return v.is_currently_selected; });
|
return std::any_of(versions.constBegin(), versions.constEnd(), [](auto const& v) { return v.is_currently_selected; });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
QString getMetaURL(ResourceProvider provider, QVariant projectID);
|
||||||
|
|
||||||
struct OverrideDep {
|
struct OverrideDep {
|
||||||
QString quilt;
|
QString quilt;
|
||||||
@ -143,8 +144,12 @@ inline auto getOverrideDeps() -> QList<OverrideDep>
|
|||||||
|
|
||||||
{ "qvIfYCYJ", "P7dR8mSH", "API", ModPlatform::ResourceProvider::MODRINTH },
|
{ "qvIfYCYJ", "P7dR8mSH", "API", ModPlatform::ResourceProvider::MODRINTH },
|
||||||
{ "lwVhp9o5", "Ha28R6CL", "KotlinLibraries", ModPlatform::ResourceProvider::MODRINTH } };
|
{ "lwVhp9o5", "Ha28R6CL", "KotlinLibraries", ModPlatform::ResourceProvider::MODRINTH } };
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString getMetaURL(ResourceProvider provider, QVariant projectID);
|
||||||
|
|
||||||
|
|
||||||
} // namespace ModPlatform
|
} // namespace ModPlatform
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(ModPlatform::IndexedPack)
|
Q_DECLARE_METATYPE(ModPlatform::IndexedPack)
|
||||||
|
@ -23,6 +23,8 @@ class FlameAPI : public NetworkResourceAPI {
|
|||||||
|
|
||||||
[[nodiscard]] auto getSortingMethods() const -> QList<ResourceAPI::SortingMethod> override;
|
[[nodiscard]] auto getSortingMethods() const -> QList<ResourceAPI::SortingMethod> override;
|
||||||
|
|
||||||
|
static inline auto validateModLoaders(ModLoaderTypes loaders) -> bool { return loaders & (Forge | Fabric | Quilt); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static int getClassId(ModPlatform::ResourceType type)
|
static int getClassId(ModPlatform::ResourceType type)
|
||||||
{
|
{
|
||||||
|
@ -470,8 +470,9 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
|
|||||||
switch (result.type) {
|
switch (result.type) {
|
||||||
case Flame::File::Type::Folder: {
|
case Flame::File::Type::Folder: {
|
||||||
logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath));
|
logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath));
|
||||||
// fall-through intentional, we treat these as plain old mods and dump them wherever.
|
// fallthrough intentional, we treat these as plain old mods and dump them wherever.
|
||||||
}
|
}
|
||||||
|
/* fallthrough */
|
||||||
case Flame::File::Type::SingleFile:
|
case Flame::File::Type::SingleFile:
|
||||||
case Flame::File::Type::Mod: {
|
case Flame::File::Type::Mod: {
|
||||||
if (!result.url.isEmpty()) {
|
if (!result.url.isEmpty()) {
|
||||||
|
473
launcher/modplatform/flame/FlamePackExportTask.cpp
Normal file
473
launcher/modplatform/flame/FlamePackExportTask.cpp
Normal file
@ -0,0 +1,473 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||||
|
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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 "FlamePackExportTask.h"
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonObject>
|
||||||
|
|
||||||
|
#include <QCryptographicHash>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QtConcurrentRun>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <memory>
|
||||||
|
#include "Json.h"
|
||||||
|
#include "MMCZip.h"
|
||||||
|
#include "minecraft/PackProfile.h"
|
||||||
|
#include "minecraft/mod/ModFolderModel.h"
|
||||||
|
#include "modplatform/ModIndex.h"
|
||||||
|
#include "modplatform/flame/FlameModIndex.h"
|
||||||
|
#include "modplatform/helpers/HashUtils.h"
|
||||||
|
#include "tasks/Task.h"
|
||||||
|
|
||||||
|
const QString FlamePackExportTask::TEMPLATE = "<li><a href=\"{url}\">{name}{authors}</a></li>\n";
|
||||||
|
const QStringList FlamePackExportTask::FILE_EXTENSIONS({ "jar", "zip" });
|
||||||
|
|
||||||
|
FlamePackExportTask::FlamePackExportTask(const QString& name,
|
||||||
|
const QString& version,
|
||||||
|
const QString& author,
|
||||||
|
InstancePtr instance,
|
||||||
|
const QString& output,
|
||||||
|
MMCZip::FilterFunction filter)
|
||||||
|
: name(name)
|
||||||
|
, version(version)
|
||||||
|
, author(author)
|
||||||
|
, instance(instance)
|
||||||
|
, mcInstance(dynamic_cast<MinecraftInstance*>(instance.get()))
|
||||||
|
, gameRoot(instance->gameRoot())
|
||||||
|
, output(output)
|
||||||
|
, filter(filter)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void FlamePackExportTask::executeTask()
|
||||||
|
{
|
||||||
|
setStatus(tr("Searching for files..."));
|
||||||
|
setProgress(0, 5);
|
||||||
|
collectFiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FlamePackExportTask::abort()
|
||||||
|
{
|
||||||
|
if (task != nullptr) {
|
||||||
|
task->abort();
|
||||||
|
task = nullptr;
|
||||||
|
emitAborted();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buildZipFuture.isRunning()) {
|
||||||
|
buildZipFuture.cancel();
|
||||||
|
// NOTE: Here we don't do `emitAborted()` because it will be done when `buildZipFuture` actually cancels, which may not occur
|
||||||
|
// immediately.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlamePackExportTask::collectFiles()
|
||||||
|
{
|
||||||
|
setAbortable(false);
|
||||||
|
QCoreApplication::processEvents();
|
||||||
|
|
||||||
|
files.clear();
|
||||||
|
if (!MMCZip::collectFileListRecursively(instance->gameRoot(), nullptr, &files, filter)) {
|
||||||
|
emitFailed(tr("Could not search for files"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingHashes.clear();
|
||||||
|
resolvedFiles.clear();
|
||||||
|
|
||||||
|
if (mcInstance != nullptr) {
|
||||||
|
mcInstance->loaderModList()->update();
|
||||||
|
connect(mcInstance->loaderModList().get(), &ModFolderModel::updateFinished, this, &FlamePackExportTask::collectHashes);
|
||||||
|
} else
|
||||||
|
collectHashes();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlamePackExportTask::collectHashes()
|
||||||
|
{
|
||||||
|
setAbortable(true);
|
||||||
|
setStatus(tr("Finding file hashes..."));
|
||||||
|
setProgress(1, 5);
|
||||||
|
auto allMods = mcInstance->loaderModList()->allMods();
|
||||||
|
ConcurrentTask::Ptr hashingTask(new ConcurrentTask(this, "MakeHashesTask", 10));
|
||||||
|
task.reset(hashingTask);
|
||||||
|
for (const QFileInfo& file : files) {
|
||||||
|
const QString relative = gameRoot.relativeFilePath(file.absoluteFilePath());
|
||||||
|
// require sensible file types
|
||||||
|
if (!std::any_of(FILE_EXTENSIONS.begin(), FILE_EXTENSIONS.end(), [&relative](const QString& extension) {
|
||||||
|
return relative.endsWith('.' + extension) || relative.endsWith('.' + extension + ".disabled");
|
||||||
|
}))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (relative.startsWith("resourcepacks/") &&
|
||||||
|
(relative.endsWith(".zip") || relative.endsWith(".zip.disabled"))) { // is resourcepack
|
||||||
|
auto hashTask = Hashing::createFlameHasher(file.absoluteFilePath());
|
||||||
|
connect(hashTask.get(), &Hashing::Hasher::resultsReady, [this, relative, file](QString hash) {
|
||||||
|
if (m_state == Task::State::Running) {
|
||||||
|
pendingHashes.insert(hash, { relative, file.absoluteFilePath(), relative.endsWith(".zip") });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
connect(hashTask.get(), &Task::failed, this, &FlamePackExportTask::emitFailed);
|
||||||
|
hashingTask->addTask(hashTask);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto modIter = std::find_if(allMods.begin(), allMods.end(), [&file](Mod* mod) { return mod->fileinfo() == file; });
|
||||||
|
modIter != allMods.end()) {
|
||||||
|
const Mod* mod = *modIter;
|
||||||
|
if (!mod || mod->type() == ResourceType::FOLDER) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (mod->metadata() && mod->metadata()->provider == ModPlatform::ResourceProvider::FLAME) {
|
||||||
|
resolvedFiles.insert(mod->fileinfo().absoluteFilePath(),
|
||||||
|
{ mod->metadata()->project_id.toInt(), mod->metadata()->file_id.toInt(), mod->enabled(), true,
|
||||||
|
mod->metadata()->name, mod->metadata()->slug, mod->authors().join(", ") });
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto hashTask = Hashing::createFlameHasher(mod->fileinfo().absoluteFilePath());
|
||||||
|
connect(hashTask.get(), &Hashing::Hasher::resultsReady, [this, mod](QString hash) {
|
||||||
|
if (m_state == Task::State::Running) {
|
||||||
|
pendingHashes.insert(hash, { mod->name(), mod->fileinfo().absoluteFilePath(), mod->enabled(), true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
connect(hashTask.get(), &Task::failed, this, &FlamePackExportTask::emitFailed);
|
||||||
|
hashingTask->addTask(hashTask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto progressStep = std::make_shared<TaskStepProgress>();
|
||||||
|
connect(hashingTask.get(), &Task::finished, this, [this, progressStep] {
|
||||||
|
progressStep->state = TaskStepState::Succeeded;
|
||||||
|
stepProgress(*progressStep);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(hashingTask.get(), &Task::succeeded, this, &FlamePackExportTask::makeApiRequest);
|
||||||
|
connect(hashingTask.get(), &Task::failed, this, [this, progressStep](QString reason) {
|
||||||
|
progressStep->state = TaskStepState::Failed;
|
||||||
|
stepProgress(*progressStep);
|
||||||
|
emitFailed(reason);
|
||||||
|
});
|
||||||
|
connect(hashingTask.get(), &Task::stepProgress, this, &FlamePackExportTask::propogateStepProgress);
|
||||||
|
|
||||||
|
connect(hashingTask.get(), &Task::progress, this, [this, progressStep](qint64 current, qint64 total) {
|
||||||
|
progressStep->update(current, total);
|
||||||
|
stepProgress(*progressStep);
|
||||||
|
});
|
||||||
|
connect(hashingTask.get(), &Task::status, this, [this, progressStep](QString status) {
|
||||||
|
progressStep->status = status;
|
||||||
|
stepProgress(*progressStep);
|
||||||
|
});
|
||||||
|
hashingTask->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlamePackExportTask::makeApiRequest()
|
||||||
|
{
|
||||||
|
if (pendingHashes.isEmpty()) {
|
||||||
|
buildZip();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setStatus(tr("Finding versions for hashes..."));
|
||||||
|
setProgress(2, 5);
|
||||||
|
auto response = std::make_shared<QByteArray>();
|
||||||
|
|
||||||
|
QList<uint> fingerprints;
|
||||||
|
for (auto& murmur : pendingHashes.keys()) {
|
||||||
|
fingerprints.push_back(murmur.toUInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
task.reset(api.matchFingerprints(fingerprints, response));
|
||||||
|
|
||||||
|
connect(task.get(), &Task::succeeded, this, [this, response] {
|
||||||
|
QJsonParseError parseError{};
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(*response, &parseError);
|
||||||
|
if (parseError.error != QJsonParseError::NoError) {
|
||||||
|
qWarning() << "Error while parsing JSON response from CurseForge::CurrentVersions at " << parseError.offset
|
||||||
|
<< " reason: " << parseError.errorString();
|
||||||
|
qWarning() << *response;
|
||||||
|
|
||||||
|
failed(parseError.errorString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
auto docObj = Json::requireObject(doc);
|
||||||
|
auto dataObj = Json::requireObject(docObj, "data");
|
||||||
|
auto dataArr = Json::requireArray(dataObj, "exactMatches");
|
||||||
|
|
||||||
|
if (dataArr.isEmpty()) {
|
||||||
|
qWarning() << "No matches found for fingerprint search!";
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (auto match : dataArr) {
|
||||||
|
auto matchObj = Json::ensureObject(match, {});
|
||||||
|
auto fileObj = Json::ensureObject(matchObj, "file", {});
|
||||||
|
|
||||||
|
if (matchObj.isEmpty() || fileObj.isEmpty()) {
|
||||||
|
qWarning() << "Fingerprint match is empty!";
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto fingerprint = QString::number(Json::ensureVariant(fileObj, "fileFingerprint").toUInt());
|
||||||
|
auto mod = pendingHashes.find(fingerprint);
|
||||||
|
if (mod == pendingHashes.end()) {
|
||||||
|
qWarning() << "Invalid fingerprint from the API response.";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
setStatus(tr("Parsing API response from CurseForge for '%1'...").arg(mod->name));
|
||||||
|
if (Json::ensureBoolean(fileObj, "isAvailable", false, "isAvailable"))
|
||||||
|
resolvedFiles.insert(mod->path, { Json::requireInteger(fileObj, "modId"), Json::requireInteger(fileObj, "id"),
|
||||||
|
mod->enabled, mod->isMod });
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Json::JsonException& e) {
|
||||||
|
qDebug() << e.cause();
|
||||||
|
qDebug() << doc;
|
||||||
|
}
|
||||||
|
pendingHashes.clear();
|
||||||
|
});
|
||||||
|
connect(task.get(), &Task::finished, this, &FlamePackExportTask::getProjectsInfo);
|
||||||
|
connect(task.get(), &NetJob::failed, this, &FlamePackExportTask::emitFailed);
|
||||||
|
task->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlamePackExportTask::getProjectsInfo()
|
||||||
|
{
|
||||||
|
setStatus(tr("Finding project info from CurseForge..."));
|
||||||
|
setProgress(3, 5);
|
||||||
|
QStringList addonIds;
|
||||||
|
for (const auto& resolved : resolvedFiles) {
|
||||||
|
if (resolved.slug.isEmpty()) {
|
||||||
|
addonIds << QString::number(resolved.addonId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto response = std::make_shared<QByteArray>();
|
||||||
|
Task::Ptr projTask;
|
||||||
|
|
||||||
|
if (addonIds.isEmpty()) {
|
||||||
|
buildZip();
|
||||||
|
return;
|
||||||
|
} else if (addonIds.size() == 1) {
|
||||||
|
projTask = api.getProject(*addonIds.begin(), response);
|
||||||
|
} else {
|
||||||
|
projTask = api.getProjects(addonIds, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(projTask.get(), &Task::succeeded, this, [this, response, addonIds] {
|
||||||
|
QJsonParseError parseError{};
|
||||||
|
auto doc = QJsonDocument::fromJson(*response, &parseError);
|
||||||
|
if (parseError.error != QJsonParseError::NoError) {
|
||||||
|
qWarning() << "Error while parsing JSON response from CurseForge projects task at " << parseError.offset
|
||||||
|
<< " reason: " << parseError.errorString();
|
||||||
|
qWarning() << *response;
|
||||||
|
failed(parseError.errorString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
QJsonArray entries;
|
||||||
|
if (addonIds.size() == 1)
|
||||||
|
entries = { Json::requireObject(Json::requireObject(doc), "data") };
|
||||||
|
else
|
||||||
|
entries = Json::requireArray(Json::requireObject(doc), "data");
|
||||||
|
|
||||||
|
for (auto entry : entries) {
|
||||||
|
auto entryObj = Json::requireObject(entry);
|
||||||
|
|
||||||
|
try {
|
||||||
|
setStatus(tr("Parsing API response from CurseForge for '%1'...").arg(Json::requireString(entryObj, "name")));
|
||||||
|
|
||||||
|
ModPlatform::IndexedPack pack;
|
||||||
|
FlameMod::loadIndexedPack(pack, entryObj);
|
||||||
|
for (auto key : resolvedFiles.keys()) {
|
||||||
|
auto val = resolvedFiles.value(key);
|
||||||
|
if (val.addonId == pack.addonId) {
|
||||||
|
val.name = pack.name;
|
||||||
|
val.slug = pack.slug;
|
||||||
|
QStringList authors;
|
||||||
|
for (auto author : pack.authors)
|
||||||
|
authors << author.name;
|
||||||
|
|
||||||
|
val.authors = authors.join(", ");
|
||||||
|
resolvedFiles[key] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Json::JsonException& e) {
|
||||||
|
qDebug() << e.cause();
|
||||||
|
qDebug() << entries;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Json::JsonException& e) {
|
||||||
|
qDebug() << e.cause();
|
||||||
|
qDebug() << doc;
|
||||||
|
}
|
||||||
|
buildZip();
|
||||||
|
});
|
||||||
|
task.reset(projTask);
|
||||||
|
task->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlamePackExportTask::buildZip()
|
||||||
|
{
|
||||||
|
setStatus(tr("Adding files..."));
|
||||||
|
setProgress(4, 5);
|
||||||
|
|
||||||
|
buildZipFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this]() {
|
||||||
|
QuaZip zip(output);
|
||||||
|
if (!zip.open(QuaZip::mdCreate)) {
|
||||||
|
QFile::remove(output);
|
||||||
|
return BuildZipResult(tr("Could not create file"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buildZipFuture.isCanceled())
|
||||||
|
return BuildZipResult();
|
||||||
|
|
||||||
|
QuaZipFile indexFile(&zip);
|
||||||
|
if (!indexFile.open(QIODevice::WriteOnly, QuaZipNewInfo("manifest.json"))) {
|
||||||
|
QFile::remove(output);
|
||||||
|
return BuildZipResult(tr("Could not create index"));
|
||||||
|
}
|
||||||
|
indexFile.write(generateIndex());
|
||||||
|
|
||||||
|
QuaZipFile modlist(&zip);
|
||||||
|
if (!modlist.open(QIODevice::WriteOnly, QuaZipNewInfo("modlist.html"))) {
|
||||||
|
QFile::remove(output);
|
||||||
|
return BuildZipResult(tr("Could not create index"));
|
||||||
|
}
|
||||||
|
QString content = "";
|
||||||
|
for (auto mod : resolvedFiles) {
|
||||||
|
if (mod.isMod) {
|
||||||
|
content += QString(TEMPLATE)
|
||||||
|
.replace("{name}", mod.name.toHtmlEscaped())
|
||||||
|
.replace("{url}", ModPlatform::getMetaURL(ModPlatform::ResourceProvider::FLAME, mod.addonId).toHtmlEscaped())
|
||||||
|
.replace("{authors}", !mod.authors.isEmpty() ? QString(" (by %1)").arg(mod.authors).toHtmlEscaped() : "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
content = "<ul>" + content + "</ul>";
|
||||||
|
modlist.write(content.toUtf8());
|
||||||
|
|
||||||
|
auto progressStep = std::make_shared<TaskStepProgress>();
|
||||||
|
|
||||||
|
size_t progress = 0;
|
||||||
|
for (const QFileInfo& file : files) {
|
||||||
|
if (buildZipFuture.isCanceled()) {
|
||||||
|
QFile::remove(output);
|
||||||
|
progressStep->state = TaskStepState::Failed;
|
||||||
|
stepProgress(*progressStep);
|
||||||
|
return BuildZipResult();
|
||||||
|
}
|
||||||
|
progressStep->update(progress, files.length());
|
||||||
|
stepProgress(*progressStep);
|
||||||
|
|
||||||
|
const QString relative = gameRoot.relativeFilePath(file.absoluteFilePath());
|
||||||
|
if (!resolvedFiles.contains(file.absoluteFilePath()) &&
|
||||||
|
!JlCompress::compressFile(&zip, file.absoluteFilePath(), "overrides/" + relative)) {
|
||||||
|
QFile::remove(output);
|
||||||
|
return BuildZipResult(tr("Could not read and compress %1").arg(relative));
|
||||||
|
}
|
||||||
|
progress++;
|
||||||
|
}
|
||||||
|
|
||||||
|
zip.close();
|
||||||
|
|
||||||
|
if (zip.getZipError() != 0) {
|
||||||
|
QFile::remove(output);
|
||||||
|
progressStep->state = TaskStepState::Failed;
|
||||||
|
stepProgress(*progressStep);
|
||||||
|
return BuildZipResult(tr("A zip error occurred"));
|
||||||
|
}
|
||||||
|
progressStep->state = TaskStepState::Succeeded;
|
||||||
|
stepProgress(*progressStep);
|
||||||
|
return BuildZipResult();
|
||||||
|
});
|
||||||
|
connect(&buildZipWatcher, &QFutureWatcher<BuildZipResult>::finished, this, &FlamePackExportTask::finish);
|
||||||
|
buildZipWatcher.setFuture(buildZipFuture);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlamePackExportTask::finish()
|
||||||
|
{
|
||||||
|
if (buildZipFuture.isCanceled())
|
||||||
|
emitAborted();
|
||||||
|
else {
|
||||||
|
const BuildZipResult result = buildZipFuture.result();
|
||||||
|
if (result.has_value())
|
||||||
|
emitFailed(result.value());
|
||||||
|
else
|
||||||
|
emitSucceeded();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray FlamePackExportTask::generateIndex()
|
||||||
|
{
|
||||||
|
QJsonObject obj;
|
||||||
|
obj["manifestType"] = "minecraftModpack";
|
||||||
|
obj["manifestVersion"] = 1;
|
||||||
|
obj["name"] = name;
|
||||||
|
obj["version"] = version;
|
||||||
|
obj["author"] = author;
|
||||||
|
obj["overrides"] = "overrides";
|
||||||
|
if (mcInstance) {
|
||||||
|
QJsonObject version;
|
||||||
|
auto profile = mcInstance->getPackProfile();
|
||||||
|
// collect all supported components
|
||||||
|
const ComponentPtr minecraft = profile->getComponent("net.minecraft");
|
||||||
|
const ComponentPtr quilt = profile->getComponent("org.quiltmc.quilt-loader");
|
||||||
|
const ComponentPtr fabric = profile->getComponent("net.fabricmc.fabric-loader");
|
||||||
|
const ComponentPtr forge = profile->getComponent("net.minecraftforge");
|
||||||
|
|
||||||
|
// convert all available components to mrpack dependencies
|
||||||
|
if (minecraft != nullptr)
|
||||||
|
version["version"] = minecraft->m_version;
|
||||||
|
QString id;
|
||||||
|
if (quilt != nullptr)
|
||||||
|
id = "quilt-" + quilt->getVersion();
|
||||||
|
else if (fabric != nullptr)
|
||||||
|
id = "fabric-" + fabric->getVersion();
|
||||||
|
else if (forge != nullptr)
|
||||||
|
id = "forge-" + forge->getVersion();
|
||||||
|
version["modLoaders"] = QJsonArray();
|
||||||
|
if (!id.isEmpty()) {
|
||||||
|
QJsonObject loader;
|
||||||
|
loader["id"] = id;
|
||||||
|
loader["primary"] = true;
|
||||||
|
version["modLoaders"] = QJsonArray({ loader });
|
||||||
|
}
|
||||||
|
obj["minecraft"] = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonArray files;
|
||||||
|
for (auto mod : resolvedFiles) {
|
||||||
|
QJsonObject file;
|
||||||
|
file["projectID"] = mod.addonId;
|
||||||
|
file["fileID"] = mod.version;
|
||||||
|
file["required"] = mod.enabled;
|
||||||
|
files << file;
|
||||||
|
}
|
||||||
|
obj["files"] = files;
|
||||||
|
|
||||||
|
return QJsonDocument(obj).toJson(QJsonDocument::Compact);
|
||||||
|
}
|
90
launcher/modplatform/flame/FlamePackExportTask.h
Normal file
90
launcher/modplatform/flame/FlamePackExportTask.h
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||||
|
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QFuture>
|
||||||
|
#include <QFutureWatcher>
|
||||||
|
#include "BaseInstance.h"
|
||||||
|
#include "MMCZip.h"
|
||||||
|
#include "minecraft/MinecraftInstance.h"
|
||||||
|
#include "modplatform/flame/FlameAPI.h"
|
||||||
|
#include "tasks/Task.h"
|
||||||
|
|
||||||
|
class FlamePackExportTask : public Task {
|
||||||
|
public:
|
||||||
|
FlamePackExportTask(const QString& name,
|
||||||
|
const QString& version,
|
||||||
|
const QString& author,
|
||||||
|
InstancePtr instance,
|
||||||
|
const QString& output,
|
||||||
|
MMCZip::FilterFunction filter);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void executeTask() override;
|
||||||
|
bool abort() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const QString TEMPLATE;
|
||||||
|
static const QStringList FILE_EXTENSIONS;
|
||||||
|
|
||||||
|
// inputs
|
||||||
|
const QString name, version, author;
|
||||||
|
const InstancePtr instance;
|
||||||
|
MinecraftInstance* mcInstance;
|
||||||
|
const QDir gameRoot;
|
||||||
|
const QString output;
|
||||||
|
const MMCZip::FilterFunction filter;
|
||||||
|
|
||||||
|
typedef std::optional<QString> BuildZipResult;
|
||||||
|
struct ResolvedFile {
|
||||||
|
int addonId;
|
||||||
|
int version;
|
||||||
|
bool enabled;
|
||||||
|
bool isMod;
|
||||||
|
|
||||||
|
QString name;
|
||||||
|
QString slug;
|
||||||
|
QString authors;
|
||||||
|
};
|
||||||
|
struct HashInfo {
|
||||||
|
QString name;
|
||||||
|
QString path;
|
||||||
|
bool enabled;
|
||||||
|
bool isMod;
|
||||||
|
};
|
||||||
|
|
||||||
|
FlameAPI api;
|
||||||
|
|
||||||
|
QFileInfoList files;
|
||||||
|
QMap<QString, HashInfo> pendingHashes{};
|
||||||
|
QMap<QString, ResolvedFile> resolvedFiles{};
|
||||||
|
Task::Ptr task;
|
||||||
|
QFuture<BuildZipResult> buildZipFuture;
|
||||||
|
QFutureWatcher<BuildZipResult> buildZipWatcher;
|
||||||
|
|
||||||
|
void collectFiles();
|
||||||
|
void collectHashes();
|
||||||
|
void makeApiRequest();
|
||||||
|
void getProjectsInfo();
|
||||||
|
void buildZip();
|
||||||
|
void finish();
|
||||||
|
|
||||||
|
QByteArray generateIndex();
|
||||||
|
};
|
@ -38,7 +38,7 @@ class ModrinthAPI : public NetworkResourceAPI {
|
|||||||
static auto getModLoaderStrings(const ModLoaderTypes types) -> const QStringList
|
static auto getModLoaderStrings(const ModLoaderTypes types) -> const QStringList
|
||||||
{
|
{
|
||||||
QStringList l;
|
QStringList l;
|
||||||
for (auto loader : { Forge, Fabric, Quilt }) {
|
for (auto loader : { Forge, Fabric, Quilt, LiteLoader }) {
|
||||||
if (types & loader) {
|
if (types & loader) {
|
||||||
l << getModLoaderString(loader);
|
l << getModLoaderString(loader);
|
||||||
}
|
}
|
||||||
@ -92,7 +92,7 @@ class ModrinthAPI : public NetworkResourceAPI {
|
|||||||
{
|
{
|
||||||
if (args.loaders.has_value()) {
|
if (args.loaders.has_value()) {
|
||||||
if (!validateModLoaders(args.loaders.value())) {
|
if (!validateModLoaders(args.loaders.value())) {
|
||||||
qWarning() << "Modrinth only have Forge and Fabric-compatible mods!";
|
qWarning() << "Modrinth - or our interface - does not support any the provided mod loaders!";
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -141,7 +141,7 @@ class ModrinthAPI : public NetworkResourceAPI {
|
|||||||
return s.isEmpty() ? QString() : s;
|
return s.isEmpty() ? QString() : s;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline auto validateModLoaders(ModLoaderTypes loaders) const -> bool { return loaders & (Forge | Fabric | Quilt); }
|
static inline auto validateModLoaders(ModLoaderTypes loaders) -> bool { return loaders & (Forge | Fabric | Quilt | LiteLoader); }
|
||||||
|
|
||||||
[[nodiscard]] std::optional<QString> getDependencyURL(DependencySearchArgs const& args) const override
|
[[nodiscard]] std::optional<QString> getDependencyURL(DependencySearchArgs const& args) const override
|
||||||
{
|
{
|
||||||
|
@ -64,7 +64,8 @@ bool ModrinthPackExportTask::abort()
|
|||||||
|
|
||||||
if (buildZipFuture.isRunning()) {
|
if (buildZipFuture.isRunning()) {
|
||||||
buildZipFuture.cancel();
|
buildZipFuture.cancel();
|
||||||
// NOTE: Here we don't do `emitAborted()` because it will be done when `buildZipFuture` actually cancels, which may not occur immediately.
|
// NOTE: Here we don't do `emitAborted()` because it will be done when `buildZipFuture` actually cancels, which may not occur
|
||||||
|
// immediately.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,6 +95,7 @@ void ModrinthPackExportTask::collectFiles()
|
|||||||
|
|
||||||
void ModrinthPackExportTask::collectHashes()
|
void ModrinthPackExportTask::collectHashes()
|
||||||
{
|
{
|
||||||
|
setStatus(tr("Finding file hashes..."));
|
||||||
for (const QFileInfo& file : files) {
|
for (const QFileInfo& file : files) {
|
||||||
QCoreApplication::processEvents();
|
QCoreApplication::processEvents();
|
||||||
|
|
||||||
@ -157,6 +159,7 @@ void ModrinthPackExportTask::makeApiRequest()
|
|||||||
if (pendingHashes.isEmpty())
|
if (pendingHashes.isEmpty())
|
||||||
buildZip();
|
buildZip();
|
||||||
else {
|
else {
|
||||||
|
setStatus(tr("Finding versions for hashes..."));
|
||||||
auto response = std::make_shared<QByteArray>();
|
auto response = std::make_shared<QByteArray>();
|
||||||
task = api.currentVersions(pendingHashes.values(), "sha512", response);
|
task = api.currentVersions(pendingHashes.values(), "sha512", response);
|
||||||
connect(task.get(), &NetJob::succeeded, [this, response]() { parseApiResponse(response); });
|
connect(task.get(), &NetJob::succeeded, [this, response]() { parseApiResponse(response); });
|
||||||
@ -263,13 +266,13 @@ void ModrinthPackExportTask::finish()
|
|||||||
|
|
||||||
QByteArray ModrinthPackExportTask::generateIndex()
|
QByteArray ModrinthPackExportTask::generateIndex()
|
||||||
{
|
{
|
||||||
QJsonObject obj;
|
QJsonObject out;
|
||||||
obj["formatVersion"] = 1;
|
out["formatVersion"] = 1;
|
||||||
obj["game"] = "minecraft";
|
out["game"] = "minecraft";
|
||||||
obj["name"] = name;
|
out["name"] = name;
|
||||||
obj["versionId"] = version;
|
out["versionId"] = version;
|
||||||
if (!summary.isEmpty())
|
if (!summary.isEmpty())
|
||||||
obj["summary"] = summary;
|
out["summary"] = summary;
|
||||||
|
|
||||||
if (mcInstance) {
|
if (mcInstance) {
|
||||||
auto profile = mcInstance->getPackProfile();
|
auto profile = mcInstance->getPackProfile();
|
||||||
@ -290,30 +293,42 @@ QByteArray ModrinthPackExportTask::generateIndex()
|
|||||||
if (forge != nullptr)
|
if (forge != nullptr)
|
||||||
dependencies["forge"] = forge->m_version;
|
dependencies["forge"] = forge->m_version;
|
||||||
|
|
||||||
obj["dependencies"] = dependencies;
|
out["dependencies"] = dependencies;
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonArray files_array;
|
|
||||||
QMapIterator<QString, ResolvedFile> iterator(resolvedFiles);
|
|
||||||
while (iterator.hasNext()) {
|
|
||||||
iterator.next();
|
|
||||||
|
|
||||||
|
QJsonArray filesOut;
|
||||||
|
for (auto iterator = resolvedFiles.constBegin(); iterator != resolvedFiles.constEnd(); iterator++) {
|
||||||
|
QJsonObject fileOut;
|
||||||
|
|
||||||
|
QString path = iterator.key();
|
||||||
const ResolvedFile& value = iterator.value();
|
const ResolvedFile& value = iterator.value();
|
||||||
|
|
||||||
QJsonObject file;
|
// detect disabled mod
|
||||||
file["path"] = iterator.key();
|
const QFileInfo pathInfo(path);
|
||||||
file["downloads"] = QJsonArray({ iterator.value().url });
|
if (pathInfo.suffix() == "disabled") {
|
||||||
|
// rename it
|
||||||
|
path = pathInfo.dir().filePath(pathInfo.completeBaseName());
|
||||||
|
// ...and make it optional
|
||||||
|
QJsonObject env;
|
||||||
|
env["client"] = "optional";
|
||||||
|
env["server"] = "optional";
|
||||||
|
fileOut["env"] = env;
|
||||||
|
}
|
||||||
|
|
||||||
|
fileOut["path"] = path;
|
||||||
|
fileOut["downloads"] = QJsonArray{ iterator.value().url };
|
||||||
|
|
||||||
QJsonObject hashes;
|
QJsonObject hashes;
|
||||||
hashes["sha1"] = value.sha1;
|
hashes["sha1"] = value.sha1;
|
||||||
hashes["sha512"] = value.sha512;
|
hashes["sha512"] = value.sha512;
|
||||||
|
fileOut["hashes"] = hashes;
|
||||||
|
|
||||||
file["hashes"] = hashes;
|
|
||||||
file["fileSize"] = value.size;
|
|
||||||
|
|
||||||
files_array << file;
|
fileOut["fileSize"] = value.size;
|
||||||
|
filesOut << fileOut;
|
||||||
}
|
}
|
||||||
obj["files"] = files_array;
|
out["files"] = filesOut;
|
||||||
|
|
||||||
return QJsonDocument(obj).toJson(QJsonDocument::Compact);
|
return QJsonDocument(out).toJson(QJsonDocument::Compact);
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,7 @@
|
|||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QLibraryInfo>
|
#include <QLibraryInfo>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <locale>
|
||||||
|
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
#include "net/NetJob.h"
|
#include "net/NetJob.h"
|
||||||
@ -448,6 +449,7 @@ QVariant TranslationsModel::data(const QModelIndex& index, int role) const
|
|||||||
return QString("%1%").arg(lang.percentTranslated(), 3, 'f', 1);
|
return QString("%1%").arg(lang.percentTranslated(), 3, 'f', 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
qWarning("TranslationModel::data not implemented when role is DisplayRole");
|
||||||
}
|
}
|
||||||
case Qt::ToolTipRole:
|
case Qt::ToolTipRole:
|
||||||
{
|
{
|
||||||
@ -520,34 +522,34 @@ Language * TranslationsModel::findLanguage(const QString& key)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TranslationsModel::setUseSystemLocale(bool useSystemLocale)
|
||||||
|
{
|
||||||
|
APPLICATION->settings()->set("UseSystemLocale", useSystemLocale);
|
||||||
|
QLocale::setDefault(QLocale(useSystemLocale ? QString::fromStdString(std::locale().name()) : defaultLangCode));
|
||||||
|
}
|
||||||
|
|
||||||
bool TranslationsModel::selectLanguage(QString key)
|
bool TranslationsModel::selectLanguage(QString key)
|
||||||
{
|
{
|
||||||
QString& langCode = key;
|
QString& langCode = key;
|
||||||
auto langPtr = findLanguage(key);
|
auto langPtr = findLanguage(key);
|
||||||
|
|
||||||
if (langCode.isEmpty())
|
if (langCode.isEmpty()) {
|
||||||
{
|
|
||||||
d->no_language_set = true;
|
d->no_language_set = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!langPtr)
|
if (!langPtr) {
|
||||||
{
|
|
||||||
qWarning() << "Selected invalid language" << key << ", defaulting to" << defaultLangCode;
|
qWarning() << "Selected invalid language" << key << ", defaulting to" << defaultLangCode;
|
||||||
langCode = defaultLangCode;
|
langCode = defaultLangCode;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
langCode = langPtr->key;
|
langCode = langPtr->key;
|
||||||
}
|
}
|
||||||
|
|
||||||
// uninstall existing translators if there are any
|
// uninstall existing translators if there are any
|
||||||
if (d->m_app_translator)
|
if (d->m_app_translator) {
|
||||||
{
|
|
||||||
QCoreApplication::removeTranslator(d->m_app_translator.get());
|
QCoreApplication::removeTranslator(d->m_app_translator.get());
|
||||||
d->m_app_translator.reset();
|
d->m_app_translator.reset();
|
||||||
}
|
}
|
||||||
if (d->m_qt_translator)
|
if (d->m_qt_translator) {
|
||||||
{
|
|
||||||
QCoreApplication::removeTranslator(d->m_qt_translator.get());
|
QCoreApplication::removeTranslator(d->m_qt_translator.get());
|
||||||
d->m_qt_translator.reset();
|
d->m_qt_translator.reset();
|
||||||
}
|
}
|
||||||
@ -557,8 +559,9 @@ bool TranslationsModel::selectLanguage(QString key)
|
|||||||
* In a multithreaded application, the default locale should be set at application startup, before any non-GUI threads are created.
|
* In a multithreaded application, the default locale should be set at application startup, before any non-GUI threads are created.
|
||||||
* This function is not reentrant.
|
* This function is not reentrant.
|
||||||
*/
|
*/
|
||||||
QLocale locale = QLocale(langCode);
|
QLocale::setDefault(
|
||||||
QLocale::setDefault(locale);
|
QLocale(APPLICATION->settings()->get("UseSystemLocale").toBool() ? QString::fromStdString(std::locale().name()) : langCode));
|
||||||
|
|
||||||
|
|
||||||
// if it's the default UI language, finish
|
// if it's the default UI language, finish
|
||||||
if(langCode == defaultLangCode)
|
if(langCode == defaultLangCode)
|
||||||
|
@ -20,8 +20,7 @@
|
|||||||
|
|
||||||
struct Language;
|
struct Language;
|
||||||
|
|
||||||
class TranslationsModel : public QAbstractListModel
|
class TranslationsModel : public QAbstractListModel {
|
||||||
{
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit TranslationsModel(QString path, QObject* parent = 0);
|
explicit TranslationsModel(QString path, QObject* parent = 0);
|
||||||
@ -38,6 +37,7 @@ public:
|
|||||||
QString selectedLanguage();
|
QString selectedLanguage();
|
||||||
|
|
||||||
void downloadIndex();
|
void downloadIndex();
|
||||||
|
void setUseSystemLocale(bool useSystemLocale);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Language* findLanguage(const QString& key);
|
Language* findLanguage(const QString& key);
|
||||||
@ -57,7 +57,6 @@ private slots:
|
|||||||
void dlGood();
|
void dlGood();
|
||||||
void translationDirChanged(const QString& path);
|
void translationDirChanged(const QString& path);
|
||||||
|
|
||||||
|
|
||||||
private: /* data */
|
private: /* data */
|
||||||
struct Private;
|
struct Private;
|
||||||
std::unique_ptr<Private> d;
|
std::unique_ptr<Private> d;
|
||||||
|
@ -107,7 +107,7 @@
|
|||||||
#include "ui/dialogs/CopyInstanceDialog.h"
|
#include "ui/dialogs/CopyInstanceDialog.h"
|
||||||
#include "ui/dialogs/EditAccountDialog.h"
|
#include "ui/dialogs/EditAccountDialog.h"
|
||||||
#include "ui/dialogs/ExportInstanceDialog.h"
|
#include "ui/dialogs/ExportInstanceDialog.h"
|
||||||
#include "ui/dialogs/ExportMrPackDialog.h"
|
#include "ui/dialogs/ExportPackDialog.h"
|
||||||
#include "ui/dialogs/ImportResourceDialog.h"
|
#include "ui/dialogs/ImportResourceDialog.h"
|
||||||
#include "ui/themes/ITheme.h"
|
#include "ui/themes/ITheme.h"
|
||||||
#include "ui/themes/ThemeManager.h"
|
#include "ui/themes/ThemeManager.h"
|
||||||
@ -205,6 +205,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
|||||||
auto exportInstanceMenu = new QMenu(this);
|
auto exportInstanceMenu = new QMenu(this);
|
||||||
exportInstanceMenu->addAction(ui->actionExportInstanceZip);
|
exportInstanceMenu->addAction(ui->actionExportInstanceZip);
|
||||||
exportInstanceMenu->addAction(ui->actionExportInstanceMrPack);
|
exportInstanceMenu->addAction(ui->actionExportInstanceMrPack);
|
||||||
|
exportInstanceMenu->addAction(ui->actionExportInstanceFlamePack);
|
||||||
ui->actionExportInstance->setMenu(exportInstanceMenu);
|
ui->actionExportInstance->setMenu(exportInstanceMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -927,21 +928,8 @@ void MainWindow::onCatToggled(bool state)
|
|||||||
|
|
||||||
void MainWindow::setCatBackground(bool enabled)
|
void MainWindow::setCatBackground(bool enabled)
|
||||||
{
|
{
|
||||||
if (enabled) {
|
view->setPaintCat(enabled);
|
||||||
view->setStyleSheet(QString(R"(
|
view->viewport()->repaint();
|
||||||
InstanceView
|
|
||||||
{
|
|
||||||
background-image: url(:/backgrounds/%1);
|
|
||||||
background-attachment: fixed;
|
|
||||||
background-clip: padding;
|
|
||||||
background-position: bottom right;
|
|
||||||
background-repeat: none;
|
|
||||||
background-color:palette(base);
|
|
||||||
})")
|
|
||||||
.arg(ThemeManager::getCatImage()));
|
|
||||||
} else {
|
|
||||||
view->setStyleSheet(QString());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::runModalTask(Task *task)
|
void MainWindow::runModalTask(Task *task)
|
||||||
@ -1292,7 +1280,17 @@ void MainWindow::globalSettingsClosed()
|
|||||||
|
|
||||||
void MainWindow::on_actionEditInstance_triggered()
|
void MainWindow::on_actionEditInstance_triggered()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if (!m_selectedInstance)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (m_selectedInstance->canEdit()) {
|
||||||
APPLICATION->showInstanceWindow(m_selectedInstance);
|
APPLICATION->showInstanceWindow(m_selectedInstance);
|
||||||
|
} else {
|
||||||
|
CustomMessageBox::selectable(this, tr("Instance not editable"),
|
||||||
|
tr("This instance is not editable. It may be broken, invalid, or too old. Check logs for details."),
|
||||||
|
QMessageBox::Critical)->show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionManageAccounts_triggered()
|
void MainWindow::on_actionManageAccounts_triggered()
|
||||||
@ -1418,11 +1416,35 @@ void MainWindow::on_actionExportInstanceMrPack_triggered()
|
|||||||
{
|
{
|
||||||
if (m_selectedInstance)
|
if (m_selectedInstance)
|
||||||
{
|
{
|
||||||
ExportMrPackDialog dlg(m_selectedInstance, this);
|
ExportPackDialog dlg(m_selectedInstance, this);
|
||||||
dlg.exec();
|
dlg.exec();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::on_actionExportInstanceFlamePack_triggered()
|
||||||
|
{
|
||||||
|
if (m_selectedInstance) {
|
||||||
|
auto instance = dynamic_cast<MinecraftInstance*>(m_selectedInstance.get());
|
||||||
|
if (instance) {
|
||||||
|
QString errorMsg;
|
||||||
|
if (instance->getPackProfile()->getComponent("org.quiltmc.quilt-loader")) {
|
||||||
|
errorMsg = tr("Quilt is currently not supported by CurseForge modpacks.");
|
||||||
|
} else if (auto cmp = instance->getPackProfile()->getComponent("net.minecraft");
|
||||||
|
cmp && cmp->getVersionFile() && cmp->getVersionFile()->type == "snapshot") {
|
||||||
|
errorMsg = tr("Snapshots are currently not supported by CurseForge modpacks.");
|
||||||
|
}
|
||||||
|
if (!errorMsg.isEmpty()) {
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setText(errorMsg);
|
||||||
|
msgBox.exec();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ExportPackDialog dlg(m_selectedInstance, this, ModPlatform::ResourceProvider::FLAME);
|
||||||
|
dlg.exec();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionRenameInstance_triggered()
|
void MainWindow::on_actionRenameInstance_triggered()
|
||||||
{
|
{
|
||||||
if (m_selectedInstance)
|
if (m_selectedInstance)
|
||||||
@ -1524,11 +1546,39 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered()
|
|||||||
QString iconPath;
|
QString iconPath;
|
||||||
QStringList args;
|
QStringList args;
|
||||||
#if defined(Q_OS_MACOS)
|
#if defined(Q_OS_MACOS)
|
||||||
|
appPath = QApplication::applicationFilePath();
|
||||||
if (appPath.startsWith("/private/var/")) {
|
if (appPath.startsWith("/private/var/")) {
|
||||||
QMessageBox::critical(this, tr("Create instance shortcut"),
|
QMessageBox::critical(this, tr("Create instance shortcut"),
|
||||||
tr("The launcher is in the folder it was extracted from, therefore it cannot create shortcuts."));
|
tr("The launcher is in the folder it was extracted from, therefore it cannot create shortcuts."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto pIcon = APPLICATION->icons()->icon(m_selectedInstance->iconKey());
|
||||||
|
if (pIcon == nullptr)
|
||||||
|
{
|
||||||
|
pIcon = APPLICATION->icons()->icon("grass");
|
||||||
|
}
|
||||||
|
|
||||||
|
iconPath = FS::PathCombine(m_selectedInstance->instanceRoot(), "Icon.icns");
|
||||||
|
|
||||||
|
QFile iconFile(iconPath);
|
||||||
|
if (!iconFile.open(QFile::WriteOnly))
|
||||||
|
{
|
||||||
|
QMessageBox::critical(this, tr("Create instance Application"), tr("Failed to create icon for Application."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QIcon icon = pIcon->icon();
|
||||||
|
|
||||||
|
bool success = icon.pixmap(1024, 1024).save(iconPath, "ICNS");
|
||||||
|
iconFile.close();
|
||||||
|
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
iconFile.remove();
|
||||||
|
QMessageBox::critical(this, tr("Create instance Application"), tr("Failed to create icon for Application."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
#elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
#elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
||||||
if (appPath.startsWith("/tmp/.mount_")) {
|
if (appPath.startsWith("/tmp/.mount_")) {
|
||||||
// AppImage!
|
// AppImage!
|
||||||
@ -1611,7 +1661,11 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered()
|
|||||||
#endif
|
#endif
|
||||||
args.append({ "--launch", m_selectedInstance->id() });
|
args.append({ "--launch", m_selectedInstance->id() });
|
||||||
if (FS::createShortcut(desktopFilePath, appPath, args, m_selectedInstance->name(), iconPath)) {
|
if (FS::createShortcut(desktopFilePath, appPath, args, m_selectedInstance->name(), iconPath)) {
|
||||||
|
#if not defined(Q_OS_MACOS)
|
||||||
QMessageBox::information(this, tr("Create instance shortcut"), tr("Created a shortcut to this instance on your desktop!"));
|
QMessageBox::information(this, tr("Create instance shortcut"), tr("Created a shortcut to this instance on your desktop!"));
|
||||||
|
#else
|
||||||
|
QMessageBox::information(this, tr("Create instance shortcut"), tr("Created a shortcut to this instance!"));
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
#if not defined(Q_OS_MACOS)
|
#if not defined(Q_OS_MACOS)
|
||||||
iconFile.remove();
|
iconFile.remove();
|
||||||
|
@ -157,6 +157,7 @@ private slots:
|
|||||||
inline void on_actionExportInstance_triggered() { on_actionExportInstanceZip_triggered(); }
|
inline void on_actionExportInstance_triggered() { on_actionExportInstanceZip_triggered(); }
|
||||||
void on_actionExportInstanceZip_triggered();
|
void on_actionExportInstanceZip_triggered();
|
||||||
void on_actionExportInstanceMrPack_triggered();
|
void on_actionExportInstanceMrPack_triggered();
|
||||||
|
void on_actionExportInstanceFlamePack_triggered();
|
||||||
|
|
||||||
void on_actionRenameInstance_triggered();
|
void on_actionRenameInstance_triggered();
|
||||||
|
|
||||||
|
@ -479,6 +479,14 @@
|
|||||||
<string>Modrinth (mrpack)</string>
|
<string>Modrinth (mrpack)</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="actionExportInstanceFlamePack">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset theme="flame"/>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>CurseForge (zip)</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
<action name="actionCreateInstanceShortcut">
|
<action name="actionCreateInstanceShortcut">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset theme="shortcut">
|
<iconset theme="shortcut">
|
||||||
|
@ -16,11 +16,13 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ExportMrPackDialog.h"
|
#include "ExportPackDialog.h"
|
||||||
#include "minecraft/mod/ModFolderModel.h"
|
#include "minecraft/mod/ModFolderModel.h"
|
||||||
|
#include "modplatform/ModIndex.h"
|
||||||
|
#include "modplatform/flame/FlamePackExportTask.h"
|
||||||
#include "ui/dialogs/CustomMessageBox.h"
|
#include "ui/dialogs/CustomMessageBox.h"
|
||||||
#include "ui/dialogs/ProgressDialog.h"
|
#include "ui/dialogs/ProgressDialog.h"
|
||||||
#include "ui_ExportMrPackDialog.h"
|
#include "ui_ExportPackDialog.h"
|
||||||
|
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QFileSystemModel>
|
#include <QFileSystemModel>
|
||||||
@ -32,17 +34,24 @@
|
|||||||
#include "MMCZip.h"
|
#include "MMCZip.h"
|
||||||
#include "modplatform/modrinth/ModrinthPackExportTask.h"
|
#include "modplatform/modrinth/ModrinthPackExportTask.h"
|
||||||
|
|
||||||
ExportMrPackDialog::ExportMrPackDialog(InstancePtr instance, QWidget* parent)
|
ExportPackDialog::ExportPackDialog(InstancePtr instance, QWidget* parent, ModPlatform::ResourceProvider provider)
|
||||||
: QDialog(parent), instance(instance), ui(new Ui::ExportMrPackDialog)
|
: QDialog(parent), instance(instance), ui(new Ui::ExportPackDialog), m_provider(provider)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
ui->name->setText(instance->name());
|
ui->name->setText(instance->name());
|
||||||
|
if (m_provider == ModPlatform::ResourceProvider::MODRINTH) {
|
||||||
ui->summary->setText(instance->notes().split(QRegularExpression("\\r?\\n"))[0]);
|
ui->summary->setText(instance->notes().split(QRegularExpression("\\r?\\n"))[0]);
|
||||||
|
setWindowTitle("Export Modrinth Pack");
|
||||||
|
} else {
|
||||||
|
setWindowTitle("Export CurseForge Pack");
|
||||||
|
ui->version->setText("");
|
||||||
|
ui->summaryLabel->setText("Author");
|
||||||
|
}
|
||||||
|
|
||||||
// ensure a valid pack is generated
|
// ensure a valid pack is generated
|
||||||
// the name and version fields mustn't be empty
|
// the name and version fields mustn't be empty
|
||||||
connect(ui->name, &QLineEdit::textEdited, this, &ExportMrPackDialog::validate);
|
connect(ui->name, &QLineEdit::textEdited, this, &ExportPackDialog::validate);
|
||||||
connect(ui->version, &QLineEdit::textEdited, this, &ExportMrPackDialog::validate);
|
connect(ui->version, &QLineEdit::textEdited, this, &ExportPackDialog::validate);
|
||||||
// the instance name can technically be empty
|
// the instance name can technically be empty
|
||||||
validate();
|
validate();
|
||||||
|
|
||||||
@ -66,6 +75,7 @@ ExportMrPackDialog::ExportMrPackDialog(InstancePtr instance, QWidget* parent)
|
|||||||
|
|
||||||
MinecraftInstance* mcInstance = dynamic_cast<MinecraftInstance*>(instance.get());
|
MinecraftInstance* mcInstance = dynamic_cast<MinecraftInstance*>(instance.get());
|
||||||
if (mcInstance) {
|
if (mcInstance) {
|
||||||
|
mcInstance->loaderModList()->update();
|
||||||
const QDir index = mcInstance->loaderModList()->indexDir();
|
const QDir index = mcInstance->loaderModList()->indexDir();
|
||||||
if (index.exists())
|
if (index.exists())
|
||||||
proxy->blockedPaths().insert(root.relativeFilePath(index.absolutePath()));
|
proxy->blockedPaths().insert(root.relativeFilePath(index.absolutePath()));
|
||||||
@ -83,43 +93,54 @@ ExportMrPackDialog::ExportMrPackDialog(InstancePtr instance, QWidget* parent)
|
|||||||
headerView->setSectionResizeMode(0, QHeaderView::Stretch);
|
headerView->setSectionResizeMode(0, QHeaderView::Stretch);
|
||||||
}
|
}
|
||||||
|
|
||||||
ExportMrPackDialog::~ExportMrPackDialog()
|
ExportPackDialog::~ExportPackDialog()
|
||||||
{
|
{
|
||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExportMrPackDialog::done(int result)
|
void ExportPackDialog::done(int result)
|
||||||
{
|
{
|
||||||
if (result == Accepted) {
|
if (result == Accepted) {
|
||||||
const QString filename = FS::RemoveInvalidFilenameChars(ui->name->text());
|
const QString filename = FS::RemoveInvalidFilenameChars(ui->name->text());
|
||||||
const QString output = QFileDialog::getSaveFileName(this, tr("Export %1").arg(ui->name->text()),
|
QString output;
|
||||||
FS::PathCombine(QDir::homePath(), filename + ".mrpack"),
|
if (m_provider == ModPlatform::ResourceProvider::MODRINTH)
|
||||||
"Modrinth pack (*.mrpack *.zip)", nullptr);
|
output = QFileDialog::getSaveFileName(this, tr("Export %1").arg(ui->name->text()),
|
||||||
|
FS::PathCombine(QDir::homePath(), filename + ".mrpack"), "Modrinth pack (*.mrpack *.zip)",
|
||||||
|
nullptr);
|
||||||
|
else
|
||||||
|
output = QFileDialog::getSaveFileName(this, tr("Export %1").arg(ui->name->text()),
|
||||||
|
FS::PathCombine(QDir::homePath(), filename + ".zip"), "CurseForge pack (*.zip)", nullptr);
|
||||||
|
|
||||||
if (output.isEmpty())
|
if (output.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
Task* task;
|
||||||
ModrinthPackExportTask task(ui->name->text(), ui->version->text(), ui->summary->text(), instance, output,
|
if (m_provider == ModPlatform::ResourceProvider::MODRINTH)
|
||||||
|
task = new ModrinthPackExportTask(ui->name->text(), ui->version->text(), ui->summary->text(), instance, output,
|
||||||
|
std::bind(&FileIgnoreProxy::filterFile, proxy, std::placeholders::_1));
|
||||||
|
else
|
||||||
|
task = new FlamePackExportTask(ui->name->text(), ui->version->text(), ui->summary->text(), instance, output,
|
||||||
std::bind(&FileIgnoreProxy::filterFile, proxy, std::placeholders::_1));
|
std::bind(&FileIgnoreProxy::filterFile, proxy, std::placeholders::_1));
|
||||||
|
|
||||||
connect(&task, &Task::failed,
|
connect(task, &Task::failed,
|
||||||
[this](const QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); });
|
[this](const QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); });
|
||||||
connect(&task, &Task::aborted, [this] {
|
connect(task, &Task::aborted, [this] {
|
||||||
CustomMessageBox::selectable(this, tr("Task aborted"), tr("The task has been aborted by the user."), QMessageBox::Information)
|
CustomMessageBox::selectable(this, tr("Task aborted"), tr("The task has been aborted by the user."), QMessageBox::Information)
|
||||||
->show();
|
->show();
|
||||||
});
|
});
|
||||||
|
connect(task, &Task::finished, [task] { task->deleteLater(); });
|
||||||
|
|
||||||
ProgressDialog progress(this);
|
ProgressDialog progress(this);
|
||||||
progress.setSkipButton(true, tr("Abort"));
|
progress.setSkipButton(true, tr("Abort"));
|
||||||
if (progress.execWithTask(&task) != QDialog::Accepted)
|
if (progress.execWithTask(task) != QDialog::Accepted)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QDialog::done(result);
|
QDialog::done(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExportMrPackDialog::validate()
|
void ExportPackDialog::validate()
|
||||||
{
|
{
|
||||||
const bool invalid = ui->name->text().isEmpty() || ui->version->text().isEmpty();
|
const bool invalid =
|
||||||
|
ui->name->text().isEmpty() || ((m_provider == ModPlatform::ResourceProvider::MODRINTH) && ui->version->text().isEmpty());
|
||||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setDisabled(invalid);
|
ui->buttonBox->button(QDialogButtonBox::Ok)->setDisabled(invalid);
|
||||||
}
|
}
|
@ -22,24 +22,28 @@
|
|||||||
#include "BaseInstance.h"
|
#include "BaseInstance.h"
|
||||||
#include "FastFileIconProvider.h"
|
#include "FastFileIconProvider.h"
|
||||||
#include "FileIgnoreProxy.h"
|
#include "FileIgnoreProxy.h"
|
||||||
|
#include "modplatform/ModIndex.h"
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class ExportMrPackDialog;
|
class ExportPackDialog;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ExportMrPackDialog : public QDialog {
|
class ExportPackDialog : public QDialog {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ExportMrPackDialog(InstancePtr instance, QWidget* parent = nullptr);
|
explicit ExportPackDialog(InstancePtr instance,
|
||||||
~ExportMrPackDialog();
|
QWidget* parent = nullptr,
|
||||||
|
ModPlatform::ResourceProvider provider = ModPlatform::ResourceProvider::MODRINTH);
|
||||||
|
~ExportPackDialog();
|
||||||
|
|
||||||
void done(int result) override;
|
void done(int result) override;
|
||||||
void validate();
|
void validate();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const InstancePtr instance;
|
const InstancePtr instance;
|
||||||
Ui::ExportMrPackDialog* ui;
|
Ui::ExportPackDialog* ui;
|
||||||
FileIgnoreProxy* proxy;
|
FileIgnoreProxy* proxy;
|
||||||
FastFileIconProvider icons;
|
FastFileIconProvider icons;
|
||||||
|
const ModPlatform::ResourceProvider m_provider;
|
||||||
};
|
};
|
@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<ui version="4.0">
|
<ui version="4.0">
|
||||||
<class>ExportMrPackDialog</class>
|
<class>ExportPackDialog</class>
|
||||||
<widget class="QDialog" name="ExportMrPackDialog">
|
<widget class="QDialog" name="ExportPackDialog">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
@ -11,7 +11,7 @@
|
|||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Export Modrinth Pack</string>
|
<string>Export Pack</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeGripEnabled">
|
<property name="sizeGripEnabled">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
@ -24,7 +24,7 @@
|
|||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<item row="3" column="0">
|
<item row="3" column="0">
|
||||||
<widget class="QLabel" name="versionLabel">
|
<widget class="QLabel" name="summaryLabel">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Summary</string>
|
<string>Summary</string>
|
||||||
</property>
|
</property>
|
||||||
@ -41,7 +41,7 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QLabel" name="summaryLabel">
|
<widget class="QLabel" name="versionLabel">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Version</string>
|
<string>Version</string>
|
||||||
</property>
|
</property>
|
||||||
@ -57,6 +57,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -103,7 +104,7 @@
|
|||||||
<connection>
|
<connection>
|
||||||
<sender>buttonBox</sender>
|
<sender>buttonBox</sender>
|
||||||
<signal>accepted()</signal>
|
<signal>accepted()</signal>
|
||||||
<receiver>ExportMrPackDialog</receiver>
|
<receiver>ExportPackDialog</receiver>
|
||||||
<slot>accept()</slot>
|
<slot>accept()</slot>
|
||||||
<hints>
|
<hints>
|
||||||
<hint type="sourcelabel">
|
<hint type="sourcelabel">
|
||||||
@ -119,7 +120,7 @@
|
|||||||
<connection>
|
<connection>
|
||||||
<sender>buttonBox</sender>
|
<sender>buttonBox</sender>
|
||||||
<signal>rejected()</signal>
|
<signal>rejected()</signal>
|
||||||
<receiver>ExportMrPackDialog</receiver>
|
<receiver>ExportPackDialog</receiver>
|
||||||
<slot>reject()</slot>
|
<slot>reject()</slot>
|
||||||
<hints>
|
<hints>
|
||||||
<hint type="sourcelabel">
|
<hint type="sourcelabel">
|
@ -34,6 +34,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ProgressDialog.h"
|
#include "ProgressDialog.h"
|
||||||
|
#include <QPoint>
|
||||||
#include "ui_ProgressDialog.h"
|
#include "ui_ProgressDialog.h"
|
||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
@ -66,8 +67,9 @@ ProgressDialog::ProgressDialog(QWidget* parent) : QDialog(parent), ui(new Ui::Pr
|
|||||||
ui->taskProgressScrollArea->setHidden(true);
|
ui->taskProgressScrollArea->setHidden(true);
|
||||||
this->setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
this->setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||||
setAttribute(Qt::WidgetAttribute::WA_QuitOnClose, true);
|
setAttribute(Qt::WidgetAttribute::WA_QuitOnClose, true);
|
||||||
setSkipButton(false);
|
|
||||||
changeProgress(0, 100);
|
changeProgress(0, 100);
|
||||||
|
updateSize(true);
|
||||||
|
setSkipButton(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProgressDialog::setSkipButton(bool present, QString label)
|
void ProgressDialog::setSkipButton(bool present, QString label)
|
||||||
@ -93,24 +95,38 @@ ProgressDialog::~ProgressDialog()
|
|||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProgressDialog::updateSize()
|
void ProgressDialog::updateSize(bool recenterParent)
|
||||||
{
|
{
|
||||||
QSize lastSize = this->size();
|
QSize lastSize = this->size();
|
||||||
QSize qSize = QSize(480, minimumSizeHint().height());
|
QPoint lastPos = this->pos();
|
||||||
|
int minHeight = ui->globalStatusDetailsLabel->minimumSize().height() + (ui->verticalLayout->spacing() * 2);
|
||||||
|
minHeight += ui->globalProgressBar->minimumSize().height() + ui->verticalLayout->spacing();
|
||||||
|
if (!ui->taskProgressScrollArea->isHidden())
|
||||||
|
minHeight += ui->taskProgressScrollArea->minimumSizeHint().height() + ui->verticalLayout->spacing();
|
||||||
|
if (ui->skipButton->isVisible())
|
||||||
|
minHeight += ui->skipButton->height() + ui->verticalLayout->spacing();
|
||||||
|
minHeight = std::max(minHeight, 60);
|
||||||
|
QSize minSize = QSize(480, minHeight);
|
||||||
|
|
||||||
// if the current window is too small
|
setMinimumSize(minSize);
|
||||||
if ((lastSize != qSize) && (lastSize.height() < qSize.height()))
|
adjustSize();
|
||||||
{
|
|
||||||
resize(qSize);
|
|
||||||
|
|
||||||
// keep the dialog in the center after a resize
|
QSize newSize = this->size();
|
||||||
this->move(
|
// if the current window is a different size
|
||||||
this->parentWidget()->x() + (this->parentWidget()->width() - this->width()) / 2,
|
auto parent = this->parentWidget();
|
||||||
this->parentWidget()->y() + (this->parentWidget()->height() - this->height()) / 2
|
if (recenterParent && parent) {
|
||||||
);
|
auto newX = std::max(0, parent->x() + ((parent->width() - newSize.width()) / 2));
|
||||||
|
auto newY = std::max(0, parent->y() + ((parent->height() - newSize.height()) / 2));
|
||||||
|
this->move(newX, newY);
|
||||||
|
}
|
||||||
|
else if (lastSize != newSize)
|
||||||
|
{
|
||||||
|
// center on old position after resize
|
||||||
|
QSize sizeDiff = lastSize - newSize; // last size was smaller, the results should be negative
|
||||||
|
auto newX = std::max(0, lastPos.x() + (sizeDiff.width() / 2));
|
||||||
|
auto newY = std::max(0, lastPos.y() + (sizeDiff.height() / 2));
|
||||||
|
this->move(newX, newY);
|
||||||
}
|
}
|
||||||
|
|
||||||
setMinimumSize(qSize);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,8 +216,11 @@ void ProgressDialog::onTaskSucceeded()
|
|||||||
|
|
||||||
void ProgressDialog::changeStatus([[maybe_unused]] const QString& status)
|
void ProgressDialog::changeStatus([[maybe_unused]] const QString& status)
|
||||||
{
|
{
|
||||||
ui->globalStatusLabel->setText(m_task->getStatus());
|
ui->globalStatusLabel->setText(task->getStatus());
|
||||||
ui->globalStatusDetailsLabel->setText(m_task->getDetails());
|
ui->globalStatusLabel->adjustSize();
|
||||||
|
ui->globalStatusDetailsLabel->setText(task->getDetails());
|
||||||
|
ui->globalStatusDetailsLabel->adjustSize();
|
||||||
|
|
||||||
|
|
||||||
updateSize();
|
updateSize();
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ public:
|
|||||||
explicit ProgressDialog(QWidget *parent = 0);
|
explicit ProgressDialog(QWidget *parent = 0);
|
||||||
~ProgressDialog();
|
~ProgressDialog();
|
||||||
|
|
||||||
void updateSize();
|
void updateSize(bool recenterParent = false);
|
||||||
|
|
||||||
int execWithTask(Task* task);
|
int execWithTask(Task* task);
|
||||||
int execWithTask(std::unique_ptr<Task> &&task);
|
int execWithTask(std::unique_ptr<Task> &&task);
|
||||||
|
@ -43,6 +43,8 @@
|
|||||||
#include "ui/pages/modplatform/flame/FlameResourcePages.h"
|
#include "ui/pages/modplatform/flame/FlameResourcePages.h"
|
||||||
#include "ui/pages/modplatform/modrinth/ModrinthResourcePages.h"
|
#include "ui/pages/modplatform/modrinth/ModrinthResourcePages.h"
|
||||||
|
|
||||||
|
#include "modplatform/flame/FlameAPI.h"
|
||||||
|
#include "modplatform/modrinth/ModrinthAPI.h"
|
||||||
#include "ui/widgets/PageContainer.h"
|
#include "ui/widgets/PageContainer.h"
|
||||||
|
|
||||||
namespace ResourceDownload {
|
namespace ResourceDownload {
|
||||||
@ -281,8 +283,11 @@ QList<BasePage*> ModDownloadDialog::getPages()
|
|||||||
{
|
{
|
||||||
QList<BasePage*> pages;
|
QList<BasePage*> pages;
|
||||||
|
|
||||||
|
auto loaders = static_cast<MinecraftInstance*>(m_instance)->getPackProfile()->getModLoaders().value();
|
||||||
|
|
||||||
|
if (ModrinthAPI::validateModLoaders(loaders))
|
||||||
pages.append(ModrinthModPage::create(this, *m_instance));
|
pages.append(ModrinthModPage::create(this, *m_instance));
|
||||||
if (APPLICATION->capabilities() & Application::SupportsFlame)
|
if (APPLICATION->capabilities() & Application::SupportsFlame && FlameAPI::validateModLoaders(loaders))
|
||||||
pages.append(FlameModPage::create(this, *m_instance));
|
pages.append(FlameModPage::create(this, *m_instance));
|
||||||
|
|
||||||
m_selectedPage = dynamic_cast<ModPage*>(pages[0]);
|
m_selectedPage = dynamic_cast<ModPage*>(pages[0]);
|
||||||
|
@ -248,8 +248,8 @@ bool AccessibleInstanceView::selectColumn(int column)
|
|||||||
if (view()->selectionBehavior() != QAbstractItemView::SelectColumns && rowCount() > 1) {
|
if (view()->selectionBehavior() != QAbstractItemView::SelectColumns && rowCount() > 1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// fallthrough intentional
|
|
||||||
}
|
}
|
||||||
|
/* fallthrough */
|
||||||
case QAbstractItemView::ContiguousSelection: {
|
case QAbstractItemView::ContiguousSelection: {
|
||||||
if ((!column || !view()->selectionModel()->isColumnSelected(column - 1, view()->rootIndex())) && !view()->selectionModel()->isColumnSelected(column + 1, view()->rootIndex())) {
|
if ((!column || !view()->selectionModel()->isColumnSelected(column - 1, view()->rootIndex())) && !view()->selectionModel()->isColumnSelected(column + 1, view()->rootIndex())) {
|
||||||
view()->clearSelection();
|
view()->clearSelection();
|
||||||
|
@ -48,6 +48,7 @@
|
|||||||
#include <QAccessible>
|
#include <QAccessible>
|
||||||
|
|
||||||
#include "VisualGroup.h"
|
#include "VisualGroup.h"
|
||||||
|
#include "ui/themes/ThemeManager.h"
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
#include <Application.h>
|
#include <Application.h>
|
||||||
@ -73,6 +74,7 @@ InstanceView::InstanceView(QWidget *parent)
|
|||||||
setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||||
setAcceptDrops(true);
|
setAcceptDrops(true);
|
||||||
setAutoScroll(true);
|
setAutoScroll(true);
|
||||||
|
setPaintCat(APPLICATION->settings()->get("TheCat").toBool());
|
||||||
}
|
}
|
||||||
|
|
||||||
InstanceView::~InstanceView()
|
InstanceView::~InstanceView()
|
||||||
@ -500,12 +502,35 @@ void InstanceView::mouseDoubleClickEvent(QMouseEvent *event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void InstanceView::setPaintCat(bool visible)
|
||||||
|
{
|
||||||
|
m_catVisible = visible;
|
||||||
|
if (visible)
|
||||||
|
m_catPixmap.load(QString(":/backgrounds/%1").arg(ThemeManager::getCatImage()));
|
||||||
|
else
|
||||||
|
m_catPixmap = QPixmap();
|
||||||
|
}
|
||||||
|
|
||||||
void InstanceView::paintEvent([[maybe_unused]] QPaintEvent *event)
|
void InstanceView::paintEvent([[maybe_unused]] QPaintEvent *event)
|
||||||
{
|
{
|
||||||
executeDelayedItemsLayout();
|
executeDelayedItemsLayout();
|
||||||
|
|
||||||
QPainter painter(this->viewport());
|
QPainter painter(this->viewport());
|
||||||
|
|
||||||
|
if (m_catVisible) {
|
||||||
|
int widWidth = this->viewport()->width();
|
||||||
|
int widHeight = this->viewport()->height();
|
||||||
|
if (m_catPixmap.width() < widWidth)
|
||||||
|
widWidth = m_catPixmap.width();
|
||||||
|
if (m_catPixmap.height() < widHeight)
|
||||||
|
widHeight = m_catPixmap.height();
|
||||||
|
auto pixmap = m_catPixmap.scaled(widWidth, widHeight, Qt::KeepAspectRatio);
|
||||||
|
QRect rectOfPixmap = pixmap.rect();
|
||||||
|
rectOfPixmap.moveBottomRight(this->viewport()->rect().bottomRight());
|
||||||
|
painter.drawPixmap(rectOfPixmap.topLeft(), pixmap);
|
||||||
|
}
|
||||||
|
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||||
QStyleOptionViewItem option;
|
QStyleOptionViewItem option;
|
||||||
initViewItemOption(&option);
|
initViewItemOption(&option);
|
||||||
|
@ -85,10 +85,8 @@ public:
|
|||||||
|
|
||||||
virtual QRegion visualRegionForSelection(const QItemSelection &selection) const override;
|
virtual QRegion visualRegionForSelection(const QItemSelection &selection) const override;
|
||||||
|
|
||||||
int spacing() const
|
int spacing() const { return m_spacing; };
|
||||||
{
|
void setPaintCat(bool visible);
|
||||||
return m_spacing;
|
|
||||||
};
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
virtual void updateGeometries() override;
|
virtual void updateGeometries() override;
|
||||||
@ -139,6 +137,8 @@ private:
|
|||||||
int m_currentItemsPerRow = -1;
|
int m_currentItemsPerRow = -1;
|
||||||
int m_currentCursorColumn= -1;
|
int m_currentCursorColumn= -1;
|
||||||
mutable QCache<int, QRect> geometryCache;
|
mutable QCache<int, QRect> geometryCache;
|
||||||
|
bool m_catVisible = false;
|
||||||
|
QPixmap m_catPixmap;
|
||||||
|
|
||||||
// point where the currently active mouse action started in geometry coordinates
|
// point where the currently active mouse action started in geometry coordinates
|
||||||
QPoint m_pressedPosition;
|
QPoint m_pressedPosition;
|
||||||
|
@ -81,6 +81,8 @@ APIPage::APIPage(QWidget *parent) :
|
|||||||
connect(ui->pasteTypeComboBox, currentIndexChangedSignal, this, &APIPage::updateBaseURLPlaceholder);
|
connect(ui->pasteTypeComboBox, currentIndexChangedSignal, this, &APIPage::updateBaseURLPlaceholder);
|
||||||
// This function needs to be called even when the ComboBox's index is still in its default state.
|
// This function needs to be called even when the ComboBox's index is still in its default state.
|
||||||
updateBaseURLPlaceholder(ui->pasteTypeComboBox->currentIndex());
|
updateBaseURLPlaceholder(ui->pasteTypeComboBox->currentIndex());
|
||||||
|
// NOTE: this allows http://, but we replace that with https later anyway
|
||||||
|
ui->metaURL->setValidator(new QRegularExpressionValidator(validUrlRegExp, ui->metaURL));
|
||||||
ui->baseURLEntry->setValidator(new QRegularExpressionValidator(validUrlRegExp, ui->baseURLEntry));
|
ui->baseURLEntry->setValidator(new QRegularExpressionValidator(validUrlRegExp, ui->baseURLEntry));
|
||||||
ui->msaClientID->setValidator(new QRegularExpressionValidator(validMSAClientID, ui->msaClientID));
|
ui->msaClientID->setValidator(new QRegularExpressionValidator(validMSAClientID, ui->msaClientID));
|
||||||
ui->flameKey->setValidator(new QRegularExpressionValidator(validFlameKey, ui->flameKey));
|
ui->flameKey->setValidator(new QRegularExpressionValidator(validFlameKey, ui->flameKey));
|
||||||
@ -163,7 +165,7 @@ void APIPage::applySettings()
|
|||||||
|
|
||||||
QString msaClientID = ui->msaClientID->text();
|
QString msaClientID = ui->msaClientID->text();
|
||||||
s->set("MSAClientIDOverride", msaClientID);
|
s->set("MSAClientIDOverride", msaClientID);
|
||||||
QUrl metaURL = ui->metaURL->text();
|
QUrl metaURL(ui->metaURL->text());
|
||||||
// Add required trailing slash
|
// Add required trailing slash
|
||||||
if (!metaURL.isEmpty() && !metaURL.path().endsWith('/'))
|
if (!metaURL.isEmpty() && !metaURL.path().endsWith('/'))
|
||||||
{
|
{
|
||||||
|
@ -169,7 +169,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="metadataDisableBtn">
|
<widget class="QCheckBox" name="metadataDisableBtn">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Disable using metadata provided by mod providers (like Modrinth or Curseforge) for mods.</string>
|
<string>Disable using metadata provided by mod providers (like Modrinth or CurseForge) for mods.</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Disable using metadata for mods</string>
|
<string>Disable using metadata for mods</string>
|
||||||
|
@ -151,9 +151,6 @@ void ExternalResourcesPage::retranslate()
|
|||||||
|
|
||||||
void ExternalResourcesPage::itemActivated(const QModelIndex&)
|
void ExternalResourcesPage::itemActivated(const QModelIndex&)
|
||||||
{
|
{
|
||||||
if (!m_controlsEnabled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection());
|
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,9 +194,6 @@ bool ExternalResourcesPage::eventFilter(QObject* obj, QEvent* ev)
|
|||||||
|
|
||||||
void ExternalResourcesPage::addItem()
|
void ExternalResourcesPage::addItem()
|
||||||
{
|
{
|
||||||
if (!m_controlsEnabled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto list = GuiUtil::BrowseForFiles(
|
auto list = GuiUtil::BrowseForFiles(
|
||||||
helpPage(), tr("Select %1", "Select whatever type of files the page contains. Example: 'Loader Mods'").arg(displayName()),
|
helpPage(), tr("Select %1", "Select whatever type of files the page contains. Example: 'Loader Mods'").arg(displayName()),
|
||||||
m_fileSelectionFilter.arg(displayName()), APPLICATION->settings()->get("CentralModsDir").toString(), this->parentWidget());
|
m_fileSelectionFilter.arg(displayName()), APPLICATION->settings()->get("CentralModsDir").toString(), this->parentWidget());
|
||||||
@ -213,9 +207,6 @@ void ExternalResourcesPage::addItem()
|
|||||||
|
|
||||||
void ExternalResourcesPage::removeItem()
|
void ExternalResourcesPage::removeItem()
|
||||||
{
|
{
|
||||||
if (!m_controlsEnabled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection());
|
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection());
|
||||||
|
|
||||||
int count = 0;
|
int count = 0;
|
||||||
@ -259,23 +250,37 @@ void ExternalResourcesPage::removeItem()
|
|||||||
|
|
||||||
void ExternalResourcesPage::removeItems(const QItemSelection& selection)
|
void ExternalResourcesPage::removeItems(const QItemSelection& selection)
|
||||||
{
|
{
|
||||||
|
if (m_instance != nullptr && m_instance->isRunning()) {
|
||||||
|
auto response = CustomMessageBox::selectable(this, "Confirm Delete",
|
||||||
|
"If you remove this resource while the game is running it may crash your game.\n"
|
||||||
|
"Are you sure you want to do this?",
|
||||||
|
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
|
||||||
|
->exec();
|
||||||
|
|
||||||
|
if (response != QMessageBox::Yes)
|
||||||
|
return;
|
||||||
|
}
|
||||||
m_model->deleteResources(selection.indexes());
|
m_model->deleteResources(selection.indexes());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExternalResourcesPage::enableItem()
|
void ExternalResourcesPage::enableItem()
|
||||||
{
|
{
|
||||||
if (!m_controlsEnabled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection());
|
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection());
|
||||||
m_model->setResourceEnabled(selection.indexes(), EnableAction::ENABLE);
|
m_model->setResourceEnabled(selection.indexes(), EnableAction::ENABLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExternalResourcesPage::disableItem()
|
void ExternalResourcesPage::disableItem()
|
||||||
{
|
{
|
||||||
if (!m_controlsEnabled)
|
if (m_instance != nullptr && m_instance->isRunning()) {
|
||||||
return;
|
auto response = CustomMessageBox::selectable(this, "Confirm disable",
|
||||||
|
"If you disable this resource while the game is running it may crash your game.\n"
|
||||||
|
"Are you sure you want to do this?",
|
||||||
|
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
|
||||||
|
->exec();
|
||||||
|
|
||||||
|
if (response != QMessageBox::Yes)
|
||||||
|
return;
|
||||||
|
}
|
||||||
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection());
|
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection());
|
||||||
m_model->setResourceEnabled(selection.indexes(), EnableAction::DISABLE);
|
m_model->setResourceEnabled(selection.indexes(), EnableAction::DISABLE);
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,5 @@ class ExternalResourcesPage : public QMainWindow, public BasePage {
|
|||||||
QString m_fileSelectionFilter;
|
QString m_fileSelectionFilter;
|
||||||
QString m_viewFilter;
|
QString m_viewFilter;
|
||||||
|
|
||||||
bool m_controlsEnabled = true;
|
|
||||||
|
|
||||||
std::shared_ptr<Setting> m_wide_bar_setting = nullptr;
|
std::shared_ptr<Setting> m_wide_bar_setting = nullptr;
|
||||||
};
|
};
|
||||||
|
@ -157,6 +157,17 @@
|
|||||||
<string>Try to check or update all selected resources (all resources if none are selected)</string>
|
<string>Try to check or update all selected resources (all resources if none are selected)</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="actionVisitItemPage">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Visit mod's page</string>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Go to mods home page</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
|
@ -60,17 +60,13 @@ InstanceSettingsPage::InstanceSettingsPage(BaseInstance *inst, QWidget *parent)
|
|||||||
m_settings = inst->settings();
|
m_settings = inst->settings();
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
// As the signal will (probably) not be triggered once we click edit, let's update it manually instead.
|
|
||||||
updateRunningStatus(m_instance->isRunning());
|
|
||||||
|
|
||||||
connect(m_instance, &BaseInstance::runningStatusChanged, this, &InstanceSettingsPage::updateRunningStatus);
|
|
||||||
connect(ui->openGlobalJavaSettingsButton, &QCommandLinkButton::clicked, this, &InstanceSettingsPage::globalSettingsButtonClicked);
|
connect(ui->openGlobalJavaSettingsButton, &QCommandLinkButton::clicked, this, &InstanceSettingsPage::globalSettingsButtonClicked);
|
||||||
connect(APPLICATION, &Application::globalSettingsAboutToOpen, this, &InstanceSettingsPage::applySettings);
|
connect(APPLICATION, &Application::globalSettingsAboutToOpen, this, &InstanceSettingsPage::applySettings);
|
||||||
connect(APPLICATION, &Application::globalSettingsClosed, this, &InstanceSettingsPage::loadSettings);
|
connect(APPLICATION, &Application::globalSettingsClosed, this, &InstanceSettingsPage::loadSettings);
|
||||||
connect(ui->instanceAccountSelector, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &InstanceSettingsPage::changeInstanceAccount);
|
connect(ui->instanceAccountSelector, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
|
||||||
|
&InstanceSettingsPage::changeInstanceAccount);
|
||||||
loadSettings();
|
loadSettings();
|
||||||
|
|
||||||
|
|
||||||
updateThresholds();
|
updateThresholds();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -523,8 +519,3 @@ void InstanceSettingsPage::updateThresholds()
|
|||||||
ui->labelMaxMemIcon->setPixmap(pix);
|
ui->labelMaxMemIcon->setPixmap(pix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceSettingsPage::updateRunningStatus(bool running)
|
|
||||||
{
|
|
||||||
setEnabled(!running);
|
|
||||||
}
|
|
||||||
|
@ -80,7 +80,6 @@ public:
|
|||||||
void updateThresholds();
|
void updateThresholds();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void updateRunningStatus(bool running);
|
|
||||||
void on_javaDetectBtn_clicked();
|
void on_javaDetectBtn_clicked();
|
||||||
void on_javaTestBtn_clicked();
|
void on_javaTestBtn_clicked();
|
||||||
void on_javaBrowseBtn_clicked();
|
void on_javaBrowseBtn_clicked();
|
||||||
|
@ -69,7 +69,6 @@ class NoBigComboBoxStyle : public QProxyStyle {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
NoBigComboBoxStyle(QStyle* style) : QProxyStyle(style) {}
|
NoBigComboBoxStyle(QStyle* style) : QProxyStyle(style) {}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ManagedPackPage* ManagedPackPage::createPage(BaseInstance* inst, QString type, QWidget* parent)
|
ManagedPackPage* ManagedPackPage::createPage(BaseInstance* inst, QString type, QWidget* parent)
|
||||||
@ -226,7 +225,8 @@ void ModrinthManagedPackPage::parseManagedPack()
|
|||||||
|
|
||||||
QString id = m_inst->getManagedPackID();
|
QString id = m_inst->getManagedPackID();
|
||||||
|
|
||||||
m_fetch_job->addNetAction(Net::Download::makeByteArray(QString("%1/project/%2/version").arg(BuildConfig.MODRINTH_PROD_URL, id), response));
|
m_fetch_job->addNetAction(
|
||||||
|
Net::Download::makeByteArray(QString("%1/project/%2/version").arg(BuildConfig.MODRINTH_PROD_URL, id), response));
|
||||||
|
|
||||||
QObject::connect(m_fetch_job.get(), &NetJob::succeeded, this, [this, response, id] {
|
QObject::connect(m_fetch_job.get(), &NetJob::succeeded, this, [this, response, id] {
|
||||||
QJsonParseError parse_error{};
|
QJsonParseError parse_error{};
|
||||||
@ -267,7 +267,6 @@ void ModrinthManagedPackPage::parseManagedPack()
|
|||||||
if (version.version == m_inst->getManagedPackVersionName())
|
if (version.version == m_inst->getManagedPackVersionName())
|
||||||
name = tr("%1 (Current)").arg(name);
|
name = tr("%1 (Current)").arg(name);
|
||||||
|
|
||||||
|
|
||||||
ui->versionsComboBox->addItem(name, QVariant(version.id));
|
ui->versionsComboBox->addItem(name, QVariant(version.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,6 +290,10 @@ QString ModrinthManagedPackPage::url() const
|
|||||||
void ModrinthManagedPackPage::suggestVersion()
|
void ModrinthManagedPackPage::suggestVersion()
|
||||||
{
|
{
|
||||||
auto index = ui->versionsComboBox->currentIndex();
|
auto index = ui->versionsComboBox->currentIndex();
|
||||||
|
if (m_pack.versions.length() == 0) {
|
||||||
|
setFailState();
|
||||||
|
return;
|
||||||
|
}
|
||||||
auto version = m_pack.versions.at(index);
|
auto version = m_pack.versions.at(index);
|
||||||
|
|
||||||
ui->changelogTextBrowser->setHtml(markdownToHTML(version.changelog.toUtf8()));
|
ui->changelogTextBrowser->setHtml(markdownToHTML(version.changelog.toUtf8()));
|
||||||
@ -301,6 +304,10 @@ void ModrinthManagedPackPage::suggestVersion()
|
|||||||
void ModrinthManagedPackPage::update()
|
void ModrinthManagedPackPage::update()
|
||||||
{
|
{
|
||||||
auto index = ui->versionsComboBox->currentIndex();
|
auto index = ui->versionsComboBox->currentIndex();
|
||||||
|
if (m_pack.versions.length() == 0) {
|
||||||
|
setFailState();
|
||||||
|
return;
|
||||||
|
}
|
||||||
auto version = m_pack.versions.at(index);
|
auto version = m_pack.versions.at(index);
|
||||||
|
|
||||||
QMap<QString, QString> extra_info;
|
QMap<QString, QString> extra_info;
|
||||||
@ -429,6 +436,10 @@ QString FlameManagedPackPage::url() const
|
|||||||
void FlameManagedPackPage::suggestVersion()
|
void FlameManagedPackPage::suggestVersion()
|
||||||
{
|
{
|
||||||
auto index = ui->versionsComboBox->currentIndex();
|
auto index = ui->versionsComboBox->currentIndex();
|
||||||
|
if (m_pack.versions.length() == 0) {
|
||||||
|
setFailState();
|
||||||
|
return;
|
||||||
|
}
|
||||||
auto version = m_pack.versions.at(index);
|
auto version = m_pack.versions.at(index);
|
||||||
|
|
||||||
ui->changelogTextBrowser->setHtml(m_api.getModFileChangelog(m_inst->getManagedPackID().toInt(), version.fileId));
|
ui->changelogTextBrowser->setHtml(m_api.getModFileChangelog(m_inst->getManagedPackID().toInt(), version.fileId));
|
||||||
@ -439,6 +450,10 @@ void FlameManagedPackPage::suggestVersion()
|
|||||||
void FlameManagedPackPage::update()
|
void FlameManagedPackPage::update()
|
||||||
{
|
{
|
||||||
auto index = ui->versionsComboBox->currentIndex();
|
auto index = ui->versionsComboBox->currentIndex();
|
||||||
|
if (m_pack.versions.length() == 0) {
|
||||||
|
setFailState();
|
||||||
|
return;
|
||||||
|
}
|
||||||
auto version = m_pack.versions.at(index);
|
auto version = m_pack.versions.at(index);
|
||||||
|
|
||||||
QMap<QString, QString> extra_info;
|
QMap<QString, QString> extra_info;
|
||||||
|
@ -45,6 +45,7 @@
|
|||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QSortFilterProxyModel>
|
#include <QSortFilterProxyModel>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
|
||||||
@ -60,6 +61,7 @@
|
|||||||
#include "minecraft/mod/Mod.h"
|
#include "minecraft/mod/Mod.h"
|
||||||
#include "minecraft/mod/ModFolderModel.h"
|
#include "minecraft/mod/ModFolderModel.h"
|
||||||
|
|
||||||
|
#include "modplatform/ModIndex.h"
|
||||||
#include "modplatform/ResourceAPI.h"
|
#include "modplatform/ResourceAPI.h"
|
||||||
|
|
||||||
#include "Version.h"
|
#include "Version.h"
|
||||||
@ -86,12 +88,28 @@ ModFolderPage::ModFolderPage(BaseInstance* inst, std::shared_ptr<ModFolderModel>
|
|||||||
ui->actionsToolbar->insertActionAfter(ui->actionAddItem, ui->actionUpdateItem);
|
ui->actionsToolbar->insertActionAfter(ui->actionAddItem, ui->actionUpdateItem);
|
||||||
connect(ui->actionUpdateItem, &QAction::triggered, this, &ModFolderPage::updateMods);
|
connect(ui->actionUpdateItem, &QAction::triggered, this, &ModFolderPage::updateMods);
|
||||||
|
|
||||||
auto check_allow_update = [this] {
|
ui->actionVisitItemPage->setToolTip(tr("Go to mod's home page"));
|
||||||
return (!m_instance || !m_instance->isRunning()) && (ui->treeView->selectionModel()->hasSelection() || !m_model->empty());
|
ui->actionsToolbar->addAction(ui->actionVisitItemPage);
|
||||||
};
|
connect(ui->actionVisitItemPage, &QAction::triggered, this, &ModFolderPage::visitModPages);
|
||||||
|
|
||||||
connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
|
auto check_allow_update = [this] { return ui->treeView->selectionModel()->hasSelection() || !m_model->empty(); };
|
||||||
[this, check_allow_update] { ui->actionUpdateItem->setEnabled(check_allow_update()); });
|
|
||||||
|
connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, [this, check_allow_update] {
|
||||||
|
ui->actionUpdateItem->setEnabled(check_allow_update());
|
||||||
|
|
||||||
|
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes();
|
||||||
|
auto mods_list = m_model->selectedMods(selection);
|
||||||
|
auto selected = std::count_if(mods_list.cbegin(), mods_list.cend(),
|
||||||
|
[](Mod* v) { return v->metadata() != nullptr || v->homeurl().size() != 0; });
|
||||||
|
if (selected <= 1) {
|
||||||
|
ui->actionVisitItemPage->setText(tr("Visit mod's page"));
|
||||||
|
ui->actionVisitItemPage->setToolTip(tr("Go to mod's home page"));
|
||||||
|
} else {
|
||||||
|
ui->actionVisitItemPage->setText(tr("Visit mods' pages"));
|
||||||
|
ui->actionVisitItemPage->setToolTip(tr("Go to the pages of the selected mods"));
|
||||||
|
}
|
||||||
|
ui->actionVisitItemPage->setEnabled(selected != 0);
|
||||||
|
});
|
||||||
|
|
||||||
connect(mods.get(), &ModFolderModel::rowsInserted, this,
|
connect(mods.get(), &ModFolderModel::rowsInserted, this,
|
||||||
[this, check_allow_update] { ui->actionUpdateItem->setEnabled(check_allow_update()); });
|
[this, check_allow_update] { ui->actionUpdateItem->setEnabled(check_allow_update()); });
|
||||||
@ -101,22 +119,9 @@ ModFolderPage::ModFolderPage(BaseInstance* inst, std::shared_ptr<ModFolderModel>
|
|||||||
|
|
||||||
connect(mods.get(), &ModFolderModel::updateFinished, this,
|
connect(mods.get(), &ModFolderModel::updateFinished, this,
|
||||||
[this, check_allow_update] { ui->actionUpdateItem->setEnabled(check_allow_update()); });
|
[this, check_allow_update] { ui->actionUpdateItem->setEnabled(check_allow_update()); });
|
||||||
|
|
||||||
connect(m_instance, &BaseInstance::runningStatusChanged, this, &ModFolderPage::runningStateChanged);
|
|
||||||
ModFolderPage::runningStateChanged(m_instance && m_instance->isRunning());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModFolderPage::runningStateChanged(bool running)
|
|
||||||
{
|
|
||||||
ui->actionDownloadItem->setEnabled(!running);
|
|
||||||
ui->actionUpdateItem->setEnabled(!running);
|
|
||||||
ui->actionAddItem->setEnabled(!running);
|
|
||||||
ui->actionEnableItem->setEnabled(!running);
|
|
||||||
ui->actionDisableItem->setEnabled(!running);
|
|
||||||
ui->actionRemoveItem->setEnabled(!running);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ModFolderPage::shouldDisplay() const
|
bool ModFolderPage::shouldDisplay() const
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
@ -135,13 +140,21 @@ bool ModFolderPage::onSelectionChanged(const QModelIndex& current, [[maybe_unuse
|
|||||||
|
|
||||||
void ModFolderPage::removeItems(const QItemSelection& selection)
|
void ModFolderPage::removeItems(const QItemSelection& selection)
|
||||||
{
|
{
|
||||||
|
if (m_instance != nullptr && m_instance->isRunning()) {
|
||||||
|
auto response = CustomMessageBox::selectable(this, "Confirm Delete",
|
||||||
|
"If you remove mods while the game is running it may crash your game.\n"
|
||||||
|
"Are you sure you want to do this?",
|
||||||
|
QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
|
||||||
|
->exec();
|
||||||
|
|
||||||
|
if (response != QMessageBox::Yes)
|
||||||
|
return;
|
||||||
|
}
|
||||||
m_model->deleteMods(selection.indexes());
|
m_model->deleteMods(selection.indexes());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModFolderPage::installMods()
|
void ModFolderPage::installMods()
|
||||||
{
|
{
|
||||||
if (!m_controlsEnabled)
|
|
||||||
return;
|
|
||||||
if (m_instance->typeName() != "Minecraft")
|
if (m_instance->typeName() != "Minecraft")
|
||||||
return; // this is a null instance or a legacy instance
|
return; // this is a null instance or a legacy instance
|
||||||
|
|
||||||
@ -207,8 +220,7 @@ void ModFolderPage::updateMods()
|
|||||||
message = tr("All selected mods are up-to-date! :)");
|
message = tr("All selected mods are up-to-date! :)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CustomMessageBox::selectable(this, tr("Update checker"), message)
|
CustomMessageBox::selectable(this, tr("Update checker"), message)->exec();
|
||||||
->exec();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,3 +287,13 @@ bool NilModFolderPage::shouldDisplay() const
|
|||||||
{
|
{
|
||||||
return m_model->dir().exists();
|
return m_model->dir().exists();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ModFolderPage::visitModPages()
|
||||||
|
{
|
||||||
|
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes();
|
||||||
|
for (auto mod : m_model->selectedMods(selection)) {
|
||||||
|
auto url = mod->metaurl();
|
||||||
|
if (!url.isEmpty())
|
||||||
|
DesktopServices::openUrl(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
||||||
|
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -59,11 +60,11 @@ class ModFolderPage : public ExternalResourcesPage {
|
|||||||
bool onSelectionChanged(const QModelIndex& current, const QModelIndex& previous) override;
|
bool onSelectionChanged(const QModelIndex& current, const QModelIndex& previous) override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void runningStateChanged(bool running);
|
|
||||||
void removeItems(const QItemSelection& selection) override;
|
void removeItems(const QItemSelection& selection) override;
|
||||||
|
|
||||||
void installMods();
|
void installMods();
|
||||||
void updateMods();
|
void updateMods();
|
||||||
|
void visitModPages();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::shared_ptr<ModFolderModel> m_model;
|
std::shared_ptr<ModFolderModel> m_model;
|
||||||
|
@ -67,8 +67,6 @@ bool ResourcePackPage::onSelectionChanged(const QModelIndex& current, [[maybe_un
|
|||||||
|
|
||||||
void ResourcePackPage::downloadRPs()
|
void ResourcePackPage::downloadRPs()
|
||||||
{
|
{
|
||||||
if (!m_controlsEnabled)
|
|
||||||
return;
|
|
||||||
if (m_instance->typeName() != "Minecraft")
|
if (m_instance->typeName() != "Minecraft")
|
||||||
return; // this is a null instance or a legacy instance
|
return; // this is a null instance or a legacy instance
|
||||||
|
|
||||||
|
@ -97,17 +97,13 @@ public:
|
|||||||
return;
|
return;
|
||||||
if ((info.suffix().compare("png", Qt::CaseInsensitive) != 0))
|
if ((info.suffix().compare("png", Qt::CaseInsensitive) != 0))
|
||||||
return;
|
return;
|
||||||
int tries = 5;
|
|
||||||
while (tries)
|
|
||||||
{
|
|
||||||
if (!m_cache->stale(m_path))
|
if (!m_cache->stale(m_path))
|
||||||
return;
|
return;
|
||||||
QImage image(m_path);
|
QImage image(m_path);
|
||||||
if (image.isNull())
|
if (image.isNull()) {
|
||||||
{
|
m_resultEmitter.emitResultsFailed(m_path);
|
||||||
QThread::msleep(500);
|
qDebug() << "Error loading screenshot: " + m_path + ". Perhaps too large?";
|
||||||
tries--;
|
return;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
QImage small;
|
QImage small;
|
||||||
if (image.width() > image.height())
|
if (image.width() > image.height())
|
||||||
@ -125,9 +121,6 @@ public:
|
|||||||
QIcon icon(QPixmap::fromImage(square));
|
QIcon icon(QPixmap::fromImage(square));
|
||||||
m_cache->add(m_path, icon);
|
m_cache->add(m_path, icon);
|
||||||
m_resultEmitter.emitResultsReady(m_path);
|
m_resultEmitter.emitResultsReady(m_path);
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_resultEmitter.emitResultsFailed(m_path);
|
|
||||||
}
|
}
|
||||||
QString m_path;
|
QString m_path;
|
||||||
SharedIconCachePtr m_cache;
|
SharedIconCachePtr m_cache;
|
||||||
@ -146,9 +139,12 @@ public:
|
|||||||
m_thumbnailCache = std::make_shared<SharedIconCache>();
|
m_thumbnailCache = std::make_shared<SharedIconCache>();
|
||||||
m_thumbnailCache->add("placeholder", APPLICATION->getThemedIcon("screenshot-placeholder"));
|
m_thumbnailCache->add("placeholder", APPLICATION->getThemedIcon("screenshot-placeholder"));
|
||||||
connect(&watcher, SIGNAL(fileChanged(QString)), SLOT(fileChanged(QString)));
|
connect(&watcher, SIGNAL(fileChanged(QString)), SLOT(fileChanged(QString)));
|
||||||
// FIXME: the watched file set is not updated when files are removed
|
|
||||||
}
|
}
|
||||||
virtual ~FilterModel() { m_thumbnailingPool.waitForDone(500); }
|
virtual ~FilterModel() {
|
||||||
|
m_thumbnailingPool.clear();
|
||||||
|
if (!m_thumbnailingPool.waitForDone(500))
|
||||||
|
qDebug() << "Thumbnail pool took longer than 500ms to finish";
|
||||||
|
}
|
||||||
virtual QVariant data(const QModelIndex &proxyIndex, int role = Qt::DisplayRole) const
|
virtual QVariant data(const QModelIndex &proxyIndex, int role = Qt::DisplayRole) const
|
||||||
{
|
{
|
||||||
auto model = sourceModel();
|
auto model = sourceModel();
|
||||||
@ -215,10 +211,12 @@ private slots:
|
|||||||
void fileChanged(QString filepath)
|
void fileChanged(QString filepath)
|
||||||
{
|
{
|
||||||
m_thumbnailCache->setStale(filepath);
|
m_thumbnailCache->setStale(filepath);
|
||||||
thumbnailImage(filepath);
|
|
||||||
// reinsert the path...
|
// reinsert the path...
|
||||||
watcher.removePath(filepath);
|
watcher.removePath(filepath);
|
||||||
|
if (QFile::exists(filepath)) {
|
||||||
watcher.addPath(filepath);
|
watcher.addPath(filepath);
|
||||||
|
thumbnailImage(filepath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -46,7 +46,6 @@
|
|||||||
#include "ui/dialogs/ProgressDialog.h"
|
#include "ui/dialogs/ProgressDialog.h"
|
||||||
#include "ui/dialogs/ResourceDownloadDialog.h"
|
#include "ui/dialogs/ResourceDownloadDialog.h"
|
||||||
|
|
||||||
|
|
||||||
ShaderPackPage::ShaderPackPage(MinecraftInstance* instance, std::shared_ptr<ShaderPackFolderModel> model, QWidget* parent)
|
ShaderPackPage::ShaderPackPage(MinecraftInstance* instance, std::shared_ptr<ShaderPackFolderModel> model, QWidget* parent)
|
||||||
: ExternalResourcesPage(instance, model, parent)
|
: ExternalResourcesPage(instance, model, parent)
|
||||||
{
|
{
|
||||||
@ -61,8 +60,6 @@ ShaderPackPage::ShaderPackPage(MinecraftInstance* instance, std::shared_ptr<Shad
|
|||||||
|
|
||||||
void ShaderPackPage::downloadShaders()
|
void ShaderPackPage::downloadShaders()
|
||||||
{
|
{
|
||||||
if (!m_controlsEnabled)
|
|
||||||
return;
|
|
||||||
if (m_instance->typeName() != "Minecraft")
|
if (m_instance->typeName() != "Minecraft")
|
||||||
return; // this is a null instance or a legacy instance
|
return; // this is a null instance or a legacy instance
|
||||||
|
|
||||||
|
@ -69,8 +69,6 @@ bool TexturePackPage::onSelectionChanged(const QModelIndex& current, [[maybe_unu
|
|||||||
|
|
||||||
void TexturePackPage::downloadTPs()
|
void TexturePackPage::downloadTPs()
|
||||||
{
|
{
|
||||||
if (!m_controlsEnabled)
|
|
||||||
return;
|
|
||||||
if (m_instance->typeName() != "Minecraft")
|
if (m_instance->typeName() != "Minecraft")
|
||||||
return; // this is a null instance or a legacy instance
|
return; // this is a null instance or a legacy instance
|
||||||
|
|
||||||
|
@ -40,14 +40,13 @@
|
|||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
|
||||||
#include <QMessageBox>
|
#include <QAbstractItemModel>
|
||||||
#include <QLabel>
|
|
||||||
#include <QEvent>
|
#include <QEvent>
|
||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
#include <QMenu>
|
#include <QLabel>
|
||||||
#include <QAbstractItemModel>
|
|
||||||
#include <QMessageBox>
|
|
||||||
#include <QListView>
|
#include <QListView>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QMessageBox>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
@ -55,28 +54,26 @@
|
|||||||
#include "ui_VersionPage.h"
|
#include "ui_VersionPage.h"
|
||||||
|
|
||||||
#include "ui/dialogs/CustomMessageBox.h"
|
#include "ui/dialogs/CustomMessageBox.h"
|
||||||
#include "ui/dialogs/VersionSelectDialog.h"
|
|
||||||
#include "ui/dialogs/NewComponentDialog.h"
|
#include "ui/dialogs/NewComponentDialog.h"
|
||||||
#include "ui/dialogs/ProgressDialog.h"
|
#include "ui/dialogs/ProgressDialog.h"
|
||||||
|
#include "ui/dialogs/VersionSelectDialog.h"
|
||||||
|
|
||||||
#include "ui/GuiUtil.h"
|
#include "ui/GuiUtil.h"
|
||||||
|
|
||||||
|
#include "DesktopServices.h"
|
||||||
|
#include "Exception.h"
|
||||||
|
#include "Version.h"
|
||||||
|
#include "icons/IconList.h"
|
||||||
#include "minecraft/PackProfile.h"
|
#include "minecraft/PackProfile.h"
|
||||||
#include "minecraft/auth/AccountList.h"
|
#include "minecraft/auth/AccountList.h"
|
||||||
#include "minecraft/mod/Mod.h"
|
#include "minecraft/mod/Mod.h"
|
||||||
#include "icons/IconList.h"
|
|
||||||
#include "Exception.h"
|
|
||||||
#include "Version.h"
|
|
||||||
#include "DesktopServices.h"
|
|
||||||
|
|
||||||
#include "meta/Index.h"
|
#include "meta/Index.h"
|
||||||
#include "meta/VersionList.h"
|
#include "meta/VersionList.h"
|
||||||
|
|
||||||
class IconProxy : public QIdentityProxyModel
|
class IconProxy : public QIdentityProxyModel {
|
||||||
{
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
|
||||||
IconProxy(QWidget* parentWidget) : QIdentityProxyModel(parentWidget)
|
IconProxy(QWidget* parentWidget) : QIdentityProxyModel(parentWidget)
|
||||||
{
|
{
|
||||||
connect(parentWidget, &QObject::destroyed, this, &IconProxy::widgetGone);
|
connect(parentWidget, &QObject::destroyed, this, &IconProxy::widgetGone);
|
||||||
@ -87,17 +84,12 @@ public:
|
|||||||
{
|
{
|
||||||
QVariant var = QIdentityProxyModel::data(proxyIndex, role);
|
QVariant var = QIdentityProxyModel::data(proxyIndex, role);
|
||||||
int column = proxyIndex.column();
|
int column = proxyIndex.column();
|
||||||
if(column == 0 && role == Qt::DecorationRole && m_parentWidget)
|
if (column == 0 && role == Qt::DecorationRole && m_parentWidget) {
|
||||||
{
|
if (!var.isNull()) {
|
||||||
if(!var.isNull())
|
|
||||||
{
|
|
||||||
auto string = var.toString();
|
auto string = var.toString();
|
||||||
if(string == "warning")
|
if (string == "warning") {
|
||||||
{
|
|
||||||
return APPLICATION->getThemedIcon("status-yellow");
|
return APPLICATION->getThemedIcon("status-yellow");
|
||||||
}
|
} else if (string == "error") {
|
||||||
else if(string == "error")
|
|
||||||
{
|
|
||||||
return APPLICATION->getThemedIcon("status-bad");
|
return APPLICATION->getThemedIcon("status-bad");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -106,10 +98,7 @@ public:
|
|||||||
return var;
|
return var;
|
||||||
}
|
}
|
||||||
private slots:
|
private slots:
|
||||||
void widgetGone()
|
void widgetGone() { m_parentWidget = nullptr; }
|
||||||
{
|
|
||||||
m_parentWidget = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QWidget* m_parentWidget = nullptr;
|
QWidget* m_parentWidget = nullptr;
|
||||||
@ -151,8 +140,7 @@ QMenu * VersionPage::createPopupMenu()
|
|||||||
return filteredMenu;
|
return filteredMenu;
|
||||||
}
|
}
|
||||||
|
|
||||||
VersionPage::VersionPage(MinecraftInstance *inst, QWidget *parent)
|
VersionPage::VersionPage(MinecraftInstance* inst, QWidget* parent) : QMainWindow(parent), ui(new Ui::VersionPage), m_inst(inst)
|
||||||
: QMainWindow(parent), ui(new Ui::VersionPage), m_inst(inst)
|
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
@ -182,10 +170,8 @@ VersionPage::VersionPage(MinecraftInstance *inst, QWidget *parent)
|
|||||||
connect(smodel, &QItemSelectionModel::currentChanged, this, &VersionPage::packageCurrent);
|
connect(smodel, &QItemSelectionModel::currentChanged, this, &VersionPage::packageCurrent);
|
||||||
|
|
||||||
connect(m_profile.get(), &PackProfile::minecraftChanged, this, &VersionPage::updateVersionControls);
|
connect(m_profile.get(), &PackProfile::minecraftChanged, this, &VersionPage::updateVersionControls);
|
||||||
controlsEnabled = !m_inst->isRunning();
|
|
||||||
updateVersionControls();
|
updateVersionControls();
|
||||||
preselect(0);
|
preselect(0);
|
||||||
connect(m_inst, &BaseInstance::runningStatusChanged, this, &VersionPage::updateRunningStatus);
|
|
||||||
connect(ui->packageView, &ModListView::customContextMenuRequested, this, &VersionPage::showContextMenu);
|
connect(ui->packageView, &ModListView::customContextMenuRequested, this, &VersionPage::showContextMenu);
|
||||||
connect(ui->filterEdit, &QLineEdit::textChanged, this, &VersionPage::onFilterTextChanged);
|
connect(ui->filterEdit, &QLineEdit::textChanged, this, &VersionPage::onFilterTextChanged);
|
||||||
}
|
}
|
||||||
@ -202,18 +188,17 @@ void VersionPage::showContextMenu(const QPoint& pos)
|
|||||||
delete menu;
|
delete menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void VersionPage::packageCurrent(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous)
|
void VersionPage::packageCurrent(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous)
|
||||||
{
|
{
|
||||||
if (!current.isValid())
|
if (!current.isValid()) {
|
||||||
{
|
|
||||||
ui->frame->clear();
|
ui->frame->clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int row = current.row();
|
int row = current.row();
|
||||||
auto patch = m_profile->getComponent(row);
|
auto patch = m_profile->getComponent(row);
|
||||||
auto severity = patch->getProblemSeverity();
|
auto severity = patch->getProblemSeverity();
|
||||||
switch(severity)
|
switch (severity) {
|
||||||
{
|
|
||||||
case ProblemSeverity::Warning:
|
case ProblemSeverity::Warning:
|
||||||
ui->frame->setName(tr("%1 possibly has issues.").arg(patch->getName()));
|
ui->frame->setName(tr("%1 possibly has issues.").arg(patch->getName()));
|
||||||
break;
|
break;
|
||||||
@ -228,14 +213,10 @@ void VersionPage::packageCurrent(const QModelIndex ¤t, [[maybe_unused]] co
|
|||||||
|
|
||||||
auto& problems = patch->getProblems();
|
auto& problems = patch->getProblems();
|
||||||
QString problemOut;
|
QString problemOut;
|
||||||
for (auto &problem: problems)
|
for (auto& problem : problems) {
|
||||||
{
|
if (problem.m_severity == ProblemSeverity::Error) {
|
||||||
if(problem.m_severity == ProblemSeverity::Error)
|
|
||||||
{
|
|
||||||
problemOut += tr("Error: ");
|
problemOut += tr("Error: ");
|
||||||
}
|
} else if (problem.m_severity == ProblemSeverity::Warning) {
|
||||||
else if(problem.m_severity == ProblemSeverity::Warning)
|
|
||||||
{
|
|
||||||
problemOut += tr("Warning: ");
|
problemOut += tr("Warning: ");
|
||||||
}
|
}
|
||||||
problemOut += problem.m_description;
|
problemOut += problem.m_description;
|
||||||
@ -244,29 +225,19 @@ void VersionPage::packageCurrent(const QModelIndex ¤t, [[maybe_unused]] co
|
|||||||
ui->frame->setDescription(problemOut);
|
ui->frame->setDescription(problemOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VersionPage::updateRunningStatus(bool running)
|
|
||||||
{
|
|
||||||
if(controlsEnabled == running) {
|
|
||||||
controlsEnabled = !running;
|
|
||||||
updateVersionControls();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void VersionPage::updateVersionControls()
|
void VersionPage::updateVersionControls()
|
||||||
{
|
{
|
||||||
// FIXME: this is a dirty hack
|
// FIXME: this is a dirty hack
|
||||||
auto minecraftVersion = Version(m_profile->getComponentVersion("net.minecraft"));
|
auto minecraftVersion = Version(m_profile->getComponentVersion("net.minecraft"));
|
||||||
|
|
||||||
ui->actionInstall_Forge->setEnabled(controlsEnabled);
|
|
||||||
|
|
||||||
bool supportsFabric = minecraftVersion >= Version("1.14");
|
bool supportsFabric = minecraftVersion >= Version("1.14");
|
||||||
ui->actionInstall_Fabric->setEnabled(controlsEnabled && supportsFabric);
|
ui->actionInstall_Fabric->setEnabled(supportsFabric);
|
||||||
|
|
||||||
bool supportsQuilt = minecraftVersion >= Version("1.14");
|
bool supportsQuilt = minecraftVersion >= Version("1.14");
|
||||||
ui->actionInstall_Quilt->setEnabled(controlsEnabled && supportsQuilt);
|
ui->actionInstall_Quilt->setEnabled(supportsQuilt);
|
||||||
|
|
||||||
bool supportsLiteLoader = minecraftVersion <= Version("1.12.2");
|
bool supportsLiteLoader = minecraftVersion <= Version("1.12.2");
|
||||||
ui->actionInstall_LiteLoader->setEnabled(controlsEnabled && supportsLiteLoader);
|
ui->actionInstall_LiteLoader->setEnabled(supportsLiteLoader);
|
||||||
|
|
||||||
updateButtons();
|
updateButtons();
|
||||||
}
|
}
|
||||||
@ -276,39 +247,25 @@ void VersionPage::updateButtons(int row)
|
|||||||
if (row == -1)
|
if (row == -1)
|
||||||
row = currentRow();
|
row = currentRow();
|
||||||
auto patch = m_profile->getComponent(row);
|
auto patch = m_profile->getComponent(row);
|
||||||
ui->actionRemove->setEnabled(controlsEnabled && patch && patch->isRemovable());
|
ui->actionRemove->setEnabled(patch && patch->isRemovable());
|
||||||
ui->actionMove_down->setEnabled(controlsEnabled && patch && patch->isMoveable());
|
ui->actionMove_down->setEnabled(patch && patch->isMoveable());
|
||||||
ui->actionMove_up->setEnabled(controlsEnabled && patch && patch->isMoveable());
|
ui->actionMove_up->setEnabled(patch && patch->isMoveable());
|
||||||
ui->actionChange_version->setEnabled(controlsEnabled && patch && patch->isVersionChangeable());
|
ui->actionChange_version->setEnabled(patch && patch->isVersionChangeable());
|
||||||
ui->actionEdit->setEnabled(controlsEnabled && patch && patch->isCustom());
|
ui->actionEdit->setEnabled(patch && patch->isCustom());
|
||||||
ui->actionCustomize->setEnabled(controlsEnabled && patch && patch->isCustomizable());
|
ui->actionCustomize->setEnabled(patch && patch->isCustomizable());
|
||||||
ui->actionRevert->setEnabled(controlsEnabled && patch && patch->isRevertible());
|
ui->actionRevert->setEnabled(patch && patch->isRevertible());
|
||||||
ui->actionDownload_All->setEnabled(controlsEnabled);
|
|
||||||
ui->actionAdd_Empty->setEnabled(controlsEnabled);
|
|
||||||
ui->actionImport_Components->setEnabled(controlsEnabled);
|
|
||||||
ui->actionReload->setEnabled(controlsEnabled);
|
|
||||||
ui->actionReplace_Minecraft_jar->setEnabled(controlsEnabled);
|
|
||||||
ui->actionAdd_to_Minecraft_jar->setEnabled(controlsEnabled);
|
|
||||||
ui->actionAdd_Agents->setEnabled(controlsEnabled);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VersionPage::reloadPackProfile()
|
bool VersionPage::reloadPackProfile()
|
||||||
{
|
{
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
m_profile->reload(Net::Mode::Online);
|
m_profile->reload(Net::Mode::Online);
|
||||||
return true;
|
return true;
|
||||||
}
|
} catch (const Exception& e) {
|
||||||
catch (const Exception &e)
|
|
||||||
{
|
|
||||||
QMessageBox::critical(this, tr("Error"), e.cause());
|
QMessageBox::critical(this, tr("Error"), e.cause());
|
||||||
return false;
|
return false;
|
||||||
}
|
} catch (...) {
|
||||||
catch (...)
|
QMessageBox::critical(this, tr("Error"), tr("Couldn't load the instance profile."));
|
||||||
{
|
|
||||||
QMessageBox::critical(
|
|
||||||
this, tr("Error"),
|
|
||||||
tr("Couldn't load the instance profile."));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -321,14 +278,12 @@ void VersionPage::on_actionReload_triggered()
|
|||||||
|
|
||||||
void VersionPage::on_actionRemove_triggered()
|
void VersionPage::on_actionRemove_triggered()
|
||||||
{
|
{
|
||||||
if (!ui->packageView->currentIndex().isValid())
|
if (!ui->packageView->currentIndex().isValid()) {
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int index = ui->packageView->currentIndex().row();
|
int index = ui->packageView->currentIndex().row();
|
||||||
auto component = m_profile->getComponent(index);
|
auto component = m_profile->getComponent(index);
|
||||||
if (component->isCustom())
|
if (component->isCustom()) {
|
||||||
{
|
|
||||||
auto response = CustomMessageBox::selectable(this, tr("Confirm Removal"),
|
auto response = CustomMessageBox::selectable(this, tr("Confirm Removal"),
|
||||||
tr("You are about to remove \"%1\".\n"
|
tr("You are about to remove \"%1\".\n"
|
||||||
"This is permanent and will completely remove the custom component.\n\n"
|
"This is permanent and will completely remove the custom component.\n\n"
|
||||||
@ -341,8 +296,7 @@ void VersionPage::on_actionRemove_triggered()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// FIXME: use actual model, not reloading.
|
// FIXME: use actual model, not reloading.
|
||||||
if (!m_profile->remove(index))
|
if (!m_profile->remove(index)) {
|
||||||
{
|
|
||||||
QMessageBox::critical(this, tr("Error"), tr("Couldn't remove file"));
|
QMessageBox::critical(this, tr("Error"), tr("Couldn't remove file"));
|
||||||
}
|
}
|
||||||
updateButtons();
|
updateButtons();
|
||||||
@ -352,17 +306,16 @@ void VersionPage::on_actionRemove_triggered()
|
|||||||
|
|
||||||
void VersionPage::on_actionInstall_mods_triggered()
|
void VersionPage::on_actionInstall_mods_triggered()
|
||||||
{
|
{
|
||||||
if(m_container)
|
if (m_container) {
|
||||||
{
|
|
||||||
m_container->selectPage("mods");
|
m_container->selectPage("mods");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VersionPage::on_actionAdd_to_Minecraft_jar_triggered()
|
void VersionPage::on_actionAdd_to_Minecraft_jar_triggered()
|
||||||
{
|
{
|
||||||
auto list = GuiUtil::BrowseForFiles("jarmod", tr("Select jar mods"), tr("Minecraft.jar mods (*.zip *.jar)"), APPLICATION->settings()->get("CentralModsDir").toString(), this->parentWidget());
|
auto list = GuiUtil::BrowseForFiles("jarmod", tr("Select jar mods"), tr("Minecraft.jar mods (*.zip *.jar)"),
|
||||||
if(!list.empty())
|
APPLICATION->settings()->get("CentralModsDir").toString(), this->parentWidget());
|
||||||
{
|
if (!list.empty()) {
|
||||||
m_profile->installJarMods(list);
|
m_profile->installJarMods(list);
|
||||||
}
|
}
|
||||||
updateButtons();
|
updateButtons();
|
||||||
@ -370,9 +323,9 @@ void VersionPage::on_actionAdd_to_Minecraft_jar_triggered()
|
|||||||
|
|
||||||
void VersionPage::on_actionReplace_Minecraft_jar_triggered()
|
void VersionPage::on_actionReplace_Minecraft_jar_triggered()
|
||||||
{
|
{
|
||||||
auto jarPath = GuiUtil::BrowseForFile("jar", tr("Select jar"), tr("Minecraft.jar replacement (*.jar)"), APPLICATION->settings()->get("CentralModsDir").toString(), this->parentWidget());
|
auto jarPath = GuiUtil::BrowseForFile("jar", tr("Select jar"), tr("Minecraft.jar replacement (*.jar)"),
|
||||||
if(!jarPath.isEmpty())
|
APPLICATION->settings()->get("CentralModsDir").toString(), this->parentWidget());
|
||||||
{
|
if (!jarPath.isEmpty()) {
|
||||||
m_profile->installCustomJar(jarPath);
|
m_profile->installCustomJar(jarPath);
|
||||||
}
|
}
|
||||||
updateButtons();
|
updateButtons();
|
||||||
@ -406,12 +359,9 @@ void VersionPage::on_actionAdd_Agents_triggered()
|
|||||||
|
|
||||||
void VersionPage::on_actionMove_up_triggered()
|
void VersionPage::on_actionMove_up_triggered()
|
||||||
{
|
{
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
m_profile->move(currentRow(), PackProfile::MoveUp);
|
m_profile->move(currentRow(), PackProfile::MoveUp);
|
||||||
}
|
} catch (const Exception& e) {
|
||||||
catch (const Exception &e)
|
|
||||||
{
|
|
||||||
QMessageBox::critical(this, tr("Error"), e.cause());
|
QMessageBox::critical(this, tr("Error"), e.cause());
|
||||||
}
|
}
|
||||||
updateButtons();
|
updateButtons();
|
||||||
@ -419,12 +369,9 @@ void VersionPage::on_actionMove_up_triggered()
|
|||||||
|
|
||||||
void VersionPage::on_actionMove_down_triggered()
|
void VersionPage::on_actionMove_down_triggered()
|
||||||
{
|
{
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
m_profile->move(currentRow(), PackProfile::MoveDown);
|
m_profile->move(currentRow(), PackProfile::MoveDown);
|
||||||
}
|
} catch (const Exception& e) {
|
||||||
catch (const Exception &e)
|
|
||||||
{
|
|
||||||
QMessageBox::critical(this, tr("Error"), e.cause());
|
QMessageBox::critical(this, tr("Error"), e.cause());
|
||||||
}
|
}
|
||||||
updateButtons();
|
updateButtons();
|
||||||
@ -433,39 +380,32 @@ void VersionPage::on_actionMove_down_triggered()
|
|||||||
void VersionPage::on_actionChange_version_triggered()
|
void VersionPage::on_actionChange_version_triggered()
|
||||||
{
|
{
|
||||||
auto versionRow = currentRow();
|
auto versionRow = currentRow();
|
||||||
if(versionRow == -1)
|
if (versionRow == -1) {
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto patch = m_profile->getComponent(versionRow);
|
auto patch = m_profile->getComponent(versionRow);
|
||||||
auto name = patch->getName();
|
auto name = patch->getName();
|
||||||
auto list = patch->getVersionList();
|
auto list = patch->getVersionList();
|
||||||
if(!list)
|
if (!list) {
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto uid = list->uid();
|
auto uid = list->uid();
|
||||||
// FIXME: this is a horrible HACK. Get version filtering information from the actual metadata...
|
// FIXME: this is a horrible HACK. Get version filtering information from the actual metadata...
|
||||||
if(uid == "net.minecraftforge")
|
if (uid == "net.minecraftforge") {
|
||||||
{
|
|
||||||
on_actionInstall_Forge_triggered();
|
on_actionInstall_Forge_triggered();
|
||||||
return;
|
return;
|
||||||
}
|
} else if (uid == "com.mumfrey.liteloader") {
|
||||||
else if (uid == "com.mumfrey.liteloader")
|
|
||||||
{
|
|
||||||
on_actionInstall_LiteLoader_triggered();
|
on_actionInstall_LiteLoader_triggered();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
VersionSelectDialog vselect(list.get(), tr("Change %1 version").arg(name), this);
|
VersionSelectDialog vselect(list.get(), tr("Change %1 version").arg(name), this);
|
||||||
if (uid == "net.fabricmc.intermediary" || uid == "org.quiltmc.hashed")
|
if (uid == "net.fabricmc.intermediary" || uid == "org.quiltmc.hashed") {
|
||||||
{
|
|
||||||
vselect.setEmptyString(tr("No intermediary mappings versions are currently available."));
|
vselect.setEmptyString(tr("No intermediary mappings versions are currently available."));
|
||||||
vselect.setEmptyErrorString(tr("Couldn't load or download the intermediary mappings version lists!"));
|
vselect.setEmptyErrorString(tr("Couldn't load or download the intermediary mappings version lists!"));
|
||||||
vselect.setExactFilter(BaseVersionList::ParentVersionRole, m_profile->getComponentVersion("net.minecraft"));
|
vselect.setExactFilter(BaseVersionList::ParentVersionRole, m_profile->getComponentVersion("net.minecraft"));
|
||||||
}
|
}
|
||||||
auto currentVersion = patch->getVersion();
|
auto currentVersion = patch->getVersion();
|
||||||
if(!currentVersion.isEmpty())
|
if (!currentVersion.isEmpty()) {
|
||||||
{
|
|
||||||
vselect.setCurrentVersion(currentVersion);
|
vselect.setCurrentVersion(currentVersion);
|
||||||
}
|
}
|
||||||
if (!vselect.exec() || !vselect.selectedVersion())
|
if (!vselect.exec() || !vselect.selectedVersion())
|
||||||
@ -473,8 +413,7 @@ void VersionPage::on_actionChange_version_triggered()
|
|||||||
|
|
||||||
qDebug() << "Change" << uid << "to" << vselect.selectedVersion()->descriptor();
|
qDebug() << "Change" << uid << "to" << vselect.selectedVersion()->descriptor();
|
||||||
bool important = false;
|
bool important = false;
|
||||||
if(uid == "net.minecraft")
|
if (uid == "net.minecraft") {
|
||||||
{
|
|
||||||
important = true;
|
important = true;
|
||||||
}
|
}
|
||||||
m_profile->setComponentVersion(uid, vselect.selectedVersion()->descriptor(), important);
|
m_profile->setComponentVersion(uid, vselect.selectedVersion()->descriptor(), important);
|
||||||
@ -484,19 +423,17 @@ void VersionPage::on_actionChange_version_triggered()
|
|||||||
|
|
||||||
void VersionPage::on_actionDownload_All_triggered()
|
void VersionPage::on_actionDownload_All_triggered()
|
||||||
{
|
{
|
||||||
if (!APPLICATION->accounts()->anyAccountIsValid())
|
if (!APPLICATION->accounts()->anyAccountIsValid()) {
|
||||||
{
|
CustomMessageBox::selectable(this, tr("Error"),
|
||||||
CustomMessageBox::selectable(
|
|
||||||
this, tr("Error"),
|
|
||||||
tr("Cannot download Minecraft or update instances unless you have at least "
|
tr("Cannot download Minecraft or update instances unless you have at least "
|
||||||
"one account added.\nPlease add your Mojang or Minecraft account."),
|
"one account added.\nPlease add your Mojang or Minecraft account."),
|
||||||
QMessageBox::Warning)->show();
|
QMessageBox::Warning)
|
||||||
|
->show();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto updateTask = m_inst->createUpdateTask(Net::Mode::Online);
|
auto updateTask = m_inst->createUpdateTask(Net::Mode::Online);
|
||||||
if (!updateTask)
|
if (!updateTask) {
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ProgressDialog tDialog(this);
|
ProgressDialog tDialog(this);
|
||||||
@ -510,23 +447,21 @@ void VersionPage::on_actionDownload_All_triggered()
|
|||||||
void VersionPage::on_actionInstall_Forge_triggered()
|
void VersionPage::on_actionInstall_Forge_triggered()
|
||||||
{
|
{
|
||||||
auto vlist = APPLICATION->metadataIndex()->get("net.minecraftforge");
|
auto vlist = APPLICATION->metadataIndex()->get("net.minecraftforge");
|
||||||
if(!vlist)
|
if (!vlist) {
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
VersionSelectDialog vselect(vlist.get(), tr("Select Forge version"), this);
|
VersionSelectDialog vselect(vlist.get(), tr("Select Forge version"), this);
|
||||||
vselect.setExactFilter(BaseVersionList::ParentVersionRole, m_profile->getComponentVersion("net.minecraft"));
|
vselect.setExactFilter(BaseVersionList::ParentVersionRole, m_profile->getComponentVersion("net.minecraft"));
|
||||||
vselect.setEmptyString(tr("No Forge versions are currently available for Minecraft ") + m_profile->getComponentVersion("net.minecraft"));
|
vselect.setEmptyString(tr("No Forge versions are currently available for Minecraft ") +
|
||||||
|
m_profile->getComponentVersion("net.minecraft"));
|
||||||
vselect.setEmptyErrorString(tr("Couldn't load or download the Forge version lists!"));
|
vselect.setEmptyErrorString(tr("Couldn't load or download the Forge version lists!"));
|
||||||
|
|
||||||
auto currentVersion = m_profile->getComponentVersion("net.minecraftforge");
|
auto currentVersion = m_profile->getComponentVersion("net.minecraftforge");
|
||||||
if(!currentVersion.isEmpty())
|
if (!currentVersion.isEmpty()) {
|
||||||
{
|
|
||||||
vselect.setCurrentVersion(currentVersion);
|
vselect.setCurrentVersion(currentVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vselect.exec() && vselect.selectedVersion())
|
if (vselect.exec() && vselect.selectedVersion()) {
|
||||||
{
|
|
||||||
auto vsn = vselect.selectedVersion();
|
auto vsn = vselect.selectedVersion();
|
||||||
m_profile->setComponentVersion("net.minecraftforge", vsn->descriptor());
|
m_profile->setComponentVersion("net.minecraftforge", vsn->descriptor());
|
||||||
m_profile->resolve(Net::Mode::Online);
|
m_profile->resolve(Net::Mode::Online);
|
||||||
@ -539,8 +474,7 @@ void VersionPage::on_actionInstall_Forge_triggered()
|
|||||||
void VersionPage::on_actionInstall_Fabric_triggered()
|
void VersionPage::on_actionInstall_Fabric_triggered()
|
||||||
{
|
{
|
||||||
auto vlist = APPLICATION->metadataIndex()->get("net.fabricmc.fabric-loader");
|
auto vlist = APPLICATION->metadataIndex()->get("net.fabricmc.fabric-loader");
|
||||||
if(!vlist)
|
if (!vlist) {
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
VersionSelectDialog vselect(vlist.get(), tr("Select Fabric Loader version"), this);
|
VersionSelectDialog vselect(vlist.get(), tr("Select Fabric Loader version"), this);
|
||||||
@ -548,13 +482,11 @@ void VersionPage::on_actionInstall_Fabric_triggered()
|
|||||||
vselect.setEmptyErrorString(tr("Couldn't load or download the Fabric Loader version lists!"));
|
vselect.setEmptyErrorString(tr("Couldn't load or download the Fabric Loader version lists!"));
|
||||||
|
|
||||||
auto currentVersion = m_profile->getComponentVersion("net.fabricmc.fabric-loader");
|
auto currentVersion = m_profile->getComponentVersion("net.fabricmc.fabric-loader");
|
||||||
if(!currentVersion.isEmpty())
|
if (!currentVersion.isEmpty()) {
|
||||||
{
|
|
||||||
vselect.setCurrentVersion(currentVersion);
|
vselect.setCurrentVersion(currentVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vselect.exec() && vselect.selectedVersion())
|
if (vselect.exec() && vselect.selectedVersion()) {
|
||||||
{
|
|
||||||
auto vsn = vselect.selectedVersion();
|
auto vsn = vselect.selectedVersion();
|
||||||
m_profile->setComponentVersion("net.fabricmc.fabric-loader", vsn->descriptor());
|
m_profile->setComponentVersion("net.fabricmc.fabric-loader", vsn->descriptor());
|
||||||
m_profile->resolve(Net::Mode::Online);
|
m_profile->resolve(Net::Mode::Online);
|
||||||
@ -566,8 +498,7 @@ void VersionPage::on_actionInstall_Fabric_triggered()
|
|||||||
void VersionPage::on_actionInstall_Quilt_triggered()
|
void VersionPage::on_actionInstall_Quilt_triggered()
|
||||||
{
|
{
|
||||||
auto vlist = APPLICATION->metadataIndex()->get("org.quiltmc.quilt-loader");
|
auto vlist = APPLICATION->metadataIndex()->get("org.quiltmc.quilt-loader");
|
||||||
if(!vlist)
|
if (!vlist) {
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
VersionSelectDialog vselect(vlist.get(), tr("Select Quilt Loader version"), this);
|
VersionSelectDialog vselect(vlist.get(), tr("Select Quilt Loader version"), this);
|
||||||
@ -575,13 +506,11 @@ void VersionPage::on_actionInstall_Quilt_triggered()
|
|||||||
vselect.setEmptyErrorString(tr("Couldn't load or download the Quilt Loader version lists!"));
|
vselect.setEmptyErrorString(tr("Couldn't load or download the Quilt Loader version lists!"));
|
||||||
|
|
||||||
auto currentVersion = m_profile->getComponentVersion("org.quiltmc.quilt-loader");
|
auto currentVersion = m_profile->getComponentVersion("org.quiltmc.quilt-loader");
|
||||||
if(!currentVersion.isEmpty())
|
if (!currentVersion.isEmpty()) {
|
||||||
{
|
|
||||||
vselect.setCurrentVersion(currentVersion);
|
vselect.setCurrentVersion(currentVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vselect.exec() && vselect.selectedVersion())
|
if (vselect.exec() && vselect.selectedVersion()) {
|
||||||
{
|
|
||||||
auto vsn = vselect.selectedVersion();
|
auto vsn = vselect.selectedVersion();
|
||||||
m_profile->setComponentVersion("org.quiltmc.quilt-loader", vsn->descriptor());
|
m_profile->setComponentVersion("org.quiltmc.quilt-loader", vsn->descriptor());
|
||||||
m_profile->resolve(Net::Mode::Online);
|
m_profile->resolve(Net::Mode::Online);
|
||||||
@ -594,14 +523,12 @@ void VersionPage::on_actionAdd_Empty_triggered()
|
|||||||
{
|
{
|
||||||
NewComponentDialog compdialog(QString(), QString(), this);
|
NewComponentDialog compdialog(QString(), QString(), this);
|
||||||
QStringList blacklist;
|
QStringList blacklist;
|
||||||
for(int i = 0; i < m_profile->rowCount(); i++)
|
for (int i = 0; i < m_profile->rowCount(); i++) {
|
||||||
{
|
|
||||||
auto comp = m_profile->getComponent(i);
|
auto comp = m_profile->getComponent(i);
|
||||||
blacklist.push_back(comp->getID());
|
blacklist.push_back(comp->getID());
|
||||||
}
|
}
|
||||||
compdialog.setBlacklist(blacklist);
|
compdialog.setBlacklist(blacklist);
|
||||||
if (compdialog.exec())
|
if (compdialog.exec()) {
|
||||||
{
|
|
||||||
qDebug() << "name:" << compdialog.name();
|
qDebug() << "name:" << compdialog.name();
|
||||||
qDebug() << "uid:" << compdialog.uid();
|
qDebug() << "uid:" << compdialog.uid();
|
||||||
m_profile->installEmpty(compdialog.uid(), compdialog.name());
|
m_profile->installEmpty(compdialog.uid(), compdialog.name());
|
||||||
@ -611,23 +538,21 @@ void VersionPage::on_actionAdd_Empty_triggered()
|
|||||||
void VersionPage::on_actionInstall_LiteLoader_triggered()
|
void VersionPage::on_actionInstall_LiteLoader_triggered()
|
||||||
{
|
{
|
||||||
auto vlist = APPLICATION->metadataIndex()->get("com.mumfrey.liteloader");
|
auto vlist = APPLICATION->metadataIndex()->get("com.mumfrey.liteloader");
|
||||||
if(!vlist)
|
if (!vlist) {
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
VersionSelectDialog vselect(vlist.get(), tr("Select LiteLoader version"), this);
|
VersionSelectDialog vselect(vlist.get(), tr("Select LiteLoader version"), this);
|
||||||
vselect.setExactFilter(BaseVersionList::ParentVersionRole, m_profile->getComponentVersion("net.minecraft"));
|
vselect.setExactFilter(BaseVersionList::ParentVersionRole, m_profile->getComponentVersion("net.minecraft"));
|
||||||
vselect.setEmptyString(tr("No LiteLoader versions are currently available for Minecraft ") + m_profile->getComponentVersion("net.minecraft"));
|
vselect.setEmptyString(tr("No LiteLoader versions are currently available for Minecraft ") +
|
||||||
|
m_profile->getComponentVersion("net.minecraft"));
|
||||||
vselect.setEmptyErrorString(tr("Couldn't load or download the LiteLoader version lists!"));
|
vselect.setEmptyErrorString(tr("Couldn't load or download the LiteLoader version lists!"));
|
||||||
|
|
||||||
auto currentVersion = m_profile->getComponentVersion("com.mumfrey.liteloader");
|
auto currentVersion = m_profile->getComponentVersion("com.mumfrey.liteloader");
|
||||||
if(!currentVersion.isEmpty())
|
if (!currentVersion.isEmpty()) {
|
||||||
{
|
|
||||||
vselect.setCurrentVersion(currentVersion);
|
vselect.setCurrentVersion(currentVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vselect.exec() && vselect.selectedVersion())
|
if (vselect.exec() && vselect.selectedVersion()) {
|
||||||
{
|
|
||||||
auto vsn = vselect.selectedVersion();
|
auto vsn = vselect.selectedVersion();
|
||||||
m_profile->setComponentVersion("com.mumfrey.liteloader", vsn->descriptor());
|
m_profile->setComponentVersion("com.mumfrey.liteloader", vsn->descriptor());
|
||||||
m_profile->resolve(Net::Mode::Online);
|
m_profile->resolve(Net::Mode::Online);
|
||||||
@ -655,16 +580,13 @@ void VersionPage::versionCurrent(const QModelIndex ¤t, [[maybe_unused]] co
|
|||||||
|
|
||||||
void VersionPage::preselect(int row)
|
void VersionPage::preselect(int row)
|
||||||
{
|
{
|
||||||
if(row < 0)
|
if (row < 0) {
|
||||||
{
|
|
||||||
row = 0;
|
row = 0;
|
||||||
}
|
}
|
||||||
if(row >= m_profile->rowCount(QModelIndex()))
|
if (row >= m_profile->rowCount(QModelIndex())) {
|
||||||
{
|
|
||||||
row = m_profile->rowCount(QModelIndex()) - 1;
|
row = m_profile->rowCount(QModelIndex()) - 1;
|
||||||
}
|
}
|
||||||
if(row < 0)
|
if (row < 0) {
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto model_index = m_profile->index(row);
|
auto model_index = m_profile->index(row);
|
||||||
@ -680,8 +602,7 @@ void VersionPage::onGameUpdateError(QString error)
|
|||||||
ComponentPtr VersionPage::current()
|
ComponentPtr VersionPage::current()
|
||||||
{
|
{
|
||||||
auto row = currentRow();
|
auto row = currentRow();
|
||||||
if(row < 0)
|
if (row < 0) {
|
||||||
{
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return m_profile->getComponent(row);
|
return m_profile->getComponent(row);
|
||||||
@ -689,8 +610,7 @@ ComponentPtr VersionPage::current()
|
|||||||
|
|
||||||
int VersionPage::currentRow()
|
int VersionPage::currentRow()
|
||||||
{
|
{
|
||||||
if (ui->packageView->selectionModel()->selectedRows().isEmpty())
|
if (ui->packageView->selectionModel()->selectedRows().isEmpty()) {
|
||||||
{
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return ui->packageView->selectionModel()->selectedRows().first().row();
|
return ui->packageView->selectionModel()->selectedRows().first().row();
|
||||||
@ -699,18 +619,15 @@ int VersionPage::currentRow()
|
|||||||
void VersionPage::on_actionCustomize_triggered()
|
void VersionPage::on_actionCustomize_triggered()
|
||||||
{
|
{
|
||||||
auto version = currentRow();
|
auto version = currentRow();
|
||||||
if(version == -1)
|
if (version == -1) {
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto patch = m_profile->getComponent(version);
|
auto patch = m_profile->getComponent(version);
|
||||||
if(!patch->getVersionFile())
|
if (!patch->getVersionFile()) {
|
||||||
{
|
|
||||||
// TODO: wait for the update task to finish here...
|
// TODO: wait for the update task to finish here...
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(!m_profile->customize(version))
|
if (!m_profile->customize(version)) {
|
||||||
{
|
|
||||||
// TODO: some error box here
|
// TODO: some error box here
|
||||||
}
|
}
|
||||||
updateButtons();
|
updateButtons();
|
||||||
@ -720,13 +637,11 @@ void VersionPage::on_actionCustomize_triggered()
|
|||||||
void VersionPage::on_actionEdit_triggered()
|
void VersionPage::on_actionEdit_triggered()
|
||||||
{
|
{
|
||||||
auto version = current();
|
auto version = current();
|
||||||
if(!version)
|
if (!version) {
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto filename = version->getFilename();
|
auto filename = version->getFilename();
|
||||||
if(!QFileInfo::exists(filename))
|
if (!QFileInfo::exists(filename)) {
|
||||||
{
|
|
||||||
qWarning() << "file" << filename << "can't be opened for editing, doesn't exist!";
|
qWarning() << "file" << filename << "can't be opened for editing, doesn't exist!";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -736,8 +651,7 @@ void VersionPage::on_actionEdit_triggered()
|
|||||||
void VersionPage::on_actionRevert_triggered()
|
void VersionPage::on_actionRevert_triggered()
|
||||||
{
|
{
|
||||||
auto version = currentRow();
|
auto version = currentRow();
|
||||||
if(version == -1)
|
if (version == -1) {
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto component = m_profile->getComponent(version);
|
auto component = m_profile->getComponent(version);
|
||||||
@ -753,8 +667,7 @@ void VersionPage::on_actionRevert_triggered()
|
|||||||
if (response != QMessageBox::Yes)
|
if (response != QMessageBox::Yes)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(!m_profile->revertToBase(version))
|
if (!m_profile->revertToBase(version)) {
|
||||||
{
|
|
||||||
// TODO: some error box here
|
// TODO: some error box here
|
||||||
}
|
}
|
||||||
updateButtons();
|
updateButtons();
|
||||||
|
@ -46,31 +46,20 @@
|
|||||||
#include "minecraft/PackProfile.h"
|
#include "minecraft/PackProfile.h"
|
||||||
#include "ui/pages/BasePage.h"
|
#include "ui/pages/BasePage.h"
|
||||||
|
|
||||||
namespace Ui
|
namespace Ui {
|
||||||
{
|
|
||||||
class VersionPage;
|
class VersionPage;
|
||||||
}
|
}
|
||||||
|
|
||||||
class VersionPage : public QMainWindow, public BasePage
|
class VersionPage : public QMainWindow, public BasePage {
|
||||||
{
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit VersionPage(MinecraftInstance* inst, QWidget* parent = 0);
|
explicit VersionPage(MinecraftInstance* inst, QWidget* parent = 0);
|
||||||
virtual ~VersionPage();
|
virtual ~VersionPage();
|
||||||
virtual QString displayName() const override
|
virtual QString displayName() const override { return tr("Version"); }
|
||||||
{
|
|
||||||
return tr("Version");
|
|
||||||
}
|
|
||||||
virtual QIcon icon() const override;
|
virtual QIcon icon() const override;
|
||||||
virtual QString id() const override
|
virtual QString id() const override { return "version"; }
|
||||||
{
|
virtual QString helpPage() const override { return "Instance-Version"; }
|
||||||
return "version";
|
|
||||||
}
|
|
||||||
virtual QString helpPage() const override
|
|
||||||
{
|
|
||||||
return "Instance-Version";
|
|
||||||
}
|
|
||||||
virtual bool shouldDisplay() const override;
|
virtual bool shouldDisplay() const override;
|
||||||
void retranslate() override;
|
void retranslate() override;
|
||||||
|
|
||||||
@ -122,7 +111,6 @@ private:
|
|||||||
std::shared_ptr<PackProfile> m_profile;
|
std::shared_ptr<PackProfile> m_profile;
|
||||||
MinecraftInstance* m_inst;
|
MinecraftInstance* m_inst;
|
||||||
int currentIdx = 0;
|
int currentIdx = 0;
|
||||||
bool controlsEnabled = false;
|
|
||||||
|
|
||||||
std::shared_ptr<Setting> m_wide_bar_setting = nullptr;
|
std::shared_ptr<Setting> m_wide_bar_setting = nullptr;
|
||||||
|
|
||||||
@ -130,7 +118,6 @@ public slots:
|
|||||||
void versionCurrent(const QModelIndex& current, const QModelIndex& previous);
|
void versionCurrent(const QModelIndex& current, const QModelIndex& previous);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void updateRunningStatus(bool running);
|
|
||||||
void onGameUpdateError(QString error);
|
void onGameUpdateError(QString error);
|
||||||
void packageCurrent(const QModelIndex& current, const QModelIndex& previous);
|
void packageCurrent(const QModelIndex& current, const QModelIndex& previous);
|
||||||
void showContextMenu(const QPoint& pos);
|
void showContextMenu(const QPoint& pos);
|
||||||
|
@ -339,6 +339,7 @@ void WorldListPage::mceditState(LoggedProcess::State state)
|
|||||||
{
|
{
|
||||||
failed = true;
|
failed = true;
|
||||||
}
|
}
|
||||||
|
/* fallthrough */
|
||||||
case LoggedProcess::Running:
|
case LoggedProcess::Running:
|
||||||
case LoggedProcess::Finished:
|
case LoggedProcess::Finished:
|
||||||
{
|
{
|
||||||
|
@ -69,6 +69,7 @@ bool JavaWizardPage::validatePage()
|
|||||||
case JavaSettingsWidget::ValidationStatus::AllOK:
|
case JavaSettingsWidget::ValidationStatus::AllOK:
|
||||||
{
|
{
|
||||||
settings->set("JavaPath", m_java_widget->javaPath());
|
settings->set("JavaPath", m_java_widget->javaPath());
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
case JavaSettingsWidget::ValidationStatus::JavaBad:
|
case JavaSettingsWidget::ValidationStatus::JavaBad:
|
||||||
{
|
{
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* PolyMC - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
||||||
|
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -33,22 +34,37 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <QLabel>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
#include <QToolTip>
|
||||||
|
|
||||||
#include "InfoFrame.h"
|
#include "InfoFrame.h"
|
||||||
#include "ui_InfoFrame.h"
|
#include "ui_InfoFrame.h"
|
||||||
|
|
||||||
#include "ui/dialogs/CustomMessageBox.h"
|
#include "ui/dialogs/CustomMessageBox.h"
|
||||||
|
|
||||||
InfoFrame::InfoFrame(QWidget *parent) :
|
void setupLinkToolTip(QLabel* label)
|
||||||
QFrame(parent),
|
{
|
||||||
ui(new Ui::InfoFrame)
|
QObject::connect(label, &QLabel::linkHovered, [label](const QString& link) {
|
||||||
|
if (auto url = QUrl(link); !url.isValid() || (url.scheme() != "http" && url.scheme() != "https"))
|
||||||
|
return;
|
||||||
|
label->setToolTip(link);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
InfoFrame::InfoFrame(QWidget* parent) : QFrame(parent), ui(new Ui::InfoFrame)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
ui->descriptionLabel->setHidden(true);
|
ui->descriptionLabel->setHidden(true);
|
||||||
ui->nameLabel->setHidden(true);
|
ui->nameLabel->setHidden(true);
|
||||||
ui->licenseLabel->setHidden(true);
|
ui->licenseLabel->setHidden(true);
|
||||||
ui->issueTrackerLabel->setHidden(true);
|
ui->issueTrackerLabel->setHidden(true);
|
||||||
|
|
||||||
|
setupLinkToolTip(ui->iconLabel);
|
||||||
|
setupLinkToolTip(ui->descriptionLabel);
|
||||||
|
setupLinkToolTip(ui->nameLabel);
|
||||||
|
setupLinkToolTip(ui->licenseLabel);
|
||||||
|
setupLinkToolTip(ui->issueTrackerLabel);
|
||||||
updateHiddenState();
|
updateHiddenState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,34 +75,32 @@ InfoFrame::~InfoFrame()
|
|||||||
|
|
||||||
void InfoFrame::updateWithMod(Mod const& m)
|
void InfoFrame::updateWithMod(Mod const& m)
|
||||||
{
|
{
|
||||||
if (m.type() == ResourceType::FOLDER)
|
if (m.type() == ResourceType::FOLDER) {
|
||||||
{
|
|
||||||
clear();
|
clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString text = "";
|
QString text = "";
|
||||||
QString name = "";
|
QString name = "";
|
||||||
|
QString link = m.metaurl();
|
||||||
if (m.name().isEmpty())
|
if (m.name().isEmpty())
|
||||||
name = m.internal_id();
|
name = m.internal_id();
|
||||||
else
|
else
|
||||||
name = m.name();
|
name = m.name();
|
||||||
|
|
||||||
if (m.homeurl().isEmpty())
|
if (link.isEmpty())
|
||||||
text = name;
|
text = name;
|
||||||
else
|
else {
|
||||||
text = "<a href=\"" + m.homeurl() + "\">" + name + "</a>";
|
text = "<a href=\"" + link + "\">" + name + "</a>";
|
||||||
|
}
|
||||||
if (!m.authors().isEmpty())
|
if (!m.authors().isEmpty())
|
||||||
text += " by " + m.authors().join(", ");
|
text += " by " + m.authors().join(", ");
|
||||||
|
|
||||||
setName(text);
|
setName(text);
|
||||||
|
|
||||||
if (m.description().isEmpty())
|
if (m.description().isEmpty()) {
|
||||||
{
|
|
||||||
setDescription(QString());
|
setDescription(QString());
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
setDescription(m.description());
|
setDescription(m.description());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,7 +147,8 @@ void InfoFrame::updateWithResource(const Resource& resource)
|
|||||||
setImage();
|
setImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString InfoFrame::renderColorCodes(QString input) {
|
QString InfoFrame::renderColorCodes(QString input)
|
||||||
|
{
|
||||||
// We have to manually set the colors for use.
|
// We have to manually set the colors for use.
|
||||||
//
|
//
|
||||||
// A color is set using §x, with x = a hex number from 0 to f.
|
// A color is set using §x, with x = a hex number from 0 to f.
|
||||||
@ -144,16 +159,12 @@ QString InfoFrame::renderColorCodes(QString input) {
|
|||||||
// TODO: Wrap links inside <a> tags
|
// TODO: Wrap links inside <a> tags
|
||||||
|
|
||||||
// https://minecraft.fandom.com/wiki/Formatting_codes#Color_codes
|
// https://minecraft.fandom.com/wiki/Formatting_codes#Color_codes
|
||||||
const QMap<QChar, QString> color_codes_map = {
|
const QMap<QChar, QString> color_codes_map = { { '0', "#000000" }, { '1', "#0000AA" }, { '2', "#00AA00" }, { '3', "#00AAAA" },
|
||||||
{'0', "#000000"}, {'1', "#0000AA"}, {'2', "#00AA00"}, {'3', "#00AAAA"}, {'4', "#AA0000"},
|
{ '4', "#AA0000" }, { '5', "#AA00AA" }, { '6', "#FFAA00" }, { '7', "#AAAAAA" },
|
||||||
{'5', "#AA00AA"}, {'6', "#FFAA00"}, {'7', "#AAAAAA"}, {'8', "#555555"}, {'9', "#5555FF"},
|
{ '8', "#555555" }, { '9', "#5555FF" }, { 'a', "#55FF55" }, { 'b', "#55FFFF" },
|
||||||
{'a', "#55FF55"}, {'b', "#55FFFF"}, {'c', "#FF5555"}, {'d', "#FF55FF"}, {'e', "#FFFF55"},
|
{ 'c', "#FF5555" }, { 'd', "#FF55FF" }, { 'e', "#FFFF55" }, { 'f', "#FFFFFF" } };
|
||||||
{'f', "#FFFFFF"}
|
|
||||||
};
|
|
||||||
// https://minecraft.fandom.com/wiki/Formatting_codes#Formatting_codes
|
// https://minecraft.fandom.com/wiki/Formatting_codes#Formatting_codes
|
||||||
const QMap<QChar, QString> formatting_codes_map = {
|
const QMap<QChar, QString> formatting_codes_map = { { 'l', "b" }, { 'm', "s" }, { 'n', "u" }, { 'o', "i" } };
|
||||||
{'l', "b"}, {'m', "s"}, {'n', "u"}, {'o', "i"}
|
|
||||||
};
|
|
||||||
|
|
||||||
QString html("<html>");
|
QString html("<html>");
|
||||||
QList<QString> tags{};
|
QList<QString> tags{};
|
||||||
@ -229,12 +240,9 @@ void InfoFrame::updateHiddenState()
|
|||||||
|
|
||||||
void InfoFrame::setName(QString text)
|
void InfoFrame::setName(QString text)
|
||||||
{
|
{
|
||||||
if(text.isEmpty())
|
if (text.isEmpty()) {
|
||||||
{
|
|
||||||
ui->nameLabel->setHidden(true);
|
ui->nameLabel->setHidden(true);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
ui->nameLabel->setText(text);
|
ui->nameLabel->setText(text);
|
||||||
ui->nameLabel->setHidden(false);
|
ui->nameLabel->setHidden(false);
|
||||||
}
|
}
|
||||||
@ -243,14 +251,11 @@ void InfoFrame::setName(QString text)
|
|||||||
|
|
||||||
void InfoFrame::setDescription(QString text)
|
void InfoFrame::setDescription(QString text)
|
||||||
{
|
{
|
||||||
if(text.isEmpty())
|
if (text.isEmpty()) {
|
||||||
{
|
|
||||||
ui->descriptionLabel->setHidden(true);
|
ui->descriptionLabel->setHidden(true);
|
||||||
updateHiddenState();
|
updateHiddenState();
|
||||||
return;
|
return;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
ui->descriptionLabel->setHidden(false);
|
ui->descriptionLabel->setHidden(false);
|
||||||
updateHiddenState();
|
updateHiddenState();
|
||||||
}
|
}
|
||||||
@ -260,8 +265,7 @@ void InfoFrame::setDescription(QString text)
|
|||||||
QChar rem('\n');
|
QChar rem('\n');
|
||||||
QString finaltext;
|
QString finaltext;
|
||||||
finaltext.reserve(intermediatetext.size());
|
finaltext.reserve(intermediatetext.size());
|
||||||
foreach(const QChar& c, intermediatetext)
|
foreach (const QChar& c, intermediatetext) {
|
||||||
{
|
|
||||||
if (c == rem && prev) {
|
if (c == rem && prev) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -270,17 +274,14 @@ void InfoFrame::setDescription(QString text)
|
|||||||
}
|
}
|
||||||
QString labeltext;
|
QString labeltext;
|
||||||
labeltext.reserve(300);
|
labeltext.reserve(300);
|
||||||
if(finaltext.length() > 290)
|
if (finaltext.length() > 290) {
|
||||||
{
|
|
||||||
ui->descriptionLabel->setOpenExternalLinks(false);
|
ui->descriptionLabel->setOpenExternalLinks(false);
|
||||||
ui->descriptionLabel->setTextFormat(Qt::TextFormat::RichText);
|
ui->descriptionLabel->setTextFormat(Qt::TextFormat::RichText);
|
||||||
m_description = text;
|
m_description = text;
|
||||||
// This allows injecting HTML here.
|
// This allows injecting HTML here.
|
||||||
labeltext.append("<html><body>" + finaltext.left(287) + "<a href=\"#mod_desc\">...</a></body></html>");
|
labeltext.append("<html><body>" + finaltext.left(287) + "<a href=\"#mod_desc\">...</a></body></html>");
|
||||||
QObject::connect(ui->descriptionLabel, &QLabel::linkActivated, this, &InfoFrame::descriptionEllipsisHandler);
|
QObject::connect(ui->descriptionLabel, &QLabel::linkActivated, this, &InfoFrame::descriptionEllipsisHandler);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
ui->descriptionLabel->setTextFormat(Qt::TextFormat::AutoText);
|
ui->descriptionLabel->setTextFormat(Qt::TextFormat::AutoText);
|
||||||
labeltext.append(finaltext);
|
labeltext.append(finaltext);
|
||||||
}
|
}
|
||||||
@ -289,14 +290,11 @@ void InfoFrame::setDescription(QString text)
|
|||||||
|
|
||||||
void InfoFrame::setLicense(QString text)
|
void InfoFrame::setLicense(QString text)
|
||||||
{
|
{
|
||||||
if(text.isEmpty())
|
if (text.isEmpty()) {
|
||||||
{
|
|
||||||
ui->licenseLabel->setHidden(true);
|
ui->licenseLabel->setHidden(true);
|
||||||
updateHiddenState();
|
updateHiddenState();
|
||||||
return;
|
return;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
ui->licenseLabel->setHidden(false);
|
ui->licenseLabel->setHidden(false);
|
||||||
updateHiddenState();
|
updateHiddenState();
|
||||||
}
|
}
|
||||||
@ -306,8 +304,7 @@ void InfoFrame::setLicense(QString text)
|
|||||||
QChar rem('\n');
|
QChar rem('\n');
|
||||||
QString finaltext;
|
QString finaltext;
|
||||||
finaltext.reserve(intermediatetext.size());
|
finaltext.reserve(intermediatetext.size());
|
||||||
foreach(const QChar& c, intermediatetext)
|
foreach (const QChar& c, intermediatetext) {
|
||||||
{
|
|
||||||
if (c == rem && prev) {
|
if (c == rem && prev) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -316,17 +313,14 @@ void InfoFrame::setLicense(QString text)
|
|||||||
}
|
}
|
||||||
QString labeltext;
|
QString labeltext;
|
||||||
labeltext.reserve(300);
|
labeltext.reserve(300);
|
||||||
if(finaltext.length() > 290)
|
if (finaltext.length() > 290) {
|
||||||
{
|
|
||||||
ui->licenseLabel->setOpenExternalLinks(false);
|
ui->licenseLabel->setOpenExternalLinks(false);
|
||||||
ui->licenseLabel->setTextFormat(Qt::TextFormat::RichText);
|
ui->licenseLabel->setTextFormat(Qt::TextFormat::RichText);
|
||||||
m_description = text;
|
m_description = text;
|
||||||
// This allows injecting HTML here.
|
// This allows injecting HTML here.
|
||||||
labeltext.append("<html><body>" + finaltext.left(287) + "<a href=\"#mod_desc\">...</a></body></html>");
|
labeltext.append("<html><body>" + finaltext.left(287) + "<a href=\"#mod_desc\">...</a></body></html>");
|
||||||
QObject::connect(ui->licenseLabel, &QLabel::linkActivated, this, &InfoFrame::licenseEllipsisHandler);
|
QObject::connect(ui->licenseLabel, &QLabel::linkActivated, this, &InfoFrame::licenseEllipsisHandler);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
ui->licenseLabel->setTextFormat(Qt::TextFormat::AutoText);
|
ui->licenseLabel->setTextFormat(Qt::TextFormat::AutoText);
|
||||||
labeltext.append(finaltext);
|
labeltext.append(finaltext);
|
||||||
}
|
}
|
||||||
@ -335,12 +329,9 @@ void InfoFrame::setLicense(QString text)
|
|||||||
|
|
||||||
void InfoFrame::setIssueTracker(QString text)
|
void InfoFrame::setIssueTracker(QString text)
|
||||||
{
|
{
|
||||||
if(text.isEmpty())
|
if (text.isEmpty()) {
|
||||||
{
|
|
||||||
ui->issueTrackerLabel->setHidden(true);
|
ui->issueTrackerLabel->setHidden(true);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
ui->issueTrackerLabel->setText(text);
|
ui->issueTrackerLabel->setText(text);
|
||||||
ui->issueTrackerLabel->setHidden(false);
|
ui->issueTrackerLabel->setHidden(false);
|
||||||
}
|
}
|
||||||
@ -359,28 +350,22 @@ void InfoFrame::setImage(QPixmap img)
|
|||||||
|
|
||||||
void InfoFrame::descriptionEllipsisHandler([[maybe_unused]] QString link)
|
void InfoFrame::descriptionEllipsisHandler([[maybe_unused]] QString link)
|
||||||
{
|
{
|
||||||
if(!m_current_box)
|
if (!m_current_box) {
|
||||||
{
|
|
||||||
m_current_box = CustomMessageBox::selectable(this, "", m_description);
|
m_current_box = CustomMessageBox::selectable(this, "", m_description);
|
||||||
connect(m_current_box, &QMessageBox::finished, this, &InfoFrame::boxClosed);
|
connect(m_current_box, &QMessageBox::finished, this, &InfoFrame::boxClosed);
|
||||||
m_current_box->show();
|
m_current_box->show();
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
m_current_box->setText(m_description);
|
m_current_box->setText(m_description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void InfoFrame::licenseEllipsisHandler([[maybe_unused]] QString link)
|
void InfoFrame::licenseEllipsisHandler([[maybe_unused]] QString link)
|
||||||
{
|
{
|
||||||
if(!m_current_box)
|
if (!m_current_box) {
|
||||||
{
|
|
||||||
m_current_box = CustomMessageBox::selectable(this, "", m_license);
|
m_current_box = CustomMessageBox::selectable(this, "", m_license);
|
||||||
connect(m_current_box, &QMessageBox::finished, this, &InfoFrame::boxClosed);
|
connect(m_current_box, &QMessageBox::finished, this, &InfoFrame::boxClosed);
|
||||||
m_current_box->show();
|
m_current_box->show();
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
m_current_box->setText(m_license);
|
m_current_box->setText(m_license);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,24 @@
|
|||||||
/* Copyright 2013-2021 MultiMC Contributors
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -21,8 +41,7 @@
|
|||||||
#include "minecraft/mod/ResourcePack.h"
|
#include "minecraft/mod/ResourcePack.h"
|
||||||
#include "minecraft/mod/TexturePack.h"
|
#include "minecraft/mod/TexturePack.h"
|
||||||
|
|
||||||
namespace Ui
|
namespace Ui {
|
||||||
{
|
|
||||||
class InfoFrame;
|
class InfoFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
#include "LanguageSelectionWidget.h"
|
#include "LanguageSelectionWidget.h"
|
||||||
|
|
||||||
#include <QVBoxLayout>
|
#include <QCheckBox>
|
||||||
#include <QTreeView>
|
|
||||||
#include <QHeaderView>
|
#include <QHeaderView>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
|
#include <QTreeView>
|
||||||
|
#include <QVBoxLayout>
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "BuildConfig.h"
|
#include "BuildConfig.h"
|
||||||
#include "translations/TranslationsModel.h"
|
|
||||||
#include "settings/Setting.h"
|
#include "settings/Setting.h"
|
||||||
|
#include "translations/TranslationsModel.h"
|
||||||
|
|
||||||
LanguageSelectionWidget::LanguageSelectionWidget(QWidget *parent) :
|
LanguageSelectionWidget::LanguageSelectionWidget(QWidget* parent) : QWidget(parent)
|
||||||
QWidget(parent)
|
|
||||||
{
|
{
|
||||||
verticalLayout = new QVBoxLayout(this);
|
verticalLayout = new QVBoxLayout(this);
|
||||||
verticalLayout->setObjectName(QStringLiteral("verticalLayout"));
|
verticalLayout->setObjectName(QStringLiteral("verticalLayout"));
|
||||||
@ -31,6 +31,13 @@ LanguageSelectionWidget::LanguageSelectionWidget(QWidget *parent) :
|
|||||||
helpUsLabel->setWordWrap(true);
|
helpUsLabel->setWordWrap(true);
|
||||||
verticalLayout->addWidget(helpUsLabel);
|
verticalLayout->addWidget(helpUsLabel);
|
||||||
|
|
||||||
|
formatCheckbox = new QCheckBox(this);
|
||||||
|
formatCheckbox->setObjectName(QStringLiteral("formatCheckbox"));
|
||||||
|
formatCheckbox->setCheckState(APPLICATION->settings()->get("UseSystemLocale").toBool() ? Qt::Checked : Qt::Unchecked);
|
||||||
|
connect(formatCheckbox, &QCheckBox::stateChanged,
|
||||||
|
[this]() { APPLICATION->translations()->setUseSystemLocale(formatCheckbox->isChecked()); });
|
||||||
|
verticalLayout->addWidget(formatCheckbox);
|
||||||
|
|
||||||
auto translations = APPLICATION->translations();
|
auto translations = APPLICATION->translations();
|
||||||
auto index = translations->selectedIndex();
|
auto index = translations->selectedIndex();
|
||||||
languageView->setModel(translations.get());
|
languageView->setModel(translations.get());
|
||||||
@ -55,13 +62,12 @@ void LanguageSelectionWidget::retranslate()
|
|||||||
QString text = tr("Don't see your language or the quality is poor?<br/><a href=\"%1\">Help us with translations!</a>")
|
QString text = tr("Don't see your language or the quality is poor?<br/><a href=\"%1\">Help us with translations!</a>")
|
||||||
.arg(BuildConfig.TRANSLATIONS_URL);
|
.arg(BuildConfig.TRANSLATIONS_URL);
|
||||||
helpUsLabel->setText(text);
|
helpUsLabel->setText(text);
|
||||||
|
formatCheckbox->setText(tr("Use system locales"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void LanguageSelectionWidget::languageRowChanged(const QModelIndex& current, const QModelIndex& previous)
|
void LanguageSelectionWidget::languageRowChanged(const QModelIndex& current, const QModelIndex& previous)
|
||||||
{
|
{
|
||||||
if (current == previous)
|
if (current == previous) {
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto translations = APPLICATION->translations();
|
auto translations = APPLICATION->translations();
|
||||||
|
@ -21,9 +21,9 @@ class QVBoxLayout;
|
|||||||
class QTreeView;
|
class QTreeView;
|
||||||
class QLabel;
|
class QLabel;
|
||||||
class Setting;
|
class Setting;
|
||||||
|
class QCheckBox;
|
||||||
|
|
||||||
class LanguageSelectionWidget: public QWidget
|
class LanguageSelectionWidget : public QWidget {
|
||||||
{
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit LanguageSelectionWidget(QWidget* parent = 0);
|
explicit LanguageSelectionWidget(QWidget* parent = 0);
|
||||||
@ -40,4 +40,5 @@ private:
|
|||||||
QVBoxLayout* verticalLayout = nullptr;
|
QVBoxLayout* verticalLayout = nullptr;
|
||||||
QTreeView* languageView = nullptr;
|
QTreeView* languageView = nullptr;
|
||||||
QLabel* helpUsLabel = nullptr;
|
QLabel* helpUsLabel = nullptr;
|
||||||
|
QCheckBox* formatCheckbox = nullptr;
|
||||||
};
|
};
|
||||||
|
@ -116,12 +116,21 @@ void WideBar::insertActionAfter(QAction* after, QAction* action)
|
|||||||
if (iter == m_entries.end())
|
if (iter == m_entries.end())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
iter++;
|
||||||
|
// the action to insert after is present
|
||||||
|
// however, the element after it isn't valid
|
||||||
|
if (iter == m_entries.end()) {
|
||||||
|
// append the action instead of inserting it
|
||||||
|
addAction(action);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
BarEntry entry;
|
BarEntry entry;
|
||||||
entry.bar_action = insertWidget((iter + 1)->bar_action, new ActionButton(action, this, m_use_default_action));
|
entry.bar_action = insertWidget(iter->bar_action, new ActionButton(action, this, m_use_default_action));
|
||||||
entry.menu_action = action;
|
entry.menu_action = action;
|
||||||
entry.type = BarEntry::Type::Action;
|
entry.type = BarEntry::Type::Action;
|
||||||
|
|
||||||
m_entries.insert(iter + 1, entry);
|
m_entries.insert(iter, entry);
|
||||||
|
|
||||||
m_menu_state = MenuState::Dirty;
|
m_menu_state = MenuState::Dirty;
|
||||||
}
|
}
|
||||||
|
@ -89,8 +89,10 @@ void FourBytes_MurmurHash2(const unsigned char* data, IncrementalHashInfo& prev)
|
|||||||
switch (prev.len) {
|
switch (prev.len) {
|
||||||
case 3:
|
case 3:
|
||||||
prev.h ^= data[2] << 16;
|
prev.h ^= data[2] << 16;
|
||||||
|
/* fall through */
|
||||||
case 2:
|
case 2:
|
||||||
prev.h ^= data[1] << 8;
|
prev.h ^= data[1] << 8;
|
||||||
|
/* fall through */
|
||||||
case 1:
|
case 1:
|
||||||
prev.h ^= data[0];
|
prev.h ^= data[0];
|
||||||
prev.h *= m;
|
prev.h *= m;
|
||||||
|
Loading…
Reference in New Issue
Block a user