From b3afe43351a42a8ffd902f2419c02cb49627f950 Mon Sep 17 00:00:00 2001 From: Naomi <103967@gmail.com> Date: Sat, 17 Aug 2024 23:28:55 +0200 Subject: [PATCH 01/10] Create initial global folders feature Signed-off-by: Naomi <103967@gmail.com> --- launcher/BaseInstance.cpp | 5 + launcher/BaseInstance.h | 5 + launcher/CMakeLists.txt | 2 + launcher/FileSystem.cpp | 20 +++ launcher/FileSystem.h | 28 ++++ launcher/minecraft/MinecraftInstance.cpp | 13 ++ launcher/minecraft/MinecraftInstance.h | 5 + .../minecraft/UpdateGlobalDirectoriesTask.cpp | 133 ++++++++++++++++++ .../minecraft/UpdateGlobalDirectoriesTask.h | 22 +++ .../pages/instance/ExternalResourcesPage.cpp | 4 + .../pages/instance/ExternalResourcesPage.ui | 21 ++- .../pages/instance/InstanceSettingsPage.cpp | 11 ++ .../ui/pages/instance/InstanceSettingsPage.ui | 58 +++++++- .../ui/pages/instance/ScreenshotsPage.cpp | 3 + launcher/ui/pages/instance/ScreenshotsPage.ui | 21 ++- launcher/ui/pages/instance/WorldListPage.cpp | 3 + launcher/ui/pages/instance/WorldListPage.ui | 15 +- 17 files changed, 348 insertions(+), 21 deletions(-) create mode 100644 launcher/minecraft/UpdateGlobalDirectoriesTask.cpp create mode 100644 launcher/minecraft/UpdateGlobalDirectoriesTask.h diff --git a/launcher/BaseInstance.cpp b/launcher/BaseInstance.cpp index 69cf95e3c..bf7fd2355 100644 --- a/launcher/BaseInstance.cpp +++ b/launcher/BaseInstance.cpp @@ -105,6 +105,11 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s m_settings->registerSetting("ManagedPackVersionName", ""); m_settings->registerSetting("Profiler", ""); + + // Global folders + m_settings->registerSetting("UseGlobalScreenshotsFolder", false); + m_settings->registerSetting("UseGlobalSavesFolder", false); + m_settings->registerSetting("UseGlobalResourcePacksFolder", false); } QString BaseInstance::getPreLaunchCommand() diff --git a/launcher/BaseInstance.h b/launcher/BaseInstance.h index 499ec7866..ad92c5bcb 100644 --- a/launcher/BaseInstance.h +++ b/launcher/BaseInstance.h @@ -269,6 +269,11 @@ class BaseInstance : public QObject, public std::enable_shared_from_this MinecraftInstance::getJarMods() const return mods; } +void MinecraftInstance::applySettings() +{ + // Global directories + m_update_global_directories_task = std::make_shared(this, nullptr); + m_update_global_directories_task->start(); +} + #include "MinecraftInstance.moc" diff --git a/launcher/minecraft/MinecraftInstance.h b/launcher/minecraft/MinecraftInstance.h index 7af0df389..806f58df6 100644 --- a/launcher/minecraft/MinecraftInstance.h +++ b/launcher/minecraft/MinecraftInstance.h @@ -51,6 +51,7 @@ class WorldList; class GameOptions; class LaunchStep; class PackProfile; +class UpdateGlobalDirectoriesTask; class MinecraftInstance : public BaseInstance { Q_OBJECT @@ -74,6 +75,7 @@ class MinecraftInstance : public BaseInstance { ////// Directories and files ////// QString jarModsDir() const; + QString screenshotsDir() const; QString resourcePacksDir() const; QString texturePacksDir() const; QString shaderPacksDir() const; @@ -159,11 +161,14 @@ class MinecraftInstance : public BaseInstance { virtual JavaVersion getJavaVersion(); + virtual void applySettings() override; + protected: QMap createCensorFilterFromSession(AuthSessionPtr session); protected: // data std::shared_ptr m_components; + std::shared_ptr m_update_global_directories_task; mutable std::shared_ptr m_loader_mod_list; mutable std::shared_ptr m_core_mod_list; mutable std::shared_ptr m_nil_mod_list; diff --git a/launcher/minecraft/UpdateGlobalDirectoriesTask.cpp b/launcher/minecraft/UpdateGlobalDirectoriesTask.cpp new file mode 100644 index 000000000..56e239d7f --- /dev/null +++ b/launcher/minecraft/UpdateGlobalDirectoriesTask.cpp @@ -0,0 +1,133 @@ +#include "UpdateGlobalDirectoriesTask.h" + +#include "Application.h" +#include "FileSystem.h" +#include "minecraft/MinecraftInstance.h" +#include "tasks/ConcurrentTask.h" +#include "ui/dialogs/CustomMessageBox.h" + +class TryCreateSymlinkTask : public Task +{ + public: + explicit TryCreateSymlinkTask(const QString& source, const QString& destination, MinecraftInstance* instance, const QString& setting) + : m_source(source), + m_destination(destination), + m_inst(instance), + m_setting(setting) + { + setObjectName("TryCreateSymlinkTask"); + } + virtual ~TryCreateSymlinkTask() + {} + + protected: + void executeTask() + { + bool create = m_inst->settings()->get(m_setting).toBool(); + + // Check if we have to delete an existing symlink + if (!create) + { + // Safety check + if (FS::isSymLink(m_destination)) + { + FS::deletePath(m_destination); + } + + emitSucceeded(); + return; + } + + // Make sure that symbolic links are supported. + if (!FS::canLink(m_source, m_destination)) + { + fail(tr("Failed to create global folder.\nSymbolic links are not supported on the filesystem")); + return; + } + + // Check if the destination already exists. + if (FS::checkFolderPathExists(m_destination)) + { + // If it's already a symlink, it might already be correct. + if (FS::isSymLink(m_destination)) + { + // If the target of the symlink is already the source, there's nothing to do. + if (FS::getSymLinkTarget(m_destination) == m_source) + { + emitSucceeded(); + return; + } + } + else if (!FS::checkFolderPathEmpty(m_destination)) + { + fail(tr("Failed to create global folder.\nEnsure that \"%1\" is empty.").arg(m_destination)); + return; + } + + FS::deletePath(m_destination); + } + + FS::create_link folderLink(m_source, m_destination); + folderLink.linkRecursively(false); + folderLink(); // TODO: Error check + + emitSucceeded(); + return; + } + + void fail(const QString& reason) + { + m_inst->settings()->set(m_setting, false); + emitFailed(reason); + } + + private: + QString m_source; + QString m_destination; + MinecraftInstance* m_inst; + QString m_setting; +}; + +UpdateGlobalDirectoriesTask::UpdateGlobalDirectoriesTask(MinecraftInstance* inst, QWidget* parent) + : Task(parent), + m_inst(inst), + m_parent(parent) +{} + +UpdateGlobalDirectoriesTask::~UpdateGlobalDirectoriesTask(){} + +void UpdateGlobalDirectoriesTask::executeTask() +{ + auto tasks = makeShared(this, "UpdateGlobalDirectoriesTask"); + + auto screenshotsTask = makeShared(FS::PathCombine(APPLICATION->dataRoot(), "screenshots"), m_inst->screenshotsDir(), m_inst, "UseGlobalScreenshotsFolder"); + connect(screenshotsTask.get(), &Task::failed, this, &UpdateGlobalDirectoriesTask::notifyFailed); + tasks->addTask(screenshotsTask); + + auto savesTask = makeShared(FS::PathCombine(APPLICATION->dataRoot(), "saves"), m_inst->worldDir(), m_inst, "UseGlobalSavesFolder"); + connect(savesTask.get(), &Task::failed, this, &UpdateGlobalDirectoriesTask::notifyFailed); + tasks->addTask(savesTask); + + auto resoucePacksTask = makeShared(FS::PathCombine(APPLICATION->dataRoot(), "resourcepacks"), m_inst->resourcePacksDir(), m_inst, "UseGlobalResourcePacksFolder"); + connect(resoucePacksTask.get(), &Task::failed, this, &UpdateGlobalDirectoriesTask::notifyFailed); + tasks->addTask(resoucePacksTask); + + auto texturePacksTask = makeShared(FS::PathCombine(APPLICATION->dataRoot(), "resourcepacks"), m_inst->texturePacksDir(), m_inst, "UseGlobalResourcePacksFolder"); + connect(texturePacksTask.get(), &Task::failed, this, &UpdateGlobalDirectoriesTask::notifyFailed); + tasks->addTask(texturePacksTask); + + m_tasks = tasks; + + connect(m_tasks.get(), &Task::succeeded, this, &UpdateGlobalDirectoriesTask::emitSucceeded); + + m_tasks->start(); + +} + + void UpdateGlobalDirectoriesTask::notifyFailed(QString reason) +{ + CustomMessageBox::selectable(m_parent, tr("Failed"), reason, QMessageBox::Warning, QMessageBox::Ok)->exec(); + emit failed(reason); +} + + diff --git a/launcher/minecraft/UpdateGlobalDirectoriesTask.h b/launcher/minecraft/UpdateGlobalDirectoriesTask.h new file mode 100644 index 000000000..4afadc8ec --- /dev/null +++ b/launcher/minecraft/UpdateGlobalDirectoriesTask.h @@ -0,0 +1,22 @@ +#pragma once + +#include "tasks/Task.h" + +class MinecraftInstance; + +class UpdateGlobalDirectoriesTask : public Task { + public: + explicit UpdateGlobalDirectoriesTask(MinecraftInstance* inst, QWidget* parent = 0); + virtual ~UpdateGlobalDirectoriesTask(); + + protected: + virtual void executeTask() override; + + protected slots: + void notifyFailed(QString reason); + + private: + MinecraftInstance* m_inst; + QWidget* m_parent; + Task::Ptr m_tasks; +}; diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.cpp b/launcher/ui/pages/instance/ExternalResourcesPage.cpp index 8f8dab46d..5c45d25ad 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.cpp +++ b/launcher/ui/pages/instance/ExternalResourcesPage.cpp @@ -38,6 +38,7 @@ #include "ui_ExternalResourcesPage.h" #include "DesktopServices.h" +#include "FileSystem.h" #include "Version.h" #include "minecraft/mod/ResourceFolderModel.h" #include "ui/GuiUtil.h" @@ -138,6 +139,9 @@ void ExternalResourcesPage::openedImpl() m_wide_bar_setting = APPLICATION->settings()->getSetting(setting_name); ui->actionsToolbar->setVisibilityState(m_wide_bar_setting->get().toByteArray()); + + // Enable the symbolic link warning when the folder is a symbolic link + ui->isSymlinkWarning->setVisible(FS::isSymLink(m_model->dir().absolutePath())); } void ExternalResourcesPage::closedImpl() diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.ui b/launcher/ui/pages/instance/ExternalResourcesPage.ui index 9d6f61db0..4df948727 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.ui +++ b/launcher/ui/pages/instance/ExternalResourcesPage.ui @@ -60,13 +60,20 @@ true - QAbstractItemView::DropOnly + QAbstractItemView::DragDropMode::DropOnly true + + + + <html><head/><body><p><span style=" color:#f5c211;">Warning: This is the global folder</span></p></body></html> + + + @@ -74,7 +81,7 @@ Actions - Qt::ToolButtonTextOnly + Qt::ToolButtonStyle::ToolButtonTextOnly true @@ -184,6 +191,11 @@ + + WideBar + QToolBar +
ui/widgets/WideBar.h
+
ModListView QTreeView @@ -195,11 +207,6 @@
ui/widgets/InfoFrame.h
1
- - WideBar - QToolBar -
ui/widgets/WideBar.h
-
treeView diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index 76add9402..dfea0a70d 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -276,6 +276,13 @@ void InstanceSettingsPage::applySettings() m_settings->reset("OnlineFixes"); } + // Global folders + m_settings->set("UseGlobalScreenshotsFolder", ui->useGlobalScreenshotsFolder->isChecked()); + m_settings->set("UseGlobalSavesFolder", ui->useGlobalSavesFolder->isChecked()); + m_settings->set("UseGlobalResourcePacksFolder", ui->useGlobalResourcePacksFolder->isChecked()); + + m_instance->applySettings(); + // FIXME: This should probably be called by a signal instead m_instance->updateRuntimeContext(); } @@ -386,6 +393,10 @@ void InstanceSettingsPage::loadSettings() ui->legacySettingsGroupBox->setChecked(m_settings->get("OverrideLegacySettings").toBool()); ui->onlineFixes->setChecked(m_settings->get("OnlineFixes").toBool()); + + ui->useGlobalScreenshotsFolder->setChecked(m_settings->get("UseGlobalScreenshotsFolder").toBool()); + ui->useGlobalSavesFolder->setChecked(m_settings->get("UseGlobalSavesFolder").toBool()); + ui->useGlobalResourcePacksFolder->setChecked(m_settings->get("UseGlobalResourcePacksFolder").toBool()); } void InstanceSettingsPage::on_javaDetectBtn_clicked() diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.ui b/launcher/ui/pages/instance/InstanceSettingsPage.ui index 9490860ae..4cb73b184 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.ui +++ b/launcher/ui/pages/instance/InstanceSettingsPage.ui @@ -36,7 +36,7 @@ - 0 + 6 @@ -218,7 +218,7 @@ - Qt::AlignCenter + Qt::AlignmentFlag::AlignCenter maxMemSpinBox @@ -399,7 +399,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -505,7 +505,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -583,7 +583,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -727,10 +727,56 @@ + + + + Qt::FocusPolicy::StrongFocus + + + Global Folders + + + false + + + false + + + + + + <html><head/><body><p><span style=" font-weight:600; color:#f5c211;">Warning</span><span style=" color:#f5c211;">: After enabling files cannot be automatically restored to their original folder.</span></p></body></html> + + + + + + + Use global screenshots folder + + + + + + + Use global saves folder + + + + + + + Use global resource packs folder + + + + + + - Qt::Vertical + Qt::Orientation::Vertical diff --git a/launcher/ui/pages/instance/ScreenshotsPage.cpp b/launcher/ui/pages/instance/ScreenshotsPage.cpp index c3f955733..5d6883212 100644 --- a/launcher/ui/pages/instance/ScreenshotsPage.cpp +++ b/launcher/ui/pages/instance/ScreenshotsPage.cpp @@ -560,6 +560,9 @@ void ScreenshotsPage::openedImpl() m_wide_bar_setting = APPLICATION->settings()->getSetting(setting_name); ui->toolBar->setVisibilityState(m_wide_bar_setting->get().toByteArray()); + + // Enable the symbolic link warning when the screenshots folder is a symbolic link + ui->globalScreenshotsFolderWarninglabel->setVisible(FS::isSymLink(m_folder)); } void ScreenshotsPage::closedImpl() diff --git a/launcher/ui/pages/instance/ScreenshotsPage.ui b/launcher/ui/pages/instance/ScreenshotsPage.ui index 2e2227a29..4dd650f9c 100644 --- a/launcher/ui/pages/instance/ScreenshotsPage.ui +++ b/launcher/ui/pages/instance/ScreenshotsPage.ui @@ -11,7 +11,7 @@ - + 0 @@ -24,13 +24,26 @@ 0 + + + + false + + + <html><head/><body><p><span style=" font-weight:600; color:#f5c211;">Warning</span><span style=" color:#f5c211;">: This is the global screenshots folder</span></p></body></html> + + + 0 + + + - QAbstractItemView::ExtendedSelection + QAbstractItemView::SelectionMode::ExtendedSelection - QAbstractItemView::SelectRows + QAbstractItemView::SelectionBehavior::SelectRows @@ -41,7 +54,7 @@ Actions - Qt::ToolButtonTextOnly + Qt::ToolButtonStyle::ToolButtonTextOnly RightToolBarArea diff --git a/launcher/ui/pages/instance/WorldListPage.cpp b/launcher/ui/pages/instance/WorldListPage.cpp index 4f30e4bb7..b758f4904 100644 --- a/launcher/ui/pages/instance/WorldListPage.cpp +++ b/launcher/ui/pages/instance/WorldListPage.cpp @@ -120,6 +120,9 @@ void WorldListPage::openedImpl() m_wide_bar_setting = APPLICATION->settings()->getSetting(setting_name); ui->toolBar->setVisibilityState(m_wide_bar_setting->get().toByteArray()); + + // Enable the symbolic link warning when the saves folder is a symbolic link + ui->globalSavesFolderWarninglabel->setVisible(FS::isSymLink(FS::PathCombine(m_inst->gameRoot(), "saves"))); } void WorldListPage::closedImpl() diff --git a/launcher/ui/pages/instance/WorldListPage.ui b/launcher/ui/pages/instance/WorldListPage.ui index d74dd0796..3ca9cad8b 100644 --- a/launcher/ui/pages/instance/WorldListPage.ui +++ b/launcher/ui/pages/instance/WorldListPage.ui @@ -11,7 +11,7 @@ - + 0 @@ -24,6 +24,13 @@ 0 + + + + <html><head/><body><p><span style=" color:#f5c211;">Warning: This is the global saves folder</span></p></body></html> + + + @@ -36,7 +43,7 @@ true - QAbstractItemView::DragDrop + QAbstractItemView::DragDropMode::DragDrop true @@ -65,10 +72,10 @@ Actions - Qt::LeftToolBarArea|Qt::RightToolBarArea + Qt::ToolBarArea::LeftToolBarArea|Qt::ToolBarArea::RightToolBarArea - Qt::ToolButtonTextOnly + Qt::ToolButtonStyle::ToolButtonTextOnly false From fbdf1a290ab7aad74531c9409242c4cbd63faaea Mon Sep 17 00:00:00 2001 From: Naomi <103967@gmail.com> Date: Sun, 18 Aug 2024 07:35:11 +0200 Subject: [PATCH 02/10] Fix formatting Signed-off-by: Naomi <103967@gmail.com> --- launcher/minecraft/MinecraftInstance.cpp | 2 +- .../minecraft/UpdateGlobalDirectoriesTask.cpp | 58 +++++++------------ 2 files changed, 23 insertions(+), 37 deletions(-) diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 468b6f92f..d0687a772 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -84,9 +84,9 @@ #include "MinecraftLoadAndCheck.h" #include "MinecraftUpdate.h" #include "PackProfile.h" +#include "minecraft/UpdateGlobalDirectoriesTask.h" #include "minecraft/gameoptions/GameOptions.h" #include "minecraft/update/FoldersTask.h" -#include "minecraft/UpdateGlobalDirectoriesTask.h" #include "tools/BaseProfiler.h" diff --git a/launcher/minecraft/UpdateGlobalDirectoriesTask.cpp b/launcher/minecraft/UpdateGlobalDirectoriesTask.cpp index 56e239d7f..2f8b92e00 100644 --- a/launcher/minecraft/UpdateGlobalDirectoriesTask.cpp +++ b/launcher/minecraft/UpdateGlobalDirectoriesTask.cpp @@ -6,19 +6,14 @@ #include "tasks/ConcurrentTask.h" #include "ui/dialogs/CustomMessageBox.h" -class TryCreateSymlinkTask : public Task -{ +class TryCreateSymlinkTask : public Task { public: explicit TryCreateSymlinkTask(const QString& source, const QString& destination, MinecraftInstance* instance, const QString& setting) - : m_source(source), - m_destination(destination), - m_inst(instance), - m_setting(setting) + : m_source(source), m_destination(destination), m_inst(instance), m_setting(setting) { setObjectName("TryCreateSymlinkTask"); } - virtual ~TryCreateSymlinkTask() - {} + virtual ~TryCreateSymlinkTask() {} protected: void executeTask() @@ -26,11 +21,9 @@ class TryCreateSymlinkTask : public Task bool create = m_inst->settings()->get(m_setting).toBool(); // Check if we have to delete an existing symlink - if (!create) - { + if (!create) { // Safety check - if (FS::isSymLink(m_destination)) - { + if (FS::isSymLink(m_destination)) { FS::deletePath(m_destination); } @@ -39,27 +32,21 @@ class TryCreateSymlinkTask : public Task } // Make sure that symbolic links are supported. - if (!FS::canLink(m_source, m_destination)) - { + if (!FS::canLink(m_source, m_destination)) { fail(tr("Failed to create global folder.\nSymbolic links are not supported on the filesystem")); return; } // Check if the destination already exists. - if (FS::checkFolderPathExists(m_destination)) - { + if (FS::checkFolderPathExists(m_destination)) { // If it's already a symlink, it might already be correct. - if (FS::isSymLink(m_destination)) - { + if (FS::isSymLink(m_destination)) { // If the target of the symlink is already the source, there's nothing to do. - if (FS::getSymLinkTarget(m_destination) == m_source) - { + if (FS::getSymLinkTarget(m_destination) == m_source) { emitSucceeded(); return; } - } - else if (!FS::checkFolderPathEmpty(m_destination)) - { + } else if (!FS::checkFolderPathEmpty(m_destination)) { fail(tr("Failed to create global folder.\nEnsure that \"%1\" is empty.").arg(m_destination)); return; } @@ -69,7 +56,7 @@ class TryCreateSymlinkTask : public Task FS::create_link folderLink(m_source, m_destination); folderLink.linkRecursively(false); - folderLink(); // TODO: Error check + folderLink(); // TODO: Error check emitSucceeded(); return; @@ -89,30 +76,32 @@ class TryCreateSymlinkTask : public Task }; UpdateGlobalDirectoriesTask::UpdateGlobalDirectoriesTask(MinecraftInstance* inst, QWidget* parent) - : Task(parent), - m_inst(inst), - m_parent(parent) + : Task(parent), m_inst(inst), m_parent(parent) {} -UpdateGlobalDirectoriesTask::~UpdateGlobalDirectoriesTask(){} +UpdateGlobalDirectoriesTask::~UpdateGlobalDirectoriesTask() {} void UpdateGlobalDirectoriesTask::executeTask() { auto tasks = makeShared(this, "UpdateGlobalDirectoriesTask"); - auto screenshotsTask = makeShared(FS::PathCombine(APPLICATION->dataRoot(), "screenshots"), m_inst->screenshotsDir(), m_inst, "UseGlobalScreenshotsFolder"); + auto screenshotsTask = makeShared(FS::PathCombine(APPLICATION->dataRoot(), "screenshots"), + m_inst->screenshotsDir(), m_inst, "UseGlobalScreenshotsFolder"); connect(screenshotsTask.get(), &Task::failed, this, &UpdateGlobalDirectoriesTask::notifyFailed); tasks->addTask(screenshotsTask); - auto savesTask = makeShared(FS::PathCombine(APPLICATION->dataRoot(), "saves"), m_inst->worldDir(), m_inst, "UseGlobalSavesFolder"); + auto savesTask = makeShared(FS::PathCombine(APPLICATION->dataRoot(), "saves"), m_inst->worldDir(), m_inst, + "UseGlobalSavesFolder"); connect(savesTask.get(), &Task::failed, this, &UpdateGlobalDirectoriesTask::notifyFailed); tasks->addTask(savesTask); - auto resoucePacksTask = makeShared(FS::PathCombine(APPLICATION->dataRoot(), "resourcepacks"), m_inst->resourcePacksDir(), m_inst, "UseGlobalResourcePacksFolder"); + auto resoucePacksTask = makeShared(FS::PathCombine(APPLICATION->dataRoot(), "resourcepacks"), + m_inst->resourcePacksDir(), m_inst, "UseGlobalResourcePacksFolder"); connect(resoucePacksTask.get(), &Task::failed, this, &UpdateGlobalDirectoriesTask::notifyFailed); tasks->addTask(resoucePacksTask); - auto texturePacksTask = makeShared(FS::PathCombine(APPLICATION->dataRoot(), "resourcepacks"), m_inst->texturePacksDir(), m_inst, "UseGlobalResourcePacksFolder"); + auto texturePacksTask = makeShared(FS::PathCombine(APPLICATION->dataRoot(), "resourcepacks"), + m_inst->texturePacksDir(), m_inst, "UseGlobalResourcePacksFolder"); connect(texturePacksTask.get(), &Task::failed, this, &UpdateGlobalDirectoriesTask::notifyFailed); tasks->addTask(texturePacksTask); @@ -121,13 +110,10 @@ void UpdateGlobalDirectoriesTask::executeTask() connect(m_tasks.get(), &Task::succeeded, this, &UpdateGlobalDirectoriesTask::emitSucceeded); m_tasks->start(); - } - void UpdateGlobalDirectoriesTask::notifyFailed(QString reason) +void UpdateGlobalDirectoriesTask::notifyFailed(QString reason) { CustomMessageBox::selectable(m_parent, tr("Failed"), reason, QMessageBox::Warning, QMessageBox::Ok)->exec(); emit failed(reason); } - - From 7673364841e564e6035561de00b3dd6f337ec9b4 Mon Sep 17 00:00:00 2001 From: Naomi <103967@gmail.com> Date: Sun, 18 Aug 2024 10:26:18 +0200 Subject: [PATCH 03/10] Make global folder path customizable Signed-off-by: Naomi <103967@gmail.com> --- launcher/BaseInstance.cpp | 4 ++ launcher/CMakeLists.txt | 3 ++ .../minecraft/UpdateGlobalDirectoriesTask.cpp | 34 +++++++----- .../pages/instance/InstanceSettingsPage.cpp | 18 ++++--- .../ui/pages/instance/InstanceSettingsPage.ui | 28 ++++------ launcher/ui/widgets/GlobalFolderWidget.cpp | 53 +++++++++++++++++++ launcher/ui/widgets/GlobalFolderWidget.h | 26 +++++++++ launcher/ui/widgets/GlobalFolderWidget.ui | 45 ++++++++++++++++ 8 files changed, 175 insertions(+), 36 deletions(-) create mode 100644 launcher/ui/widgets/GlobalFolderWidget.cpp create mode 100644 launcher/ui/widgets/GlobalFolderWidget.h create mode 100644 launcher/ui/widgets/GlobalFolderWidget.ui diff --git a/launcher/BaseInstance.cpp b/launcher/BaseInstance.cpp index bf7fd2355..b6128592c 100644 --- a/launcher/BaseInstance.cpp +++ b/launcher/BaseInstance.cpp @@ -48,6 +48,7 @@ #include "settings/OverrideSetting.h" #include "settings/Setting.h" +#include "Application.h" #include "BuildConfig.h" #include "Commandline.h" #include "FileSystem.h" @@ -108,8 +109,11 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s // Global folders m_settings->registerSetting("UseGlobalScreenshotsFolder", false); + m_settings->registerSetting("GlobalScreenshotsPath", FS::PathCombine(APPLICATION->dataRoot(), "screenshots")); m_settings->registerSetting("UseGlobalSavesFolder", false); + m_settings->registerSetting("GlobalSavesPath", FS::PathCombine(APPLICATION->dataRoot(), "saves")); m_settings->registerSetting("UseGlobalResourcePacksFolder", false); + m_settings->registerSetting("GlobalResourcePacksPath", FS::PathCombine(APPLICATION->dataRoot(), "resourcepacks")); } QString BaseInstance::getPreLaunchCommand() diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 64822d6da..45fc13de1 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -1050,6 +1050,8 @@ SET(LAUNCHER_SOURCES ui/widgets/DropLabel.h ui/widgets/FocusLineEdit.cpp ui/widgets/FocusLineEdit.h + ui/widgets/GlobalFolderWidget.cpp + ui/widgets/GlobalFolderWidget.h ui/widgets/IconLabel.cpp ui/widgets/IconLabel.h ui/widgets/JavaSettingsWidget.cpp @@ -1157,6 +1159,7 @@ qt_wrap_ui(LAUNCHER_UI ui/widgets/InstanceCardWidget.ui ui/widgets/CustomCommands.ui ui/widgets/EnvironmentVariables.ui + ui/widgets/GlobalFolderWidget.ui ui/widgets/InfoFrame.ui ui/widgets/ModFilterWidget.ui ui/widgets/SubTaskProgressBar.ui diff --git a/launcher/minecraft/UpdateGlobalDirectoriesTask.cpp b/launcher/minecraft/UpdateGlobalDirectoriesTask.cpp index 2f8b92e00..7c470074b 100644 --- a/launcher/minecraft/UpdateGlobalDirectoriesTask.cpp +++ b/launcher/minecraft/UpdateGlobalDirectoriesTask.cpp @@ -38,15 +38,17 @@ class TryCreateSymlinkTask : public Task { } // Check if the destination already exists. - if (FS::checkFolderPathExists(m_destination)) { - // If it's already a symlink, it might already be correct. - if (FS::isSymLink(m_destination)) { - // If the target of the symlink is already the source, there's nothing to do. - if (FS::getSymLinkTarget(m_destination) == m_source) { - emitSucceeded(); - return; - } - } else if (!FS::checkFolderPathEmpty(m_destination)) { + // If it's already a symlink, it might already be correct. + if (FS::isSymLink(m_destination)) { + // If the target of the symlink is already the source, there's nothing to do. + if (FS::getSymLinkTarget(m_destination) == m_source) { + emitSucceeded(); + return; + } + + FS::deletePath(m_destination); + } else if (FS::checkFolderPathExists(m_destination)) { + if (!FS::checkFolderPathEmpty(m_destination)) { fail(tr("Failed to create global folder.\nEnsure that \"%1\" is empty.").arg(m_destination)); return; } @@ -54,6 +56,12 @@ class TryCreateSymlinkTask : public Task { FS::deletePath(m_destination); } + // Make sure the source folder exists + if (!FS::ensureFolderPathExists(m_source)) { + fail(tr("Failed to create global folder.\nEnsure that \"%1\" exists.").arg(m_source)); + return; + } + FS::create_link folderLink(m_source, m_destination); folderLink.linkRecursively(false); folderLink(); // TODO: Error check @@ -85,22 +93,22 @@ void UpdateGlobalDirectoriesTask::executeTask() { auto tasks = makeShared(this, "UpdateGlobalDirectoriesTask"); - auto screenshotsTask = makeShared(FS::PathCombine(APPLICATION->dataRoot(), "screenshots"), + auto screenshotsTask = makeShared(m_inst->settings()->get("GlobalScreenshotsPath").toString(), m_inst->screenshotsDir(), m_inst, "UseGlobalScreenshotsFolder"); connect(screenshotsTask.get(), &Task::failed, this, &UpdateGlobalDirectoriesTask::notifyFailed); tasks->addTask(screenshotsTask); - auto savesTask = makeShared(FS::PathCombine(APPLICATION->dataRoot(), "saves"), m_inst->worldDir(), m_inst, + auto savesTask = makeShared(m_inst->settings()->get("GlobalSavesPath").toString(), m_inst->worldDir(), m_inst, "UseGlobalSavesFolder"); connect(savesTask.get(), &Task::failed, this, &UpdateGlobalDirectoriesTask::notifyFailed); tasks->addTask(savesTask); - auto resoucePacksTask = makeShared(FS::PathCombine(APPLICATION->dataRoot(), "resourcepacks"), + auto resoucePacksTask = makeShared(m_inst->settings()->get("GlobalResourcePacksPath").toString(), m_inst->resourcePacksDir(), m_inst, "UseGlobalResourcePacksFolder"); connect(resoucePacksTask.get(), &Task::failed, this, &UpdateGlobalDirectoriesTask::notifyFailed); tasks->addTask(resoucePacksTask); - auto texturePacksTask = makeShared(FS::PathCombine(APPLICATION->dataRoot(), "resourcepacks"), + auto texturePacksTask = makeShared(m_inst->settings()->get("GlobalResourcePacksPath").toString(), m_inst->texturePacksDir(), m_inst, "UseGlobalResourcePacksFolder"); connect(texturePacksTask.get(), &Task::failed, this, &UpdateGlobalDirectoriesTask::notifyFailed); tasks->addTask(texturePacksTask); diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index dfea0a70d..44815e890 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -277,9 +277,12 @@ void InstanceSettingsPage::applySettings() } // Global folders - m_settings->set("UseGlobalScreenshotsFolder", ui->useGlobalScreenshotsFolder->isChecked()); - m_settings->set("UseGlobalSavesFolder", ui->useGlobalSavesFolder->isChecked()); - m_settings->set("UseGlobalResourcePacksFolder", ui->useGlobalResourcePacksFolder->isChecked()); + m_settings->set("UseGlobalScreenshotsFolder", ui->globalScreenshotsFolder->isEnabled()); + m_settings->set("GlobalScreenshotsPath", ui->globalScreenshotsFolder->getPath()); + m_settings->set("UseGlobalSavesFolder", ui->globalSavesFolder->isEnabled()); + m_settings->set("GlobalSavesPath", ui->globalSavesFolder->getPath()); + m_settings->set("UseGlobalResourcePacksFolder", ui->globalResourcePackFolder->isEnabled()); + m_settings->set("GlobalResourcePacksPath", ui->globalResourcePackFolder->getPath()); m_instance->applySettings(); @@ -394,9 +397,12 @@ void InstanceSettingsPage::loadSettings() ui->legacySettingsGroupBox->setChecked(m_settings->get("OverrideLegacySettings").toBool()); ui->onlineFixes->setChecked(m_settings->get("OnlineFixes").toBool()); - ui->useGlobalScreenshotsFolder->setChecked(m_settings->get("UseGlobalScreenshotsFolder").toBool()); - ui->useGlobalSavesFolder->setChecked(m_settings->get("UseGlobalSavesFolder").toBool()); - ui->useGlobalResourcePacksFolder->setChecked(m_settings->get("UseGlobalResourcePacksFolder").toBool()); + ui->globalScreenshotsFolder->initialize(m_settings->get("UseGlobalScreenshotsFolder").toBool(), + m_settings->get("GlobalScreenshotsPath").toString(), "Use global screenshots folder"); + ui->globalSavesFolder->initialize(m_settings->get("UseGlobalSavesFolder").toBool(), m_settings->get("GlobalSavesPath").toString(), + "Use global saves folder"); + ui->globalResourcePackFolder->initialize(m_settings->get("UseGlobalResourcePacksFolder").toBool(), + m_settings->get("GlobalResourcePacksPath").toString(), "Use global resource packs folder"); } void InstanceSettingsPage::on_javaDetectBtn_clicked() diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.ui b/launcher/ui/pages/instance/InstanceSettingsPage.ui index 4cb73b184..37f0cff25 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.ui +++ b/launcher/ui/pages/instance/InstanceSettingsPage.ui @@ -7,7 +7,7 @@ 0 0 691 - 581 + 591 @@ -36,7 +36,7 @@ - 6 + 0 @@ -750,25 +750,13 @@ - - - Use global screenshots folder - - + - - - Use global saves folder - - + - - - Use global resource packs folder - - + @@ -805,6 +793,12 @@
ui/widgets/EnvironmentVariables.h
1 + + GlobalFolderWidget + QWidget +
ui/widgets/GlobalFolderWidget.h
+ 1 +
openGlobalJavaSettingsButton diff --git a/launcher/ui/widgets/GlobalFolderWidget.cpp b/launcher/ui/widgets/GlobalFolderWidget.cpp new file mode 100644 index 000000000..5f7222379 --- /dev/null +++ b/launcher/ui/widgets/GlobalFolderWidget.cpp @@ -0,0 +1,53 @@ +#include "GlobalFolderWidget.h" +#include "ui_GlobalFolderWidget.h" + +#include + +#include "FileSystem.h" + +GlobalFolderWidget::GlobalFolderWidget(QWidget* parent) : QWidget(parent), ui(new Ui::GlobalFolderWidget) +{ + ui->setupUi(this); +} + +GlobalFolderWidget::~GlobalFolderWidget() +{ + delete ui; +} + +void GlobalFolderWidget::initialize(bool enabled, const QString& path, const QString& label) +{ + ui->enabledCheckBox->setChecked(enabled); + ui->enabledCheckBox->setText(label); + + ui->pathTextBox->setEnabled(enabled); + ui->pathTextBox->setText(path); + + ui->pathBrowseBtn->setEnabled(enabled); +} + +bool GlobalFolderWidget::isEnabled() const +{ + return ui->enabledCheckBox->isChecked(); +} + +QString GlobalFolderWidget::getPath() const +{ + return ui->pathTextBox->text(); +} + +void GlobalFolderWidget::on_enabledCheckBox_toggled(bool checked) +{ + ui->pathTextBox->setEnabled(checked); + ui->pathBrowseBtn->setEnabled(checked); +} + +void GlobalFolderWidget::on_pathBrowseBtn_clicked() +{ + QString path = QFileDialog::getExistingDirectory(this, tr("Select global folder"), ui->pathTextBox->text()); + if (path.isEmpty()) { + return; + } + + ui->pathTextBox->setText(path); +} diff --git a/launcher/ui/widgets/GlobalFolderWidget.h b/launcher/ui/widgets/GlobalFolderWidget.h new file mode 100644 index 000000000..80241c522 --- /dev/null +++ b/launcher/ui/widgets/GlobalFolderWidget.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +namespace Ui { +class GlobalFolderWidget; +} + +class GlobalFolderWidget : public QWidget { + Q_OBJECT + + public: + explicit GlobalFolderWidget(QWidget* parent = 0); + virtual ~GlobalFolderWidget(); + void initialize(bool enabled, const QString& path, const QString& label = ""); + + bool isEnabled() const; + QString getPath() const; + + private slots: + void on_enabledCheckBox_toggled(bool checked); + void on_pathBrowseBtn_clicked(); + + private: + Ui::GlobalFolderWidget* ui; +}; diff --git a/launcher/ui/widgets/GlobalFolderWidget.ui b/launcher/ui/widgets/GlobalFolderWidget.ui new file mode 100644 index 000000000..35f28a9c0 --- /dev/null +++ b/launcher/ui/widgets/GlobalFolderWidget.ui @@ -0,0 +1,45 @@ + + + GlobalFolderWidget + + + + 0 + 0 + 518 + 44 + + + + + + + + + + + + + + + + + false + + + + + + + false + + + Browse + + + + + + + + From 7f35195686e2efcaedfa8061787f930f960eb305 Mon Sep 17 00:00:00 2001 From: Naomi <103967@gmail.com> Date: Fri, 23 Aug 2024 09:41:31 +0200 Subject: [PATCH 04/10] Add file conflict dialog Signed-off-by: Naomi <103967@gmail.com> --- launcher/CMakeLists.txt | 3 + launcher/FileSystem.cpp | 1 + .../minecraft/UpdateGlobalDirectoriesTask.cpp | 71 +++++++++-- launcher/ui/dialogs/FileConflictDialog.cpp | 85 +++++++++++++ launcher/ui/dialogs/FileConflictDialog.h | 34 ++++++ launcher/ui/dialogs/FileConflictDialog.ui | 114 ++++++++++++++++++ 6 files changed, 300 insertions(+), 8 deletions(-) create mode 100644 launcher/ui/dialogs/FileConflictDialog.cpp create mode 100644 launcher/ui/dialogs/FileConflictDialog.h create mode 100644 launcher/ui/dialogs/FileConflictDialog.ui diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 45fc13de1..17280d37b 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -999,6 +999,8 @@ SET(LAUNCHER_SOURCES ui/dialogs/ExportPackDialog.h ui/dialogs/ExportToModListDialog.cpp ui/dialogs/ExportToModListDialog.h + ui/dialogs/FileConflictDialog.cpp + ui/dialogs/FileConflictDialog.h ui/dialogs/IconPickerDialog.cpp ui/dialogs/IconPickerDialog.h ui/dialogs/ImportResourceDialog.cpp @@ -1174,6 +1176,7 @@ qt_wrap_ui(LAUNCHER_UI ui/dialogs/ExportInstanceDialog.ui ui/dialogs/ExportPackDialog.ui ui/dialogs/ExportToModListDialog.ui + ui/dialogs/FileConflictDialog.ui ui/dialogs/IconPickerDialog.ui ui/dialogs/ImportResourceDialog.ui ui/dialogs/MSALoginDialog.ui diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index 565b2e982..f3ec3a9ea 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -1737,4 +1737,5 @@ QString getSymLinkTarget(const QString& path) { return QFileInfo(path).symLinkTarget(); } + } // namespace FS diff --git a/launcher/minecraft/UpdateGlobalDirectoriesTask.cpp b/launcher/minecraft/UpdateGlobalDirectoriesTask.cpp index 7c470074b..79927cd0a 100644 --- a/launcher/minecraft/UpdateGlobalDirectoriesTask.cpp +++ b/launcher/minecraft/UpdateGlobalDirectoriesTask.cpp @@ -1,15 +1,67 @@ #include "UpdateGlobalDirectoriesTask.h" +#include + #include "Application.h" #include "FileSystem.h" #include "minecraft/MinecraftInstance.h" #include "tasks/ConcurrentTask.h" #include "ui/dialogs/CustomMessageBox.h" +#include "ui/dialogs/FileConflictDialog.h" + +/** + * @brief Move a file or folder, and ask the user what to do in case of a conflict. + * @param source What to move. + * @param destination Where to move it to. + * @param recursive If true, all direct children will be moved 1 by 1. + * If false, the source will be directly moved to the destination. + * @param parent The parent of the dialog. + * @return True if everything could be moved. + */ +bool interactiveMove(const QString& source, const QString& destination, bool recursive = false, QWidget* parent = nullptr) +{ + const QFileInfo sourceInfo(source); + + // Make sure the source exists. + if (!sourceInfo.exists()) + return false; + + if (recursive) { + // Recursive doesn't make sense if the source isn't a directory. + if (!sourceInfo.isDir()) + return false; + + QDirIterator sourceIt(source, QDir::Filter::Files | QDir::Filter::Dirs | QDir::Filter::Hidden | QDir::Filter::NoDotAndDotDot); + + while (sourceIt.hasNext()) { + if (!interactiveMove(sourceIt.next(), FS::PathCombine(destination, sourceIt.fileName()), false)) + return false; + } + + return true; + } + + if (QFile(destination).exists()) { + FileConflictDialog dialog(source, destination, true, parent); + FileConflictDialog::Result result = dialog.execWithResult(); + + if (result == FileConflictDialog::Cancel) + return false; + else if (result == FileConflictDialog::ChooseDestination) + return FS::deletePath(source); + } + + return FS::move(source, destination); +} class TryCreateSymlinkTask : public Task { public: - explicit TryCreateSymlinkTask(const QString& source, const QString& destination, MinecraftInstance* instance, const QString& setting) - : m_source(source), m_destination(destination), m_inst(instance), m_setting(setting) + explicit TryCreateSymlinkTask(const QString& source, + const QString& destination, + MinecraftInstance* instance, + const QString& setting, + QWidget* parent) + : m_source(source), m_destination(destination), m_inst(instance), m_setting(setting), m_parent(parent) { setObjectName("TryCreateSymlinkTask"); } @@ -49,8 +101,10 @@ class TryCreateSymlinkTask : public Task { FS::deletePath(m_destination); } else if (FS::checkFolderPathExists(m_destination)) { if (!FS::checkFolderPathEmpty(m_destination)) { - fail(tr("Failed to create global folder.\nEnsure that \"%1\" is empty.").arg(m_destination)); - return; + if (!interactiveMove(m_destination, m_source, true, m_parent)) { + fail(tr("Failed to create global folder.\nEnsure that \"%1\" is empty.").arg(m_destination)); + return; + } } FS::deletePath(m_destination); @@ -81,6 +135,7 @@ class TryCreateSymlinkTask : public Task { QString m_destination; MinecraftInstance* m_inst; QString m_setting; + QWidget* m_parent; }; UpdateGlobalDirectoriesTask::UpdateGlobalDirectoriesTask(MinecraftInstance* inst, QWidget* parent) @@ -94,22 +149,22 @@ void UpdateGlobalDirectoriesTask::executeTask() auto tasks = makeShared(this, "UpdateGlobalDirectoriesTask"); auto screenshotsTask = makeShared(m_inst->settings()->get("GlobalScreenshotsPath").toString(), - m_inst->screenshotsDir(), m_inst, "UseGlobalScreenshotsFolder"); + m_inst->screenshotsDir(), m_inst, "UseGlobalScreenshotsFolder", m_parent); connect(screenshotsTask.get(), &Task::failed, this, &UpdateGlobalDirectoriesTask::notifyFailed); tasks->addTask(screenshotsTask); auto savesTask = makeShared(m_inst->settings()->get("GlobalSavesPath").toString(), m_inst->worldDir(), m_inst, - "UseGlobalSavesFolder"); + "UseGlobalSavesFolder", m_parent); connect(savesTask.get(), &Task::failed, this, &UpdateGlobalDirectoriesTask::notifyFailed); tasks->addTask(savesTask); auto resoucePacksTask = makeShared(m_inst->settings()->get("GlobalResourcePacksPath").toString(), - m_inst->resourcePacksDir(), m_inst, "UseGlobalResourcePacksFolder"); + m_inst->resourcePacksDir(), m_inst, "UseGlobalResourcePacksFolder", m_parent); connect(resoucePacksTask.get(), &Task::failed, this, &UpdateGlobalDirectoriesTask::notifyFailed); tasks->addTask(resoucePacksTask); auto texturePacksTask = makeShared(m_inst->settings()->get("GlobalResourcePacksPath").toString(), - m_inst->texturePacksDir(), m_inst, "UseGlobalResourcePacksFolder"); + m_inst->texturePacksDir(), m_inst, "UseGlobalResourcePacksFolder", m_parent); connect(texturePacksTask.get(), &Task::failed, this, &UpdateGlobalDirectoriesTask::notifyFailed); tasks->addTask(texturePacksTask); diff --git a/launcher/ui/dialogs/FileConflictDialog.cpp b/launcher/ui/dialogs/FileConflictDialog.cpp new file mode 100644 index 000000000..891c17566 --- /dev/null +++ b/launcher/ui/dialogs/FileConflictDialog.cpp @@ -0,0 +1,85 @@ +#include "FileConflictDialog.h" +#include "ui_FileConflictDialog.h" + +#include +#include +#include + +#include "Application.h" + +FileConflictDialog::FileConflictDialog(QString source, QString destination, bool move, QWidget* parent) + : QDialog(parent), ui(new Ui::FileConflictDialog), m_result(Result::Cancel) +{ + ui->setupUi(this); + + QLocale locale; + + // Setup buttons + connect(ui->buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &FileConflictDialog::cancel); + if (move) { + setWindowTitle("File conflict while moving files"); + + auto chooseSourceButton = ui->buttonBox->addButton(tr("Keep source"), QDialogButtonBox::DestructiveRole); + chooseSourceButton->setIcon(APPLICATION->getThemedIcon("delete")); + connect(chooseSourceButton, &QPushButton::clicked, this, &FileConflictDialog::chooseSource); + + auto chooseDestinationButton = ui->buttonBox->addButton(tr("Keep destination"), QDialogButtonBox::DestructiveRole); + chooseDestinationButton->setIcon(APPLICATION->getThemedIcon("delete")); + connect(chooseDestinationButton, &QPushButton::clicked, this, &FileConflictDialog::chooseDestination); + } else { + setWindowTitle("File conflict while copying files"); + + auto chooseSourceButton = ui->buttonBox->addButton(tr("Overwrite destination"), QDialogButtonBox::DestructiveRole); + chooseSourceButton->setIcon(APPLICATION->getThemedIcon("delete")); + connect(chooseSourceButton, &QPushButton::clicked, this, &FileConflictDialog::chooseSource); + + auto chooseDestinationButton = ui->buttonBox->addButton(tr("Skip"), QDialogButtonBox::DestructiveRole); + connect(chooseDestinationButton, &QPushButton::clicked, this, &FileConflictDialog::chooseDestination); + } + + // Setup info + QFileInfo sourceInfo(source); + ui->sourceInfoLabel->setText(tr("Name: %1
Size: %2
Last modified: %3") + .arg(source, sourceInfo.isDir() ? "-" : locale.formattedDataSize(sourceInfo.size()), + sourceInfo.lastModified().toString(locale.dateTimeFormat()))); + + QFileInfo destinationInfo(destination); + ui->destinationInfoLabel->setText(tr("Name: %1
Size: %2
Last modified: %3") + .arg(destination, + destinationInfo.isDir() ? "-" : locale.formattedDataSize(destinationInfo.size()), + destinationInfo.lastModified().toString(locale.dateTimeFormat()))); +} + +FileConflictDialog::~FileConflictDialog() +{ + delete ui; +} + +FileConflictDialog::Result FileConflictDialog::execWithResult() +{ + exec(); + return m_result; +} + +FileConflictDialog::Result FileConflictDialog::getResult() const +{ + return m_result; +} + +void FileConflictDialog::chooseSource() +{ + m_result = Result::ChooseSource; + accept(); +} + +void FileConflictDialog::chooseDestination() +{ + m_result = Result::ChooseDestination; + accept(); +} + +void FileConflictDialog::cancel() +{ + m_result = Result::Cancel; + reject(); +} diff --git a/launcher/ui/dialogs/FileConflictDialog.h b/launcher/ui/dialogs/FileConflictDialog.h new file mode 100644 index 000000000..02c55df93 --- /dev/null +++ b/launcher/ui/dialogs/FileConflictDialog.h @@ -0,0 +1,34 @@ +#pragma once + +#include + +namespace Ui { +class FileConflictDialog; +} + +class FileConflictDialog : public QDialog { + Q_OBJECT + + public: + enum Result { Cancel, ChooseSource, ChooseDestination }; + + /// @brief Create a new file conflict dialog + /// @param source The source path. What to copy/move. + /// @param destination The destination path. Where to copy/move. + /// @param move Whether the conflict is for a move or copy action + /// @param parent The parent of the dialog + explicit FileConflictDialog(QString source, QString destination, bool move = false, QWidget* parent = nullptr); + ~FileConflictDialog() override; + + Result execWithResult(); + Result getResult() const; + + private slots: + void chooseSource(); + void chooseDestination(); + void cancel(); + + private: + Ui::FileConflictDialog* ui; + Result m_result; +}; diff --git a/launcher/ui/dialogs/FileConflictDialog.ui b/launcher/ui/dialogs/FileConflictDialog.ui new file mode 100644 index 000000000..c71afd59a --- /dev/null +++ b/launcher/ui/dialogs/FileConflictDialog.ui @@ -0,0 +1,114 @@ + + + FileConflictDialog + + + + 0 + 0 + 411 + 219 + + + + + 0 + 0 + + + + + + + + + + Would you like to overwrite the destination? + + + Qt::AlignmentFlag::AlignCenter + + + + + + + QLayout::SizeConstraint::SetDefaultConstraint + + + + + + + + 0 + 0 + + + + <html><head/><body><p><span style=" font-weight:700;">Source</span></p></body></html> + + + Qt::AlignmentFlag::AlignHCenter|Qt::AlignmentFlag::AlignTop + + + + + + + + 0 + 0 + + + + <html><head/><body><p><b>Name:</b></p><p>Size:</p><p>Date:</p></body></html> + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop + + + + + + + + + + + <html><head/><body><p><span style=" font-weight:700;">Destination</span></p></body></html> + + + Qt::AlignmentFlag::AlignHCenter|Qt::AlignmentFlag::AlignTop + + + + + + + <html><head/><body><p>Name:</p><p>Size:</p><p>Date:</p></body></html> + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop + + + + + + + + + + + Qt::Orientation::Horizontal + + + QDialogButtonBox::StandardButton::Cancel + + + + + + + + From f5ed35fb8a9fadacbf3f61c3169aaf91c9b89002 Mon Sep 17 00:00:00 2001 From: Naomi <103967@gmail.com> Date: Fri, 6 Sep 2024 11:52:30 +0200 Subject: [PATCH 05/10] Review feedback Signed-off-by: Naomi <103967@gmail.com> --- .../minecraft/UpdateGlobalDirectoriesTask.cpp | 19 +++++---- launcher/ui/dialogs/FileConflictDialog.cpp | 39 ++++++++++++------- launcher/ui/dialogs/FileConflictDialog.h | 2 + 3 files changed, 39 insertions(+), 21 deletions(-) diff --git a/launcher/minecraft/UpdateGlobalDirectoriesTask.cpp b/launcher/minecraft/UpdateGlobalDirectoriesTask.cpp index 79927cd0a..42d5d1147 100644 --- a/launcher/minecraft/UpdateGlobalDirectoriesTask.cpp +++ b/launcher/minecraft/UpdateGlobalDirectoriesTask.cpp @@ -26,11 +26,8 @@ bool interactiveMove(const QString& source, const QString& destination, bool rec if (!sourceInfo.exists()) return false; - if (recursive) { - // Recursive doesn't make sense if the source isn't a directory. - if (!sourceInfo.isDir()) - return false; - + // Recursive doesn't make sense if the source isn't a directory. + if (recursive && sourceInfo.isDir()) { QDirIterator sourceIt(source, QDir::Filter::Files | QDir::Filter::Dirs | QDir::Filter::Hidden | QDir::Filter::NoDotAndDotDot); while (sourceIt.hasNext()) { @@ -49,6 +46,8 @@ bool interactiveMove(const QString& source, const QString& destination, bool rec return false; else if (result == FileConflictDialog::ChooseDestination) return FS::deletePath(source); + else if (result == FileConflictDialog::ChooseSource) + FS::deletePath(destination); } return FS::move(source, destination); @@ -118,9 +117,15 @@ class TryCreateSymlinkTask : public Task { FS::create_link folderLink(m_source, m_destination); folderLink.linkRecursively(false); - folderLink(); // TODO: Error check - emitSucceeded(); + if (folderLink()) { + emitSucceeded(); + } else { + fail(tr("Failed to create global folder. Error %1: %2") + .arg(folderLink.getOSError().value()) + .arg(folderLink.getOSError().message().c_str())); + } + return; } diff --git a/launcher/ui/dialogs/FileConflictDialog.cpp b/launcher/ui/dialogs/FileConflictDialog.cpp index 891c17566..e1c8d490d 100644 --- a/launcher/ui/dialogs/FileConflictDialog.cpp +++ b/launcher/ui/dialogs/FileConflictDialog.cpp @@ -2,6 +2,7 @@ #include "ui_FileConflictDialog.h" #include +#include #include #include @@ -12,12 +13,10 @@ FileConflictDialog::FileConflictDialog(QString source, QString destination, bool { ui->setupUi(this); - QLocale locale; - // Setup buttons connect(ui->buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &FileConflictDialog::cancel); if (move) { - setWindowTitle("File conflict while moving files"); + setWindowTitle(tr("File conflict while moving files")); auto chooseSourceButton = ui->buttonBox->addButton(tr("Keep source"), QDialogButtonBox::DestructiveRole); chooseSourceButton->setIcon(APPLICATION->getThemedIcon("delete")); @@ -27,7 +26,7 @@ FileConflictDialog::FileConflictDialog(QString source, QString destination, bool chooseDestinationButton->setIcon(APPLICATION->getThemedIcon("delete")); connect(chooseDestinationButton, &QPushButton::clicked, this, &FileConflictDialog::chooseDestination); } else { - setWindowTitle("File conflict while copying files"); + setWindowTitle(tr("File conflict while copying files")); auto chooseSourceButton = ui->buttonBox->addButton(tr("Overwrite destination"), QDialogButtonBox::DestructiveRole); chooseSourceButton->setIcon(APPLICATION->getThemedIcon("delete")); @@ -38,16 +37,8 @@ FileConflictDialog::FileConflictDialog(QString source, QString destination, bool } // Setup info - QFileInfo sourceInfo(source); - ui->sourceInfoLabel->setText(tr("Name: %1
Size: %2
Last modified: %3") - .arg(source, sourceInfo.isDir() ? "-" : locale.formattedDataSize(sourceInfo.size()), - sourceInfo.lastModified().toString(locale.dateTimeFormat()))); - - QFileInfo destinationInfo(destination); - ui->destinationInfoLabel->setText(tr("Name: %1
Size: %2
Last modified: %3") - .arg(destination, - destinationInfo.isDir() ? "-" : locale.formattedDataSize(destinationInfo.size()), - destinationInfo.lastModified().toString(locale.dateTimeFormat()))); + ui->sourceInfoLabel->setText(GetFileInfoText(source)); + ui->destinationInfoLabel->setText(GetFileInfoText(destination)); } FileConflictDialog::~FileConflictDialog() @@ -83,3 +74,23 @@ void FileConflictDialog::cancel() m_result = Result::Cancel; reject(); } + +QString FileConflictDialog::GetFileInfoText(const QString& filePath) const +{ + QLocale locale; + QFileInfo fileInfo(filePath); + + if (fileInfo.isDir()) { + QDir dirInfo(filePath); + return tr("Name: %1
Size: %2
Last modified: %3
Items: %4") + .arg(filePath) + .arg("-") + .arg(fileInfo.lastModified().toString(locale.dateTimeFormat())) + .arg(dirInfo.entryList(QDir::AllEntries | QDir::NoDotAndDotDot).count()); + } else { + return tr("Name: %1
Size: %2
Last modified: %3") + .arg(filePath) + .arg(locale.formattedDataSize(fileInfo.size())) + .arg(fileInfo.lastModified().toString(locale.dateTimeFormat())); + } +} diff --git a/launcher/ui/dialogs/FileConflictDialog.h b/launcher/ui/dialogs/FileConflictDialog.h index 02c55df93..ef02f7a0e 100644 --- a/launcher/ui/dialogs/FileConflictDialog.h +++ b/launcher/ui/dialogs/FileConflictDialog.h @@ -29,6 +29,8 @@ class FileConflictDialog : public QDialog { void cancel(); private: + QString GetFileInfoText(const QString& filePath) const; + Ui::FileConflictDialog* ui; Result m_result; }; From c3d6c8bdba6bb2564b5184545867340934b4a20e Mon Sep 17 00:00:00 2001 From: Naomi <103967@gmail.com> Date: Sat, 21 Sep 2024 09:37:39 +0200 Subject: [PATCH 06/10] Implement global overridable global-folders-settings Signed-off-by: Naomi <103967@gmail.com> --- launcher/Application.cpp | 8 ++ launcher/BaseInstance.cpp | 15 ++-- launcher/ui/pages/global/MinecraftPage.cpp | 20 +++++ launcher/ui/pages/global/MinecraftPage.ui | 77 +++++++++++++++++++ .../pages/instance/InstanceSettingsPage.cpp | 37 +++++++-- .../ui/pages/instance/InstanceSettingsPage.ui | 51 +++++++++++- 6 files changed, 193 insertions(+), 15 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 6c0319984..99eea3b29 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -765,6 +765,14 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) // FTBApp instances m_settings->registerSetting("FTBAppInstancesPath", ""); + // Global folders + m_settings->registerSetting("UseGlobalScreenshotsFolder", false); + m_settings->registerSetting("GlobalScreenshotsPath", FS::PathCombine(APPLICATION->dataRoot(), "screenshots")); + m_settings->registerSetting("UseGlobalSavesFolder", false); + m_settings->registerSetting("GlobalSavesPath", FS::PathCombine(APPLICATION->dataRoot(), "saves")); + m_settings->registerSetting("UseGlobalResourcePacksFolder", false); + m_settings->registerSetting("GlobalResourcePacksPath", FS::PathCombine(APPLICATION->dataRoot(), "resourcepacks")); + // Init page provider { m_globalSettingsProvider = std::make_shared(tr("Settings")); diff --git a/launcher/BaseInstance.cpp b/launcher/BaseInstance.cpp index b6128592c..3780aeb46 100644 --- a/launcher/BaseInstance.cpp +++ b/launcher/BaseInstance.cpp @@ -108,12 +108,15 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s m_settings->registerSetting("Profiler", ""); // Global folders - m_settings->registerSetting("UseGlobalScreenshotsFolder", false); - m_settings->registerSetting("GlobalScreenshotsPath", FS::PathCombine(APPLICATION->dataRoot(), "screenshots")); - m_settings->registerSetting("UseGlobalSavesFolder", false); - m_settings->registerSetting("GlobalSavesPath", FS::PathCombine(APPLICATION->dataRoot(), "saves")); - m_settings->registerSetting("UseGlobalResourcePacksFolder", false); - m_settings->registerSetting("GlobalResourcePacksPath", FS::PathCombine(APPLICATION->dataRoot(), "resourcepacks")); + auto globalScreenshotsSetting = m_settings->registerSetting("OverrideGlobalScreenshots", false); + m_settings->registerOverride(globalSettings->getSetting("UseGlobalScreenshotsFolder"), globalScreenshotsSetting); + m_settings->registerOverride(globalSettings->getSetting("GlobalScreenshotsPath"), globalScreenshotsSetting); + auto globalSavesSetting = m_settings->registerSetting("OverrideGlobalSaves", false); + m_settings->registerOverride(globalSettings->getSetting("UseGlobalSavesFolder"), globalSavesSetting); + m_settings->registerOverride(globalSettings->getSetting("GlobalSavesPath"), globalSavesSetting); + auto globalResourcePacksSetting = m_settings->registerSetting("OverrideGlobalResourcePacks", false); + m_settings->registerOverride(globalSettings->getSetting("UseGlobalResourcePacksFolder"), globalResourcePacksSetting); + m_settings->registerOverride(globalSettings->getSetting("GlobalResourcePacksPath"), globalResourcePacksSetting); } QString BaseInstance::getPreLaunchCommand() diff --git a/launcher/ui/pages/global/MinecraftPage.cpp b/launcher/ui/pages/global/MinecraftPage.cpp index 3431dcb9c..00247cc10 100644 --- a/launcher/ui/pages/global/MinecraftPage.cpp +++ b/launcher/ui/pages/global/MinecraftPage.cpp @@ -42,6 +42,7 @@ #include #include "Application.h" +#include "InstanceList.h" #include "settings/SettingsObject.h" #ifdef Q_OS_LINUX @@ -123,6 +124,19 @@ void MinecraftPage::applySettings() // Legacy settings s->set("OnlineFixes", ui->onlineFixes->isChecked()); + + // Global folders + s->set("UseGlobalScreenshotsFolder", ui->globalScreenshotsFolder->isEnabled()); + s->set("GlobalScreenshotsPath", ui->globalScreenshotsFolder->getPath()); + s->set("UseGlobalSavesFolder", ui->globalSavesFolder->isEnabled()); + s->set("GlobalSavesPath", ui->globalSavesFolder->getPath()); + s->set("UseGlobalResourcePacksFolder", ui->globalResourcePacksFolder->isEnabled()); + s->set("GlobalResourcePacksPath", ui->globalResourcePacksFolder->getPath()); + + // Run applySettings for each instance + for (int i = 0; i < APPLICATION->instances()->count(); i++) { + APPLICATION->instances()->at(i)->applySettings(); + } } void MinecraftPage::loadSettings() @@ -177,6 +191,12 @@ void MinecraftPage::loadSettings() ui->quitAfterGameStopCheck->setChecked(s->get("QuitAfterGameStop").toBool()); ui->onlineFixes->setChecked(s->get("OnlineFixes").toBool()); + + // Global folders + ui->globalScreenshotsFolder->initialize(s->get("UseGlobalScreenshotsFolder").toBool(), s->get("GlobalScreenshotsPath").toString()); + ui->globalSavesFolder->initialize(s->get("UseGlobalSavesFolder").toBool(), s->get("GlobalSavesPath").toString()); + ui->globalResourcePacksFolder->initialize(s->get("UseGlobalResourcePacksFolder").toBool(), + s->get("GlobalResourcePacksPath").toString()); } void MinecraftPage::retranslate() diff --git a/launcher/ui/pages/global/MinecraftPage.ui b/launcher/ui/pages/global/MinecraftPage.ui index 7d2741250..f8457facd 100644 --- a/launcher/ui/pages/global/MinecraftPage.ui +++ b/launcher/ui/pages/global/MinecraftPage.ui @@ -337,10 +337,87 @@
+ + + + 0 + 0 + + + + Global Folders + + + + + + + 0 + 0 + + + + Settings + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop + + + + + + <html><head/><body><p><span style=" font-weight:600; color:#f5c211;">Warning</span><span style=" color:#f5c211;">: After enabling files cannot be automatically restored to their original folder.</span></p></body></html> + + + + + + + + + + + + + + + + + + + + 0 + 11 + + + + <html><head/><body><p>A global folder is a folder that is shared across instances.</p><p>For example: If two instances share the same global saves folder, they can both play on the same worlds without having to copy the world over.</p></body></html> + + + false + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop + + + true + + + + +
+ + + GlobalFolderWidget + QWidget +
ui/widgets/GlobalFolderWidget.h
+ 1 +
+
maximizedCheckBox windowWidthSpinBox diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.cpp b/launcher/ui/pages/instance/InstanceSettingsPage.cpp index 44815e890..fb3d23f8c 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.cpp +++ b/launcher/ui/pages/instance/InstanceSettingsPage.cpp @@ -277,12 +277,33 @@ void InstanceSettingsPage::applySettings() } // Global folders - m_settings->set("UseGlobalScreenshotsFolder", ui->globalScreenshotsFolder->isEnabled()); - m_settings->set("GlobalScreenshotsPath", ui->globalScreenshotsFolder->getPath()); - m_settings->set("UseGlobalSavesFolder", ui->globalSavesFolder->isEnabled()); - m_settings->set("GlobalSavesPath", ui->globalSavesFolder->getPath()); - m_settings->set("UseGlobalResourcePacksFolder", ui->globalResourcePackFolder->isEnabled()); - m_settings->set("GlobalResourcePacksPath", ui->globalResourcePackFolder->getPath()); + bool overrideGlobalScreenshotsSetting = ui->globalScreenshotsGroupBox->isChecked(); + m_settings->set("OverrideGlobalScreenshots", overrideGlobalScreenshotsSetting); + if (overrideGlobalScreenshotsSetting) { + m_settings->set("UseGlobalScreenshotsFolder", ui->globalScreenshotsFolder->isEnabled()); + m_settings->set("GlobalScreenshotsPath", ui->globalScreenshotsFolder->getPath()); + } else { + m_settings->reset("UseGlobalScreenshotsFolder"); + m_settings->reset("GlobalScreenshotsPath"); + } + bool overrideGlobalSavesSetting = ui->globalSavesGroupBox->isChecked(); + m_settings->set("OverrideGlobalSaves", overrideGlobalSavesSetting); + if (overrideGlobalSavesSetting) { + m_settings->set("UseGlobalSavesFolder", ui->globalSavesFolder->isEnabled()); + m_settings->set("GlobalSavesPath", ui->globalSavesFolder->getPath()); + } else { + m_settings->reset("UseGlobalSavesFolder"); + m_settings->reset("GlobalSavesPath"); + } + bool overrideGlobalResourcePacksSetting = ui->globalResourcePackGroupBox->isChecked(); + m_settings->set("OverrideGlobalResourcePacks", overrideGlobalResourcePacksSetting); + if (overrideGlobalResourcePacksSetting) { + m_settings->set("UseGlobalResourcePacksFolder", ui->globalResourcePackFolder->isEnabled()); + m_settings->set("GlobalResourcePacksPath", ui->globalResourcePackFolder->getPath()); + } else { + m_settings->reset("UseGlobalResourcePacksFolder"); + m_settings->reset("GlobalResourcePacksPath"); + } m_instance->applySettings(); @@ -397,10 +418,14 @@ void InstanceSettingsPage::loadSettings() ui->legacySettingsGroupBox->setChecked(m_settings->get("OverrideLegacySettings").toBool()); ui->onlineFixes->setChecked(m_settings->get("OnlineFixes").toBool()); + // Global folders + ui->globalScreenshotsGroupBox->setChecked(m_settings->get("OverrideGlobalScreenshots").toBool()); ui->globalScreenshotsFolder->initialize(m_settings->get("UseGlobalScreenshotsFolder").toBool(), m_settings->get("GlobalScreenshotsPath").toString(), "Use global screenshots folder"); + ui->globalSavesGroupBox->setChecked(m_settings->get("OverrideGlobalSaves").toBool()); ui->globalSavesFolder->initialize(m_settings->get("UseGlobalSavesFolder").toBool(), m_settings->get("GlobalSavesPath").toString(), "Use global saves folder"); + ui->globalResourcePackGroupBox->setChecked(m_settings->get("OverrideGlobalResourcePacks").toBool()); ui->globalResourcePackFolder->initialize(m_settings->get("UseGlobalResourcePacksFolder").toBool(), m_settings->get("GlobalResourcePacksPath").toString(), "Use global resource packs folder"); } diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.ui b/launcher/ui/pages/instance/InstanceSettingsPage.ui index 37f0cff25..23cd2af41 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.ui +++ b/launcher/ui/pages/instance/InstanceSettingsPage.ui @@ -750,13 +750,58 @@ - + + + Override global screenshots setting + + + true + + + false + + + + + + + - + + + Override global saves setting + + + true + + + false + + + + + + + - + + + Override global resource pack setting + + + true + + + false + + + + + + + From d6aaee6ea6801bd341c1386ce97e11266a8830df Mon Sep 17 00:00:00 2001 From: Naomi <103967@gmail.com> Date: Sun, 22 Jun 2025 14:23:08 +0200 Subject: [PATCH 07/10] Change global folders to shared folders - For each instance, a custom (shared) screenshots/worlds/resourcepacks folder can be set - Remove ability to set shared folders for all instances (Might add later in different form) Signed-off-by: Naomi <103967@gmail.com> --- launcher/Application.cpp | 8 ---- launcher/BaseInstance.cpp | 17 +++---- launcher/CMakeLists.txt | 10 ++-- launcher/minecraft/MinecraftInstance.cpp | 8 ++-- launcher/minecraft/MinecraftInstance.h | 4 +- ...sk.cpp => UpdateSharedDirectoriesTask.cpp} | 46 +++++++++---------- ...esTask.h => UpdateSharedDirectoriesTask.h} | 6 +-- .../pages/instance/ExternalResourcesPage.ui | 2 +- .../ui/pages/instance/ScreenshotsPage.cpp | 2 +- launcher/ui/pages/instance/ScreenshotsPage.ui | 4 +- launcher/ui/pages/instance/WorldListPage.cpp | 2 +- launcher/ui/pages/instance/WorldListPage.ui | 4 +- .../ui/widgets/MinecraftSettingsWidget.cpp | 27 +++++------ .../ui/widgets/MinecraftSettingsWidget.ui | 26 +++++------ ...olderWidget.cpp => SharedFolderWidget.cpp} | 20 ++++---- ...balFolderWidget.h => SharedFolderWidget.h} | 10 ++-- ...lFolderWidget.ui => SharedFolderWidget.ui} | 4 +- 17 files changed, 95 insertions(+), 105 deletions(-) rename launcher/minecraft/{UpdateGlobalDirectoriesTask.cpp => UpdateSharedDirectoriesTask.cpp} (80%) rename launcher/minecraft/{UpdateGlobalDirectoriesTask.h => UpdateSharedDirectoriesTask.h} (66%) rename launcher/ui/widgets/{GlobalFolderWidget.cpp => SharedFolderWidget.cpp} (56%) rename launcher/ui/widgets/{GlobalFolderWidget.h => SharedFolderWidget.h} (64%) rename launcher/ui/widgets/{GlobalFolderWidget.ui => SharedFolderWidget.ui} (91%) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index bab46e354..99b72870b 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -894,14 +894,6 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) // Custom Technic Client ID m_settings->registerSetting("TechnicClientID", ""); - // Global folders - m_settings->registerSetting("UseGlobalScreenshotsFolder", false); - m_settings->registerSetting("GlobalScreenshotsPath", FS::PathCombine(APPLICATION->dataRoot(), "screenshots")); - m_settings->registerSetting("UseGlobalSavesFolder", false); - m_settings->registerSetting("GlobalSavesPath", FS::PathCombine(APPLICATION->dataRoot(), "saves")); - m_settings->registerSetting("UseGlobalResourcePacksFolder", false); - m_settings->registerSetting("GlobalResourcePacksPath", FS::PathCombine(APPLICATION->dataRoot(), "resourcepacks")); - // Init page provider { m_globalSettingsProvider = std::make_shared(tr("Settings")); diff --git a/launcher/BaseInstance.cpp b/launcher/BaseInstance.cpp index bc0877cd1..b5d7b2a03 100644 --- a/launcher/BaseInstance.cpp +++ b/launcher/BaseInstance.cpp @@ -126,16 +126,13 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s m_settings->registerSetting("Profiler", ""); - // Global folders - auto globalScreenshotsSetting = m_settings->registerSetting("OverrideGlobalScreenshots", false); - m_settings->registerOverride(globalSettings->getSetting("UseGlobalScreenshotsFolder"), globalScreenshotsSetting); - m_settings->registerOverride(globalSettings->getSetting("GlobalScreenshotsPath"), globalScreenshotsSetting); - auto globalSavesSetting = m_settings->registerSetting("OverrideGlobalSaves", false); - m_settings->registerOverride(globalSettings->getSetting("UseGlobalSavesFolder"), globalSavesSetting); - m_settings->registerOverride(globalSettings->getSetting("GlobalSavesPath"), globalSavesSetting); - auto globalResourcePacksSetting = m_settings->registerSetting("OverrideGlobalResourcePacks", false); - m_settings->registerOverride(globalSettings->getSetting("UseGlobalResourcePacksFolder"), globalResourcePacksSetting); - m_settings->registerOverride(globalSettings->getSetting("GlobalResourcePacksPath"), globalResourcePacksSetting); + // Shared folders + m_settings->registerSetting("UseSharedScreenshotsFolder", false); + m_settings->registerSetting("SharedScreenshotsPath", FS::PathCombine(APPLICATION->dataRoot(), "screenshots")); + m_settings->registerSetting("UseSharedSavesFolder", false); + m_settings->registerSetting("SharedSavesPath", FS::PathCombine(APPLICATION->dataRoot(), "saves")); + m_settings->registerSetting("UseSharedResourcePacksFolder", false); + m_settings->registerSetting("SharedResourcePacksPath", FS::PathCombine(APPLICATION->dataRoot(), "resourcepacks")); } QString BaseInstance::getPreLaunchCommand() diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 555c7fd6f..773913210 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -315,8 +315,8 @@ set(MINECRAFT_SOURCES minecraft/Library.cpp minecraft/Library.h minecraft/MojangDownloadInfo.h - minecraft/UpdateGlobalDirectoriesTask.cpp - minecraft/UpdateGlobalDirectoriesTask.h + minecraft/UpdateSharedDirectoriesTask.cpp + minecraft/UpdateSharedDirectoriesTask.h minecraft/VanillaInstanceCreationTask.cpp minecraft/VanillaInstanceCreationTask.h minecraft/VersionFile.cpp @@ -1126,8 +1126,8 @@ SET(LAUNCHER_SOURCES ui/widgets/CustomCommands.h ui/widgets/EnvironmentVariables.cpp ui/widgets/EnvironmentVariables.h - ui/widgets/GlobalFolderWidget.cpp - ui/widgets/GlobalFolderWidget.h + ui/widgets/SharedFolderWidget.cpp + ui/widgets/SharedFolderWidget.h ui/widgets/IconLabel.cpp ui/widgets/IconLabel.h ui/widgets/JavaWizardWidget.cpp @@ -1235,7 +1235,7 @@ qt_wrap_ui(LAUNCHER_UI ui/pages/modplatform/technic/TechnicPage.ui ui/widgets/CustomCommands.ui ui/widgets/EnvironmentVariables.ui - ui/widgets/GlobalFolderWidget.ui + ui/widgets/SharedFolderWidget.ui ui/widgets/InfoFrame.ui ui/widgets/ModFilterWidget.ui ui/widgets/SubTaskProgressBar.ui diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 6cd46598c..9c9f66241 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -85,7 +85,7 @@ #include "AssetsUtils.h" #include "MinecraftLoadAndCheck.h" #include "PackProfile.h" -#include "minecraft/UpdateGlobalDirectoriesTask.h" +#include "minecraft/UpdateSharedDirectoriesTask.h" #include "minecraft/gameoptions/GameOptions.h" #include "minecraft/update/FoldersTask.h" @@ -1316,9 +1316,9 @@ QList MinecraftInstance::getJarMods() const void MinecraftInstance::applySettings() { - // Global directories - m_update_global_directories_task = std::make_shared(this, nullptr); - m_update_global_directories_task->start(); + // Shared directories + m_update_shared_directories_task = std::make_shared(this, nullptr); + m_update_shared_directories_task->start(); } #include "MinecraftInstance.moc" diff --git a/launcher/minecraft/MinecraftInstance.h b/launcher/minecraft/MinecraftInstance.h index 4934c6a0e..c92eba1be 100644 --- a/launcher/minecraft/MinecraftInstance.h +++ b/launcher/minecraft/MinecraftInstance.h @@ -52,7 +52,7 @@ class WorldList; class GameOptions; class LaunchStep; class PackProfile; -class UpdateGlobalDirectoriesTask; +class UpdateSharedDirectoriesTask; class MinecraftInstance : public BaseInstance { Q_OBJECT @@ -167,7 +167,7 @@ class MinecraftInstance : public BaseInstance { protected: // data std::shared_ptr m_components; - std::shared_ptr m_update_global_directories_task; + std::shared_ptr m_update_shared_directories_task; mutable std::shared_ptr m_loader_mod_list; mutable std::shared_ptr m_core_mod_list; mutable std::shared_ptr m_nil_mod_list; diff --git a/launcher/minecraft/UpdateGlobalDirectoriesTask.cpp b/launcher/minecraft/UpdateSharedDirectoriesTask.cpp similarity index 80% rename from launcher/minecraft/UpdateGlobalDirectoriesTask.cpp rename to launcher/minecraft/UpdateSharedDirectoriesTask.cpp index 4b3717104..0b8e469a1 100644 --- a/launcher/minecraft/UpdateGlobalDirectoriesTask.cpp +++ b/launcher/minecraft/UpdateSharedDirectoriesTask.cpp @@ -1,4 +1,4 @@ -#include "UpdateGlobalDirectoriesTask.h" +#include "UpdateSharedDirectoriesTask.h" #include @@ -84,7 +84,7 @@ class TryCreateSymlinkTask : public Task { // Make sure that symbolic links are supported. if (!FS::canLink(m_source, m_destination)) { - fail(tr("Failed to create global folder.\nSymbolic links are not supported on the filesystem")); + fail(tr("Failed to create shared folder.\nSymbolic links are not supported on the filesystem")); return; } @@ -101,7 +101,7 @@ class TryCreateSymlinkTask : public Task { } else if (FS::checkFolderPathExists(m_destination)) { if (!FS::checkFolderPathEmpty(m_destination)) { if (!interactiveMove(m_destination, m_source, true, m_parent)) { - fail(tr("Failed to create global folder.\nEnsure that \"%1\" is empty.").arg(m_destination)); + fail(tr("Failed to create shared folder.\nEnsure that \"%1\" is empty.").arg(m_destination)); return; } } @@ -111,7 +111,7 @@ class TryCreateSymlinkTask : public Task { // Make sure the source folder exists if (!FS::ensureFolderPathExists(m_source)) { - fail(tr("Failed to create global folder.\nEnsure that \"%1\" exists.").arg(m_source)); + fail(tr("Failed to create shared folder.\nEnsure that \"%1\" exists.").arg(m_source)); return; } @@ -121,7 +121,7 @@ class TryCreateSymlinkTask : public Task { if (folderLink()) { emitSucceeded(); } else { - fail(tr("Failed to create global folder. Error %1: %2") + fail(tr("Failed to create shared folder. Error %1: %2") .arg(folderLink.getOSError().value()) .arg(folderLink.getOSError().message().c_str())); } @@ -143,44 +143,44 @@ class TryCreateSymlinkTask : public Task { QWidget* m_parent; }; -UpdateGlobalDirectoriesTask::UpdateGlobalDirectoriesTask(MinecraftInstance* inst, QWidget* parent) +UpdateSharedDirectoriesTask::UpdateSharedDirectoriesTask(MinecraftInstance* inst, QWidget* parent) : Task(parent), m_inst(inst), m_parent(parent) {} -UpdateGlobalDirectoriesTask::~UpdateGlobalDirectoriesTask() {} +UpdateSharedDirectoriesTask::~UpdateSharedDirectoriesTask() {} -void UpdateGlobalDirectoriesTask::executeTask() +void UpdateSharedDirectoriesTask::executeTask() { - auto tasks = makeShared("UpdateGlobalDirectoriesTask"); + auto tasks = makeShared("UpdateSharedDirectoriesTask"); - auto screenshotsTask = makeShared(m_inst->settings()->get("GlobalScreenshotsPath").toString(), - m_inst->screenshotsDir(), m_inst, "UseGlobalScreenshotsFolder", m_parent); - connect(screenshotsTask.get(), &Task::failed, this, &UpdateGlobalDirectoriesTask::notifyFailed); + auto screenshotsTask = makeShared(m_inst->settings()->get("SharedScreenshotsPath").toString(), + m_inst->screenshotsDir(), m_inst, "UseSharedScreenshotsFolder", m_parent); + connect(screenshotsTask.get(), &Task::failed, this, &UpdateSharedDirectoriesTask::notifyFailed); tasks->addTask(screenshotsTask); - auto savesTask = makeShared(m_inst->settings()->get("GlobalSavesPath").toString(), m_inst->worldDir(), m_inst, - "UseGlobalSavesFolder", m_parent); - connect(savesTask.get(), &Task::failed, this, &UpdateGlobalDirectoriesTask::notifyFailed); + auto savesTask = makeShared(m_inst->settings()->get("SharedSavesPath").toString(), m_inst->worldDir(), m_inst, + "UseSharedSavesFolder", m_parent); + connect(savesTask.get(), &Task::failed, this, &UpdateSharedDirectoriesTask::notifyFailed); tasks->addTask(savesTask); - auto resoucePacksTask = makeShared(m_inst->settings()->get("GlobalResourcePacksPath").toString(), - m_inst->resourcePacksDir(), m_inst, "UseGlobalResourcePacksFolder", m_parent); - connect(resoucePacksTask.get(), &Task::failed, this, &UpdateGlobalDirectoriesTask::notifyFailed); + auto resoucePacksTask = makeShared(m_inst->settings()->get("SharedResourcePacksPath").toString(), + m_inst->resourcePacksDir(), m_inst, "UseSharedResourcePacksFolder", m_parent); + connect(resoucePacksTask.get(), &Task::failed, this, &UpdateSharedDirectoriesTask::notifyFailed); tasks->addTask(resoucePacksTask); - auto texturePacksTask = makeShared(m_inst->settings()->get("GlobalResourcePacksPath").toString(), - m_inst->texturePacksDir(), m_inst, "UseGlobalResourcePacksFolder", m_parent); - connect(texturePacksTask.get(), &Task::failed, this, &UpdateGlobalDirectoriesTask::notifyFailed); + auto texturePacksTask = makeShared(m_inst->settings()->get("SharedResourcePacksPath").toString(), + m_inst->texturePacksDir(), m_inst, "UseSharedResourcePacksFolder", m_parent); + connect(texturePacksTask.get(), &Task::failed, this, &UpdateSharedDirectoriesTask::notifyFailed); tasks->addTask(texturePacksTask); m_tasks = tasks; - connect(m_tasks.get(), &Task::succeeded, this, &UpdateGlobalDirectoriesTask::emitSucceeded); + connect(m_tasks.get(), &Task::succeeded, this, &UpdateSharedDirectoriesTask::emitSucceeded); m_tasks->start(); } -void UpdateGlobalDirectoriesTask::notifyFailed(QString reason) +void UpdateSharedDirectoriesTask::notifyFailed(QString reason) { CustomMessageBox::selectable(m_parent, tr("Failed"), reason, QMessageBox::Warning, QMessageBox::Ok)->exec(); emit failed(reason); diff --git a/launcher/minecraft/UpdateGlobalDirectoriesTask.h b/launcher/minecraft/UpdateSharedDirectoriesTask.h similarity index 66% rename from launcher/minecraft/UpdateGlobalDirectoriesTask.h rename to launcher/minecraft/UpdateSharedDirectoriesTask.h index 4afadc8ec..dadab0d71 100644 --- a/launcher/minecraft/UpdateGlobalDirectoriesTask.h +++ b/launcher/minecraft/UpdateSharedDirectoriesTask.h @@ -4,10 +4,10 @@ class MinecraftInstance; -class UpdateGlobalDirectoriesTask : public Task { +class UpdateSharedDirectoriesTask : public Task { public: - explicit UpdateGlobalDirectoriesTask(MinecraftInstance* inst, QWidget* parent = 0); - virtual ~UpdateGlobalDirectoriesTask(); + explicit UpdateSharedDirectoriesTask(MinecraftInstance* inst, QWidget* parent = 0); + virtual ~UpdateSharedDirectoriesTask(); protected: virtual void executeTask() override; diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.ui b/launcher/ui/pages/instance/ExternalResourcesPage.ui index 5af31b9c4..934f00255 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.ui +++ b/launcher/ui/pages/instance/ExternalResourcesPage.ui @@ -70,7 +70,7 @@ - <html><head/><body><p><span style=" color:#f5c211;">Warning: This is the global folder</span></p></body></html> + <html><head/><body><p><span style=" font-weight:700; color:#f5c211;">Warning</span><span style=" color:#f5c211;">: This is a shared folder and potentially shared with multiple instances</span></p></body></html> diff --git a/launcher/ui/pages/instance/ScreenshotsPage.cpp b/launcher/ui/pages/instance/ScreenshotsPage.cpp index c65d17808..904ae1bd3 100644 --- a/launcher/ui/pages/instance/ScreenshotsPage.cpp +++ b/launcher/ui/pages/instance/ScreenshotsPage.cpp @@ -565,7 +565,7 @@ void ScreenshotsPage::openedImpl() ui->toolBar->setVisibilityState(QByteArray::fromBase64(m_wide_bar_setting->get().toString().toUtf8())); // Enable the symbolic link warning when the screenshots folder is a symbolic link - ui->globalScreenshotsFolderWarninglabel->setVisible(FS::isSymLink(m_folder)); + ui->sharedScreenshotsFolderWarninglabel->setVisible(FS::isSymLink(m_folder)); } void ScreenshotsPage::closedImpl() diff --git a/launcher/ui/pages/instance/ScreenshotsPage.ui b/launcher/ui/pages/instance/ScreenshotsPage.ui index 4dd650f9c..beb2c6427 100644 --- a/launcher/ui/pages/instance/ScreenshotsPage.ui +++ b/launcher/ui/pages/instance/ScreenshotsPage.ui @@ -25,12 +25,12 @@ 0 - + false - <html><head/><body><p><span style=" font-weight:600; color:#f5c211;">Warning</span><span style=" color:#f5c211;">: This is the global screenshots folder</span></p></body></html> + <html><head/><body><p><span style=" font-weight:600; color:#f5c211;">Warning</span><span style=" color:#f5c211;">: This is a shared folder and potentially shared with multiple instances</span></p></body></html> 0 diff --git a/launcher/ui/pages/instance/WorldListPage.cpp b/launcher/ui/pages/instance/WorldListPage.cpp index 582493265..52911bd57 100644 --- a/launcher/ui/pages/instance/WorldListPage.cpp +++ b/launcher/ui/pages/instance/WorldListPage.cpp @@ -128,7 +128,7 @@ void WorldListPage::openedImpl() ui->toolBar->setVisibilityState(QByteArray::fromBase64(m_wide_bar_setting->get().toString().toUtf8())); // Enable the symbolic link warning when the saves folder is a symbolic link - ui->globalSavesFolderWarninglabel->setVisible(FS::isSymLink(FS::PathCombine(m_inst->gameRoot(), "saves"))); + ui->sharedSavesFolderWarninglabel->setVisible(FS::isSymLink(FS::PathCombine(m_inst->gameRoot(), "saves"))); } void WorldListPage::closedImpl() diff --git a/launcher/ui/pages/instance/WorldListPage.ui b/launcher/ui/pages/instance/WorldListPage.ui index 53cd0cf30..b89240bcb 100644 --- a/launcher/ui/pages/instance/WorldListPage.ui +++ b/launcher/ui/pages/instance/WorldListPage.ui @@ -25,9 +25,9 @@ 0 - + - <html><head/><body><p><span style=" color:#f5c211;">Warning: This is the global saves folder</span></p></body></html> + <html><head/><body><p><span style=" font-weight:700; color:#f5c211;">Warning</span><span style=" color:#f5c211;">: This is a shared folder and potentially shared with multiple instances</span></p></body></html> diff --git a/launcher/ui/widgets/MinecraftSettingsWidget.cpp b/launcher/ui/widgets/MinecraftSettingsWidget.cpp index 5a5033971..89ce06413 100644 --- a/launcher/ui/widgets/MinecraftSettingsWidget.cpp +++ b/launcher/ui/widgets/MinecraftSettingsWidget.cpp @@ -53,7 +53,8 @@ MinecraftSettingsWidget::MinecraftSettingsWidget(MinecraftInstancePtr instance, m_ui->setupUi(this); if (m_instance == nullptr) { - m_ui->settingsTabs->removeTab(1); + m_ui->settingsTabs->removeTab(m_ui->settingsTabs->indexOf(m_ui->javaPage)); + m_ui->settingsTabs->removeTab(m_ui->settingsTabs->indexOf(m_ui->sharedFoldersTab)); m_ui->openGlobalSettingsButton->setVisible(false); m_ui->instanceAccountGroupBox->hide(); @@ -282,11 +283,11 @@ void MinecraftSettingsWidget::loadSettings() m_ui->quilt->blockSignals(false); m_ui->liteLoader->blockSignals(false); - // Global folders - m_ui->globalScreenshotsFolder->initialize(settings->get("UseGlobalScreenshotsFolder").toBool(), settings->get("GlobalScreenshotsPath").toString()); - m_ui->globalSavesFolder->initialize(settings->get("UseGlobalSavesFolder").toBool(), settings->get("GlobalSavesPath").toString()); - m_ui->globalResourcePacksFolder->initialize(settings->get("UseGlobalResourcePacksFolder").toBool(), - settings->get("GlobalResourcePacksPath").toString()); + // Shared folders + m_ui->sharedScreenshotsFolder->initialize(settings->get("UseSharedScreenshotsFolder").toBool(), settings->get("SharedScreenshotsPath").toString()); + m_ui->sharedSavesFolder->initialize(settings->get("UseSharedSavesFolder").toBool(), settings->get("SharedSavesPath").toString()); + m_ui->sharedResourcePacksFolder->initialize(settings->get("UseSharedResourcePacksFolder").toBool(), + settings->get("SharedResourcePacksPath").toString()); } m_ui->legacySettingsGroupBox->setChecked(settings->get("OverrideLegacySettings").toBool()); @@ -477,13 +478,13 @@ void MinecraftSettingsWidget::saveSettings() } if (m_instance != nullptr) { - // Global folders - settings->set("UseGlobalScreenshotsFolder", m_ui->globalScreenshotsFolder->isEnabled()); - settings->set("GlobalScreenshotsPath", m_ui->globalScreenshotsFolder->getPath()); - settings->set("UseGlobalSavesFolder", m_ui->globalSavesFolder->isEnabled()); - settings->set("GlobalSavesPath", m_ui->globalSavesFolder->getPath()); - settings->set("UseGlobalResourcePacksFolder", m_ui->globalResourcePacksFolder->isEnabled()); - settings->set("GlobalResourcePacksPath", m_ui->globalResourcePacksFolder->getPath()); + // Shared folders + settings->set("UseSharedScreenshotsFolder", m_ui->sharedScreenshotsFolder->isEnabled()); + settings->set("SharedScreenshotsPath", m_ui->sharedScreenshotsFolder->getPath()); + settings->set("UseSharedSavesFolder", m_ui->sharedSavesFolder->isEnabled()); + settings->set("SharedSavesPath", m_ui->sharedSavesFolder->getPath()); + settings->set("UseSharedResourcePacksFolder", m_ui->sharedResourcePacksFolder->isEnabled()); + settings->set("SharedResourcePacksPath", m_ui->sharedResourcePacksFolder->getPath()); } } diff --git a/launcher/ui/widgets/MinecraftSettingsWidget.ui b/launcher/ui/widgets/MinecraftSettingsWidget.ui index 7772851fd..8358595e7 100644 --- a/launcher/ui/widgets/MinecraftSettingsWidget.ui +++ b/launcher/ui/widgets/MinecraftSettingsWidget.ui @@ -801,7 +801,7 @@ It is most likely you will need to change the path - please refer to the mod's w - + 0 @@ -809,11 +809,11 @@ It is most likely you will need to change the path - please refer to the mod's w - Global Folders + Shared Folders - + - + 0 @@ -826,28 +826,28 @@ It is most likely you will need to change the path - please refer to the mod's w Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop - + - + <html><head/><body><p><span style=" font-weight:600; color:#f5c211;">Warning</span><span style=" color:#f5c211;">: After enabling files cannot be automatically restored to their original folder.</span></p></body></html> - + - + - + - + 0 @@ -855,7 +855,7 @@ It is most likely you will need to change the path - please refer to the mod's w - <html><head/><body><p>A global folder is a folder that is shared across instances.</p><p>For example: If two instances share the same global saves folder, they can both play on the same worlds without having to copy the world over.</p></body></html> + <html><head/><body><p>A shared folder is a folder that can be shared across instances.</p><p>For example: If two instances share the same shared saves folder, they can both play on the same worlds without having to copy the world over.</p></body></html> false @@ -888,9 +888,9 @@ It is most likely you will need to change the path - please refer to the mod's w 1 - GlobalFolderWidget + SharedFolderWidget QWidget -
ui/widgets/GlobalFolderWidget.h
+
ui/widgets/SharedFolderWidget.h
1
diff --git a/launcher/ui/widgets/GlobalFolderWidget.cpp b/launcher/ui/widgets/SharedFolderWidget.cpp similarity index 56% rename from launcher/ui/widgets/GlobalFolderWidget.cpp rename to launcher/ui/widgets/SharedFolderWidget.cpp index 5f7222379..3ae8d007b 100644 --- a/launcher/ui/widgets/GlobalFolderWidget.cpp +++ b/launcher/ui/widgets/SharedFolderWidget.cpp @@ -1,21 +1,21 @@ -#include "GlobalFolderWidget.h" -#include "ui_GlobalFolderWidget.h" +#include "SharedFolderWidget.h" +#include "ui_SharedFolderWidget.h" #include #include "FileSystem.h" -GlobalFolderWidget::GlobalFolderWidget(QWidget* parent) : QWidget(parent), ui(new Ui::GlobalFolderWidget) +SharedFolderWidget::SharedFolderWidget(QWidget* parent) : QWidget(parent), ui(new Ui::SharedFolderWidget) { ui->setupUi(this); } -GlobalFolderWidget::~GlobalFolderWidget() +SharedFolderWidget::~SharedFolderWidget() { delete ui; } -void GlobalFolderWidget::initialize(bool enabled, const QString& path, const QString& label) +void SharedFolderWidget::initialize(bool enabled, const QString& path, const QString& label) { ui->enabledCheckBox->setChecked(enabled); ui->enabledCheckBox->setText(label); @@ -26,25 +26,25 @@ void GlobalFolderWidget::initialize(bool enabled, const QString& path, const QSt ui->pathBrowseBtn->setEnabled(enabled); } -bool GlobalFolderWidget::isEnabled() const +bool SharedFolderWidget::isEnabled() const { return ui->enabledCheckBox->isChecked(); } -QString GlobalFolderWidget::getPath() const +QString SharedFolderWidget::getPath() const { return ui->pathTextBox->text(); } -void GlobalFolderWidget::on_enabledCheckBox_toggled(bool checked) +void SharedFolderWidget::on_enabledCheckBox_toggled(bool checked) { ui->pathTextBox->setEnabled(checked); ui->pathBrowseBtn->setEnabled(checked); } -void GlobalFolderWidget::on_pathBrowseBtn_clicked() +void SharedFolderWidget::on_pathBrowseBtn_clicked() { - QString path = QFileDialog::getExistingDirectory(this, tr("Select global folder"), ui->pathTextBox->text()); + QString path = QFileDialog::getExistingDirectory(this, tr("Select shared folder"), ui->pathTextBox->text()); if (path.isEmpty()) { return; } diff --git a/launcher/ui/widgets/GlobalFolderWidget.h b/launcher/ui/widgets/SharedFolderWidget.h similarity index 64% rename from launcher/ui/widgets/GlobalFolderWidget.h rename to launcher/ui/widgets/SharedFolderWidget.h index 80241c522..0d40df152 100644 --- a/launcher/ui/widgets/GlobalFolderWidget.h +++ b/launcher/ui/widgets/SharedFolderWidget.h @@ -3,15 +3,15 @@ #include namespace Ui { -class GlobalFolderWidget; +class SharedFolderWidget; } -class GlobalFolderWidget : public QWidget { +class SharedFolderWidget : public QWidget { Q_OBJECT public: - explicit GlobalFolderWidget(QWidget* parent = 0); - virtual ~GlobalFolderWidget(); + explicit SharedFolderWidget(QWidget* parent = 0); + virtual ~SharedFolderWidget(); void initialize(bool enabled, const QString& path, const QString& label = ""); bool isEnabled() const; @@ -22,5 +22,5 @@ class GlobalFolderWidget : public QWidget { void on_pathBrowseBtn_clicked(); private: - Ui::GlobalFolderWidget* ui; + Ui::SharedFolderWidget* ui; }; diff --git a/launcher/ui/widgets/GlobalFolderWidget.ui b/launcher/ui/widgets/SharedFolderWidget.ui similarity index 91% rename from launcher/ui/widgets/GlobalFolderWidget.ui rename to launcher/ui/widgets/SharedFolderWidget.ui index 35f28a9c0..6f0d857fd 100644 --- a/launcher/ui/widgets/GlobalFolderWidget.ui +++ b/launcher/ui/widgets/SharedFolderWidget.ui @@ -1,7 +1,7 @@ - GlobalFolderWidget - + SharedFolderWidget + 0 From 62e4845dcab8f7a2b457aa1edd540a12d061c067 Mon Sep 17 00:00:00 2001 From: Naomi <103967@gmail.com> Date: Sun, 22 Jun 2025 15:17:28 +0200 Subject: [PATCH 08/10] Review feedback Signed-off-by: Naomi <103967@gmail.com> --- launcher/FileSystem.cpp | 5 --- launcher/FileSystem.h | 7 ---- launcher/minecraft/MinecraftInstance.cpp | 2 +- .../minecraft/UpdateSharedDirectoriesTask.cpp | 35 +++++++++---------- .../minecraft/UpdateSharedDirectoriesTask.h | 5 ++- launcher/ui/dialogs/FileConflictDialog.ui | 16 ++++----- .../pages/instance/ExternalResourcesPage.ui | 12 +++---- launcher/ui/pages/instance/ScreenshotsPage.ui | 6 ++-- launcher/ui/pages/instance/WorldListPage.ui | 6 ++-- .../ui/widgets/MinecraftSettingsWidget.ui | 4 +-- 10 files changed, 42 insertions(+), 56 deletions(-) diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index 9182282d9..97969e0db 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -277,11 +277,6 @@ bool ensureFolderPathExists(const QString folderPathName) return ensureFolderPathExists(QFileInfo(folderPathName)); } -bool checkFolderPathExists(const QString& folderPathName) -{ - return QDir(folderPathName).exists(); -} - bool checkFolderPathEmpty(const QString& folderPathName) { return QDir(folderPathName).isEmpty(QDir::Filters(QDir::AllEntries | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System)); diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h index 7771da8bb..455d81457 100644 --- a/launcher/FileSystem.h +++ b/launcher/FileSystem.h @@ -99,13 +99,6 @@ bool ensureFolderPathExists(const QFileInfo folderPath); */ bool ensureFolderPathExists(const QString folderPathName); -/** - * @brief Check if the given folder exists - * @param folderPathName The path to a folder to check - * @return True if the given folder exists - */ -bool checkFolderPathExists(const QString& folderPathName); - /** * @brief Check if the given folder is empty or doesn't exist * @param folderPathName The path to a folder to check diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 9c9f66241..506ae0e36 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -1317,7 +1317,7 @@ QList MinecraftInstance::getJarMods() const void MinecraftInstance::applySettings() { // Shared directories - m_update_shared_directories_task = std::make_shared(this, nullptr); + m_update_shared_directories_task = std::make_shared(this); m_update_shared_directories_task->start(); } diff --git a/launcher/minecraft/UpdateSharedDirectoriesTask.cpp b/launcher/minecraft/UpdateSharedDirectoriesTask.cpp index 0b8e469a1..a72953b52 100644 --- a/launcher/minecraft/UpdateSharedDirectoriesTask.cpp +++ b/launcher/minecraft/UpdateSharedDirectoriesTask.cpp @@ -42,12 +42,15 @@ bool interactiveMove(const QString& source, const QString& destination, bool rec FileConflictDialog dialog(source, destination, true, parent); FileConflictDialog::Result result = dialog.execWithResult(); - if (result == FileConflictDialog::Cancel) + switch(result) { + case FileConflictDialog::Cancel: return false; - else if (result == FileConflictDialog::ChooseDestination) + case FileConflictDialog::ChooseDestination: return FS::deletePath(source); - else if (result == FileConflictDialog::ChooseSource) + case FileConflictDialog::ChooseSource: FS::deletePath(destination); + break; + } } return FS::move(source, destination); @@ -58,9 +61,8 @@ class TryCreateSymlinkTask : public Task { explicit TryCreateSymlinkTask(const QString& source, const QString& destination, MinecraftInstance* instance, - const QString& setting, - QWidget* parent) - : m_source(source), m_destination(destination), m_inst(instance), m_setting(setting), m_parent(parent) + const QString& setting) + : m_source(source), m_destination(destination), m_inst(instance), m_setting(setting) { setObjectName("TryCreateSymlinkTask"); } @@ -98,9 +100,9 @@ class TryCreateSymlinkTask : public Task { } FS::deletePath(m_destination); - } else if (FS::checkFolderPathExists(m_destination)) { + } else if (QFileInfo::exists(m_destination)) { if (!FS::checkFolderPathEmpty(m_destination)) { - if (!interactiveMove(m_destination, m_source, true, m_parent)) { + if (!interactiveMove(m_destination, m_source, true)) { fail(tr("Failed to create shared folder.\nEnsure that \"%1\" is empty.").arg(m_destination)); return; } @@ -140,36 +142,33 @@ class TryCreateSymlinkTask : public Task { QString m_destination; MinecraftInstance* m_inst; QString m_setting; - QWidget* m_parent; }; -UpdateSharedDirectoriesTask::UpdateSharedDirectoriesTask(MinecraftInstance* inst, QWidget* parent) - : Task(parent), m_inst(inst), m_parent(parent) +UpdateSharedDirectoriesTask::UpdateSharedDirectoriesTask(MinecraftInstance* inst) + : Task(), m_inst(inst) {} -UpdateSharedDirectoriesTask::~UpdateSharedDirectoriesTask() {} - void UpdateSharedDirectoriesTask::executeTask() { auto tasks = makeShared("UpdateSharedDirectoriesTask"); auto screenshotsTask = makeShared(m_inst->settings()->get("SharedScreenshotsPath").toString(), - m_inst->screenshotsDir(), m_inst, "UseSharedScreenshotsFolder", m_parent); + m_inst->screenshotsDir(), m_inst, "UseSharedScreenshotsFolder"); connect(screenshotsTask.get(), &Task::failed, this, &UpdateSharedDirectoriesTask::notifyFailed); tasks->addTask(screenshotsTask); auto savesTask = makeShared(m_inst->settings()->get("SharedSavesPath").toString(), m_inst->worldDir(), m_inst, - "UseSharedSavesFolder", m_parent); + "UseSharedSavesFolder"); connect(savesTask.get(), &Task::failed, this, &UpdateSharedDirectoriesTask::notifyFailed); tasks->addTask(savesTask); auto resoucePacksTask = makeShared(m_inst->settings()->get("SharedResourcePacksPath").toString(), - m_inst->resourcePacksDir(), m_inst, "UseSharedResourcePacksFolder", m_parent); + m_inst->resourcePacksDir(), m_inst, "UseSharedResourcePacksFolder"); connect(resoucePacksTask.get(), &Task::failed, this, &UpdateSharedDirectoriesTask::notifyFailed); tasks->addTask(resoucePacksTask); auto texturePacksTask = makeShared(m_inst->settings()->get("SharedResourcePacksPath").toString(), - m_inst->texturePacksDir(), m_inst, "UseSharedResourcePacksFolder", m_parent); + m_inst->texturePacksDir(), m_inst, "UseSharedResourcePacksFolder"); connect(texturePacksTask.get(), &Task::failed, this, &UpdateSharedDirectoriesTask::notifyFailed); tasks->addTask(texturePacksTask); @@ -182,6 +181,6 @@ void UpdateSharedDirectoriesTask::executeTask() void UpdateSharedDirectoriesTask::notifyFailed(QString reason) { - CustomMessageBox::selectable(m_parent, tr("Failed"), reason, QMessageBox::Warning, QMessageBox::Ok)->exec(); + CustomMessageBox::selectable(nullptr, tr("Failed"), reason, QMessageBox::Warning, QMessageBox::Ok)->exec(); emit failed(reason); } diff --git a/launcher/minecraft/UpdateSharedDirectoriesTask.h b/launcher/minecraft/UpdateSharedDirectoriesTask.h index dadab0d71..c1c1f852b 100644 --- a/launcher/minecraft/UpdateSharedDirectoriesTask.h +++ b/launcher/minecraft/UpdateSharedDirectoriesTask.h @@ -6,8 +6,8 @@ class MinecraftInstance; class UpdateSharedDirectoriesTask : public Task { public: - explicit UpdateSharedDirectoriesTask(MinecraftInstance* inst, QWidget* parent = 0); - virtual ~UpdateSharedDirectoriesTask(); + explicit UpdateSharedDirectoriesTask(MinecraftInstance* inst); + virtual ~UpdateSharedDirectoriesTask = default; protected: virtual void executeTask() override; @@ -17,6 +17,5 @@ class UpdateSharedDirectoriesTask : public Task { private: MinecraftInstance* m_inst; - QWidget* m_parent; Task::Ptr m_tasks; }; diff --git a/launcher/ui/dialogs/FileConflictDialog.ui b/launcher/ui/dialogs/FileConflictDialog.ui index c71afd59a..207c16852 100644 --- a/launcher/ui/dialogs/FileConflictDialog.ui +++ b/launcher/ui/dialogs/FileConflictDialog.ui @@ -26,14 +26,14 @@ Would you like to overwrite the destination? - Qt::AlignmentFlag::AlignCenter + Qt::AlignCenter
- QLayout::SizeConstraint::SetDefaultConstraint + QLayout::SetDefaultConstraint @@ -49,7 +49,7 @@ <html><head/><body><p><span style=" font-weight:700;">Source</span></p></body></html> - Qt::AlignmentFlag::AlignHCenter|Qt::AlignmentFlag::AlignTop + Qt::AlignHCenter|Qt::AlignTop
@@ -65,7 +65,7 @@ <html><head/><body><p><b>Name:</b></p><p>Size:</p><p>Date:</p></body></html> - Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop
@@ -79,7 +79,7 @@ <html><head/><body><p><span style=" font-weight:700;">Destination</span></p></body></html> - Qt::AlignmentFlag::AlignHCenter|Qt::AlignmentFlag::AlignTop + Qt::AlignHCenter|Qt::AlignTop @@ -89,7 +89,7 @@ <html><head/><body><p>Name:</p><p>Size:</p><p>Date:</p></body></html> - Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop @@ -100,10 +100,10 @@ - Qt::Orientation::Horizontal + Qt::Horizontal - QDialogButtonBox::StandardButton::Cancel + QDialogButtonBox::Cancel diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.ui b/launcher/ui/pages/instance/ExternalResourcesPage.ui index 934f00255..47b41320b 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.ui +++ b/launcher/ui/pages/instance/ExternalResourcesPage.ui @@ -60,7 +60,7 @@ true - QAbstractItemView::DragDropMode::DropOnly + QAbstractItemView::DropOnly true @@ -235,11 +235,6 @@ - - WideBar - QToolBar -
ui/widgets/WideBar.h
-
ModListView QTreeView @@ -251,6 +246,11 @@
ui/widgets/InfoFrame.h
1
+ + WideBar + QToolBar +
ui/widgets/WideBar.h
+
treeView diff --git a/launcher/ui/pages/instance/ScreenshotsPage.ui b/launcher/ui/pages/instance/ScreenshotsPage.ui index beb2c6427..225ab379f 100644 --- a/launcher/ui/pages/instance/ScreenshotsPage.ui +++ b/launcher/ui/pages/instance/ScreenshotsPage.ui @@ -40,10 +40,10 @@ - QAbstractItemView::SelectionMode::ExtendedSelection + QAbstractItemView::ExtendedSelection - QAbstractItemView::SelectionBehavior::SelectRows + QAbstractItemView::SelectRows @@ -54,7 +54,7 @@ Actions
- Qt::ToolButtonStyle::ToolButtonTextOnly + Qt::ToolButtonTextOnly RightToolBarArea diff --git a/launcher/ui/pages/instance/WorldListPage.ui b/launcher/ui/pages/instance/WorldListPage.ui index b89240bcb..f8ebae21b 100644 --- a/launcher/ui/pages/instance/WorldListPage.ui +++ b/launcher/ui/pages/instance/WorldListPage.ui @@ -43,7 +43,7 @@ true - QAbstractItemView::DragDropMode::DragDrop + QAbstractItemView::DragDrop true @@ -72,10 +72,10 @@ Actions - Qt::ToolBarArea::LeftToolBarArea|Qt::ToolBarArea::RightToolBarArea + Qt::LeftToolBarArea|Qt::RightToolBarArea - Qt::ToolButtonStyle::ToolButtonTextOnly + Qt::ToolButtonTextOnly false diff --git a/launcher/ui/widgets/MinecraftSettingsWidget.ui b/launcher/ui/widgets/MinecraftSettingsWidget.ui index 8358595e7..4fdb99f04 100644 --- a/launcher/ui/widgets/MinecraftSettingsWidget.ui +++ b/launcher/ui/widgets/MinecraftSettingsWidget.ui @@ -824,7 +824,7 @@ It is most likely you will need to change the path - please refer to the mod's w Settings - Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop @@ -861,7 +861,7 @@ It is most likely you will need to change the path - please refer to the mod's w false - Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop true From db4e7d8820e9df548be07cad3686ca08891d889b Mon Sep 17 00:00:00 2001 From: Naomi <103967@gmail.com> Date: Sun, 22 Jun 2025 16:31:59 +0200 Subject: [PATCH 09/10] Restructure updating of shared directories Signed-off-by: Naomi <103967@gmail.com> --- launcher/CMakeLists.txt | 2 - launcher/FileSystem.cpp | 87 ++++++++ launcher/FileSystem.h | 13 ++ launcher/minecraft/MinecraftInstance.cpp | 35 +++- launcher/minecraft/MinecraftInstance.h | 4 +- .../minecraft/UpdateSharedDirectoriesTask.cpp | 186 ------------------ .../minecraft/UpdateSharedDirectoriesTask.h | 21 -- 7 files changed, 134 insertions(+), 214 deletions(-) delete mode 100644 launcher/minecraft/UpdateSharedDirectoriesTask.cpp delete mode 100644 launcher/minecraft/UpdateSharedDirectoriesTask.h diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 773913210..382a31662 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -315,8 +315,6 @@ set(MINECRAFT_SOURCES minecraft/Library.cpp minecraft/Library.h minecraft/MojangDownloadInfo.h - minecraft/UpdateSharedDirectoriesTask.cpp - minecraft/UpdateSharedDirectoriesTask.h minecraft/VanillaInstanceCreationTask.cpp minecraft/VanillaInstanceCreationTask.h minecraft/VersionFile.cpp diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index 97969e0db..48654a7c4 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -55,6 +55,8 @@ #include "DesktopServices.h" #include "PSaveFile.h" #include "StringUtils.h" +#include "ui/dialogs/CustomMessageBox.h" +#include "ui/dialogs/FileConflictDialog.h" #if defined Q_OS_WIN32 #define NOMINMAX @@ -674,6 +676,44 @@ bool move(const QString& source, const QString& dest) return true; } +bool interactiveMove(const QString& source, const QString& destination, bool recursive /* = false */, QWidget* parent /* = nullptr */) +{ + const QFileInfo sourceInfo(source); + + // Make sure the source exists. + if (!sourceInfo.exists()) + return false; + + // Recursive doesn't make sense if the source isn't a directory. + if (recursive && sourceInfo.isDir()) { + QDirIterator sourceIt(source, QDir::Filter::Files | QDir::Filter::Dirs | QDir::Filter::Hidden | QDir::Filter::NoDotAndDotDot); + + while (sourceIt.hasNext()) { + if (!interactiveMove(sourceIt.next(), FS::PathCombine(destination, sourceIt.fileName()), false)) + return false; + } + + return true; + } + + if (QFile(destination).exists()) { + FileConflictDialog dialog(source, destination, true, parent); + FileConflictDialog::Result result = dialog.execWithResult(); + + switch(result) { + case FileConflictDialog::Cancel: + return false; + case FileConflictDialog::ChooseDestination: + return FS::deletePath(source); + case FileConflictDialog::ChooseSource: + FS::deletePath(destination); + break; + } + } + + return FS::move(source, destination); +} + bool deletePath(QString path) { std::error_code err; @@ -1711,4 +1751,51 @@ QString getSymLinkTarget(const QString& path) return QFileInfo(path).symLinkTarget(); } +bool tryCreateSymlink(const QString& source, const QString& destination, const QString& symlinkName /* = "symbolic link" */) { + // Make sure that symbolic links are supported. + if (!FS::canLink(source, destination)) { + CustomMessageBox::selectable(nullptr, QObject::tr("Failed"), QObject::tr("Failed to create %1.\nSymbolic links are not supported on the filesystem").arg(symlinkName), QMessageBox::Warning, QMessageBox::Ok)->exec(); + return false; + } + + // Check if the destination already exists. + // If it's already a symlink, it might already be correct. + if (FS::isSymLink(destination)) { + // If the target of the symlink is already the source, there's nothing to do. + if (FS::getSymLinkTarget(destination) == source) { + return true; + } + + FS::deletePath(destination); + } else if (QFileInfo::exists(destination)) { + if (!FS::checkFolderPathEmpty(destination)) { + if (!interactiveMove(destination, source, true)) { + CustomMessageBox::selectable(nullptr, QObject::tr("Failed"), QObject::tr("Failed to create %1.\nEnsure that \"%2\" is empty.").arg(symlinkName).arg(destination), QMessageBox::Warning, QMessageBox::Ok)->exec(); + return false; + } + } + + FS::deletePath(destination); + } + + // Make sure the source folder exists + if (!FS::ensureFolderPathExists(source)) { + CustomMessageBox::selectable(nullptr, QObject::tr("Failed"), QObject::tr("Failed to create %1.\nEnsure that \"%2\" exists.").arg(symlinkName).arg(source), QMessageBox::Warning, QMessageBox::Ok)->exec(); + return false; + } + + FS::create_link folderLink(source, destination); + folderLink.linkRecursively(false); + + if (!folderLink()) { + CustomMessageBox::selectable(nullptr, QObject::tr("Failed"), QObject::tr("Failed to create %1. Error %2: %3") + .arg(symlinkName) + .arg(folderLink.getOSError().value()) + .arg(folderLink.getOSError().message().c_str()), + QMessageBox::Warning, QMessageBox::Ok)->exec(); + } + + return true; +} + } // namespace FS diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h index 455d81457..1681c6093 100644 --- a/launcher/FileSystem.h +++ b/launcher/FileSystem.h @@ -293,6 +293,17 @@ class create_link : public QObject { */ bool move(const QString& source, const QString& dest); +/** + * @brief Move a file or folder, and ask the user what to do in case of a conflict. + * @param source What to move. + * @param destination Where to move it to. + * @param recursive If true, all direct children will be moved 1 by 1. + * If false, the source will be directly moved to the destination. + * @param parent The parent of the dialog. + * @return True if everything could be moved. + */ +bool interactiveMove(const QString& source, const QString& destination, bool recursive = false, QWidget* parent = nullptr); + /** * Delete a folder recursively */ @@ -587,4 +598,6 @@ bool isSymLink(const QString& path); */ QString getSymLinkTarget(const QString& path); +bool tryCreateSymlink(const QString& source, const QString& destination, const QString& symlinkName = "symbolic link"); + } // namespace FS diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 506ae0e36..14860d778 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -85,7 +85,6 @@ #include "AssetsUtils.h" #include "MinecraftLoadAndCheck.h" #include "PackProfile.h" -#include "minecraft/UpdateSharedDirectoriesTask.h" #include "minecraft/gameoptions/GameOptions.h" #include "minecraft/update/FoldersTask.h" @@ -1317,8 +1316,38 @@ QList MinecraftInstance::getJarMods() const void MinecraftInstance::applySettings() { // Shared directories - m_update_shared_directories_task = std::make_shared(this); - m_update_shared_directories_task->start(); + updateSharedDirectories(); +} + +bool MinecraftInstance::updateSharedDirectories() +{ + const std::vector> sharedDirectories = { + { "UseSharedScreenshotsFolder", m_settings->get("SharedScreenshotsPath").toString(), screenshotsDir() }, + { "UseSharedSavesFolder", m_settings->get("SharedSavesPath").toString(), worldDir() }, + { "UseSharedResourcePacksFolder", m_settings->get("SharedResourcePacksPath").toString(), resourcePacksDir() }, + { "UseSharedResourcePacksFolder", m_settings->get("SharedResourcePacksPath").toString(), texturePacksDir() } + }; + + bool success = true; + for (const auto& [useSetting, source, destination] : sharedDirectories) { + // Create symlink if useSetting is true, remove symlink if false. + if (m_settings->get(useSetting).toBool()) + { + // Try to create symlink, set setting to false if failed. + if(!FS::tryCreateSymlink(source, destination, tr("shared folder"))) { + m_settings->set(useSetting, false); + success = false; + } + } + else { + // Safety check + if (FS::isSymLink(destination)) { + success = FS::deletePath(destination) && success; + } + } + } + + return success; } #include "MinecraftInstance.moc" diff --git a/launcher/minecraft/MinecraftInstance.h b/launcher/minecraft/MinecraftInstance.h index c92eba1be..35e119972 100644 --- a/launcher/minecraft/MinecraftInstance.h +++ b/launcher/minecraft/MinecraftInstance.h @@ -52,7 +52,6 @@ class WorldList; class GameOptions; class LaunchStep; class PackProfile; -class UpdateSharedDirectoriesTask; class MinecraftInstance : public BaseInstance { Q_OBJECT @@ -165,9 +164,10 @@ class MinecraftInstance : public BaseInstance { protected: QMap createCensorFilterFromSession(AuthSessionPtr session); + bool updateSharedDirectories(); + protected: // data std::shared_ptr m_components; - std::shared_ptr m_update_shared_directories_task; mutable std::shared_ptr m_loader_mod_list; mutable std::shared_ptr m_core_mod_list; mutable std::shared_ptr m_nil_mod_list; diff --git a/launcher/minecraft/UpdateSharedDirectoriesTask.cpp b/launcher/minecraft/UpdateSharedDirectoriesTask.cpp deleted file mode 100644 index a72953b52..000000000 --- a/launcher/minecraft/UpdateSharedDirectoriesTask.cpp +++ /dev/null @@ -1,186 +0,0 @@ -#include "UpdateSharedDirectoriesTask.h" - -#include - -#include "Application.h" -#include "FileSystem.h" -#include "minecraft/MinecraftInstance.h" -#include "tasks/ConcurrentTask.h" -#include "ui/dialogs/CustomMessageBox.h" -#include "ui/dialogs/FileConflictDialog.h" - -/** - * @brief Move a file or folder, and ask the user what to do in case of a conflict. - * @param source What to move. - * @param destination Where to move it to. - * @param recursive If true, all direct children will be moved 1 by 1. - * If false, the source will be directly moved to the destination. - * @param parent The parent of the dialog. - * @return True if everything could be moved. - */ -bool interactiveMove(const QString& source, const QString& destination, bool recursive = false, QWidget* parent = nullptr) -{ - const QFileInfo sourceInfo(source); - - // Make sure the source exists. - if (!sourceInfo.exists()) - return false; - - // Recursive doesn't make sense if the source isn't a directory. - if (recursive && sourceInfo.isDir()) { - QDirIterator sourceIt(source, QDir::Filter::Files | QDir::Filter::Dirs | QDir::Filter::Hidden | QDir::Filter::NoDotAndDotDot); - - while (sourceIt.hasNext()) { - if (!interactiveMove(sourceIt.next(), FS::PathCombine(destination, sourceIt.fileName()), false)) - return false; - } - - return true; - } - - if (QFile(destination).exists()) { - FileConflictDialog dialog(source, destination, true, parent); - FileConflictDialog::Result result = dialog.execWithResult(); - - switch(result) { - case FileConflictDialog::Cancel: - return false; - case FileConflictDialog::ChooseDestination: - return FS::deletePath(source); - case FileConflictDialog::ChooseSource: - FS::deletePath(destination); - break; - } - } - - return FS::move(source, destination); -} - -class TryCreateSymlinkTask : public Task { - public: - explicit TryCreateSymlinkTask(const QString& source, - const QString& destination, - MinecraftInstance* instance, - const QString& setting) - : m_source(source), m_destination(destination), m_inst(instance), m_setting(setting) - { - setObjectName("TryCreateSymlinkTask"); - } - virtual ~TryCreateSymlinkTask() {} - - protected: - void executeTask() - { - bool create = m_inst->settings()->get(m_setting).toBool(); - - // Check if we have to delete an existing symlink - if (!create) { - // Safety check - if (FS::isSymLink(m_destination)) { - FS::deletePath(m_destination); - } - - emitSucceeded(); - return; - } - - // Make sure that symbolic links are supported. - if (!FS::canLink(m_source, m_destination)) { - fail(tr("Failed to create shared folder.\nSymbolic links are not supported on the filesystem")); - return; - } - - // Check if the destination already exists. - // If it's already a symlink, it might already be correct. - if (FS::isSymLink(m_destination)) { - // If the target of the symlink is already the source, there's nothing to do. - if (FS::getSymLinkTarget(m_destination) == m_source) { - emitSucceeded(); - return; - } - - FS::deletePath(m_destination); - } else if (QFileInfo::exists(m_destination)) { - if (!FS::checkFolderPathEmpty(m_destination)) { - if (!interactiveMove(m_destination, m_source, true)) { - fail(tr("Failed to create shared folder.\nEnsure that \"%1\" is empty.").arg(m_destination)); - return; - } - } - - FS::deletePath(m_destination); - } - - // Make sure the source folder exists - if (!FS::ensureFolderPathExists(m_source)) { - fail(tr("Failed to create shared folder.\nEnsure that \"%1\" exists.").arg(m_source)); - return; - } - - FS::create_link folderLink(m_source, m_destination); - folderLink.linkRecursively(false); - - if (folderLink()) { - emitSucceeded(); - } else { - fail(tr("Failed to create shared folder. Error %1: %2") - .arg(folderLink.getOSError().value()) - .arg(folderLink.getOSError().message().c_str())); - } - - return; - } - - void fail(const QString& reason) - { - m_inst->settings()->set(m_setting, false); - emitFailed(reason); - } - - private: - QString m_source; - QString m_destination; - MinecraftInstance* m_inst; - QString m_setting; -}; - -UpdateSharedDirectoriesTask::UpdateSharedDirectoriesTask(MinecraftInstance* inst) - : Task(), m_inst(inst) -{} - -void UpdateSharedDirectoriesTask::executeTask() -{ - auto tasks = makeShared("UpdateSharedDirectoriesTask"); - - auto screenshotsTask = makeShared(m_inst->settings()->get("SharedScreenshotsPath").toString(), - m_inst->screenshotsDir(), m_inst, "UseSharedScreenshotsFolder"); - connect(screenshotsTask.get(), &Task::failed, this, &UpdateSharedDirectoriesTask::notifyFailed); - tasks->addTask(screenshotsTask); - - auto savesTask = makeShared(m_inst->settings()->get("SharedSavesPath").toString(), m_inst->worldDir(), m_inst, - "UseSharedSavesFolder"); - connect(savesTask.get(), &Task::failed, this, &UpdateSharedDirectoriesTask::notifyFailed); - tasks->addTask(savesTask); - - auto resoucePacksTask = makeShared(m_inst->settings()->get("SharedResourcePacksPath").toString(), - m_inst->resourcePacksDir(), m_inst, "UseSharedResourcePacksFolder"); - connect(resoucePacksTask.get(), &Task::failed, this, &UpdateSharedDirectoriesTask::notifyFailed); - tasks->addTask(resoucePacksTask); - - auto texturePacksTask = makeShared(m_inst->settings()->get("SharedResourcePacksPath").toString(), - m_inst->texturePacksDir(), m_inst, "UseSharedResourcePacksFolder"); - connect(texturePacksTask.get(), &Task::failed, this, &UpdateSharedDirectoriesTask::notifyFailed); - tasks->addTask(texturePacksTask); - - m_tasks = tasks; - - connect(m_tasks.get(), &Task::succeeded, this, &UpdateSharedDirectoriesTask::emitSucceeded); - - m_tasks->start(); -} - -void UpdateSharedDirectoriesTask::notifyFailed(QString reason) -{ - CustomMessageBox::selectable(nullptr, tr("Failed"), reason, QMessageBox::Warning, QMessageBox::Ok)->exec(); - emit failed(reason); -} diff --git a/launcher/minecraft/UpdateSharedDirectoriesTask.h b/launcher/minecraft/UpdateSharedDirectoriesTask.h deleted file mode 100644 index c1c1f852b..000000000 --- a/launcher/minecraft/UpdateSharedDirectoriesTask.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include "tasks/Task.h" - -class MinecraftInstance; - -class UpdateSharedDirectoriesTask : public Task { - public: - explicit UpdateSharedDirectoriesTask(MinecraftInstance* inst); - virtual ~UpdateSharedDirectoriesTask = default; - - protected: - virtual void executeTask() override; - - protected slots: - void notifyFailed(QString reason); - - private: - MinecraftInstance* m_inst; - Task::Ptr m_tasks; -}; From a478fa60d3016445d076bd9d746aa8e2b40a9e49 Mon Sep 17 00:00:00 2001 From: Naomi <103967@gmail.com> Date: Sun, 22 Jun 2025 16:56:09 +0200 Subject: [PATCH 10/10] Clang-format Signed-off-by: Naomi <103967@gmail.com> --- launcher/FileSystem.cpp | 38 +++++++++++++------ launcher/minecraft/MinecraftInstance.cpp | 8 ++-- launcher/ui/pages/instance/WorldListPage.cpp | 2 +- .../ui/widgets/MinecraftSettingsWidget.cpp | 3 +- 4 files changed, 32 insertions(+), 19 deletions(-) diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index 48654a7c4..ff543b3a8 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -680,11 +680,11 @@ bool interactiveMove(const QString& source, const QString& destination, bool rec { const QFileInfo sourceInfo(source); - // Make sure the source exists. + // Make sure the source exists. if (!sourceInfo.exists()) return false; - // Recursive doesn't make sense if the source isn't a directory. + // Recursive doesn't make sense if the source isn't a directory. if (recursive && sourceInfo.isDir()) { QDirIterator sourceIt(source, QDir::Filter::Files | QDir::Filter::Dirs | QDir::Filter::Hidden | QDir::Filter::NoDotAndDotDot); @@ -700,7 +700,7 @@ bool interactiveMove(const QString& source, const QString& destination, bool rec FileConflictDialog dialog(source, destination, true, parent); FileConflictDialog::Result result = dialog.execWithResult(); - switch(result) { + switch (result) { case FileConflictDialog::Cancel: return false; case FileConflictDialog::ChooseDestination: @@ -1751,10 +1751,15 @@ QString getSymLinkTarget(const QString& path) return QFileInfo(path).symLinkTarget(); } -bool tryCreateSymlink(const QString& source, const QString& destination, const QString& symlinkName /* = "symbolic link" */) { +bool tryCreateSymlink(const QString& source, const QString& destination, const QString& symlinkName /* = "symbolic link" */) +{ // Make sure that symbolic links are supported. if (!FS::canLink(source, destination)) { - CustomMessageBox::selectable(nullptr, QObject::tr("Failed"), QObject::tr("Failed to create %1.\nSymbolic links are not supported on the filesystem").arg(symlinkName), QMessageBox::Warning, QMessageBox::Ok)->exec(); + CustomMessageBox::selectable( + nullptr, QObject::tr("Failed"), + QObject::tr("Failed to create %1.\nSymbolic links are not supported on the filesystem").arg(symlinkName), QMessageBox::Warning, + QMessageBox::Ok) + ->exec(); return false; } @@ -1770,7 +1775,11 @@ bool tryCreateSymlink(const QString& source, const QString& destination, const Q } else if (QFileInfo::exists(destination)) { if (!FS::checkFolderPathEmpty(destination)) { if (!interactiveMove(destination, source, true)) { - CustomMessageBox::selectable(nullptr, QObject::tr("Failed"), QObject::tr("Failed to create %1.\nEnsure that \"%2\" is empty.").arg(symlinkName).arg(destination), QMessageBox::Warning, QMessageBox::Ok)->exec(); + CustomMessageBox::selectable( + nullptr, QObject::tr("Failed"), + QObject::tr("Failed to create %1.\nEnsure that \"%2\" is empty.").arg(symlinkName).arg(destination), + QMessageBox::Warning, QMessageBox::Ok) + ->exec(); return false; } } @@ -1780,7 +1789,10 @@ bool tryCreateSymlink(const QString& source, const QString& destination, const Q // Make sure the source folder exists if (!FS::ensureFolderPathExists(source)) { - CustomMessageBox::selectable(nullptr, QObject::tr("Failed"), QObject::tr("Failed to create %1.\nEnsure that \"%2\" exists.").arg(symlinkName).arg(source), QMessageBox::Warning, QMessageBox::Ok)->exec(); + CustomMessageBox::selectable(nullptr, QObject::tr("Failed"), + QObject::tr("Failed to create %1.\nEnsure that \"%2\" exists.").arg(symlinkName).arg(source), + QMessageBox::Warning, QMessageBox::Ok) + ->exec(); return false; } @@ -1788,11 +1800,13 @@ bool tryCreateSymlink(const QString& source, const QString& destination, const Q folderLink.linkRecursively(false); if (!folderLink()) { - CustomMessageBox::selectable(nullptr, QObject::tr("Failed"), QObject::tr("Failed to create %1. Error %2: %3") - .arg(symlinkName) - .arg(folderLink.getOSError().value()) - .arg(folderLink.getOSError().message().c_str()), - QMessageBox::Warning, QMessageBox::Ok)->exec(); + CustomMessageBox::selectable(nullptr, QObject::tr("Failed"), + QObject::tr("Failed to create %1. Error %2: %3") + .arg(symlinkName) + .arg(folderLink.getOSError().value()) + .arg(folderLink.getOSError().message().c_str()), + QMessageBox::Warning, QMessageBox::Ok) + ->exec(); } return true; diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 14860d778..6c9d80d30 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -1331,15 +1331,13 @@ bool MinecraftInstance::updateSharedDirectories() bool success = true; for (const auto& [useSetting, source, destination] : sharedDirectories) { // Create symlink if useSetting is true, remove symlink if false. - if (m_settings->get(useSetting).toBool()) - { + if (m_settings->get(useSetting).toBool()) { // Try to create symlink, set setting to false if failed. - if(!FS::tryCreateSymlink(source, destination, tr("shared folder"))) { + if (!FS::tryCreateSymlink(source, destination, tr("shared folder"))) { m_settings->set(useSetting, false); success = false; } - } - else { + } else { // Safety check if (FS::isSymLink(destination)) { success = FS::deletePath(destination) && success; diff --git a/launcher/ui/pages/instance/WorldListPage.cpp b/launcher/ui/pages/instance/WorldListPage.cpp index 52911bd57..247269703 100644 --- a/launcher/ui/pages/instance/WorldListPage.cpp +++ b/launcher/ui/pages/instance/WorldListPage.cpp @@ -48,10 +48,10 @@ #include #include #include +#include #include #include #include -#include #include "FileSystem.h" #include "tools/MCEditTool.h" diff --git a/launcher/ui/widgets/MinecraftSettingsWidget.cpp b/launcher/ui/widgets/MinecraftSettingsWidget.cpp index 89ce06413..285d5fae3 100644 --- a/launcher/ui/widgets/MinecraftSettingsWidget.cpp +++ b/launcher/ui/widgets/MinecraftSettingsWidget.cpp @@ -284,7 +284,8 @@ void MinecraftSettingsWidget::loadSettings() m_ui->liteLoader->blockSignals(false); // Shared folders - m_ui->sharedScreenshotsFolder->initialize(settings->get("UseSharedScreenshotsFolder").toBool(), settings->get("SharedScreenshotsPath").toString()); + m_ui->sharedScreenshotsFolder->initialize(settings->get("UseSharedScreenshotsFolder").toBool(), + settings->get("SharedScreenshotsPath").toString()); m_ui->sharedSavesFolder->initialize(settings->get("UseSharedSavesFolder").toBool(), settings->get("SharedSavesPath").toString()); m_ui->sharedResourcePacksFolder->initialize(settings->get("UseSharedResourcePacksFolder").toBool(), settings->get("SharedResourcePacksPath").toString());