aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/update-flake.yml2
-rw-r--r--CMakeLists.txt32
-rw-r--r--launcher/Application.cpp8
-rw-r--r--launcher/CMakeLists.txt8
-rw-r--r--launcher/FileSystem.cpp60
-rw-r--r--launcher/minecraft/MinecraftInstance.cpp15
-rw-r--r--launcher/minecraft/PackProfile.cpp3
-rw-r--r--launcher/minecraft/mod/Mod.cpp4
-rw-r--r--launcher/minecraft/mod/Mod.h2
-rw-r--r--launcher/minecraft/mod/ModFolderModel.cpp17
-rw-r--r--launcher/minecraft/mod/Resource.cpp8
-rw-r--r--launcher/minecraft/mod/Resource.h2
-rw-r--r--launcher/minecraft/mod/ResourceFolderModel.cpp89
-rw-r--r--launcher/minecraft/mod/ResourceFolderModel.h14
-rw-r--r--launcher/minecraft/mod/tasks/LocalResourceParse.cpp9
-rw-r--r--launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp2
-rw-r--r--launcher/modplatform/EnsureMetadataTask.cpp6
-rw-r--r--launcher/modplatform/ModIndex.h1
-rw-r--r--launcher/modplatform/flame/FlameAPI.h2
-rw-r--r--launcher/modplatform/flame/FlameInstanceCreationTask.cpp8
-rw-r--r--launcher/modplatform/flame/FlamePackExportTask.cpp473
-rw-r--r--launcher/modplatform/flame/FlamePackExportTask.h90
-rw-r--r--launcher/modplatform/flame/PackManifest.cpp9
-rw-r--r--launcher/modplatform/modrinth/ModrinthAPI.h6
-rw-r--r--launcher/modplatform/modrinth/ModrinthPackExportTask.cpp55
-rw-r--r--launcher/translations/TranslationsModel.cpp30
-rw-r--r--launcher/translations/TranslationsModel.h29
-rw-r--r--launcher/ui/MainWindow.cpp85
-rw-r--r--launcher/ui/MainWindow.h1
-rw-r--r--launcher/ui/MainWindow.ui8
-rw-r--r--launcher/ui/dialogs/ExportPackDialog.cpp (renamed from launcher/ui/dialogs/ExportMrPackDialog.cpp)63
-rw-r--r--launcher/ui/dialogs/ExportPackDialog.h (renamed from launcher/ui/dialogs/ExportMrPackDialog.h)14
-rw-r--r--launcher/ui/dialogs/ExportPackDialog.ui (renamed from launcher/ui/dialogs/ExportMrPackDialog.ui)15
-rw-r--r--launcher/ui/dialogs/ResourceDownloadDialog.cpp9
-rw-r--r--launcher/ui/pages/global/LauncherPage.ui2
-rw-r--r--launcher/ui/pages/instance/ExternalResourcesPage.cpp33
-rw-r--r--launcher/ui/pages/instance/ExternalResourcesPage.h2
-rw-r--r--launcher/ui/pages/instance/InstanceSettingsPage.cpp13
-rw-r--r--launcher/ui/pages/instance/InstanceSettingsPage.h3
-rw-r--r--launcher/ui/pages/instance/ManagedPackPage.cpp25
-rw-r--r--launcher/ui/pages/instance/ModFolderPage.cpp31
-rw-r--r--launcher/ui/pages/instance/ModFolderPage.h1
-rw-r--r--launcher/ui/pages/instance/ResourcePackPage.cpp2
-rw-r--r--launcher/ui/pages/instance/ShaderPackPage.cpp3
-rw-r--r--launcher/ui/pages/instance/TexturePackPage.cpp2
-rw-r--r--launcher/ui/pages/instance/VersionPage.cpp306
-rw-r--r--launcher/ui/pages/instance/VersionPage.h55
-rw-r--r--launcher/ui/widgets/LanguageSelectionWidget.cpp28
-rw-r--r--launcher/ui/widgets/LanguageSelectionWidget.h25
-rw-r--r--launcher/ui/widgets/WideBar.cpp13
-rw-r--r--tests/FileSystem_test.cpp16
-rw-r--r--tests/ResourceModel_test.cpp8
-rw-r--r--tests/Task_test.cpp147
-rw-r--r--tests/Version_test.cpp17
54 files changed, 1287 insertions, 624 deletions
diff --git a/.github/workflows/update-flake.yml b/.github/workflows/update-flake.yml
index c790c9c6..ad22120e 100644
--- a/.github/workflows/update-flake.yml
+++ b/.github/workflows/update-flake.yml
@@ -7,10 +7,12 @@ on:
workflow_dispatch:
permissions:
+ contents: write
pull-requests: write
jobs:
update-flake:
+ if: github.repository == 'PrismLauncher/PrismLauncher'
runs-on: ubuntu-latest
steps:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5358d1a8..c238086e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -85,6 +85,38 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DTOML_ENABLE_FLOAT16=0")
# set CXXFLAGS for build targets
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}")
+option(DEBUG_ADDRESS_SANITIZER "Enable Address Sanitizer in Debug builds" on)
+
+# If this is a Debug build turn on address sanitiser
+if (CMAKE_BUILD_TYPE STREQUAL "Debug" AND DEBUG_ADDRESS_SANITIZER)
+ message(STATUS "Address Sanitizer enabled for Debug builds, Turn it off with -DDEBUG_ADDRESS_SANITIZER=off")
+ if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
+ if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
+ # using clang with clang-cl front end
+ message(STATUS "Address Sanitizer available on Clang MSVC frontend")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /O1 /Oy-")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /O1 /Oy-")
+ else()
+ # AppleClang and Clang
+ message(STATUS "Address Sanitizer available on Clang")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer")
+ endif()
+ elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
+ # GCC
+ message(STATUS "Address Sanitizer available on GCC")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer")
+ link_libraries("asan")
+ elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
+ message(STATUS "Address Sanitizer available on MSVC")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /O1 /Oy-")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /O1 /Oy-")
+ else()
+ message(STATUS "Address Sanitizer not available on compiler ${CMAKE_CXX_COMPILER_ID}")
+ endif()
+endif()
+
option(ENABLE_LTO "Enable Link Time Optimization" off)
if(ENABLE_LTO)
diff --git a/launcher/Application.cpp b/launcher/Application.cpp
index a3040968..e6070006 100644
--- a/launcher/Application.cpp
+++ b/launcher/Application.cpp
@@ -572,6 +572,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
// Language
m_settings->registerSetting("Language", QString());
+ m_settings->registerSetting("UseSystemLocale", false);
// Console
m_settings->registerSetting("ShowConsole", false);
@@ -922,12 +923,7 @@ bool Application::createSetupWizard()
}
return false;
}();
- bool languageRequired = [&]()
- {
- if (settings()->get("Language").toString().isEmpty())
- return true;
- return false;
- }();
+ bool languageRequired = settings()->get("Language").toString().isEmpty();
bool pasteInterventionRequired = settings()->get("PastebinURL") != "";
bool themeInterventionRequired = settings()->get("ApplicationTheme") == "";
bool wizardRequired = javaRequired || languageRequired || pasteInterventionRequired || themeInterventionRequired;
diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt
index 312288a1..af3bc28e 100644
--- a/launcher/CMakeLists.txt
+++ b/launcher/CMakeLists.txt
@@ -514,6 +514,8 @@ set(FLAME_SOURCES
modplatform/flame/FlameCheckUpdate.h
modplatform/flame/FlameInstanceCreationTask.h
modplatform/flame/FlameInstanceCreationTask.cpp
+ modplatform/flame/FlamePackExportTask.h
+ modplatform/flame/FlamePackExportTask.cpp
)
set(MODRINTH_SOURCES
@@ -907,8 +909,8 @@ SET(LAUNCHER_SOURCES
ui/dialogs/EditAccountDialog.h
ui/dialogs/ExportInstanceDialog.cpp
ui/dialogs/ExportInstanceDialog.h
- ui/dialogs/ExportMrPackDialog.cpp
- ui/dialogs/ExportMrPackDialog.h
+ ui/dialogs/ExportPackDialog.cpp
+ ui/dialogs/ExportPackDialog.h
ui/dialogs/IconPickerDialog.cpp
ui/dialogs/IconPickerDialog.h
ui/dialogs/ImportResourceDialog.cpp
@@ -1055,7 +1057,7 @@ qt_wrap_ui(LAUNCHER_UI
ui/dialogs/ProfileSelectDialog.ui
ui/dialogs/SkinUploadDialog.ui
ui/dialogs/ExportInstanceDialog.ui
- ui/dialogs/ExportMrPackDialog.ui
+ ui/dialogs/ExportPackDialog.ui
ui/dialogs/IconPickerDialog.ui
ui/dialogs/ImportResourceDialog.ui
ui/dialogs/MSALoginDialog.ui
diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp
index 812d45eb..4538702f 100644
--- a/launcher/FileSystem.cpp
+++ b/launcher/FileSystem.cpp
@@ -778,9 +778,43 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri
destination = PathCombine(getDesktopDir(), RemoveInvalidFilenameChars(name));
}
#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);
QTextStream stream(&f);
@@ -797,6 +831,28 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri
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;
#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
diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp
index aab930de..4867cc7a 100644
--- a/launcher/minecraft/MinecraftInstance.cpp
+++ b/launcher/minecraft/MinecraftInstance.cpp
@@ -1112,36 +1112,27 @@ JavaVersion MinecraftInstance::getJavaVersion()
std::shared_ptr<ModFolderModel> MinecraftInstance::loaderModList()
{
- if (!m_loader_mod_list)
- {
+ if (!m_loader_mod_list) {
bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
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;
}
std::shared_ptr<ModFolderModel> MinecraftInstance::coreModList()
{
- if (!m_core_mod_list)
- {
+ if (!m_core_mod_list) {
bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
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;
}
std::shared_ptr<ModFolderModel> MinecraftInstance::nilModList()
{
- if (!m_nil_mod_list)
- {
+ if (!m_nil_mod_list) {
bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool();
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;
}
diff --git a/launcher/minecraft/PackProfile.cpp b/launcher/minecraft/PackProfile.cpp
index aff05dbc..e8fd2157 100644
--- a/launcher/minecraft/PackProfile.cpp
+++ b/launcher/minecraft/PackProfile.cpp
@@ -65,7 +65,8 @@
static const QMap<QString, ResourceAPI::ModLoaderType> modloaderMapping{
{"net.minecraftforge", ResourceAPI::Forge},
{"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)
diff --git a/launcher/minecraft/mod/Mod.cpp b/launcher/minecraft/mod/Mod.cpp
index fa1a0253..880dacb1 100644
--- a/launcher/minecraft/mod/Mod.cpp
+++ b/launcher/minecraft/mod/Mod.cpp
@@ -126,7 +126,7 @@ bool Mod::applyFilter(QRegularExpression filter) const
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) {
qDebug() << QString("Destroying metadata for '%1' on purpose").arg(name());
@@ -139,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&
diff --git a/launcher/minecraft/mod/Mod.h b/launcher/minecraft/mod/Mod.h
index d6272f4d..b67bd465 100644
--- a/launcher/minecraft/mod/Mod.h
+++ b/launcher/minecraft/mod/Mod.h
@@ -93,7 +93,7 @@ public:
[[nodiscard]] bool applyFilter(QRegularExpression filter) const override;
// 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);
diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp
index af98d834..51383edf 100644
--- a/launcher/minecraft/mod/ModFolderModel.cpp
+++ b/launcher/minecraft/mod/ModFolderModel.cpp
@@ -199,10 +199,10 @@ Task* ModFolderModel::createParseTask(Resource& resource)
bool ModFolderModel::uninstallMod(const QString& filename, bool preserve_metadata)
{
- for(auto mod : allMods()){
- if(mod->fileinfo().fileName() == filename){
+ for(auto mod : allMods()) {
+ if(mod->fileinfo().fileName() == filename) {
auto index_dir = indexDir();
- mod->destroy(index_dir, preserve_metadata);
+ mod->destroy(index_dir, preserve_metadata, false);
update();
@@ -215,16 +215,11 @@ bool ModFolderModel::uninstallMod(const QString& filename, bool preserve_metadat
bool ModFolderModel::deleteMods(const QModelIndexList& indexes)
{
- if(!m_can_interact) {
- return false;
- }
-
- if(indexes.isEmpty())
+ if (indexes.isEmpty())
return true;
- for (auto i: indexes)
- {
- if(i.column() != 0) {
+ for (auto i : indexes) {
+ if (i.column() != 0) {
continue;
}
auto m = at(i.row());
diff --git a/launcher/minecraft/mod/Resource.cpp b/launcher/minecraft/mod/Resource.cpp
index e5077260..098a617f 100644
--- a/launcher/minecraft/mod/Resource.cpp
+++ b/launcher/minecraft/mod/Resource.cpp
@@ -148,14 +148,10 @@ bool Resource::enable(EnableAction action)
return true;
}
-bool Resource::destroy()
+bool Resource::destroy(bool attemptTrash)
{
m_type = ResourceType::UNKNOWN;
-
- if (FS::trash(m_file_info.filePath()))
- return true;
-
- return FS::deletePath(m_file_info.filePath());
+ return (attemptTrash && FS::trash(m_file_info.filePath())) || FS::deletePath(m_file_info.filePath());
}
bool Resource::isSymLinkUnder(const QString& instPath) const
diff --git a/launcher/minecraft/mod/Resource.h b/launcher/minecraft/mod/Resource.h
index a5e9ae91..94f3160c 100644
--- a/launcher/minecraft/mod/Resource.h
+++ b/launcher/minecraft/mod/Resource.h
@@ -92,7 +92,7 @@ class Resource : public QObject {
}
// Delete all files of this resource.
- bool destroy();
+ bool destroy(bool attemptTrash = true);
[[nodiscard]] auto isSymLink() const -> bool { return m_file_info.isSymLink(); }
diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp
index 7700fd36..39a61067 100644
--- a/launcher/minecraft/mod/ResourceFolderModel.cpp
+++ b/launcher/minecraft/mod/ResourceFolderModel.cpp
@@ -1,14 +1,15 @@
#include "ResourceFolderModel.h"
+#include <QMessageBox>
#include <QCoreApplication>
#include <QDebug>
#include <QFileInfo>
#include <QIcon>
+#include <QMenu>
#include <QMimeData>
#include <QStyle>
#include <QThreadPool>
#include <QUrl>
-#include <QMenu>
#include "Application.h"
#include "FileSystem.h"
@@ -18,6 +19,7 @@
#include "settings/Setting.h"
#include "tasks/Task.h"
+#include "ui/dialogs/CustomMessageBox.h"
ResourceFolderModel::ResourceFolderModel(QDir dir, BaseInstance* instance, QObject* parent, bool create_dir)
: 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)
{
- 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
original_path = FS::NormalizePath(original_path);
QFileInfo file_info(original_path);
@@ -159,7 +157,7 @@ bool ResourceFolderModel::uninstallResource(QString file_name)
{
for (auto& resource : m_resources) {
if (resource->fileinfo().fileName() == file_name) {
- auto res = resource->destroy();
+ auto res = resource->destroy(false);
update();
@@ -171,9 +169,6 @@ bool ResourceFolderModel::uninstallResource(QString file_name)
bool ResourceFolderModel::deleteResources(const QModelIndexList& indexes)
{
- if (!m_can_interact)
- return false;
-
if (indexes.isEmpty())
return true;
@@ -192,11 +187,8 @@ bool ResourceFolderModel::deleteResources(const QModelIndexList& indexes)
return true;
}
-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())
return true;
@@ -249,15 +241,18 @@ bool ResourceFolderModel::update()
connect(m_current_update_task.get(), &Task::succeeded, this, &ResourceFolderModel::onUpdateSucceeded,
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, [=] {
- m_current_update_task.reset();
- if (m_scheduled_update) {
- m_scheduled_update = false;
- update();
- } else {
- emit updateFinished();
- }
- }, Qt::ConnectionType::QueuedConnection);
+ connect(
+ m_current_update_task.get(), &Task::finished, this,
+ [=] {
+ m_current_update_task.reset();
+ if (m_scheduled_update) {
+ m_scheduled_update = false;
+ update();
+ } else {
+ emit updateFinished();
+ }
+ },
+ Qt::ConnectionType::QueuedConnection);
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 defaultFlags = QAbstractListModel::flags(index);
- auto flags = defaultFlags;
- if (!m_can_interact) {
- flags &= ~Qt::ItemIsDropEnabled;
- } else {
- flags |= Qt::ItemIsDropEnabled;
- if (index.isValid()) {
- flags |= Qt::ItemIsUserCheckable;
- }
- }
+ auto flags = defaultFlags | Qt::ItemIsDropEnabled;
+ if (index.isValid())
+ flags |= Qt::ItemIsUserCheckable;
return flags;
}
@@ -428,16 +417,17 @@ QVariant ResourceFolderModel::data(const QModelIndex& index, int role) const
if (column == NAME_COLUMN) {
if (at(row).isSymLinkUnder(instDirPath())) {
return m_resources[row]->internal_id() +
- tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original."
- "\nCanonical Path: %1")
- .arg(at(row).fileinfo().canonicalFilePath());;
+ tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original."
+ "\nCanonical Path: %1")
+ .arg(at(row).fileinfo().canonicalFilePath());
+ ;
}
if (at(row).isMoreThanOneHardLink()) {
return m_resources[row]->internal_id() +
- tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original.");
+ tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original.");
}
}
-
+
return m_resources[row]->internal_id();
case Qt::DecorationRole: {
if (column == NAME_COLUMN && (at(row).isSymLinkUnder(instDirPath()) || at(row).isMoreThanOneHardLink()))
@@ -463,8 +453,20 @@ bool ResourceFolderModel::setData(const QModelIndex& index, const QVariant& valu
if (row < 0 || row >= rowCount(index.parent()) || !index.isValid())
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 false;
}
@@ -583,16 +585,6 @@ SortType ResourceFolderModel::columnToSortKey(size_t column) const
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 */
[[nodiscard]] bool ResourceFolderModel::ProxyModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
{
@@ -628,6 +620,7 @@ void ResourceFolderModel::enableInteraction(bool enabled)
return (compare_result.first > 0);
}
-QString ResourceFolderModel::instDirPath() const {
+QString ResourceFolderModel::instDirPath() const
+{
return QFileInfo(m_instance->instanceRoot()).absoluteFilePath();
}
diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h
index eb1d7c4f..454b84c3 100644
--- a/launcher/minecraft/mod/ResourceFolderModel.h
+++ b/launcher/minecraft/mod/ResourceFolderModel.h
@@ -14,8 +14,8 @@
#include "BaseInstance.h"
-#include "tasks/Task.h"
#include "tasks/ConcurrentTask.h"
+#include "tasks/Task.h"
class QSortFilterProxyModel;
@@ -141,10 +141,6 @@ class ResourceFolderModel : public QAbstractListModel {
QString instDirPath() const;
- public slots:
- void enableInteraction(bool enabled);
- void disableInteraction(bool disabled) { enableInteraction(!disabled); }
-
signals:
void updateFinished();
@@ -193,7 +189,11 @@ class ResourceFolderModel : public QAbstractListModel {
* if the resource is complex and has more stuff to parse.
*/
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:
// 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")};
QList<QHeaderView::ResizeMode> m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Stretch, QHeaderView::ResizeToContents };
- bool m_can_interact = true;
-
QDir m_dir;
BaseInstance* m_instance;
QFileSystemWatcher m_watcher;
diff --git a/launcher/minecraft/mod/tasks/LocalResourceParse.cpp b/launcher/minecraft/mod/tasks/LocalResourceParse.cpp
index 4d760df2..0894049c 100644
--- a/launcher/minecraft/mod/tasks/LocalResourceParse.cpp
+++ b/launcher/minecraft/mod/tasks/LocalResourceParse.cpp
@@ -44,7 +44,11 @@ static const QMap<PackedResourceType, QString> s_packed_type_names = {
namespace ResourceUtils {
PackedResourceType identify(QFileInfo file){
if (file.exists() && file.isFile()) {
- if (ResourcePackUtils::validate(file)) {
+ if (ModUtils::validate(file)) {
+ // mods can contain resource and data packs so they must be tested first
+ qDebug() << file.fileName() << "is a mod";
+ return PackedResourceType::Mod;
+ } else if (ResourcePackUtils::validate(file)) {
qDebug() << file.fileName() << "is a resource pack";
return PackedResourceType::ResourcePack;
} else if (TexturePackUtils::validate(file)) {
@@ -53,9 +57,6 @@ PackedResourceType identify(QFileInfo file){
} else if (DataPackUtils::validate(file)) {
qDebug() << file.fileName() << "is a data pack";
return PackedResourceType::DataPack;
- } else if (ModUtils::validate(file)) {
- qDebug() << file.fileName() << "is a mod";
- return PackedResourceType::Mod;
} else if (WorldSaveUtils::validate(file)) {
qDebug() << file.fileName() << "is a world save";
return PackedResourceType::WorldSave;
diff --git a/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp b/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp
index 3677a1dc..ef353c70 100644
--- a/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp
+++ b/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp
@@ -103,7 +103,7 @@ void ModFolderLoadTask::executeTask()
while (iter.hasNext()) {
auto mod = iter.next().value();
if (mod->status() == ModStatus::NotInstalled) {
- mod->destroy(m_index_dir, false);
+ mod->destroy(m_index_dir, false, false);
iter.remove();
}
}
diff --git a/launcher/modplatform/EnsureMetadataTask.cpp b/launcher/modplatform/EnsureMetadataTask.cpp
index 93b5ce76..c3eadd06 100644
--- a/launcher/modplatform/EnsureMetadataTask.cpp
+++ b/launcher/modplatform/EnsureMetadataTask.cpp
@@ -145,7 +145,8 @@ void EnsureMetadataTask::executeTask()
connect(project_task.get(), &Task::finished, this, [=] {
invalidade_leftover();
project_task->deleteLater();
- m_current_task = nullptr;
+ if (m_current_task)
+ m_current_task.reset();
});
m_current_task = project_task;
@@ -154,7 +155,8 @@ void EnsureMetadataTask::executeTask()
connect(version_task.get(), &Task::finished, [=] {
version_task->deleteLater();
- m_current_task = nullptr;
+ if (m_current_task)
+ m_current_task.reset();
});
if (m_mods.size() > 1)
diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h
index 3f51e700..2aa91602 100644
--- a/launcher/modplatform/ModIndex.h
+++ b/launcher/modplatform/ModIndex.h
@@ -128,6 +128,7 @@ struct IndexedPack {
return std::any_of(versions.constBegin(), versions.constEnd(), [](auto const& v) { return v.is_currently_selected; });
}
};
+QString getMetaURL(ResourceProvider provider, QVariant projectID);
struct OverrideDep {
QString quilt;
diff --git a/launcher/modplatform/flame/FlameAPI.h b/launcher/modplatform/flame/FlameAPI.h
index 0a6dc78f..49bc316f 100644
--- a/launcher/modplatform/flame/FlameAPI.h
+++ b/launcher/modplatform/flame/FlameAPI.h
@@ -23,6 +23,8 @@ class FlameAPI : public NetworkResourceAPI {
[[nodiscard]] auto getSortingMethods() const -> QList<ResourceAPI::SortingMethod> override;
+ static inline auto validateModLoaders(ModLoaderTypes loaders) -> bool { return loaders & (Forge | Fabric | Quilt); }
+
private:
static int getClassId(ModPlatform::ResourceType type)
{
diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp
index e7641d64..b57db288 100644
--- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp
+++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp
@@ -563,6 +563,8 @@ void FlameCreationTask::validateZIPResouces()
if (FS::move(localPath, destPath)) {
return destPath;
}
+ } else {
+ qDebug() << "Target folder of" << fileName << "is correct at" << targetFolder;
}
return localPath;
};
@@ -584,6 +586,9 @@ void FlameCreationTask::validateZIPResouces()
QString worldPath;
switch (type) {
+ case PackedResourceType::Mod :
+ validatePath(fileName, targetFolder, "mods");
+ break;
case PackedResourceType::ResourcePack :
validatePath(fileName, targetFolder, "resourcepacks");
break;
@@ -593,9 +598,6 @@ void FlameCreationTask::validateZIPResouces()
case PackedResourceType::DataPack :
validatePath(fileName, targetFolder, "datapacks");
break;
- case PackedResourceType::Mod :
- validatePath(fileName, targetFolder, "mods");
- break;
case PackedResourceType::ShaderPack :
// in theroy flame API can't do this but who knows, that *may* change ?
// better to handle it if it *does* occure in the future
diff --git a/launcher/modplatform/flame/FlamePackExportTask.cpp b/launcher/modplatform/flame/FlamePackExportTask.cpp
new file mode 100644
index 00000000..ac0da214
--- /dev/null
+++ b/launcher/modplatform/flame/FlamePackExportTask.cpp
@@ -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);
+}
diff --git a/launcher/modplatform/flame/FlamePackExportTask.h b/launcher/modplatform/flame/FlamePackExportTask.h
new file mode 100644
index 00000000..3dee0a7e
--- /dev/null
+++ b/launcher/modplatform/flame/FlamePackExportTask.h
@@ -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();
+};
diff --git a/launcher/modplatform/flame/PackManifest.cpp b/launcher/modplatform/flame/PackManifest.cpp
index 22008297..ee4d0766 100644
--- a/launcher/modplatform/flame/PackManifest.cpp
+++ b/launcher/modplatform/flame/PackManifest.cpp
@@ -76,13 +76,8 @@ bool Flame::File::parseFromObject(const QJsonObject& obj, bool throw_on_blocked
// It is also optional
type = File::Type::SingleFile;
- if (fileName.endsWith(".zip")) {
- // this is probably a resource pack
- targetFolder = "resourcepacks";
- } else {
- // this is probably a mod, dunno what else could modpacks download
- targetFolder = "mods";
- }
+ targetFolder = "mods";
+
// get the hash
hash = QString();
auto hashes = Json::ensureArray(obj, "hashes");
diff --git a/launcher/modplatform/modrinth/ModrinthAPI.h b/launcher/modplatform/modrinth/ModrinthAPI.h
index e83ed2bf..58af14cc 100644
--- a/launcher/modplatform/modrinth/ModrinthAPI.h
+++ b/launcher/modplatform/modrinth/ModrinthAPI.h
@@ -38,7 +38,7 @@ class ModrinthAPI : public NetworkResourceAPI {
static auto getModLoaderStrings(const ModLoaderTypes types) -> const QStringList
{
QStringList l;
- for (auto loader : { Forge, Fabric, Quilt }) {
+ for (auto loader : { Forge, Fabric, Quilt, LiteLoader }) {
if (types & loader) {
l << getModLoaderString(loader);
}
@@ -92,7 +92,7 @@ class ModrinthAPI : public NetworkResourceAPI {
{
if (args.loaders.has_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 {};
}
}
@@ -141,7 +141,7 @@ class ModrinthAPI : public NetworkResourceAPI {
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
{
diff --git a/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp b/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp
index 4cd88aa6..30fe566d 100644
--- a/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp
+++ b/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp
@@ -64,7 +64,8 @@ bool ModrinthPackExportTask::abort()
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.
+ // NOTE: Here we don't do `emitAborted()` because it will be done when `buildZipFuture` actually cancels, which may not occur
+ // immediately.
return true;
}
@@ -94,6 +95,7 @@ void ModrinthPackExportTask::collectFiles()
void ModrinthPackExportTask::collectHashes()
{
+ setStatus(tr("Finding file hashes..."));
for (const QFileInfo& file : files) {
QCoreApplication::processEvents();
@@ -157,6 +159,7 @@ void ModrinthPackExportTask::makeApiRequest()
if (pendingHashes.isEmpty())
buildZip();
else {
+ setStatus(tr("Finding versions for hashes..."));
auto response = std::make_shared<QByteArray>();
task = api.currentVersions(pendingHashes.values(), "sha512", response);
connect(task.get(), &NetJob::succeeded, [this, response]() { parseApiResponse(response); });
@@ -263,13 +266,13 @@ void ModrinthPackExportTask::finish()
QByteArray ModrinthPackExportTask::generateIndex()
{
- QJsonObject obj;
- obj["formatVersion"] = 1;
- obj["game"] = "minecraft";
- obj["name"] = name;
- obj["versionId"] = version;
+ QJsonObject out;
+ out["formatVersion"] = 1;
+ out["game"] = "minecraft";
+ out["name"] = name;
+ out["versionId"] = version;
if (!summary.isEmpty())
- obj["summary"] = summary;
+ out["summary"] = summary;
if (mcInstance) {
auto profile = mcInstance->getPackProfile();
@@ -290,30 +293,40 @@ QByteArray ModrinthPackExportTask::generateIndex()
if (forge != nullptr)
dependencies["forge"] = forge->m_version;
- obj["dependencies"] = dependencies;
+ out["dependencies"] = dependencies;
}
- QJsonArray files;
- 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();
- QJsonObject file;
- file["path"] = iterator.key();
- file["downloads"] = QJsonArray({ iterator.value().url });
+ // detect disabled mod
+ const QFileInfo pathInfo(path);
+ 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;
hashes["sha1"] = value.sha1;
hashes["sha512"] = value.sha512;
+ fileOut["hashes"] = hashes;
- file["hashes"] = hashes;
- file["fileSize"] = value.size;
-
- files << file;
+ fileOut["fileSize"] = value.size;
+ filesOut << fileOut;
}
- obj["files"] = files;
+ out["files"] = filesOut;
- return QJsonDocument(obj).toJson(QJsonDocument::Compact);
+ return QJsonDocument(out).toJson(QJsonDocument::Compact);
}
diff --git a/launcher/translations/TranslationsModel.cpp b/launcher/translations/TranslationsModel.cpp
index 489dff86..2763cca2 100644
--- a/launcher/translations/TranslationsModel.cpp
+++ b/launcher/translations/TranslationsModel.cpp
@@ -42,6 +42,7 @@
#include <QDir>
#include <QLibraryInfo>
#include <QDebug>
+#include <locale>
#include "FileSystem.h"
#include "net/NetJob.h"
@@ -527,34 +528,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)
{
- QString &langCode = key;
+ QString& langCode = key;
auto langPtr = findLanguage(key);
- if (langCode.isEmpty())
- {
+ if (langCode.isEmpty()) {
d->no_language_set = true;
}
- if(!langPtr)
- {
+ if (!langPtr) {
qWarning() << "Selected invalid language" << key << ", defaulting to" << defaultLangCode;
langCode = defaultLangCode;
- }
- else
- {
+ } else {
langCode = langPtr->key;
}
// uninstall existing translators if there are any
- if (d->m_app_translator)
- {
+ if (d->m_app_translator) {
QCoreApplication::removeTranslator(d->m_app_translator.get());
d->m_app_translator.reset();
}
- if (d->m_qt_translator)
- {
+ if (d->m_qt_translator) {
QCoreApplication::removeTranslator(d->m_qt_translator.get());
d->m_qt_translator.reset();
}
@@ -564,8 +565,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.
* This function is not reentrant.
*/
- QLocale locale = QLocale(langCode);
- QLocale::setDefault(locale);
+ QLocale::setDefault(
+ QLocale(APPLICATION->settings()->get("UseSystemLocale").toBool() ? QString::fromStdString(std::locale().name()) : langCode));
+
// if it's the default UI language, finish
if(langCode == defaultLangCode)
diff --git a/launcher/translations/TranslationsModel.h b/launcher/translations/TranslationsModel.h
index 3abf84e6..cff23ce7 100644
--- a/launcher/translations/TranslationsModel.h
+++ b/launcher/translations/TranslationsModel.h
@@ -20,17 +20,16 @@
struct Language;
-class TranslationsModel : public QAbstractListModel
-{
+class TranslationsModel : public QAbstractListModel {
Q_OBJECT
-public:
- explicit TranslationsModel(QString path, QObject *parent = 0);
+ public:
+ explicit TranslationsModel(QString path, QObject* parent = 0);
virtual ~TranslationsModel();
- QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
- int rowCount(const QModelIndex &parent = QModelIndex()) const override;
- int columnCount(const QModelIndex & parent) const override;
+ int rowCount(const QModelIndex& parent = QModelIndex()) const override;
+ int columnCount(const QModelIndex& parent) const override;
bool selectLanguage(QString key);
void updateLanguage(QString key);
@@ -38,27 +37,27 @@ public:
QString selectedLanguage();
void downloadIndex();
+ void setUseSystemLocale(bool useSystemLocale);
-private:
- Language *findLanguage(const QString & key);
+ private:
+ Language* findLanguage(const QString& key);
void reloadLocalFiles();
void downloadTranslation(QString key);
void downloadNext();
// hide copy constructor
- TranslationsModel(const TranslationsModel &) = delete;
+ TranslationsModel(const TranslationsModel&) = delete;
// hide assign op
- TranslationsModel &operator=(const TranslationsModel &) = delete;
+ TranslationsModel& operator=(const TranslationsModel&) = delete;
-private slots:
+ private slots:
void indexReceived();
void indexFailed(QString reason);
void dlFailed(QString reason);
void dlGood();
- void translationDirChanged(const QString &path);
+ void translationDirChanged(const QString& path);
-
-private: /* data */
+ private: /* data */
struct Private;
std::unique_ptr<Private> d;
};
diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp
index 515abf07..ed894ead 100644
--- a/launcher/ui/MainWindow.cpp
+++ b/launcher/ui/MainWindow.cpp
@@ -107,7 +107,7 @@
#include "ui/dialogs/CopyInstanceDialog.h"
#include "ui/dialogs/EditAccountDialog.h"
#include "ui/dialogs/ExportInstanceDialog.h"
-#include "ui/dialogs/ExportMrPackDialog.h"
+#include "ui/dialogs/ExportPackDialog.h"
#include "ui/dialogs/ImportResourceDialog.h"
#include "ui/themes/ITheme.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);
exportInstanceMenu->addAction(ui->actionExportInstanceZip);
exportInstanceMenu->addAction(ui->actionExportInstanceMrPack);
+ exportInstanceMenu->addAction(ui->actionExportInstanceFlamePack);
ui->actionExportInstance->setMenu(exportInstanceMenu);
}
@@ -1279,7 +1280,17 @@ void MainWindow::globalSettingsClosed()
void MainWindow::on_actionEditInstance_triggered()
{
- APPLICATION->showInstanceWindow(m_selectedInstance);
+
+ if (!m_selectedInstance)
+ return;
+
+ if (m_selectedInstance->canEdit()) {
+ 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()
@@ -1405,11 +1416,35 @@ void MainWindow::on_actionExportInstanceMrPack_triggered()
{
if (m_selectedInstance)
{
- ExportMrPackDialog dlg(m_selectedInstance, this);
+ ExportPackDialog dlg(m_selectedInstance, this);
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()
{
if (m_selectedInstance)
@@ -1511,11 +1546,39 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered()
QString iconPath;
QStringList args;
#if defined(Q_OS_MACOS)
- if (appPath.startsWith("/private/var/")) {
- QMessageBox::critical(this, tr("Create instance shortcut"),
- tr("The launcher is in the folder it was extracted from, therefore it cannot create shortcuts."));
- return;
- }
+ appPath = QApplication::applicationFilePath();
+ if (appPath.startsWith("/private/var/")) {
+ QMessageBox::critical(this, tr("Create instance shortcut"),
+ tr("The launcher is in the folder it was extracted from, therefore it cannot create shortcuts."));
+ 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)
if (appPath.startsWith("/tmp/.mount_")) {
// AppImage!
@@ -1598,7 +1661,11 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered()
#endif
args.append({ "--launch", m_selectedInstance->id() });
if (FS::createShortcut(desktopFilePath, appPath, args, m_selectedInstance->name(), iconPath)) {
- QMessageBox::information(this, tr("Create instance shortcut"), tr("Created a shortcut to this instance on your desktop!"));
+#if not defined(Q_OS_MACOS)
+ 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 {
#if not defined(Q_OS_MACOS)
iconFile.remove();
diff --git a/launcher/ui/MainWindow.h b/launcher/ui/MainWindow.h
index 3bb20c4a..5f74b501 100644
--- a/launcher/ui/MainWindow.h
+++ b/launcher/ui/MainWindow.h
@@ -157,6 +157,7 @@ private slots:
inline void on_actionExportInstance_triggered() { on_actionExportInstanceZip_triggered(); }
void on_actionExportInstanceZip_triggered();
void on_actionExportInstanceMrPack_triggered();
+ void on_actionExportInstanceFlamePack_triggered();
void on_actionRenameInstance_triggered();
diff --git a/launcher/ui/MainWindow.ui b/launcher/ui/MainWindow.ui
index 113dfc1e..190219b9 100644
--- a/launcher/ui/MainWindow.ui
+++ b/launcher/ui/MainWindow.ui
@@ -479,6 +479,14 @@
<string>Modrinth (mrpack)</string>
</property>
</action>
+ <action name="actionExportInstanceFlamePack">
+ <property name="icon">
+ <iconset theme="flame"/>
+ </property>
+ <property name="text">
+ <string>CurseForge (zip)</string>
+ </property>
+ </action>
<action name="actionCreateInstanceShortcut">
<property name="icon">
<iconset theme="shortcut">
diff --git a/launcher/ui/dialogs/ExportMrPackDialog.cpp b/launcher/ui/dialogs/ExportPackDialog.cpp
index b302f7ba..2abe2805 100644
--- a/launcher/ui/dialogs/ExportMrPackDialog.cpp
+++ b/launcher/ui/dialogs/ExportPackDialog.cpp
@@ -16,11 +16,13 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-#include "ExportMrPackDialog.h"
+#include "ExportPackDialog.h"
#include "minecraft/mod/ModFolderModel.h"
+#include "modplatform/ModIndex.h"
+#include "modplatform/flame/FlamePackExportTask.h"
#include "ui/dialogs/CustomMessageBox.h"
#include "ui/dialogs/ProgressDialog.h"
-#include "ui_ExportMrPackDialog.h"
+#include "ui_ExportPackDialog.h"
#include <QFileDialog>
#include <QFileSystemModel>
@@ -32,17 +34,24 @@
#include "MMCZip.h"
#include "modplatform/modrinth/ModrinthPackExportTask.h"
-ExportMrPackDialog::ExportMrPackDialog(InstancePtr instance, QWidget* parent)
- : QDialog(parent), instance(instance), ui(new Ui::ExportMrPackDialog)
+ExportPackDialog::ExportPackDialog(InstancePtr instance, QWidget* parent, ModPlatform::ResourceProvider provider)
+ : QDialog(parent), instance(instance), ui(new Ui::ExportPackDialog), m_provider(provider)
{
ui->setupUi(this);
ui->name->setText(instance->name());
- ui->summary->setText(instance->notes().split(QRegularExpression("\\r?\\n"))[0]);
+ if (m_provider == ModPlatform::ResourceProvider::MODRINTH) {
+ 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
// the name and version fields mustn't be empty
- connect(ui->name, &QLineEdit::textEdited, this, &ExportMrPackDialog::validate);
- connect(ui->version, &QLineEdit::textEdited, this, &ExportMrPackDialog::validate);
+ connect(ui->name, &QLineEdit::textEdited, this, &ExportPackDialog::validate);
+ connect(ui->version, &QLineEdit::textEdited, this, &ExportPackDialog::validate);
// the instance name can technically be empty
validate();
@@ -66,6 +75,7 @@ ExportMrPackDialog::ExportMrPackDialog(InstancePtr instance, QWidget* parent)
MinecraftInstance* mcInstance = dynamic_cast<MinecraftInstance*>(instance.get());
if (mcInstance) {
+ mcInstance->loaderModList()->update();
const QDir index = mcInstance->loaderModList()->indexDir();
if (index.exists())
proxy->blockedPaths().insert(root.relativeFilePath(index.absolutePath()));
@@ -83,43 +93,54 @@ ExportMrPackDialog::ExportMrPackDialog(InstancePtr instance, QWidget* parent)
headerView->setSectionResizeMode(0, QHeaderView::Stretch);
}
-ExportMrPackDialog::~ExportMrPackDialog()
+ExportPackDialog::~ExportPackDialog()
{
delete ui;
}
-void ExportMrPackDialog::done(int result)
+void ExportPackDialog::done(int result)
{
if (result == Accepted) {
const QString filename = FS::RemoveInvalidFilenameChars(ui->name->text());
- const QString output = QFileDialog::getSaveFileName(this, tr("Export %1").arg(ui->name->text()),
- FS::PathCombine(QDir::homePath(), filename + ".mrpack"),
- "Modrinth pack (*.mrpack *.zip)", nullptr);
+ QString output;
+ if (m_provider == ModPlatform::ResourceProvider::MODRINTH)
+ 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())
return;
-
- ModrinthPackExportTask task(ui->name->text(), ui->version->text(), ui->summary->text(), instance, output,
- std::bind(&FileIgnoreProxy::filterFile, proxy, std::placeholders::_1));
-
- connect(&task, &Task::failed,
+ Task* task;
+ 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));
+
+ connect(task, &Task::failed,
[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)
->show();
});
+ connect(task, &Task::finished, [task] { task->deleteLater(); });
ProgressDialog progress(this);
progress.setSkipButton(true, tr("Abort"));
- if (progress.execWithTask(&task) != QDialog::Accepted)
+ if (progress.execWithTask(task) != QDialog::Accepted)
return;
}
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);
}
diff --git a/launcher/ui/dialogs/ExportMrPackDialog.h b/launcher/ui/dialogs/ExportPackDialog.h
index 1c70c4ae..830c24d2 100644
--- a/launcher/ui/dialogs/ExportMrPackDialog.h
+++ b/launcher/ui/dialogs/ExportPackDialog.h
@@ -22,24 +22,28 @@
#include "BaseInstance.h"
#include "FastFileIconProvider.h"
#include "FileIgnoreProxy.h"
+#include "modplatform/ModIndex.h"
namespace Ui {
-class ExportMrPackDialog;
+class ExportPackDialog;
}
-class ExportMrPackDialog : public QDialog {
+class ExportPackDialog : public QDialog {
Q_OBJECT
public:
- explicit ExportMrPackDialog(InstancePtr instance, QWidget* parent = nullptr);
- ~ExportMrPackDialog();
+ explicit ExportPackDialog(InstancePtr instance,
+ QWidget* parent = nullptr,
+ ModPlatform::ResourceProvider provider = ModPlatform::ResourceProvider::MODRINTH);
+ ~ExportPackDialog();
void done(int result) override;
void validate();
private:
const InstancePtr instance;
- Ui::ExportMrPackDialog* ui;
+ Ui::ExportPackDialog* ui;
FileIgnoreProxy* proxy;
FastFileIconProvider icons;
+ const ModPlatform::ResourceProvider m_provider;
};
diff --git a/launcher/ui/dialogs/ExportMrPackDialog.ui b/launcher/ui/dialogs/ExportPackDialog.ui
index 9a789737..3976e28f 100644
--- a/launcher/ui/dialogs/ExportMrPackDialog.ui
+++ b/launcher/ui/dialogs/ExportPackDialog.ui
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
- <class>ExportMrPackDialog</class>
- <widget class="QDialog" name="ExportMrPackDialog">
+ <class>ExportPackDialog</class>
+ <widget class="QDialog" name="ExportPackDialog">
<property name="geometry">
<rect>
<x>0</x>
@@ -11,7 +11,7 @@
</rect>
</property>
<property name="windowTitle">
- <string>Export Modrinth Pack</string>
+ <string>Export Pack</string>
</property>
<property name="sizeGripEnabled">
<bool>true</bool>
@@ -24,7 +24,7 @@
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="3" column="0">
- <widget class="QLabel" name="versionLabel">
+ <widget class="QLabel" name="summaryLabel">
<property name="text">
<string>Summary</string>
</property>
@@ -41,7 +41,7 @@
</widget>
</item>
<item row="1" column="0">
- <widget class="QLabel" name="summaryLabel">
+ <widget class="QLabel" name="versionLabel">
<property name="text">
<string>Version</string>
</property>
@@ -57,6 +57,7 @@
</property>
</widget>
</item>
+
</layout>
</widget>
</item>
@@ -103,7 +104,7 @@
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
- <receiver>ExportMrPackDialog</receiver>
+ <receiver>ExportPackDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
@@ -119,7 +120,7 @@
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
- <receiver>ExportMrPackDialog</receiver>
+ <receiver>ExportPackDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.cpp b/launcher/ui/dialogs/ResourceDownloadDialog.cpp
index 4f59f560..b17eced3 100644
--- a/launcher/ui/dialogs/ResourceDownloadDialog.cpp
+++ b/launcher/ui/dialogs/ResourceDownloadDialog.cpp
@@ -43,6 +43,8 @@
#include "ui/pages/modplatform/flame/FlameResourcePages.h"
#include "ui/pages/modplatform/modrinth/ModrinthResourcePages.h"
+#include "modplatform/flame/FlameAPI.h"
+#include "modplatform/modrinth/ModrinthAPI.h"
#include "ui/widgets/PageContainer.h"
namespace ResourceDownload {
@@ -281,8 +283,11 @@ QList<BasePage*> ModDownloadDialog::getPages()
{
QList<BasePage*> pages;
- pages.append(ModrinthModPage::create(this, *m_instance));
- if (APPLICATION->capabilities() & Application::SupportsFlame)
+ auto loaders = static_cast<MinecraftInstance*>(m_instance)->getPackProfile()->getModLoaders().value();
+
+ if (ModrinthAPI::validateModLoaders(loaders))
+ pages.append(ModrinthModPage::create(this, *m_instance));
+ if (APPLICATION->capabilities() & Application::SupportsFlame && FlameAPI::validateModLoaders(loaders))
pages.append(FlameModPage::create(this, *m_instance));
m_selectedPage = dynamic_cast<ModPage*>(pages[0]);
diff --git a/launcher/ui/pages/global/LauncherPage.ui b/launcher/ui/pages/global/LauncherPage.ui
index d9116bfc..26408f44 100644
--- a/launcher/ui/pages/global/LauncherPage.ui
+++ b/launcher/ui/pages/global/LauncherPage.ui
@@ -169,7 +169,7 @@
<item>
<widget class="QCheckBox" name="metadataDisableBtn">
<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 name="text">
<string>Disable using metadata for mods</string>
diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.cpp b/launcher/ui/pages/instance/ExternalResourcesPage.cpp
index 173bcb66..12038f88 100644
--- a/launcher/ui/pages/instance/ExternalResourcesPage.cpp
+++ b/launcher/ui/pages/instance/ExternalResourcesPage.cpp
@@ -151,9 +151,6 @@ void ExternalResourcesPage::retranslate()
void ExternalResourcesPage::itemActivated(const QModelIndex&)
{
- if (!m_controlsEnabled)
- return;
-
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection());
}
@@ -197,9 +194,6 @@ bool ExternalResourcesPage::eventFilter(QObject* obj, QEvent* ev)
void ExternalResourcesPage::addItem()
{
- if (!m_controlsEnabled)
- return;
-
auto list = GuiUtil::BrowseForFiles(
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());
@@ -213,9 +207,6 @@ void ExternalResourcesPage::addItem()
void ExternalResourcesPage::removeItem()
{
- if (!m_controlsEnabled)
- return;
-
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection());
int count = 0;
@@ -259,23 +250,37 @@ void ExternalResourcesPage::removeItem()
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());
}
void ExternalResourcesPage::enableItem()
{
- if (!m_controlsEnabled)
- return;
-
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection());
m_model->setResourceEnabled(selection.indexes(), EnableAction::ENABLE);
}
void ExternalResourcesPage::disableItem()
{
- if (!m_controlsEnabled)
- return;
+ if (m_instance != nullptr && m_instance->isRunning()) {
+ 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());
m_model->setResourceEnabled(selection.indexes(), EnableAction::DISABLE);
}
diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.h b/launcher/ui/pages/instance/ExternalResourcesPage.h
index 6c0a12cb..97d922d8 100644
--- a/launcher/ui/pages/instance/ExternalResourcesPage.h
+++ b/launcher/ui/pages/instance/ExternalResourcesPage.h
@@ -73,7 +73,5 @@ class ExternalResourcesPage : public QMainWindow, public BasePage {
QString m_fileSelectionFilter;
QString m_viewFilter;
- bool m_controlsEnabled = true;
-
std::shared_ptr<Setting> m_wide_bar_setting = nullptr;
};
diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp
index 2a7c5b27..943ff17f 100644
--- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp
+++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp
@@ -60,17 +60,13 @@ InstanceSettingsPage::InstanceSettingsPage(BaseInstance *inst, QWidget *parent)
m_settings = inst->settings();
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(APPLICATION, &Application::globalSettingsAboutToOpen, this, &InstanceSettingsPage::applySettings);
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();
-
updateThresholds();
}
@@ -523,8 +519,3 @@ void InstanceSettingsPage::updateThresholds()
ui->labelMaxMemIcon->setPixmap(pix);
}
}
-
-void InstanceSettingsPage::updateRunningStatus(bool running)
-{
- setEnabled(!running);
-}
diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.h b/launcher/ui/pages/instance/InstanceSettingsPage.h
index 0438fe3b..036b4181 100644
--- a/launcher/ui/pages/instance/InstanceSettingsPage.h
+++ b/launcher/ui/pages/instance/InstanceSettingsPage.h
@@ -79,8 +79,7 @@ public:
void updateThresholds();
-private slots:
- void updateRunningStatus(bool running);
+ private slots:
void on_javaDetectBtn_clicked();
void on_javaTestBtn_clicked();
void on_javaBrowseBtn_clicked();
diff --git a/launcher/ui/pages/instance/ManagedPackPage.cpp b/launcher/ui/pages/instance/ManagedPackPage.cpp
index d89c5bfc..0fc0c986 100644
--- a/launcher/ui/pages/instance/ManagedPackPage.cpp
+++ b/launcher/ui/pages/instance/ManagedPackPage.cpp
@@ -69,7 +69,6 @@ class NoBigComboBoxStyle : public QProxyStyle {
private:
NoBigComboBoxStyle(QStyle* style) : QProxyStyle(style) {}
-
};
ManagedPackPage* ManagedPackPage::createPage(BaseInstance* inst, QString type, QWidget* parent)
@@ -91,13 +90,13 @@ ManagedPackPage::ManagedPackPage(BaseInstance* inst, InstanceWindow* instance_wi
// NOTE: GTK2 themes crash with the proxy style.
// This seems like an upstream bug, so there's not much else that can be done.
- if (!QStyleFactory::keys().contains("gtk2")){
+ if (!QStyleFactory::keys().contains("gtk2")) {
auto comboStyle = NoBigComboBoxStyle::getInstance(ui->versionsComboBox->style());
ui->versionsComboBox->setStyle(comboStyle);
}
ui->reloadButton->setVisible(false);
- connect(ui->reloadButton, &QPushButton::clicked, this, [this](bool){
+ connect(ui->reloadButton, &QPushButton::clicked, this, [this](bool) {
ui->reloadButton->setVisible(false);
m_loaded = false;
@@ -226,7 +225,8 @@ void ModrinthManagedPackPage::parseManagedPack()
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] {
QJsonParseError parse_error{};
@@ -267,7 +267,6 @@ void ModrinthManagedPackPage::parseManagedPack()
if (version.version == m_inst->getManagedPackVersionName())
name = tr("%1 (Current)").arg(name);
-
ui->versionsComboBox->addItem(name, QVariant(version.id));
}
@@ -291,6 +290,10 @@ QString ModrinthManagedPackPage::url() const
void ModrinthManagedPackPage::suggestVersion()
{
auto index = ui->versionsComboBox->currentIndex();
+ if (m_pack.versions.length() == 0) {
+ setFailState();
+ return;
+ }
auto version = m_pack.versions.at(index);
ui->changelogTextBrowser->setHtml(markdownToHTML(version.changelog.toUtf8()));
@@ -301,6 +304,10 @@ void ModrinthManagedPackPage::suggestVersion()
void ModrinthManagedPackPage::update()
{
auto index = ui->versionsComboBox->currentIndex();
+ if (m_pack.versions.length() == 0) {
+ setFailState();
+ return;
+ }
auto version = m_pack.versions.at(index);
QMap<QString, QString> extra_info;
@@ -429,6 +436,10 @@ QString FlameManagedPackPage::url() const
void FlameManagedPackPage::suggestVersion()
{
auto index = ui->versionsComboBox->currentIndex();
+ if (m_pack.versions.length() == 0) {
+ setFailState();
+ return;
+ }
auto version = m_pack.versions.at(index);
ui->changelogTextBrowser->setHtml(m_api.getModFileChangelog(m_inst->getManagedPackID().toInt(), version.fileId));
@@ -439,6 +450,10 @@ void FlameManagedPackPage::suggestVersion()
void FlameManagedPackPage::update()
{
auto index = ui->versionsComboBox->currentIndex();
+ if (m_pack.versions.length() == 0) {
+ setFailState();
+ return;
+ }
auto version = m_pack.versions.at(index);
QMap<QString, QString> extra_info;
diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp
index 041d1bfd..cef292bd 100644
--- a/launcher/ui/pages/instance/ModFolderPage.cpp
+++ b/launcher/ui/pages/instance/ModFolderPage.cpp
@@ -89,12 +89,10 @@ ModFolderPage::ModFolderPage(BaseInstance* inst, std::shared_ptr<ModFolderModel>
connect(ui->actionUpdateItem, &QAction::triggered, this, &ModFolderPage::updateMods);
ui->actionVisitItemPage->setToolTip(tr("Go to mod's home page"));
- ui->actionsToolbar->insertActionAfter(ui->actionViewFolder, ui->actionVisitItemPage);
+ ui->actionsToolbar->addAction(ui->actionVisitItemPage);
connect(ui->actionVisitItemPage, &QAction::triggered, this, &ModFolderPage::visitModPages);
- auto check_allow_update = [this] {
- return (!m_instance || !m_instance->isRunning()) && (ui->treeView->selectionModel()->hasSelection() || !m_model->empty());
- };
+ auto check_allow_update = [this] { return ui->treeView->selectionModel()->hasSelection() || !m_model->empty(); };
connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, [this, check_allow_update] {
ui->actionUpdateItem->setEnabled(check_allow_update());
@@ -121,22 +119,9 @@ ModFolderPage::ModFolderPage(BaseInstance* inst, std::shared_ptr<ModFolderModel>
connect(mods.get(), &ModFolderModel::updateFinished, this,
[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
{
return true;
@@ -155,13 +140,21 @@ bool ModFolderPage::onSelectionChanged(const QModelIndex& current, const QModelI
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());
}
void ModFolderPage::installMods()
{
- if (!m_controlsEnabled)
- return;
if (m_instance->typeName() != "Minecraft")
return; // this is a null instance or a legacy instance
diff --git a/launcher/ui/pages/instance/ModFolderPage.h b/launcher/ui/pages/instance/ModFolderPage.h
index 2c1a9c77..a23dcae1 100644
--- a/launcher/ui/pages/instance/ModFolderPage.h
+++ b/launcher/ui/pages/instance/ModFolderPage.h
@@ -60,7 +60,6 @@ class ModFolderPage : public ExternalResourcesPage {
bool onSelectionChanged(const QModelIndex& current, const QModelIndex& previous) override;
private slots:
- void runningStateChanged(bool running);
void removeItems(const QItemSelection& selection) override;
void installMods();
diff --git a/launcher/ui/pages/instance/ResourcePackPage.cpp b/launcher/ui/pages/instance/ResourcePackPage.cpp
index 24bfb38d..12b371df 100644
--- a/launcher/ui/pages/instance/ResourcePackPage.cpp
+++ b/launcher/ui/pages/instance/ResourcePackPage.cpp
@@ -67,8 +67,6 @@ bool ResourcePackPage::onSelectionChanged(const QModelIndex& current, const QMod
void ResourcePackPage::downloadRPs()
{
- if (!m_controlsEnabled)
- return;
if (m_instance->typeName() != "Minecraft")
return; // this is a null instance or a legacy instance
diff --git a/launcher/ui/pages/instance/ShaderPackPage.cpp b/launcher/ui/pages/instance/ShaderPackPage.cpp
index 2d0c10aa..dc8b0a05 100644
--- a/launcher/ui/pages/instance/ShaderPackPage.cpp
+++ b/launcher/ui/pages/instance/ShaderPackPage.cpp
@@ -46,7 +46,6 @@
#include "ui/dialogs/ProgressDialog.h"
#include "ui/dialogs/ResourceDownloadDialog.h"
-
ShaderPackPage::ShaderPackPage(MinecraftInstance* instance, std::shared_ptr<ShaderPackFolderModel> model, QWidget* parent)
: ExternalResourcesPage(instance, model, parent)
{
@@ -61,8 +60,6 @@ ShaderPackPage::ShaderPackPage(MinecraftInstance* instance, std::shared_ptr<Shad
void ShaderPackPage::downloadShaders()
{
- if (!m_controlsEnabled)
- return;
if (m_instance->typeName() != "Minecraft")
return; // this is a null instance or a legacy instance
diff --git a/launcher/ui/pages/instance/TexturePackPage.cpp b/launcher/ui/pages/instance/TexturePackPage.cpp
index 427aba11..e477ceda 100644
--- a/launcher/ui/pages/instance/TexturePackPage.cpp
+++ b/launcher/ui/pages/instance/TexturePackPage.cpp
@@ -69,8 +69,6 @@ bool TexturePackPage::onSelectionChanged(const QModelIndex& current, const QMode
void TexturePackPage::downloadTPs()
{
- if (!m_controlsEnabled)
- return;
if (m_instance->typeName() != "Minecraft")
return; // this is a null instance or a legacy instance
diff --git a/launcher/ui/pages/instance/VersionPage.cpp b/launcher/ui/pages/instance/VersionPage.cpp
index 59107c53..a180c804 100644
--- a/launcher/ui/pages/instance/VersionPage.cpp
+++ b/launcher/ui/pages/instance/VersionPage.cpp
@@ -40,14 +40,13 @@
#include "Application.h"
-#include <QMessageBox>
-#include <QLabel>
+#include <QAbstractItemModel>
#include <QEvent>
#include <QKeyEvent>
+#include <QLabel>
+#include <QListView>
#include <QMenu>
-#include <QAbstractItemModel>
#include <QMessageBox>
-#include <QListView>
#include <QString>
#include <QUrl>
@@ -55,49 +54,42 @@
#include "ui_VersionPage.h"
#include "ui/dialogs/CustomMessageBox.h"
-#include "ui/dialogs/VersionSelectDialog.h"
#include "ui/dialogs/NewComponentDialog.h"
#include "ui/dialogs/ProgressDialog.h"
+#include "ui/dialogs/VersionSelectDialog.h"
#include "ui/GuiUtil.h"
+#include "DesktopServices.h"
+#include "Exception.h"
+#include "Version.h"
+#include "icons/IconList.h"
#include "minecraft/PackProfile.h"
#include "minecraft/auth/AccountList.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/VersionList.h"
-class IconProxy : public QIdentityProxyModel
-{
+class IconProxy : public QIdentityProxyModel {
Q_OBJECT
-public:
-
- IconProxy(QWidget *parentWidget) : QIdentityProxyModel(parentWidget)
+ public:
+ IconProxy(QWidget* parentWidget) : QIdentityProxyModel(parentWidget)
{
connect(parentWidget, &QObject::destroyed, this, &IconProxy::widgetGone);
m_parentWidget = parentWidget;
}
- virtual QVariant data(const QModelIndex &proxyIndex, int role = Qt::DisplayRole) const override
+ virtual QVariant data(const QModelIndex& proxyIndex, int role = Qt::DisplayRole) const override
{
QVariant var = QIdentityProxyModel::data(proxyIndex, role);
int column = proxyIndex.column();
- if(column == 0 && role == Qt::DecorationRole && m_parentWidget)
- {
- if(!var.isNull())
- {
+ if (column == 0 && role == Qt::DecorationRole && m_parentWidget) {
+ if (!var.isNull()) {
auto string = var.toString();
- if(string == "warning")
- {
+ if (string == "warning") {
return APPLICATION->getThemedIcon("status-yellow");
- }
- else if(string == "error")
- {
+ } else if (string == "error") {
return APPLICATION->getThemedIcon("status-bad");
}
}
@@ -105,14 +97,11 @@ public:
}
return var;
}
-private slots:
- void widgetGone()
- {
- m_parentWidget = nullptr;
- }
+ private slots:
+ void widgetGone() { m_parentWidget = nullptr; }
-private:
- QWidget *m_parentWidget = nullptr;
+ private:
+ QWidget* m_parentWidget = nullptr;
};
QIcon VersionPage::icon() const
@@ -144,15 +133,14 @@ void VersionPage::closedImpl()
m_wide_bar_setting->set(ui->toolBar->getVisibilityState());
}
-QMenu * VersionPage::createPopupMenu()
+QMenu* VersionPage::createPopupMenu()
{
QMenu* filteredMenu = QMainWindow::createPopupMenu();
- filteredMenu->removeAction( ui->toolBar->toggleViewAction() );
+ filteredMenu->removeAction(ui->toolBar->toggleViewAction());
return filteredMenu;
}
-VersionPage::VersionPage(MinecraftInstance *inst, QWidget *parent)
- : QMainWindow(parent), ui(new Ui::VersionPage), m_inst(inst)
+VersionPage::VersionPage(MinecraftInstance* inst, QWidget* parent) : QMainWindow(parent), ui(new Ui::VersionPage), m_inst(inst)
{
ui->setupUi(this);
@@ -182,10 +170,8 @@ VersionPage::VersionPage(MinecraftInstance *inst, QWidget *parent)
connect(smodel, &QItemSelectionModel::currentChanged, this, &VersionPage::packageCurrent);
connect(m_profile.get(), &PackProfile::minecraftChanged, this, &VersionPage::updateVersionControls);
- controlsEnabled = !m_inst->isRunning();
updateVersionControls();
preselect(0);
- connect(m_inst, &BaseInstance::runningStatusChanged, this, &VersionPage::updateRunningStatus);
connect(ui->packageView, &ModListView::customContextMenuRequested, this, &VersionPage::showContextMenu);
connect(ui->filterEdit, &QLineEdit::textChanged, this, &VersionPage::onFilterTextChanged);
}
@@ -202,18 +188,16 @@ void VersionPage::showContextMenu(const QPoint& pos)
delete menu;
}
-void VersionPage::packageCurrent(const QModelIndex &current, const QModelIndex &previous)
+void VersionPage::packageCurrent(const QModelIndex& current, const QModelIndex& previous)
{
- if (!current.isValid())
- {
+ if (!current.isValid()) {
ui->frame->clear();
return;
}
int row = current.row();
auto patch = m_profile->getComponent(row);
auto severity = patch->getProblemSeverity();
- switch(severity)
- {
+ switch (severity) {
case ProblemSeverity::Warning:
ui->frame->setName(tr("%1 possibly has issues.").arg(patch->getName()));
break;
@@ -226,16 +210,12 @@ void VersionPage::packageCurrent(const QModelIndex &current, const QModelIndex &
return;
}
- auto &problems = patch->getProblems();
+ auto& problems = patch->getProblems();
QString problemOut;
- for (auto &problem: problems)
- {
- if(problem.m_severity == ProblemSeverity::Error)
- {
+ for (auto& problem : problems) {
+ if (problem.m_severity == ProblemSeverity::Error) {
problemOut += tr("Error: ");
- }
- else if(problem.m_severity == ProblemSeverity::Warning)
- {
+ } else if (problem.m_severity == ProblemSeverity::Warning) {
problemOut += tr("Warning: ");
}
problemOut += problem.m_description;
@@ -244,71 +224,47 @@ void VersionPage::packageCurrent(const QModelIndex &current, const QModelIndex &
ui->frame->setDescription(problemOut);
}
-void VersionPage::updateRunningStatus(bool running)
-{
- if(controlsEnabled == running) {
- controlsEnabled = !running;
- updateVersionControls();
- }
-}
-
void VersionPage::updateVersionControls()
{
// FIXME: this is a dirty hack
auto minecraftVersion = Version(m_profile->getComponentVersion("net.minecraft"));
- ui->actionInstall_Forge->setEnabled(controlsEnabled);
-
bool supportsFabric = minecraftVersion >= Version("1.14");
- ui->actionInstall_Fabric->setEnabled(controlsEnabled && supportsFabric);
+ ui->actionInstall_Fabric->setEnabled(supportsFabric);
bool supportsQuilt = minecraftVersion >= Version("1.14");
- ui->actionInstall_Quilt->setEnabled(controlsEnabled && supportsQuilt);
+ ui->actionInstall_Quilt->setEnabled(supportsQuilt);
bool supportsLiteLoader = minecraftVersion <= Version("1.12.2");
- ui->actionInstall_LiteLoader->setEnabled(controlsEnabled && supportsLiteLoader);
+ ui->actionInstall_LiteLoader->setEnabled(supportsLiteLoader);
updateButtons();
}
void VersionPage::updateButtons(int row)
{
- if(row == -1)
+ if (row == -1)
row = currentRow();
auto patch = m_profile->getComponent(row);
- ui->actionRemove->setEnabled(controlsEnabled && patch && patch->isRemovable());
- ui->actionMove_down->setEnabled(controlsEnabled && patch && patch->isMoveable());
- ui->actionMove_up->setEnabled(controlsEnabled && patch && patch->isMoveable());
- ui->actionChange_version->setEnabled(controlsEnabled && patch && patch->isVersionChangeable());
- ui->actionEdit->setEnabled(controlsEnabled && patch && patch->isCustom());
- ui->actionCustomize->setEnabled(controlsEnabled && patch && patch->isCustomizable());
- ui->actionRevert->setEnabled(controlsEnabled && 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);
+ ui->actionRemove->setEnabled(patch && patch->isRemovable());
+ ui->actionMove_down->setEnabled(patch && patch->isMoveable());
+ ui->actionMove_up->setEnabled(patch && patch->isMoveable());
+ ui->actionChange_version->setEnabled(patch && patch->isVersionChangeable());
+ ui->actionEdit->setEnabled(patch && patch->isCustom());
+ ui->actionCustomize->setEnabled(patch && patch->isCustomizable());
+ ui->actionRevert->setEnabled(patch && patch->isRevertible());
}
bool VersionPage::reloadPackProfile()
{
- try
- {
+ try {
m_profile->reload(Net::Mode::Online);
return true;
- }
- catch (const Exception &e)
- {
+ } catch (const Exception& e) {
QMessageBox::critical(this, tr("Error"), e.cause());
return false;
- }
- catch (...)
- {
- QMessageBox::critical(
- this, tr("Error"),
- tr("Couldn't load the instance profile."));
+ } catch (...) {
+ QMessageBox::critical(this, tr("Error"), tr("Couldn't load the instance profile."));
return false;
}
}
@@ -321,14 +277,12 @@ void VersionPage::on_actionReload_triggered()
void VersionPage::on_actionRemove_triggered()
{
- if (!ui->packageView->currentIndex().isValid())
- {
+ if (!ui->packageView->currentIndex().isValid()) {
return;
}
int index = ui->packageView->currentIndex().row();
auto component = m_profile->getComponent(index);
- if (component->isCustom())
- {
+ if (component->isCustom()) {
auto response = CustomMessageBox::selectable(this, tr("Confirm Removal"),
tr("You are about to remove \"%1\".\n"
"This is permanent and will completely remove the custom component.\n\n"
@@ -341,8 +295,7 @@ void VersionPage::on_actionRemove_triggered()
return;
}
// 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"));
}
updateButtons();
@@ -352,17 +305,16 @@ void VersionPage::on_actionRemove_triggered()
void VersionPage::on_actionInstall_mods_triggered()
{
- if(m_container)
- {
+ if (m_container) {
m_container->selectPage("mods");
}
}
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());
- if(!list.empty())
- {
+ auto list = GuiUtil::BrowseForFiles("jarmod", tr("Select jar mods"), tr("Minecraft.jar mods (*.zip *.jar)"),
+ APPLICATION->settings()->get("CentralModsDir").toString(), this->parentWidget());
+ if (!list.empty()) {
m_profile->installJarMods(list);
}
updateButtons();
@@ -370,9 +322,9 @@ void VersionPage::on_actionAdd_to_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());
- if(!jarPath.isEmpty())
- {
+ auto jarPath = GuiUtil::BrowseForFile("jar", tr("Select jar"), tr("Minecraft.jar replacement (*.jar)"),
+ APPLICATION->settings()->get("CentralModsDir").toString(), this->parentWidget());
+ if (!jarPath.isEmpty()) {
m_profile->installCustomJar(jarPath);
}
updateButtons();
@@ -406,12 +358,9 @@ void VersionPage::on_actionAdd_Agents_triggered()
void VersionPage::on_actionMove_up_triggered()
{
- try
- {
+ try {
m_profile->move(currentRow(), PackProfile::MoveUp);
- }
- catch (const Exception &e)
- {
+ } catch (const Exception& e) {
QMessageBox::critical(this, tr("Error"), e.cause());
}
updateButtons();
@@ -419,12 +368,9 @@ void VersionPage::on_actionMove_up_triggered()
void VersionPage::on_actionMove_down_triggered()
{
- try
- {
+ try {
m_profile->move(currentRow(), PackProfile::MoveDown);
- }
- catch (const Exception &e)
- {
+ } catch (const Exception& e) {
QMessageBox::critical(this, tr("Error"), e.cause());
}
updateButtons();
@@ -433,39 +379,32 @@ void VersionPage::on_actionMove_down_triggered()
void VersionPage::on_actionChange_version_triggered()
{
auto versionRow = currentRow();
- if(versionRow == -1)
- {
+ if (versionRow == -1) {
return;
}
auto patch = m_profile->getComponent(versionRow);
auto name = patch->getName();
auto list = patch->getVersionList();
- if(!list)
- {
+ if (!list) {
return;
}
auto uid = list->uid();
// 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();
return;
- }
- else if (uid == "com.mumfrey.liteloader")
- {
+ } else if (uid == "com.mumfrey.liteloader") {
on_actionInstall_LiteLoader_triggered();
return;
}
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.setEmptyErrorString(tr("Couldn't load or download the intermediary mappings version lists!"));
vselect.setExactFilter(BaseVersionList::ParentVersionRole, m_profile->getComponentVersion("net.minecraft"));
}
auto currentVersion = patch->getVersion();
- if(!currentVersion.isEmpty())
- {
+ if (!currentVersion.isEmpty()) {
vselect.setCurrentVersion(currentVersion);
}
if (!vselect.exec() || !vselect.selectedVersion())
@@ -473,8 +412,7 @@ void VersionPage::on_actionChange_version_triggered()
qDebug() << "Change" << uid << "to" << vselect.selectedVersion()->descriptor();
bool important = false;
- if(uid == "net.minecraft")
- {
+ if (uid == "net.minecraft") {
important = true;
}
m_profile->setComponentVersion(uid, vselect.selectedVersion()->descriptor(), important);
@@ -484,19 +422,17 @@ void VersionPage::on_actionChange_version_triggered()
void VersionPage::on_actionDownload_All_triggered()
{
- if (!APPLICATION->accounts()->anyAccountIsValid())
- {
- CustomMessageBox::selectable(
- this, tr("Error"),
- tr("Cannot download Minecraft or update instances unless you have at least "
- "one account added.\nPlease add your Mojang or Minecraft account."),
- QMessageBox::Warning)->show();
+ if (!APPLICATION->accounts()->anyAccountIsValid()) {
+ CustomMessageBox::selectable(this, tr("Error"),
+ tr("Cannot download Minecraft or update instances unless you have at least "
+ "one account added.\nPlease add your Mojang or Minecraft account."),
+ QMessageBox::Warning)
+ ->show();
return;
}
auto updateTask = m_inst->createUpdateTask(Net::Mode::Online);
- if (!updateTask)
- {
+ if (!updateTask) {
return;
}
ProgressDialog tDialog(this);
@@ -510,28 +446,26 @@ void VersionPage::on_actionDownload_All_triggered()
void VersionPage::on_actionInstall_Forge_triggered()
{
auto vlist = APPLICATION->metadataIndex()->get("net.minecraftforge");
- if(!vlist)
- {
+ if (!vlist) {
return;
}
VersionSelectDialog vselect(vlist.get(), tr("Select Forge version"), this);
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!"));
auto currentVersion = m_profile->getComponentVersion("net.minecraftforge");
- if(!currentVersion.isEmpty())
- {
+ if (!currentVersion.isEmpty()) {
vselect.setCurrentVersion(currentVersion);
}
- if (vselect.exec() && vselect.selectedVersion())
- {
+ if (vselect.exec() && vselect.selectedVersion()) {
auto vsn = vselect.selectedVersion();
m_profile->setComponentVersion("net.minecraftforge", vsn->descriptor());
m_profile->resolve(Net::Mode::Online);
// m_profile->installVersion();
- preselect(m_profile->rowCount(QModelIndex())-1);
+ preselect(m_profile->rowCount(QModelIndex()) - 1);
m_container->refreshContainer();
}
}
@@ -539,8 +473,7 @@ void VersionPage::on_actionInstall_Forge_triggered()
void VersionPage::on_actionInstall_Fabric_triggered()
{
auto vlist = APPLICATION->metadataIndex()->get("net.fabricmc.fabric-loader");
- if(!vlist)
- {
+ if (!vlist) {
return;
}
VersionSelectDialog vselect(vlist.get(), tr("Select Fabric Loader version"), this);
@@ -548,17 +481,15 @@ void VersionPage::on_actionInstall_Fabric_triggered()
vselect.setEmptyErrorString(tr("Couldn't load or download the Fabric Loader version lists!"));
auto currentVersion = m_profile->getComponentVersion("net.fabricmc.fabric-loader");
- if(!currentVersion.isEmpty())
- {
+ if (!currentVersion.isEmpty()) {
vselect.setCurrentVersion(currentVersion);
}
- if (vselect.exec() && vselect.selectedVersion())
- {
+ if (vselect.exec() && vselect.selectedVersion()) {
auto vsn = vselect.selectedVersion();
m_profile->setComponentVersion("net.fabricmc.fabric-loader", vsn->descriptor());
m_profile->resolve(Net::Mode::Online);
- preselect(m_profile->rowCount(QModelIndex())-1);
+ preselect(m_profile->rowCount(QModelIndex()) - 1);
m_container->refreshContainer();
}
}
@@ -566,8 +497,7 @@ void VersionPage::on_actionInstall_Fabric_triggered()
void VersionPage::on_actionInstall_Quilt_triggered()
{
auto vlist = APPLICATION->metadataIndex()->get("org.quiltmc.quilt-loader");
- if(!vlist)
- {
+ if (!vlist) {
return;
}
VersionSelectDialog vselect(vlist.get(), tr("Select Quilt Loader version"), this);
@@ -575,17 +505,15 @@ void VersionPage::on_actionInstall_Quilt_triggered()
vselect.setEmptyErrorString(tr("Couldn't load or download the Quilt Loader version lists!"));
auto currentVersion = m_profile->getComponentVersion("org.quiltmc.quilt-loader");
- if(!currentVersion.isEmpty())
- {
+ if (!currentVersion.isEmpty()) {
vselect.setCurrentVersion(currentVersion);
}
- if (vselect.exec() && vselect.selectedVersion())
- {
+ if (vselect.exec() && vselect.selectedVersion()) {
auto vsn = vselect.selectedVersion();
m_profile->setComponentVersion("org.quiltmc.quilt-loader", vsn->descriptor());
m_profile->resolve(Net::Mode::Online);
- preselect(m_profile->rowCount(QModelIndex())-1);
+ preselect(m_profile->rowCount(QModelIndex()) - 1);
m_container->refreshContainer();
}
}
@@ -594,14 +522,12 @@ void VersionPage::on_actionAdd_Empty_triggered()
{
NewComponentDialog compdialog(QString(), QString(), this);
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);
blacklist.push_back(comp->getID());
}
compdialog.setBlacklist(blacklist);
- if (compdialog.exec())
- {
+ if (compdialog.exec()) {
qDebug() << "name:" << compdialog.name();
qDebug() << "uid:" << compdialog.uid();
m_profile->installEmpty(compdialog.uid(), compdialog.name());
@@ -611,28 +537,26 @@ void VersionPage::on_actionAdd_Empty_triggered()
void VersionPage::on_actionInstall_LiteLoader_triggered()
{
auto vlist = APPLICATION->metadataIndex()->get("com.mumfrey.liteloader");
- if(!vlist)
- {
+ if (!vlist) {
return;
}
VersionSelectDialog vselect(vlist.get(), tr("Select LiteLoader version"), this);
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!"));
auto currentVersion = m_profile->getComponentVersion("com.mumfrey.liteloader");
- if(!currentVersion.isEmpty())
- {
+ if (!currentVersion.isEmpty()) {
vselect.setCurrentVersion(currentVersion);
}
- if (vselect.exec() && vselect.selectedVersion())
- {
+ if (vselect.exec() && vselect.selectedVersion()) {
auto vsn = vselect.selectedVersion();
m_profile->setComponentVersion("com.mumfrey.liteloader", vsn->descriptor());
m_profile->resolve(Net::Mode::Online);
// m_profile->installVersion(vselect.selectedVersion());
- preselect(m_profile->rowCount(QModelIndex())-1);
+ preselect(m_profile->rowCount(QModelIndex()) - 1);
m_container->refreshContainer();
}
}
@@ -647,7 +571,7 @@ void VersionPage::on_actionMinecraftFolder_triggered()
DesktopServices::openDirectory(m_inst->gameRoot(), true);
}
-void VersionPage::versionCurrent(const QModelIndex &current, const QModelIndex &previous)
+void VersionPage::versionCurrent(const QModelIndex& current, const QModelIndex& previous)
{
currentIdx = current.row();
updateButtons(currentIdx);
@@ -655,16 +579,13 @@ void VersionPage::versionCurrent(const QModelIndex &current, const QModelIndex &
void VersionPage::preselect(int row)
{
- if(row < 0)
- {
+ if (row < 0) {
row = 0;
}
- if(row >= m_profile->rowCount(QModelIndex()))
- {
+ if (row >= m_profile->rowCount(QModelIndex())) {
row = m_profile->rowCount(QModelIndex()) - 1;
}
- if(row < 0)
- {
+ if (row < 0) {
return;
}
auto model_index = m_profile->index(row);
@@ -680,8 +601,7 @@ void VersionPage::onGameUpdateError(QString error)
ComponentPtr VersionPage::current()
{
auto row = currentRow();
- if(row < 0)
- {
+ if (row < 0) {
return nullptr;
}
return m_profile->getComponent(row);
@@ -689,8 +609,7 @@ ComponentPtr VersionPage::current()
int VersionPage::currentRow()
{
- if (ui->packageView->selectionModel()->selectedRows().isEmpty())
- {
+ if (ui->packageView->selectionModel()->selectedRows().isEmpty()) {
return -1;
}
return ui->packageView->selectionModel()->selectedRows().first().row();
@@ -699,18 +618,15 @@ int VersionPage::currentRow()
void VersionPage::on_actionCustomize_triggered()
{
auto version = currentRow();
- if(version == -1)
- {
+ if (version == -1) {
return;
}
auto patch = m_profile->getComponent(version);
- if(!patch->getVersionFile())
- {
+ if (!patch->getVersionFile()) {
// TODO: wait for the update task to finish here...
return;
}
- if(!m_profile->customize(version))
- {
+ if (!m_profile->customize(version)) {
// TODO: some error box here
}
updateButtons();
@@ -720,13 +636,11 @@ void VersionPage::on_actionCustomize_triggered()
void VersionPage::on_actionEdit_triggered()
{
auto version = current();
- if(!version)
- {
+ if (!version) {
return;
}
auto filename = version->getFilename();
- if(!QFileInfo::exists(filename))
- {
+ if (!QFileInfo::exists(filename)) {
qWarning() << "file" << filename << "can't be opened for editing, doesn't exist!";
return;
}
@@ -736,8 +650,7 @@ void VersionPage::on_actionEdit_triggered()
void VersionPage::on_actionRevert_triggered()
{
auto version = currentRow();
- if(version == -1)
- {
+ if (version == -1) {
return;
}
auto component = m_profile->getComponent(version);
@@ -753,8 +666,7 @@ void VersionPage::on_actionRevert_triggered()
if (response != QMessageBox::Yes)
return;
- if(!m_profile->revertToBase(version))
- {
+ if (!m_profile->revertToBase(version)) {
// TODO: some error box here
}
updateButtons();
@@ -762,7 +674,7 @@ void VersionPage::on_actionRevert_triggered()
m_container->refreshContainer();
}
-void VersionPage::onFilterTextChanged(const QString &newContents)
+void VersionPage::onFilterTextChanged(const QString& newContents)
{
m_filterModel->setFilterFixedString(newContents);
}
diff --git a/launcher/ui/pages/instance/VersionPage.h b/launcher/ui/pages/instance/VersionPage.h
index d0087714..45d383f4 100644
--- a/launcher/ui/pages/instance/VersionPage.h
+++ b/launcher/ui/pages/instance/VersionPage.h
@@ -46,38 +46,27 @@
#include "minecraft/PackProfile.h"
#include "ui/pages/BasePage.h"
-namespace Ui
-{
+namespace Ui {
class VersionPage;
}
-class VersionPage : public QMainWindow, public BasePage
-{
+class VersionPage : public QMainWindow, public BasePage {
Q_OBJECT
-public:
- explicit VersionPage(MinecraftInstance *inst, QWidget *parent = 0);
+ public:
+ explicit VersionPage(MinecraftInstance* inst, QWidget* parent = 0);
virtual ~VersionPage();
- virtual QString displayName() const override
- {
- return tr("Version");
- }
+ virtual QString displayName() const override { return tr("Version"); }
virtual QIcon icon() const override;
- virtual QString id() const override
- {
- return "version";
- }
- virtual QString helpPage() const override
- {
- return "Instance-Version";
- }
+ virtual QString id() const override { return "version"; }
+ virtual QString helpPage() const override { return "Instance-Version"; }
virtual bool shouldDisplay() const override;
void retranslate() override;
void openedImpl() override;
void closedImpl() override;
-private slots:
+ private slots:
void on_actionChange_version_triggered();
void on_actionInstall_Forge_triggered();
void on_actionInstall_Fabric_triggered();
@@ -103,36 +92,34 @@ private slots:
void updateVersionControls();
-private:
+ private:
ComponentPtr current();
int currentRow();
void updateButtons(int row = -1);
void preselect(int row = 0);
int doUpdate();
-protected:
- QMenu * createPopupMenu() override;
+ protected:
+ QMenu* createPopupMenu() override;
/// FIXME: this shouldn't be necessary!
bool reloadPackProfile();
-private:
- Ui::VersionPage *ui;
- QSortFilterProxyModel *m_filterModel;
+ private:
+ Ui::VersionPage* ui;
+ QSortFilterProxyModel* m_filterModel;
std::shared_ptr<PackProfile> m_profile;
- MinecraftInstance *m_inst;
+ MinecraftInstance* m_inst;
int currentIdx = 0;
- bool controlsEnabled = false;
std::shared_ptr<Setting> m_wide_bar_setting = nullptr;
-public slots:
- void versionCurrent(const QModelIndex &current, const QModelIndex &previous);
+ public slots:
+ void versionCurrent(const QModelIndex& current, const QModelIndex& previous);
-private slots:
- void updateRunningStatus(bool running);
+ private slots:
void onGameUpdateError(QString error);
- void packageCurrent(const QModelIndex &current, const QModelIndex &previous);
- void showContextMenu(const QPoint &pos);
- void onFilterTextChanged(const QString & newContents);
+ void packageCurrent(const QModelIndex& current, const QModelIndex& previous);
+ void showContextMenu(const QPoint& pos);
+ void onFilterTextChanged(const QString& newContents);
};
diff --git a/launcher/ui/widgets/LanguageSelectionWidget.cpp b/launcher/ui/widgets/LanguageSelectionWidget.cpp
index 256b09da..37d05347 100644
--- a/launcher/ui/widgets/LanguageSelectionWidget.cpp
+++ b/launcher/ui/widgets/LanguageSelectionWidget.cpp
@@ -1,16 +1,16 @@
#include "LanguageSelectionWidget.h"
-#include <QVBoxLayout>
-#include <QTreeView>
+#include <QCheckBox>
#include <QHeaderView>
#include <QLabel>
+#include <QTreeView>
+#include <QVBoxLayout>
#include "Application.h"
#include "BuildConfig.h"
-#include "translations/TranslationsModel.h"
#include "settings/Setting.h"
+#include "translations/TranslationsModel.h"
-LanguageSelectionWidget::LanguageSelectionWidget(QWidget *parent) :
- QWidget(parent)
+LanguageSelectionWidget::LanguageSelectionWidget(QWidget* parent) : QWidget(parent)
{
verticalLayout = new QVBoxLayout(this);
verticalLayout->setObjectName(QStringLiteral("verticalLayout"));
@@ -31,6 +31,13 @@ LanguageSelectionWidget::LanguageSelectionWidget(QWidget *parent) :
helpUsLabel->setWordWrap(true);
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 index = translations->selectedIndex();
languageView->setModel(translations.get());
@@ -38,7 +45,7 @@ LanguageSelectionWidget::LanguageSelectionWidget(QWidget *parent) :
languageView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
languageView->header()->setSectionResizeMode(0, QHeaderView::Stretch);
connect(languageView->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &LanguageSelectionWidget::languageRowChanged);
- verticalLayout->setContentsMargins(0,0,0,0);
+ verticalLayout->setContentsMargins(0, 0, 0, 0);
auto language_setting = APPLICATION->settings()->getSetting("Language");
connect(language_setting.get(), &Setting::SettingChanged, this, &LanguageSelectionWidget::languageSettingChanged);
@@ -53,15 +60,14 @@ QString LanguageSelectionWidget::getSelectedLanguageKey() const
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>")
- .arg(BuildConfig.TRANSLATIONS_URL);
+ .arg(BuildConfig.TRANSLATIONS_URL);
helpUsLabel->setText(text);
-
+ formatCheckbox->setText(tr("Use system locales"));
}
void LanguageSelectionWidget::languageRowChanged(const QModelIndex& current, const QModelIndex& previous)
{
- if (current == previous)
- {
+ if (current == previous) {
return;
}
auto translations = APPLICATION->translations();
@@ -70,7 +76,7 @@ void LanguageSelectionWidget::languageRowChanged(const QModelIndex& current, con
translations->updateLanguage(key);
}
-void LanguageSelectionWidget::languageSettingChanged(const Setting &, const QVariant)
+void LanguageSelectionWidget::languageSettingChanged(const Setting&, const QVariant)
{
auto translations = APPLICATION->translations();
auto index = translations->selectedIndex();
diff --git a/launcher/ui/widgets/LanguageSelectionWidget.h b/launcher/ui/widgets/LanguageSelectionWidget.h
index 4a88924c..5e86a288 100644
--- a/launcher/ui/widgets/LanguageSelectionWidget.h
+++ b/launcher/ui/widgets/LanguageSelectionWidget.h
@@ -21,23 +21,24 @@ class QVBoxLayout;
class QTreeView;
class QLabel;
class Setting;
+class QCheckBox;
-class LanguageSelectionWidget: public QWidget
-{
+class LanguageSelectionWidget : public QWidget {
Q_OBJECT
-public:
- explicit LanguageSelectionWidget(QWidget *parent = 0);
- virtual ~LanguageSelectionWidget() { };
+ public:
+ explicit LanguageSelectionWidget(QWidget* parent = 0);
+ virtual ~LanguageSelectionWidget(){};
QString getSelectedLanguageKey() const;
void retranslate();
-protected slots:
- void languageRowChanged(const QModelIndex &current, const QModelIndex &previous);
- void languageSettingChanged(const Setting &, const QVariant);
+ protected slots:
+ void languageRowChanged(const QModelIndex& current, const QModelIndex& previous);
+ void languageSettingChanged(const Setting&, const QVariant);
-private:
- QVBoxLayout *verticalLayout = nullptr;
- QTreeView *languageView = nullptr;
- QLabel *helpUsLabel = nullptr;
+ private:
+ QVBoxLayout* verticalLayout = nullptr;
+ QTreeView* languageView = nullptr;
+ QLabel* helpUsLabel = nullptr;
+ QCheckBox* formatCheckbox = nullptr;
};
diff --git a/launcher/ui/widgets/WideBar.cpp b/launcher/ui/widgets/WideBar.cpp
index ac34e3aa..a77c45fe 100644
--- a/launcher/ui/widgets/WideBar.cpp
+++ b/launcher/ui/widgets/WideBar.cpp
@@ -116,12 +116,21 @@ void WideBar::insertActionAfter(QAction* after, QAction* action)
if (iter == m_entries.end())
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;
- 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.type = BarEntry::Type::Action;
- m_entries.insert(iter + 1, entry);
+ m_entries.insert(iter, entry);
m_menu_state = MenuState::Dirty;
}
diff --git a/tests/FileSystem_test.cpp b/tests/FileSystem_test.cpp
index ec1f0bcf..a41345c2 100644
--- a/tests/FileSystem_test.cpp
+++ b/tests/FileSystem_test.cpp
@@ -42,6 +42,10 @@ class LinkTask : public Task {
m_lnk->debug(true);
}
+ ~LinkTask() {
+ delete m_lnk;
+ }
+
void matcher(const IPathMatcher *filter)
{
m_lnk->matcher(filter);
@@ -219,7 +223,8 @@ slots:
qDebug() << tempDir.path();
qDebug() << target_dir.path();
FS::copy c(folder, target_dir.path());
- c.matcher(new RegexpMatcher("[.]?mcmeta"));
+ RegexpMatcher re("[.]?mcmeta");
+ c.matcher(&re);
c();
for(auto entry: target_dir.entryList())
@@ -253,7 +258,8 @@ slots:
qDebug() << tempDir.path();
qDebug() << target_dir.path();
FS::copy c(folder, target_dir.path());
- c.matcher(new RegexpMatcher("[.]?mcmeta"));
+ RegexpMatcher re("[.]?mcmeta");
+ c.matcher(&re);
c.whitelist(true);
c();
@@ -460,7 +466,8 @@ slots:
qDebug() << target_dir.path();
LinkTask lnk_tsk(folder, target_dir.path());
- lnk_tsk.matcher(new RegexpMatcher("[.]?mcmeta"));
+ RegexpMatcher re("[.]?mcmeta");
+ lnk_tsk.matcher(&re);
lnk_tsk.linkRecursively(true);
QObject::connect(&lnk_tsk, &Task::finished, [&]{
QVERIFY2(lnk_tsk.wasSuccessful(), "Task finished but was not successful when it should have been.");
@@ -511,7 +518,8 @@ slots:
qDebug() << target_dir.path();
LinkTask lnk_tsk(folder, target_dir.path());
- lnk_tsk.matcher(new RegexpMatcher("[.]?mcmeta"));
+ RegexpMatcher re("[.]?mcmeta");
+ lnk_tsk.matcher(&re);
lnk_tsk.linkRecursively(true);
lnk_tsk.whitelist(true);
QObject::connect(&lnk_tsk, &Task::finished, [&]{
diff --git a/tests/ResourceModel_test.cpp b/tests/ResourceModel_test.cpp
index c0d9cd95..30353d3f 100644
--- a/tests/ResourceModel_test.cpp
+++ b/tests/ResourceModel_test.cpp
@@ -38,6 +38,7 @@ class DummyResourceModel : public ResourceModel {
public:
DummyResourceModel() : ResourceModel(new DummyResourceAPI) {}
+ ~DummyResourceModel() {}
[[nodiscard]] auto metaEntryBase() const -> QString override { return ""; };
@@ -58,7 +59,10 @@ class DummyResourceModel : public ResourceModel {
class ResourceModelTest : public QObject {
Q_OBJECT
private slots:
- void test_abstract_item_model() { [[maybe_unused]] auto tester = new QAbstractItemModelTester(new DummyResourceModel); }
+ void test_abstract_item_model() {
+ auto dummy = DummyResourceModel();
+ auto tester = QAbstractItemModelTester(&dummy);
+ }
void test_search()
{
@@ -78,6 +82,8 @@ class ResourceModelTest : public QObject {
QVERIFY(processed_pack->addonId.toString() == Json::requireString(processed_response, "project_id"));
QVERIFY(processed_pack->description == Json::requireString(processed_response, "description"));
QVERIFY(processed_pack->authors.first().name == Json::requireString(processed_response, "author"));
+
+ delete model;
}
};
diff --git a/tests/Task_test.cpp b/tests/Task_test.cpp
index dabe5da2..c59d4bb7 100644
--- a/tests/Task_test.cpp
+++ b/tests/Task_test.cpp
@@ -1,6 +1,6 @@
#include <QTest>
-#include <QTimer>
#include <QThread>
+#include <QTimer>
#include <tasks/ConcurrentTask.h>
#include <tasks/MultipleOptionsTask.h>
@@ -19,10 +19,7 @@ class BasicTask : public Task {
BasicTask(bool show_debug_log = true) : Task(nullptr, show_debug_log) {}
private:
- void executeTask() override
- {
- emitSucceeded();
- };
+ void executeTask() override { emitSucceeded(); };
};
/* Does nothing. Only used for testing. */
@@ -34,7 +31,7 @@ class BasicTask_MultiStep : public Task {
private:
auto isMultiStep() const -> bool override { return true; }
- void executeTask() override {};
+ void executeTask() override{};
};
class BigConcurrentTask : public ConcurrentTask {
@@ -44,7 +41,7 @@ class BigConcurrentTask : public ConcurrentTask {
{
// This is here only to help fill the stack a bit more quickly (if there's an issue, of course :^))
// Each tasks thus adds 1024 * 4 bytes to the stack, at the very least.
- [[maybe_unused]] volatile std::array<uint32_t, 1024> some_data_on_the_stack {};
+ [[maybe_unused]] volatile std::array<uint32_t, 1024> some_data_on_the_stack{};
ConcurrentTask::startNext();
}
@@ -53,49 +50,42 @@ class BigConcurrentTask : public ConcurrentTask {
class BigConcurrentTaskThread : public QThread {
Q_OBJECT
- BigConcurrentTask big_task;
-
+ QTimer m_deadline;
void run() override
{
- QTimer deadline;
- deadline.setInterval(10000);
- connect(&deadline, &QTimer::timeout, this, [this]{ passed_the_deadline = true; });
- deadline.start();
+ BigConcurrentTask big_task;
+ m_deadline.setInterval(10000);
// NOTE: Arbitrary value that manages to trigger a problem when there is one.
// Considering each tasks, in a problematic state, adds 1024 * 4 bytes to the stack,
// this number is enough to fill up 16 MiB of stack, more than enough to cause a problem.
static const unsigned s_num_tasks = 1 << 12;
- auto sub_tasks = new BasicTask::Ptr[s_num_tasks];
-
for (unsigned i = 0; i < s_num_tasks; i++) {
auto sub_task = makeShared<BasicTask>(false);
- sub_tasks[i] = sub_task;
big_task.addTask(sub_task);
}
- big_task.run();
+ connect(&big_task, &Task::finished, this, &QThread::quit);
+ connect(&m_deadline, &QTimer::timeout, this, [&] { passed_the_deadline = true; quit(); });
- while (!big_task.isFinished() && !passed_the_deadline)
- QCoreApplication::processEvents();
+ m_deadline.start();
+ big_task.run();
- emit finished();
+ exec();
}
public:
bool passed_the_deadline = false;
-
- signals:
- void finished();
};
class TaskTest : public QObject {
Q_OBJECT
private slots:
- void test_SetStatus_NoMultiStep(){
+ void test_SetStatus_NoMultiStep()
+ {
BasicTask t;
- QString status {"test status"};
+ QString status{ "test status" };
t.setStatus(status);
@@ -103,9 +93,10 @@ class TaskTest : public QObject {
QCOMPARE(t.getStepProgress().isEmpty(), TaskStepProgressList{}.isEmpty());
}
- void test_SetStatus_MultiStep(){
+ void test_SetStatus_MultiStep()
+ {
BasicTask_MultiStep t;
- QString status {"test status"};
+ QString status{ "test status" };
t.setStatus(status);
@@ -115,7 +106,8 @@ class TaskTest : public QObject {
QCOMPARE(t.getStepProgress().isEmpty(), TaskStepProgressList{}.isEmpty());
}
- void test_SetProgress(){
+ void test_SetProgress()
+ {
BasicTask t;
int current = 42;
int total = 207;
@@ -126,17 +118,18 @@ class TaskTest : public QObject {
QCOMPARE(t.getTotalProgress(), total);
}
- void test_basicRun(){
+ void test_basicRun()
+ {
BasicTask t;
- QObject::connect(&t, &Task::finished, [&]{ QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been."); });
+ QObject::connect(&t, &Task::finished,
+ [&] { QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been."); });
t.start();
- QVERIFY2(QTest::qWaitFor([&]() {
- return t.isFinished();
- }, 1000), "Task didn't finish as it should.");
+ QVERIFY2(QTest::qWaitFor([&]() { return t.isFinished(); }, 1000), "Task didn't finish as it should.");
}
- void test_basicConcurrentRun(){
+ void test_basicConcurrentRun()
+ {
auto t1 = makeShared<BasicTask>();
auto t2 = makeShared<BasicTask>();
auto t3 = makeShared<BasicTask>();
@@ -147,21 +140,20 @@ class TaskTest : public QObject {
t.addTask(t2);
t.addTask(t3);
- QObject::connect(&t, &Task::finished, [&]{
- QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been.");
- QVERIFY(t1->wasSuccessful());
- QVERIFY(t2->wasSuccessful());
- QVERIFY(t3->wasSuccessful());
+ QObject::connect(&t, &Task::finished, [&t, &t1, &t2, &t3] {
+ QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been.");
+ QVERIFY(t1->wasSuccessful());
+ QVERIFY(t2->wasSuccessful());
+ QVERIFY(t3->wasSuccessful());
});
t.start();
- QVERIFY2(QTest::qWaitFor([&]() {
- return t.isFinished();
- }, 1000), "Task didn't finish as it should.");
+ QVERIFY2(QTest::qWaitFor([&]() { return t.isFinished(); }, 1000), "Task didn't finish as it should.");
}
// Tests if starting new tasks after the 6 initial ones is working
- void test_moreConcurrentRun(){
+ void test_moreConcurrentRun()
+ {
auto t1 = makeShared<BasicTask>();
auto t2 = makeShared<BasicTask>();
auto t3 = makeShared<BasicTask>();
@@ -184,26 +176,25 @@ class TaskTest : public QObject {
t.addTask(t8);
t.addTask(t9);
- QObject::connect(&t, &Task::finished, [&]{
- QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been.");
- QVERIFY(t1->wasSuccessful());
- QVERIFY(t2->wasSuccessful());
- QVERIFY(t3->wasSuccessful());
- QVERIFY(t4->wasSuccessful());
- QVERIFY(t5->wasSuccessful());
- QVERIFY(t6->wasSuccessful());
- QVERIFY(t7->wasSuccessful());
- QVERIFY(t8->wasSuccessful());
- QVERIFY(t9->wasSuccessful());
+ QObject::connect(&t, &Task::finished, [&t, &t1, &t2, &t3, &t4, &t5, &t6, &t7, &t8, &t9] {
+ QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been.");
+ QVERIFY(t1->wasSuccessful());
+ QVERIFY(t2->wasSuccessful());
+ QVERIFY(t3->wasSuccessful());
+ QVERIFY(t4->wasSuccessful());
+ QVERIFY(t5->wasSuccessful());
+ QVERIFY(t6->wasSuccessful());
+ QVERIFY(t7->wasSuccessful());
+ QVERIFY(t8->wasSuccessful());
+ QVERIFY(t9->wasSuccessful());
});
t.start();
- QVERIFY2(QTest::qWaitFor([&]() {
- return t.isFinished();
- }, 1000), "Task didn't finish as it should.");
+ QVERIFY2(QTest::qWaitFor([&]() { return t.isFinished(); }, 1000), "Task didn't finish as it should.");
}
- void test_basicSequentialRun(){
+ void test_basicSequentialRun()
+ {
auto t1 = makeShared<BasicTask>();
auto t2 = makeShared<BasicTask>();
auto t3 = makeShared<BasicTask>();
@@ -214,20 +205,19 @@ class TaskTest : public QObject {
t.addTask(t2);
t.addTask(t3);
- QObject::connect(&t, &Task::finished, [&]{
- QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been.");
- QVERIFY(t1->wasSuccessful());
- QVERIFY(t2->wasSuccessful());
- QVERIFY(t3->wasSuccessful());
+ QObject::connect(&t, &Task::finished, [&t, &t1, &t2, &t3] {
+ QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been.");
+ QVERIFY(t1->wasSuccessful());
+ QVERIFY(t2->wasSuccessful());
+ QVERIFY(t3->wasSuccessful());
});
t.start();
- QVERIFY2(QTest::qWaitFor([&]() {
- return t.isFinished();
- }, 1000), "Task didn't finish as it should.");
+ QVERIFY2(QTest::qWaitFor([&]() { return t.isFinished(); }, 1000), "Task didn't finish as it should.");
}
- void test_basicMultipleOptionsRun(){
+ void test_basicMultipleOptionsRun()
+ {
auto t1 = makeShared<BasicTask>();
auto t2 = makeShared<BasicTask>();
auto t3 = makeShared<BasicTask>();
@@ -238,33 +228,30 @@ class TaskTest : public QObject {
t.addTask(t2);
t.addTask(t3);
- QObject::connect(&t, &Task::finished, [&]{
- QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been.");
- QVERIFY(t1->wasSuccessful());
- QVERIFY(!t2->wasSuccessful());
- QVERIFY(!t3->wasSuccessful());
+ QObject::connect(&t, &Task::finished, [&t, &t1, &t2, &t3] {
+ QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been.");
+ QVERIFY(t1->wasSuccessful());
+ QVERIFY(!t2->wasSuccessful());
+ QVERIFY(!t3->wasSuccessful());
});
t.start();
- QVERIFY2(QTest::qWaitFor([&]() {
- return t.isFinished();
- }, 1000), "Task didn't finish as it should.");
+ QVERIFY2(QTest::qWaitFor([&]() { return t.isFinished(); }, 1000), "Task didn't finish as it should.");
}
void test_stackOverflowInConcurrentTask()
{
QEventLoop loop;
- auto thread = new BigConcurrentTaskThread;
+ BigConcurrentTaskThread thread;
- connect(thread, &BigConcurrentTaskThread::finished, &loop, &QEventLoop::quit);
+ connect(&thread, &BigConcurrentTaskThread::finished, &loop, &QEventLoop::quit);
- thread->start();
+ thread.start();
loop.exec();
- QVERIFY(!thread->passed_the_deadline);
- thread->deleteLater();
+ QVERIFY(!thread.passed_the_deadline);
}
};
diff --git a/tests/Version_test.cpp b/tests/Version_test.cpp
index afb4c610..f5488cbc 100644
--- a/tests/Version_test.cpp
+++ b/tests/Version_test.cpp
@@ -20,6 +20,8 @@
class VersionTest : public QObject {
Q_OBJECT
+ QStringList m_flex_test_names = {};
+
void addDataColumns()
{
QTest::addColumn<QString>("first");
@@ -101,8 +103,9 @@ class VersionTest : public QObject {
QString first{split_line.first().simplified()};
QString second{split_line.last().simplified()};
- auto new_test_name = test_name_template.arg(QString::number(test_number), "lessThan").toLatin1().data();
- QTest::newRow(new_test_name) << first << second << true << false;
+ auto new_test_name = test_name_template.arg(QString::number(test_number), "lessThan");
+ m_flex_test_names.append(new_test_name);
+ QTest::newRow(m_flex_test_names.last().toLatin1().data()) << first << second << true << false;
continue;
}
@@ -112,8 +115,9 @@ class VersionTest : public QObject {
QString first{split_line.first().simplified()};
QString second{split_line.last().simplified()};
- auto new_test_name = test_name_template.arg(QString::number(test_number), "equals").toLatin1().data();
- QTest::newRow(new_test_name) << first << second << false << true;
+ auto new_test_name = test_name_template.arg(QString::number(test_number), "equals");
+ m_flex_test_names.append(new_test_name);
+ QTest::newRow(m_flex_test_names.last().toLatin1().data()) << first << second << false << true;
continue;
}
@@ -123,8 +127,9 @@ class VersionTest : public QObject {
QString first{split_line.first().simplified()};
QString second{split_line.last().simplified()};
- auto new_test_name = test_name_template.arg(QString::number(test_number), "greaterThan").toLatin1().data();
- QTest::newRow(new_test_name) << first << second << false << false;
+ auto new_test_name = test_name_template.arg(QString::number(test_number), "greaterThan");
+ m_flex_test_names.append(new_test_name);
+ QTest::newRow(m_flex_test_names.last().toLatin1().data()) << first << second << false << false;
continue;
}