diff options
Diffstat (limited to 'launcher')
93 files changed, 1433 insertions, 616 deletions
| diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 2da8ac56..45cd9422 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -1,8 +1,9 @@  // SPDX-License-Identifier: GPL-3.0-only  /* - *  PolyMC - Minecraft Launcher + *  Prism Launcher - Minecraft Launcher   *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>   *  Copyright (C) 2022 Lenny McLennington <lenny@sneed.church> + *  Copyright (C) 2022 Tayou <tayou@gmx.net>   *   *  This program is free software: you can redistribute it and/or modify   *  it under the terms of the GNU General Public License as published by @@ -54,12 +55,6 @@  #include "ui/pages/global/APIPage.h"  #include "ui/pages/global/CustomCommandsPage.h" -#include "ui/themes/ITheme.h" -#include "ui/themes/SystemTheme.h" -#include "ui/themes/DarkTheme.h" -#include "ui/themes/BrightTheme.h" -#include "ui/themes/CustomTheme.h" -  #ifdef Q_OS_WIN  #include "ui/WinDarkmode.h"  #include <versionhelpers.h> @@ -74,6 +69,8 @@  #include "ui/pagedialog/PageDialog.h" +#include "ui/themes/ThemeManager.h" +  #include "ApplicationMessage.h"  #include <iostream> @@ -501,6 +498,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)          // Theming          m_settings->registerSetting("IconTheme", QString("pe_colored"));          m_settings->registerSetting("ApplicationTheme", QString("system")); +        m_settings->registerSetting("BackgroundCat", QString("kitteh"));          // Remembered state          m_settings->registerSetting("LastUsedGroupForNewInstance", QString()); @@ -565,7 +563,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)          // Memory          m_settings->registerSetting({"MinMemAlloc", "MinMemoryAlloc"}, 512); -        m_settings->registerSetting({"MaxMemAlloc", "MaxMemoryAlloc"}, 4096); +        m_settings->registerSetting({"MaxMemAlloc", "MaxMemoryAlloc"}, suitableMaxMem());          m_settings->registerSetting("PermGen", 128);          // Java Settings @@ -613,6 +611,8 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)          // The cat          m_settings->registerSetting("TheCat", false); +        m_settings->registerSetting("ToolbarsLocked", false); +          m_settings->registerSetting("InstSortMode", "Name");          m_settings->registerSetting("SelectedInstance", QString()); @@ -749,29 +749,8 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)          qDebug() << "<> Instance icons intialized.";      } -    // Icon themes -    { -        // TODO: icon themes and instance icons do not mesh well together. Rearrange and fix discrepancies! -        // set icon theme search path! -        auto searchPaths = QIcon::themeSearchPaths(); -        searchPaths.append("iconthemes"); -        QIcon::setThemeSearchPaths(searchPaths); -        qDebug() << "<> Icon themes initialized."; -    } - -    // Initialize widget themes -    { -        auto insertTheme = [this](ITheme * theme) -        { -            m_themes.insert(std::make_pair(theme->id(), std::unique_ptr<ITheme>(theme))); -        }; -        auto darkTheme = new DarkTheme(); -        insertTheme(new SystemTheme()); -        insertTheme(darkTheme); -        insertTheme(new BrightTheme()); -        insertTheme(new CustomTheme(darkTheme, "custom")); -        qDebug() << "<> Widget themes initialized."; -    } +    // Themes +    m_themeManager = std::make_unique<ThemeManager>(m_mainWindow);      // initialize and load all instances      { @@ -948,6 +927,12 @@ bool Application::event(QEvent* event) {          m_prevAppState = ev->applicationState();      }  #endif + +    if (event->type() == QEvent::FileOpen) { +        auto ev = static_cast<QFileOpenEvent*>(event); +        m_mainWindow->droppedURLs({ ev->url() }); +    } +      return QApplication::event(event);  } @@ -1125,45 +1110,19 @@ std::shared_ptr<JavaInstallList> Application::javalist()      return m_javalist;  } -std::vector<ITheme *> Application::getValidApplicationThemes() +QList<ITheme*> Application::getValidApplicationThemes()  { -    std::vector<ITheme *> ret; -    auto iter = m_themes.cbegin(); -    while (iter != m_themes.cend()) -    { -        ret.push_back((*iter).second.get()); -        iter++; -    } -    return ret; +    return m_themeManager->getValidApplicationThemes();  }  void Application::setApplicationTheme(const QString& name, bool initial)  { -    auto systemPalette = qApp->palette(); -    auto themeIter = m_themes.find(name); -    if(themeIter != m_themes.end()) -    { -        auto & theme = (*themeIter).second; -        theme->apply(initial); -#ifdef Q_OS_WIN -        if (m_mainWindow && IsWindows10OrGreater()) { -            if (QString::compare(theme->id(), "dark") == 0) { -                    WinDarkmode::setDarkWinTitlebar(m_mainWindow->winId(), true); -            } else { -                    WinDarkmode::setDarkWinTitlebar(m_mainWindow->winId(), false); -            } -        } -#endif -    } -    else -    { -        qWarning() << "Tried to set invalid theme:" << name; -    } +    m_themeManager->setApplicationTheme(name, initial);  }  void Application::setIconTheme(const QString& name)  { -    QIcon::setThemeName(name); +    m_themeManager->setIconTheme(name);  }  QIcon Application::getThemedIcon(const QString& name) @@ -1632,3 +1591,17 @@ QString Application::getUserAgentUncached()      return BuildConfig.USER_AGENT_UNCACHED;  } + +int Application::suitableMaxMem() +{ +    float totalRAM = (float)Sys::getSystemRam() / (float)Sys::mebibyte; +    int maxMemoryAlloc; + +    // If totalRAM < 6GB, use (totalRAM / 1.5), else 4GB +    if (totalRAM < (4096 * 1.5)) +        maxMemoryAlloc = (int) (totalRAM / 1.5); +    else +        maxMemoryAlloc = 4096; + +    return maxMemoryAlloc; +} diff --git a/launcher/Application.h b/launcher/Application.h index 8fa0ab10..4c2f62d4 100644 --- a/launcher/Application.h +++ b/launcher/Application.h @@ -1,7 +1,8 @@  // SPDX-License-Identifier: GPL-3.0-only  /* - *  PolyMC - Minecraft Launcher + *  Prism Launcher - Minecraft Launcher   *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> + *  Copyright (C) 2022 Tayou <tayou@gmx.net>   *   *  This program is free software: you can redistribute it and/or modify   *  it under the terms of the GNU General Public License as published by @@ -68,6 +69,7 @@ class BaseDetachedToolFactory;  class TranslationsModel;  class ITheme;  class MCEditTool; +class ThemeManager;  namespace Meta {      class Index; @@ -118,7 +120,7 @@ public:      void setIconTheme(const QString& name); -    std::vector<ITheme *> getValidApplicationThemes(); +    QList<ITheme*> getValidApplicationThemes();      void setApplicationTheme(const QString& name, bool initial); @@ -198,6 +200,8 @@ public:      void ShowGlobalSettings(class QWidget * parent, QString open_page = QString()); +    int suitableMaxMem(); +  signals:      void updateAllowedChanged(bool status);      void globalSettingsAboutToOpen(); @@ -255,9 +259,9 @@ private:      std::shared_ptr<JavaInstallList> m_javalist;      std::shared_ptr<TranslationsModel> m_translations;      std::shared_ptr<GenericPageProvider> m_globalSettingsProvider; -    std::map<QString, std::unique_ptr<ITheme>> m_themes;      std::unique_ptr<MCEditTool> m_mcedit;      QSet<QString> m_features; +    std::unique_ptr<ThemeManager> m_themeManager;      QMap<QString, std::shared_ptr<BaseProfilerFactory>> m_profilers; diff --git a/launcher/BaseInstaller.h b/launcher/BaseInstaller.h index b2e6a14d..a1b80e93 100644 --- a/launcher/BaseInstaller.h +++ b/launcher/BaseInstaller.h @@ -17,13 +17,14 @@  #include <memory> +#include "BaseVersion.h" +  class MinecraftInstance;  class QDir;  class QString;  class QObject;  class Task;  class BaseVersion; -typedef std::shared_ptr<BaseVersion> BaseVersionPtr;  class BaseInstaller  { @@ -35,7 +36,7 @@ public:      virtual bool add(MinecraftInstance *to);      virtual bool remove(MinecraftInstance *from); -    virtual Task *createInstallTask(MinecraftInstance *instance, BaseVersionPtr version, QObject *parent) = 0; +    virtual Task *createInstallTask(MinecraftInstance *instance, BaseVersion::Ptr version, QObject *parent) = 0;  protected:      virtual QString id() const = 0; diff --git a/launcher/BaseVersion.h b/launcher/BaseVersion.h index b88105fb..ca0e4502 100644 --- a/launcher/BaseVersion.h +++ b/launcher/BaseVersion.h @@ -25,6 +25,7 @@  class BaseVersion  {  public: +    using Ptr = std::shared_ptr<BaseVersion>;      virtual ~BaseVersion() {}      /*!       * A string used to identify this version in config files. @@ -54,6 +55,4 @@ public:      };  }; -typedef std::shared_ptr<BaseVersion> BaseVersionPtr; - -Q_DECLARE_METATYPE(BaseVersionPtr) +Q_DECLARE_METATYPE(BaseVersion::Ptr) diff --git a/launcher/BaseVersionList.cpp b/launcher/BaseVersionList.cpp index b4a7d6dd..4ed82612 100644 --- a/launcher/BaseVersionList.cpp +++ b/launcher/BaseVersionList.cpp @@ -40,20 +40,20 @@ BaseVersionList::BaseVersionList(QObject *parent) : QAbstractListModel(parent)  {  } -BaseVersionPtr BaseVersionList::findVersion(const QString &descriptor) +BaseVersion::Ptr BaseVersionList::findVersion(const QString &descriptor)  {      for (int i = 0; i < count(); i++)      {          if (at(i)->descriptor() == descriptor)              return at(i);      } -    return BaseVersionPtr(); +    return nullptr;  } -BaseVersionPtr BaseVersionList::getRecommended() const +BaseVersion::Ptr BaseVersionList::getRecommended() const  {      if (count() <= 0) -        return BaseVersionPtr(); +        return nullptr;      else          return at(0);  } @@ -66,7 +66,7 @@ QVariant BaseVersionList::data(const QModelIndex &index, int role) const      if (index.row() > count())          return QVariant(); -    BaseVersionPtr version = at(index.row()); +    BaseVersion::Ptr version = at(index.row());      switch (role)      { diff --git a/launcher/BaseVersionList.h b/launcher/BaseVersionList.h index 80a91e8f..31f29022 100644 --- a/launcher/BaseVersionList.h +++ b/launcher/BaseVersionList.h @@ -70,7 +70,7 @@ public:      virtual bool isLoaded() = 0;      //! Gets the version at the given index. -    virtual const BaseVersionPtr at(int i) const = 0; +    virtual const BaseVersion::Ptr at(int i) const = 0;      //! Returns the number of versions in the list.      virtual int count() const = 0; @@ -90,13 +90,13 @@ public:       * \return A const pointer to the version with the given descriptor. NULL if       * one doesn't exist.       */ -    virtual BaseVersionPtr findVersion(const QString &descriptor); +    virtual BaseVersion::Ptr findVersion(const QString &descriptor);      /*!       * \brief Gets the recommended version from this list       * If the list doesn't support recommended versions, this works exactly as getLatestStable       */ -    virtual BaseVersionPtr getRecommended() const; +    virtual BaseVersion::Ptr getRecommended() const;      /*!       * Sorts the version list. @@ -117,5 +117,5 @@ slots:       * then copies the versions and sets their parents correctly.       * \param versions List of versions whose parents should be set.       */ -    virtual void updateListData(QList<BaseVersionPtr> versions) = 0; +    virtual void updateListData(QList<BaseVersion::Ptr> versions) = 0;  }; diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 0dae47df..8db93429 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -24,13 +24,15 @@ set(CORE_SOURCES      NullInstance.h      MMCZip.h      MMCZip.cpp -    MMCStrings.h -    MMCStrings.cpp +    StringUtils.h +    StringUtils.cpp      RuntimeContext.h      # Basic instance manipulation tasks (derived from InstanceTask)      InstanceCreationTask.h      InstanceCreationTask.cpp +    InstanceCopyPrefs.h +    InstanceCopyPrefs.cpp      InstanceCopyTask.h      InstanceCopyTask.cpp      InstanceImportTask.h @@ -539,9 +541,6 @@ set(ATLAUNCHER_SOURCES  ################################ COMPILE ################################ -# we need zlib -find_package(ZLIB REQUIRED) -  set(LOGIC_SOURCES      ${CORE_SOURCES}      ${PATHMATCHER_SOURCES} @@ -650,6 +649,8 @@ SET(LAUNCHER_SOURCES      ui/themes/ITheme.h      ui/themes/SystemTheme.cpp      ui/themes/SystemTheme.h +    ui/themes/ThemeManager.cpp +    ui/themes/ThemeManager.h      # Processes      LaunchController.h @@ -1062,96 +1063,95 @@ if(INSTALL_BUNDLE STREQUAL "full")          COMPONENT Runtime      )      # Bundle plugins -    if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") -        # Image formats -        install( -            DIRECTORY "${QT_PLUGINS_DIR}/imageformats" -            DESTINATION ${PLUGIN_DEST_DIR} -            COMPONENT Runtime -            REGEX "tga|tiff|mng" EXCLUDE -        ) -        # Icon engines -        install( -            DIRECTORY "${QT_PLUGINS_DIR}/iconengines" -            DESTINATION ${PLUGIN_DEST_DIR} -            COMPONENT Runtime -            REGEX "fontawesome" EXCLUDE -        ) -        # Platform plugins +    # Image formats +    install( +        DIRECTORY "${QT_PLUGINS_DIR}/imageformats" +        CONFIGURATIONS Debug RelWithDebInfo "" +        DESTINATION ${PLUGIN_DEST_DIR} +        COMPONENT Runtime +        REGEX "tga|tiff|mng" EXCLUDE +    ) +    install( +        DIRECTORY "${QT_PLUGINS_DIR}/imageformats" +        CONFIGURATIONS Release MinSizeRel +        DESTINATION ${PLUGIN_DEST_DIR} +        COMPONENT Runtime +        REGEX "tga|tiff|mng" EXCLUDE +        REGEX "d\\." EXCLUDE +        REGEX "_debug\\." EXCLUDE +        REGEX "\\.dSYM" EXCLUDE +    ) +    # Icon engines +    install( +        DIRECTORY "${QT_PLUGINS_DIR}/iconengines" +        CONFIGURATIONS Debug RelWithDebInfo "" +        DESTINATION ${PLUGIN_DEST_DIR} +        COMPONENT Runtime +        REGEX "fontawesome" EXCLUDE +    ) +    install( +        DIRECTORY "${QT_PLUGINS_DIR}/iconengines" +        CONFIGURATIONS Release MinSizeRel +        DESTINATION ${PLUGIN_DEST_DIR} +        COMPONENT Runtime +        REGEX "fontawesome" EXCLUDE +        REGEX "d\\." EXCLUDE +        REGEX "_debug\\." EXCLUDE +        REGEX "\\.dSYM" EXCLUDE +    ) +    # Platform plugins +    install( +        DIRECTORY "${QT_PLUGINS_DIR}/platforms" +        CONFIGURATIONS Debug RelWithDebInfo "" +        DESTINATION ${PLUGIN_DEST_DIR} +        COMPONENT Runtime +        REGEX "minimal|linuxfb|offscreen" EXCLUDE +    ) +    install( +        DIRECTORY "${QT_PLUGINS_DIR}/platforms" +        CONFIGURATIONS Release MinSizeRel +        DESTINATION ${PLUGIN_DEST_DIR} +        COMPONENT Runtime +        REGEX "minimal|linuxfb|offscreen" EXCLUDE +        REGEX "[^2]d\\." EXCLUDE +        REGEX "_debug\\." EXCLUDE +        REGEX "\\.dSYM" EXCLUDE +    ) +    # Style plugins +    if(EXISTS "${QT_PLUGINS_DIR}/styles")          install( -            DIRECTORY "${QT_PLUGINS_DIR}/platforms" +            DIRECTORY "${QT_PLUGINS_DIR}/styles" +            CONFIGURATIONS Debug RelWithDebInfo ""              DESTINATION ${PLUGIN_DEST_DIR}              COMPONENT Runtime -            REGEX "minimal|linuxfb|offscreen" EXCLUDE          ) -        # Style plugins -        if(EXISTS "${QT_PLUGINS_DIR}/styles") -            install( -                DIRECTORY "${QT_PLUGINS_DIR}/styles" -                DESTINATION ${PLUGIN_DEST_DIR} -                COMPONENT Runtime -            ) -        endif() -        # TLS plugins (Qt 6 only) -        if(EXISTS "${QT_PLUGINS_DIR}/tls") -            install( -                DIRECTORY "${QT_PLUGINS_DIR}/tls" -                DESTINATION ${PLUGIN_DEST_DIR} -                COMPONENT Runtime -            ) -        endif() -    else() -        # Image formats          install( -            DIRECTORY "${QT_PLUGINS_DIR}/imageformats" +            DIRECTORY "${QT_PLUGINS_DIR}/styles" +            CONFIGURATIONS Release MinSizeRel              DESTINATION ${PLUGIN_DEST_DIR}              COMPONENT Runtime -            REGEX "tga|tiff|mng" EXCLUDE              REGEX "d\\." EXCLUDE              REGEX "_debug\\." EXCLUDE              REGEX "\\.dSYM" EXCLUDE          ) -        # Icon engines +    endif() +    # TLS plugins (Qt 6 only) +    if(EXISTS "${QT_PLUGINS_DIR}/tls")          install( -            DIRECTORY "${QT_PLUGINS_DIR}/iconengines" +            DIRECTORY "${QT_PLUGINS_DIR}/tls" +            CONFIGURATIONS Debug RelWithDebInfo ""              DESTINATION ${PLUGIN_DEST_DIR}              COMPONENT Runtime -            REGEX "fontawesome" EXCLUDE -            REGEX "d\\." EXCLUDE -            REGEX "_debug\\." EXCLUDE -            REGEX "\\.dSYM" EXCLUDE          ) -        # Platform plugins          install( -            DIRECTORY "${QT_PLUGINS_DIR}/platforms" +            DIRECTORY "${QT_PLUGINS_DIR}/tls" +            CONFIGURATIONS Release MinSizeRel              DESTINATION ${PLUGIN_DEST_DIR}              COMPONENT Runtime -            REGEX "minimal|linuxfb|offscreen" EXCLUDE -            REGEX "d\\." EXCLUDE +            REGEX "dd\\." EXCLUDE              REGEX "_debug\\." EXCLUDE              REGEX "\\.dSYM" EXCLUDE          ) -        # Style plugins -        if(EXISTS "${QT_PLUGINS_DIR}/styles") -            install( -                DIRECTORY "${QT_PLUGINS_DIR}/styles" -                DESTINATION ${PLUGIN_DEST_DIR} -                COMPONENT Runtime -                REGEX "d\\." EXCLUDE -                REGEX "_debug\\." EXCLUDE -                REGEX "\\.dSYM" EXCLUDE -            ) -        endif() -        # TLS plugins (Qt 6 only) -        if(EXISTS "${QT_PLUGINS_DIR}/tls") -            install( -                DIRECTORY "${QT_PLUGINS_DIR}/tls" -                DESTINATION ${PLUGIN_DEST_DIR} -                COMPONENT Runtime -                REGEX "_debug\\." EXCLUDE -                REGEX "\\.dSYM" EXCLUDE -            ) -        endif()      endif()      configure_file(          "${CMAKE_CURRENT_SOURCE_DIR}/install_prereqs.cmake.in" diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index bf0849ec..4a8f4bd3 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -44,7 +44,9 @@  #include <QStandardPaths>  #include <QTextStream>  #include <QUrl> +  #include "DesktopServices.h" +#include "StringUtils.h"  #if defined Q_OS_WIN32  #include <objbase.h> @@ -79,22 +81,6 @@ namespace fs = std::filesystem;  namespace fs = ghc::filesystem;  #endif -#if defined Q_OS_WIN32 - -std::wstring toStdString(QString s) -{ -    return s.toStdWString(); -} - -#else - -std::string toStdString(QString s) -{ -    return s.toStdString(); -} - -#endif -  namespace FS {  void ensureExists(const QDir& dir) @@ -194,7 +180,7 @@ bool copy::operator()(const QString& offset)          auto dst_path = PathCombine(dst, relative_dst_path);          ensureFilePathExists(dst_path); -        fs::copy(toStdString(src_path), toStdString(dst_path), opt, err); +        fs::copy(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), opt, err);          if (err) {              qWarning() << "Failed to copy files:" << QString::fromStdString(err.message());              qDebug() << "Source file:" << src_path; @@ -216,7 +202,7 @@ bool copy::operator()(const QString& offset)      }      // If the root src is not a directory, the previous iterator won't run. -    if (!fs::is_directory(toStdString(src))) +    if (!fs::is_directory(StringUtils::toStdString(src)))          copy_file(src, "");      return err.value() == 0; @@ -226,7 +212,7 @@ bool deletePath(QString path)  {      std::error_code err; -    fs::remove_all(toStdString(path), err); +    fs::remove_all(StringUtils::toStdString(path), err);      if (err) {          qWarning() << "Failed to remove files:" << QString::fromStdString(err.message()); @@ -417,7 +403,7 @@ bool overrideFolder(QString overwritten_path, QString override_path)      fs::copy_options opt = copy_opts::recursive | copy_opts::overwrite_existing;      // FIXME: hello traveller! Apparently std::copy does NOT overwrite existing files on GNU libstdc++ on Windows? -    fs::copy(toStdString(override_path), toStdString(overwritten_path), opt, err); +    fs::copy(StringUtils::toStdString(override_path), StringUtils::toStdString(overwritten_path), opt, err);      if (err) {          qCritical() << QString("Failed to apply override from %1 to %2").arg(override_path, overwritten_path); diff --git a/launcher/InstanceCopyPrefs.cpp b/launcher/InstanceCopyPrefs.cpp new file mode 100644 index 00000000..7b93a516 --- /dev/null +++ b/launcher/InstanceCopyPrefs.cpp @@ -0,0 +1,135 @@ +// +// Created by marcelohdez on 10/22/22. +// + +#include "InstanceCopyPrefs.h" + +bool InstanceCopyPrefs::allTrue() const +{ +    return copySaves && +        keepPlaytime && +        copyGameOptions && +        copyResourcePacks && +        copyShaderPacks && +        copyServers && +        copyMods && +        copyScreenshots; +} + +// Returns a single RegEx string of the selected folders/files to filter out (ex: ".minecraft/saves|.minecraft/server.dat") +QString InstanceCopyPrefs::getSelectedFiltersAsRegex() const +{ +    QStringList filters; + +    if(!copySaves) +        filters << "saves"; + +    if(!copyGameOptions) +        filters << "options.txt"; + +    if(!copyResourcePacks) +        filters << "resourcepacks" << "texturepacks"; + +    if(!copyShaderPacks) +        filters << "shaderpacks"; + +    if(!copyServers) +        filters << "servers.dat" << "servers.dat_old" << "server-resource-packs"; + +    if(!copyMods) +        filters << "coremods" << "mods" << "config"; + +    if(!copyScreenshots) +        filters << "screenshots"; + +    // If we have any filters to add, join them as a single regex string to return: +    if (!filters.isEmpty()) { +        const QString MC_ROOT = "[.]?minecraft/"; +        // Ensure first filter starts with root, then join other filters with OR regex before root (ex: ".minecraft/saves|.minecraft/mods"): +        return MC_ROOT + filters.join("|" + MC_ROOT); +    } + +    return {}; +} + +// ======= Getters ======= +bool InstanceCopyPrefs::isCopySavesEnabled() const +{ +    return copySaves; +} + +bool InstanceCopyPrefs::isKeepPlaytimeEnabled() const +{ +    return keepPlaytime; +} + +bool InstanceCopyPrefs::isCopyGameOptionsEnabled() const +{ +    return copyGameOptions; +} + +bool InstanceCopyPrefs::isCopyResourcePacksEnabled() const +{ +    return copyResourcePacks; +} + +bool InstanceCopyPrefs::isCopyShaderPacksEnabled() const +{ +    return copyShaderPacks; +} + +bool InstanceCopyPrefs::isCopyServersEnabled() const +{ +    return copyServers; +} + +bool InstanceCopyPrefs::isCopyModsEnabled() const +{ +    return copyMods; +} + +bool InstanceCopyPrefs::isCopyScreenshotsEnabled() const +{ +    return copyScreenshots; +} + +// ======= Setters ======= +void InstanceCopyPrefs::enableCopySaves(bool b) +{ +    copySaves = b; +} + +void InstanceCopyPrefs::enableKeepPlaytime(bool b) +{ +    keepPlaytime = b; +} + +void InstanceCopyPrefs::enableCopyGameOptions(bool b) +{ +    copyGameOptions = b; +} + +void InstanceCopyPrefs::enableCopyResourcePacks(bool b) +{ +    copyResourcePacks = b; +} + +void InstanceCopyPrefs::enableCopyShaderPacks(bool b) +{ +    copyShaderPacks = b; +} + +void InstanceCopyPrefs::enableCopyServers(bool b) +{ +    copyServers = b; +} + +void InstanceCopyPrefs::enableCopyMods(bool b) +{ +    copyMods = b; +} + +void InstanceCopyPrefs::enableCopyScreenshots(bool b) +{ +    copyScreenshots = b; +} diff --git a/launcher/InstanceCopyPrefs.h b/launcher/InstanceCopyPrefs.h new file mode 100644 index 00000000..6988b2df --- /dev/null +++ b/launcher/InstanceCopyPrefs.h @@ -0,0 +1,41 @@ +// +// Created by marcelohdez on 10/22/22. +// + +#pragma once + +#include <QStringList> + +struct InstanceCopyPrefs { +   public: +    [[nodiscard]] bool allTrue() const; +    [[nodiscard]] QString getSelectedFiltersAsRegex() const; +    // Getters +    [[nodiscard]] bool isCopySavesEnabled() const; +    [[nodiscard]] bool isKeepPlaytimeEnabled() const; +    [[nodiscard]] bool isCopyGameOptionsEnabled() const; +    [[nodiscard]] bool isCopyResourcePacksEnabled() const; +    [[nodiscard]] bool isCopyShaderPacksEnabled() const; +    [[nodiscard]] bool isCopyServersEnabled() const; +    [[nodiscard]] bool isCopyModsEnabled() const; +    [[nodiscard]] bool isCopyScreenshotsEnabled() const; +    // Setters +    void enableCopySaves(bool b); +    void enableKeepPlaytime(bool b); +    void enableCopyGameOptions(bool b); +    void enableCopyResourcePacks(bool b); +    void enableCopyShaderPacks(bool b); +    void enableCopyServers(bool b); +    void enableCopyMods(bool b); +    void enableCopyScreenshots(bool b); + +   protected: // data +    bool copySaves = true; +    bool keepPlaytime = true; +    bool copyGameOptions = true; +    bool copyResourcePacks = true; +    bool copyShaderPacks = true; +    bool copyServers = true; +    bool copyMods = true; +    bool copyScreenshots = true; +}; diff --git a/launcher/InstanceCopyTask.cpp b/launcher/InstanceCopyTask.cpp index b1e33884..a4ea947d 100644 --- a/launcher/InstanceCopyTask.cpp +++ b/launcher/InstanceCopyTask.cpp @@ -5,15 +5,17 @@  #include "pathmatcher/RegexpMatcher.h"  #include <QtConcurrentRun> -InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, bool copySaves, bool keepPlaytime) +InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, const InstanceCopyPrefs& prefs)  {      m_origInstance = origInstance; -    m_keepPlaytime = keepPlaytime; +    m_keepPlaytime = prefs.isKeepPlaytimeEnabled(); -    if(!copySaves) +    QString filters = prefs.getSelectedFiltersAsRegex(); +    if (!filters.isEmpty())      { +        // Set regex filter:          // FIXME: get this from the original instance type... -        auto matcherReal = new RegexpMatcher("[.]?minecraft/saves"); +        auto matcherReal = new RegexpMatcher(filters);          matcherReal->caseSensitive(false);          m_matcher.reset(matcherReal);      } diff --git a/launcher/InstanceCopyTask.h b/launcher/InstanceCopyTask.h index 82901732..1f29b854 100644 --- a/launcher/InstanceCopyTask.h +++ b/launcher/InstanceCopyTask.h @@ -1,20 +1,21 @@  #pragma once -#include "tasks/Task.h" -#include "net/NetJob.h" -#include <QUrl>  #include <QFuture>  #include <QFutureWatcher> -#include "settings/SettingsObject.h" -#include "BaseVersion.h" +#include <QUrl>  #include "BaseInstance.h" +#include "BaseVersion.h" +#include "InstanceCopyPrefs.h"  #include "InstanceTask.h" +#include "net/NetJob.h" +#include "settings/SettingsObject.h" +#include "tasks/Task.h"  class InstanceCopyTask : public InstanceTask  {      Q_OBJECT  public: -    explicit InstanceCopyTask(InstancePtr origInstance, bool copySaves, bool keepPlaytime); +    explicit InstanceCopyTask(InstancePtr origInstance, const InstanceCopyPrefs& prefs);  protected:      //! Entry point for tasks. @@ -22,7 +23,8 @@ protected:      void copyFinished();      void copyAborted(); -private: /* data */ +private: +    /* data */      InstancePtr m_origInstance;      QFuture<bool> m_copyFuture;      QFutureWatcher<bool> m_copyFutureWatcher; diff --git a/launcher/JavaCommon.cpp b/launcher/JavaCommon.cpp index aa4d1123..52cc868a 100644 --- a/launcher/JavaCommon.cpp +++ b/launcher/JavaCommon.cpp @@ -36,7 +36,7 @@  #include "JavaCommon.h"  #include "java/JavaUtils.h"  #include "ui/dialogs/CustomMessageBox.h" -#include <MMCStrings.h> +  #include <QRegularExpression>  bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget *parent) diff --git a/launcher/MMCStrings.h b/launcher/MMCStrings.h deleted file mode 100644 index 48052a00..00000000 --- a/launcher/MMCStrings.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include <QString> - -namespace Strings -{ -    int naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs); -} diff --git a/launcher/MMCStrings.cpp b/launcher/StringUtils.cpp index dc91c8d6..0f3c3669 100644 --- a/launcher/MMCStrings.cpp +++ b/launcher/StringUtils.cpp @@ -1,26 +1,28 @@ -#include "MMCStrings.h" +#include "StringUtils.h" + +/// If you're wondering where these came from exactly, then know you're not the only one =D  /// TAKEN FROM Qt, because it doesn't expose it intelligently -static inline QChar getNextChar(const QString &s, int location) +static inline QChar getNextChar(const QString& s, int location)  {      return (location < s.length()) ? s.at(location) : QChar();  }  /// TAKEN FROM Qt, because it doesn't expose it intelligently -int Strings::naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs) +int StringUtils::naturalCompare(const QString& s1, const QString& s2, Qt::CaseSensitivity cs)  { -    for (int l1 = 0, l2 = 0; l1 <= s1.count() && l2 <= s2.count(); ++l1, ++l2) -    { +    int l1 = 0, l2 = 0; +    while (l1 <= s1.count() && l2 <= s2.count()) {          // skip spaces, tabs and 0's          QChar c1 = getNextChar(s1, l1);          while (c1.isSpace())              c1 = getNextChar(s1, ++l1); +          QChar c2 = getNextChar(s2, l2);          while (c2.isSpace())              c2 = getNextChar(s2, ++l2); -        if (c1.isDigit() && c2.isDigit()) -        { +        if (c1.isDigit() && c2.isDigit()) {              while (c1.digitValue() == 0)                  c1 = getNextChar(s1, ++l1);              while (c2.digitValue() == 0) @@ -30,11 +32,8 @@ int Strings::naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensit              int lookAheadLocation2 = l2;              int currentReturnValue = 0;              // find the last digit, setting currentReturnValue as we go if it isn't equal -            for (QChar lookAhead1 = c1, lookAhead2 = c2; -                 (lookAheadLocation1 <= s1.length() && lookAheadLocation2 <= s2.length()); -                 lookAhead1 = getNextChar(s1, ++lookAheadLocation1), -                       lookAhead2 = getNextChar(s2, ++lookAheadLocation2)) -            { +            for (QChar lookAhead1 = c1, lookAhead2 = c2; (lookAheadLocation1 <= s1.length() && lookAheadLocation2 <= s2.length()); +                 lookAhead1 = getNextChar(s1, ++lookAheadLocation1), lookAhead2 = getNextChar(s2, ++lookAheadLocation2)) {                  bool is1ADigit = !lookAhead1.isNull() && lookAhead1.isDigit();                  bool is2ADigit = !lookAhead2.isNull() && lookAhead2.isDigit();                  if (!is1ADigit && !is2ADigit) @@ -43,14 +42,10 @@ int Strings::naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensit                      return -1;                  if (!is2ADigit)                      return 1; -                if (currentReturnValue == 0) -                { -                    if (lookAhead1 < lookAhead2) -                    { +                if (currentReturnValue == 0) { +                    if (lookAhead1 < lookAhead2) {                          currentReturnValue = -1; -                    } -                    else if (lookAhead1 > lookAhead2) -                    { +                    } else if (lookAhead1 > lookAhead2) {                          currentReturnValue = 1;                      }                  } @@ -58,19 +53,24 @@ int Strings::naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensit              if (currentReturnValue != 0)                  return currentReturnValue;          } -        if (cs == Qt::CaseInsensitive) -        { + +        if (cs == Qt::CaseInsensitive) {              if (!c1.isLower())                  c1 = c1.toLower();              if (!c2.isLower())                  c2 = c2.toLower();          } +          int r = QString::localeAwareCompare(c1, c2);          if (r < 0)              return -1;          if (r > 0)              return 1; + +        l1 += 1; +        l2 += 1;      } +      // The two strings are the same (02 == 2) so fall back to the normal sort      return QString::compare(s1, s2, cs);  } diff --git a/launcher/StringUtils.h b/launcher/StringUtils.h new file mode 100644 index 00000000..1799605b --- /dev/null +++ b/launcher/StringUtils.h @@ -0,0 +1,32 @@ +#pragma once + +#include <QString> + +namespace StringUtils { + +#if defined Q_OS_WIN32 +using string = std::wstring; + +inline string toStdString(QString s) +{ +    return s.toStdWString(); +} +inline QString fromStdString(string s) +{ +    return QString::fromStdWString(s); +} +#else +using string = std::string; + +inline string toStdString(QString s) +{ +    return s.toStdString(); +} +inline QString fromStdString(string s) +{ +    return QString::fromStdString(s); +} +#endif + +int naturalCompare(const QString& s1, const QString& s2, Qt::CaseSensitivity cs); +}  // namespace StringUtils diff --git a/launcher/java/JavaInstall.cpp b/launcher/java/JavaInstall.cpp index 5bcf7bcb..d5932bcb 100644 --- a/launcher/java/JavaInstall.cpp +++ b/launcher/java/JavaInstall.cpp @@ -1,9 +1,10 @@  #include "JavaInstall.h" -#include <MMCStrings.h> + +#include "StringUtils.h"  bool JavaInstall::operator<(const JavaInstall &rhs)  { -    auto archCompare = Strings::naturalCompare(arch, rhs.arch, Qt::CaseInsensitive); +    auto archCompare = StringUtils::naturalCompare(arch, rhs.arch, Qt::CaseInsensitive);      if(archCompare != 0)          return archCompare < 0;      if(id < rhs.id) @@ -14,7 +15,7 @@ bool JavaInstall::operator<(const JavaInstall &rhs)      {          return false;      } -    return Strings::naturalCompare(path, rhs.path, Qt::CaseInsensitive) < 0; +    return StringUtils::naturalCompare(path, rhs.path, Qt::CaseInsensitive) < 0;  }  bool JavaInstall::operator==(const JavaInstall &rhs) diff --git a/launcher/java/JavaInstallList.cpp b/launcher/java/JavaInstallList.cpp index 0249bd22..e2f0aa00 100644 --- a/launcher/java/JavaInstallList.cpp +++ b/launcher/java/JavaInstallList.cpp @@ -41,7 +41,6 @@  #include "java/JavaInstallList.h"  #include "java/JavaCheckerJob.h"  #include "java/JavaUtils.h" -#include "MMCStrings.h"  #include "minecraft/VersionFilterData.h"  JavaInstallList::JavaInstallList(QObject *parent) : BaseVersionList(parent) @@ -73,7 +72,7 @@ void JavaInstallList::load()      }  } -const BaseVersionPtr JavaInstallList::at(int i) const +const BaseVersion::Ptr JavaInstallList::at(int i) const  {      return m_vlist.at(i);  } @@ -122,7 +121,7 @@ BaseVersionList::RoleList JavaInstallList::providesRoles() const  } -void JavaInstallList::updateListData(QList<BaseVersionPtr> versions) +void JavaInstallList::updateListData(QList<BaseVersion::Ptr> versions)  {      beginResetModel();      m_vlist = versions; @@ -137,7 +136,7 @@ void JavaInstallList::updateListData(QList<BaseVersionPtr> versions)      m_loadTask.reset();  } -bool sortJavas(BaseVersionPtr left, BaseVersionPtr right) +bool sortJavas(BaseVersion::Ptr left, BaseVersion::Ptr right)  {      auto rleft = std::dynamic_pointer_cast<JavaInstall>(right);      auto rright = std::dynamic_pointer_cast<JavaInstall>(left); @@ -210,11 +209,11 @@ void JavaListLoadTask::javaCheckerFinished()          }      } -    QList<BaseVersionPtr> javas_bvp; +    QList<BaseVersion::Ptr> javas_bvp;      for (auto java : candidates)      {          //qDebug() << java->id << java->arch << " at " << java->path; -        BaseVersionPtr bp_java = std::dynamic_pointer_cast<BaseVersion>(java); +        BaseVersion::Ptr bp_java = std::dynamic_pointer_cast<BaseVersion>(java);          if (bp_java)          { diff --git a/launcher/java/JavaInstallList.h b/launcher/java/JavaInstallList.h index 3c237edf..733dc7e1 100644 --- a/launcher/java/JavaInstallList.h +++ b/launcher/java/JavaInstallList.h @@ -42,7 +42,7 @@ public:      Task::Ptr getLoadTask() override;      bool isLoaded() override; -    const BaseVersionPtr at(int i) const override; +    const BaseVersion::Ptr at(int i) const override;      int count() const override;      void sortVersions() override; @@ -50,7 +50,7 @@ public:      RoleList providesRoles() const override;  public slots: -    void updateListData(QList<BaseVersionPtr> versions) override; +    void updateListData(QList<BaseVersion::Ptr> versions) override;  protected:      void load(); @@ -59,7 +59,7 @@ protected:  protected:      Status m_status = Status::NotDone;      shared_qobject_ptr<JavaListLoadTask> m_loadTask; -    QList<BaseVersionPtr> m_vlist; +    QList<BaseVersion::Ptr> m_vlist;  };  class JavaListLoadTask : public Task diff --git a/launcher/java/JavaUtils.cpp b/launcher/java/JavaUtils.cpp index 6c0c60cd..5efbc7a8 100644 --- a/launcher/java/JavaUtils.cpp +++ b/launcher/java/JavaUtils.cpp @@ -439,19 +439,28 @@ QList<QString> JavaUtils::FindJavaPaths()              javas.append(FS::PathCombine(prefix, "bin/java"));          }      }; +    // java installed in a snap is installed in the standard directory, but underneath $SNAP +    auto snap = qEnvironmentVariable("SNAP"); +    auto scanJavaDirs = [&](const QString & dirPath) +    { +        scanJavaDir(dirPath); +        if (!snap.isNull()) { +            scanJavaDir(snap + dirPath); +        } +    };      // oracle RPMs -    scanJavaDir("/usr/java"); +    scanJavaDirs("/usr/java");      // general locations used by distro packaging -    scanJavaDir("/usr/lib/jvm"); -    scanJavaDir("/usr/lib64/jvm"); -    scanJavaDir("/usr/lib32/jvm"); +    scanJavaDirs("/usr/lib/jvm"); +    scanJavaDirs("/usr/lib64/jvm"); +    scanJavaDirs("/usr/lib32/jvm");      // javas stored in Prism Launcher's folder -    scanJavaDir("java"); +    scanJavaDirs("java");      // manually installed JDKs in /opt -    scanJavaDir("/opt/jdk"); -    scanJavaDir("/opt/jdks"); +    scanJavaDirs("/opt/jdk"); +    scanJavaDirs("/opt/jdks");      // flatpak -    scanJavaDir("/app/jdk"); +    scanJavaDirs("/app/jdk");      javas = addJavasFromEnv(javas);      javas.removeDuplicates();      return javas; diff --git a/launcher/java/JavaVersion.cpp b/launcher/java/JavaVersion.cpp index 179ccd8d..0e4fc1d3 100644 --- a/launcher/java/JavaVersion.cpp +++ b/launcher/java/JavaVersion.cpp @@ -1,5 +1,6 @@  #include "JavaVersion.h" -#include <MMCStrings.h> + +#include "StringUtils.h"  #include <QRegularExpression>  #include <QString> @@ -98,12 +99,12 @@ bool JavaVersion::operator<(const JavaVersion &rhs)          else if(thisPre && rhsPre)          {              // both are prereleases - use natural compare... -            return Strings::naturalCompare(m_prerelease, rhs.m_prerelease, Qt::CaseSensitive) < 0; +            return StringUtils::naturalCompare(m_prerelease, rhs.m_prerelease, Qt::CaseSensitive) < 0;          }          // neither is prerelease, so they are the same -> this cannot be less than rhs          return false;      } -    else return Strings::naturalCompare(m_string, rhs.m_string, Qt::CaseSensitive) < 0; +    else return StringUtils::naturalCompare(m_string, rhs.m_string, Qt::CaseSensitive) < 0;  }  bool JavaVersion::operator==(const JavaVersion &rhs) diff --git a/launcher/launch/LaunchTask.cpp b/launcher/launch/LaunchTask.cpp index 28fcc4f4..9e1794b3 100644 --- a/launcher/launch/LaunchTask.cpp +++ b/launcher/launch/LaunchTask.cpp @@ -37,7 +37,6 @@  #include "launch/LaunchTask.h"  #include "MessageLevel.h" -#include "MMCStrings.h"  #include "java/JavaChecker.h"  #include "tasks/Task.h"  #include <QDebug> diff --git a/launcher/main.cpp b/launcher/main.cpp index e2116f38..df596449 100644 --- a/launcher/main.cpp +++ b/launcher/main.cpp @@ -91,5 +91,7 @@ int main(int argc, char *argv[])          return 1;      case Application::Succeeded:          return 0; +    default: +        return -1;      }  } diff --git a/launcher/meta/Index.cpp b/launcher/meta/Index.cpp index 6802470d..eec1b329 100644 --- a/launcher/meta/Index.cpp +++ b/launcher/meta/Index.cpp @@ -24,7 +24,7 @@ Index::Index(QObject *parent)      : QAbstractListModel(parent)  {  } -Index::Index(const QVector<VersionListPtr> &lists, QObject *parent) +Index::Index(const QVector<VersionList::Ptr> &lists, QObject *parent)      : QAbstractListModel(parent), m_lists(lists)  {      for (int i = 0; i < m_lists.size(); ++i) @@ -41,7 +41,7 @@ QVariant Index::data(const QModelIndex &index, int role) const          return QVariant();      } -    VersionListPtr list = m_lists.at(index.row()); +    VersionList::Ptr list = m_lists.at(index.row());      switch (role)      {      case Qt::DisplayRole: @@ -81,9 +81,9 @@ bool Index::hasUid(const QString &uid) const      return m_uids.contains(uid);  } -VersionListPtr Index::get(const QString &uid) +VersionList::Ptr Index::get(const QString &uid)  { -    VersionListPtr out = m_uids.value(uid, nullptr); +    VersionList::Ptr out = m_uids.value(uid, nullptr);      if(!out)      {          out = std::make_shared<VersionList>(uid); @@ -92,7 +92,7 @@ VersionListPtr Index::get(const QString &uid)      return out;  } -VersionPtr Index::get(const QString &uid, const QString &version) +Version::Ptr Index::get(const QString &uid, const QString &version)  {      auto list = get(uid);      return list->getVersion(version); @@ -105,7 +105,7 @@ void Index::parse(const QJsonObject& obj)  void Index::merge(const std::shared_ptr<Index> &other)  { -    const QVector<VersionListPtr> lists = std::dynamic_pointer_cast<Index>(other)->m_lists; +    const QVector<VersionList::Ptr> lists = std::dynamic_pointer_cast<Index>(other)->m_lists;      // initial load, no need to merge      if (m_lists.isEmpty())      { @@ -120,7 +120,7 @@ void Index::merge(const std::shared_ptr<Index> &other)      }      else      { -        for (const VersionListPtr &list : lists) +        for (const VersionList::Ptr &list : lists)          {              if (m_uids.contains(list->uid()))              { @@ -138,7 +138,7 @@ void Index::merge(const std::shared_ptr<Index> &other)      }  } -void Index::connectVersionList(const int row, const VersionListPtr &list) +void Index::connectVersionList(const int row, const VersionList::Ptr &list)  {      connect(list.get(), &VersionList::nameChanged, this, [this, row]()      { diff --git a/launcher/meta/Index.h b/launcher/meta/Index.h index d33ab0c8..06ea09dc 100644 --- a/launcher/meta/Index.h +++ b/launcher/meta/Index.h @@ -19,20 +19,19 @@  #include <memory>  #include "BaseEntity.h" +#include "meta/VersionList.h"  class Task;  namespace Meta  { -using VersionListPtr = std::shared_ptr<class VersionList>; -using VersionPtr = std::shared_ptr<class Version>;  class Index : public QAbstractListModel, public BaseEntity  {      Q_OBJECT  public:      explicit Index(QObject *parent = nullptr); -    explicit Index(const QVector<VersionListPtr> &lists, QObject *parent = nullptr); +    explicit Index(const QVector<VersionList::Ptr> &lists, QObject *parent = nullptr);      enum      { @@ -49,21 +48,21 @@ public:      QString localFilename() const override { return "index.json"; }      // queries -    VersionListPtr get(const QString &uid); -    VersionPtr get(const QString &uid, const QString &version); +    VersionList::Ptr get(const QString &uid); +    Version::Ptr get(const QString &uid, const QString &version);      bool hasUid(const QString &uid) const; -    QVector<VersionListPtr> lists() const { return m_lists; } +    QVector<VersionList::Ptr> lists() const { return m_lists; }  public: // for usage by parsers only      void merge(const std::shared_ptr<Index> &other);      void parse(const QJsonObject &obj) override;  private: -    QVector<VersionListPtr> m_lists; -    QHash<QString, VersionListPtr> m_uids; +    QVector<VersionList::Ptr> m_lists; +    QHash<QString, VersionList::Ptr> m_uids; -    void connectVersionList(const int row, const VersionListPtr &list); +    void connectVersionList(const int row, const VersionList::Ptr &list);  };  } diff --git a/launcher/meta/JsonFormat.cpp b/launcher/meta/JsonFormat.cpp index 796da4bb..473f37d6 100644 --- a/launcher/meta/JsonFormat.cpp +++ b/launcher/meta/JsonFormat.cpp @@ -37,11 +37,11 @@ MetadataVersion currentFormatVersion()  static std::shared_ptr<Index> parseIndexInternal(const QJsonObject &obj)  {      const QVector<QJsonObject> objects = requireIsArrayOf<QJsonObject>(obj, "packages"); -    QVector<VersionListPtr> lists; +    QVector<VersionList::Ptr> lists;      lists.reserve(objects.size());      std::transform(objects.begin(), objects.end(), std::back_inserter(lists), [](const QJsonObject &obj)      { -        VersionListPtr list = std::make_shared<VersionList>(requireString(obj, "uid")); +        VersionList::Ptr list = std::make_shared<VersionList>(requireString(obj, "uid"));          list->setName(ensureString(obj, "name", QString()));          return list;      }); @@ -49,9 +49,9 @@ static std::shared_ptr<Index> parseIndexInternal(const QJsonObject &obj)  }  // Version -static VersionPtr parseCommonVersion(const QString &uid, const QJsonObject &obj) +static Version::Ptr parseCommonVersion(const QString &uid, const QJsonObject &obj)  { -    VersionPtr version = std::make_shared<Version>(uid, requireString(obj, "version")); +    Version::Ptr version = std::make_shared<Version>(uid, requireString(obj, "version"));      version->setTime(QDateTime::fromString(requireString(obj, "releaseTime"), Qt::ISODate).toMSecsSinceEpoch() / 1000);      version->setType(ensureString(obj, "type", QString()));      version->setRecommended(ensureBoolean(obj, QString("recommended"), false)); @@ -63,9 +63,9 @@ static VersionPtr parseCommonVersion(const QString &uid, const QJsonObject &obj)      return version;  } -static std::shared_ptr<Version> parseVersionInternal(const QJsonObject &obj) +static Version::Ptr parseVersionInternal(const QJsonObject &obj)  { -    VersionPtr version = parseCommonVersion(requireString(obj, "uid"), obj); +    Version::Ptr version = parseCommonVersion(requireString(obj, "uid"), obj);      version->setData(OneSixVersionFormat::versionFileFromJson(QJsonDocument(obj),                                             QString("%1/%2.json").arg(version->uid(), version->version()), @@ -74,12 +74,12 @@ static std::shared_ptr<Version> parseVersionInternal(const QJsonObject &obj)  }  // Version list / package -static std::shared_ptr<VersionList> parseVersionListInternal(const QJsonObject &obj) +static VersionList::Ptr parseVersionListInternal(const QJsonObject &obj)  {      const QString uid = requireString(obj, "uid");      const QVector<QJsonObject> versionsRaw = requireIsArrayOf<QJsonObject>(obj, "versions"); -    QVector<VersionPtr> versions; +    QVector<Version::Ptr> versions;      versions.reserve(versionsRaw.size());      std::transform(versionsRaw.begin(), versionsRaw.end(), std::back_inserter(versions), [uid](const QJsonObject &vObj)      { @@ -88,7 +88,7 @@ static std::shared_ptr<VersionList> parseVersionListInternal(const QJsonObject &          return version;      }); -    VersionListPtr list = std::make_shared<VersionList>(uid); +    VersionList::Ptr list = std::make_shared<VersionList>(uid);      list->setName(ensureString(obj, "name", QString()));      list->setVersions(versions);      return list; diff --git a/launcher/meta/Version.cpp b/launcher/meta/Version.cpp index a8dc3169..68cfa55c 100644 --- a/launcher/meta/Version.cpp +++ b/launcher/meta/Version.cpp @@ -54,7 +54,7 @@ void Meta::Version::parse(const QJsonObject& obj)      parseVersion(obj, this);  } -void Meta::Version::mergeFromList(const Meta::VersionPtr& other) +void Meta::Version::mergeFromList(const Meta::Version::Ptr& other)  {      if(other->m_providesRecommendations)      { @@ -85,7 +85,7 @@ void Meta::Version::mergeFromList(const Meta::VersionPtr& other)      }  } -void Meta::Version::merge(const VersionPtr &other) +void Meta::Version::merge(const Version::Ptr &other)  {      mergeFromList(other);      if(other->m_data) diff --git a/launcher/meta/Version.h b/launcher/meta/Version.h index dea8dc8a..7228fa36 100644 --- a/launcher/meta/Version.h +++ b/launcher/meta/Version.h @@ -30,13 +30,14 @@  namespace Meta  { -using VersionPtr = std::shared_ptr<class Version>;  class Version : public QObject, public BaseVersion, public BaseEntity  {      Q_OBJECT -public: /* con/des */ +public: +    using Ptr = std::shared_ptr<Version>; +      explicit Version(const QString &uid, const QString &version);      virtual ~Version(); @@ -78,8 +79,8 @@ public: /* con/des */          return m_data != nullptr;      } -    void merge(const VersionPtr &other); -    void mergeFromList(const VersionPtr &other); +    void merge(const Version::Ptr &other); +    void mergeFromList(const Version::Ptr &other);      void parse(const QJsonObject &obj) override;      QString localFilename() const override; @@ -113,4 +114,4 @@ private:  };  } -Q_DECLARE_METATYPE(Meta::VersionPtr) +Q_DECLARE_METATYPE(Meta::Version::Ptr) diff --git a/launcher/meta/VersionList.cpp b/launcher/meta/VersionList.cpp index f609e94c..7f001dfc 100644 --- a/launcher/meta/VersionList.cpp +++ b/launcher/meta/VersionList.cpp @@ -40,7 +40,7 @@ bool VersionList::isLoaded()      return BaseEntity::isLoaded();  } -const BaseVersionPtr VersionList::at(int i) const +const BaseVersion::Ptr VersionList::at(int i) const  {      return m_versions.at(i);  } @@ -52,7 +52,7 @@ int VersionList::count() const  void VersionList::sortVersions()  {      beginResetModel(); -    std::sort(m_versions.begin(), m_versions.end(), [](const VersionPtr &a, const VersionPtr &b) +    std::sort(m_versions.begin(), m_versions.end(), [](const Version::Ptr &a, const Version::Ptr &b)      {          return *a.get() < *b.get();      }); @@ -66,7 +66,7 @@ QVariant VersionList::data(const QModelIndex &index, int role) const          return QVariant();      } -    VersionPtr version = m_versions.at(index.row()); +    Version::Ptr version = m_versions.at(index.row());      switch (role)      { @@ -129,9 +129,9 @@ QString VersionList::humanReadable() const      return m_name.isEmpty() ? m_uid : m_name;  } -VersionPtr VersionList::getVersion(const QString &version) +Version::Ptr VersionList::getVersion(const QString &version)  { -    VersionPtr out = m_lookup.value(version, nullptr); +    Version::Ptr out = m_lookup.value(version, nullptr);      if(!out)      {          out = std::make_shared<Version>(m_uid, version); @@ -143,7 +143,7 @@ VersionPtr VersionList::getVersion(const QString &version)  bool VersionList::hasVersion(QString version) const  {      auto ver = std::find_if(m_versions.constBegin(), m_versions.constEnd(), -            [&](Meta::VersionPtr const& a){ return a->version() == version; }); +            [&](Meta::Version::Ptr const& a){ return a->version() == version; });      return (ver != m_versions.constEnd());  } @@ -153,11 +153,11 @@ void VersionList::setName(const QString &name)      emit nameChanged(name);  } -void VersionList::setVersions(const QVector<VersionPtr> &versions) +void VersionList::setVersions(const QVector<Version::Ptr> &versions)  {      beginResetModel();      m_versions = versions; -    std::sort(m_versions.begin(), m_versions.end(), [](const VersionPtr &a, const VersionPtr &b) +    std::sort(m_versions.begin(), m_versions.end(), [](const Version::Ptr &a, const Version::Ptr &b)      {          return a->rawTime() > b->rawTime();      }); @@ -168,7 +168,7 @@ void VersionList::setVersions(const QVector<VersionPtr> &versions)      }      // FIXME: this is dumb, we have 'recommended' as part of the metadata already... -    auto recommendedIt = std::find_if(m_versions.constBegin(), m_versions.constEnd(), [](const VersionPtr &ptr) { return ptr->type() == "release"; }); +    auto recommendedIt = std::find_if(m_versions.constBegin(), m_versions.constEnd(), [](const Version::Ptr &ptr) { return ptr->type() == "release"; });      m_recommended = recommendedIt == m_versions.constEnd() ? nullptr : *recommendedIt;      endResetModel();  } @@ -179,7 +179,7 @@ void VersionList::parse(const QJsonObject& obj)  }  // FIXME: this is dumb, we have 'recommended' as part of the metadata already... -static const Meta::VersionPtr &getBetterVersion(const Meta::VersionPtr &a, const Meta::VersionPtr &b) +static const Meta::Version::Ptr &getBetterVersion(const Meta::Version::Ptr &a, const Meta::Version::Ptr &b)  {      if(!a)          return b; @@ -194,7 +194,7 @@ static const Meta::VersionPtr &getBetterVersion(const Meta::VersionPtr &a, const      return (a->type() == "release" ? a : b);  } -void VersionList::mergeFromIndex(const VersionListPtr &other) +void VersionList::mergeFromIndex(const VersionList::Ptr &other)  {      if (m_name != other->m_name)      { @@ -202,7 +202,7 @@ void VersionList::mergeFromIndex(const VersionListPtr &other)      }  } -void VersionList::merge(const VersionListPtr &other) +void VersionList::merge(const VersionList::Ptr &other)  {      if (m_name != other->m_name)      { @@ -216,7 +216,7 @@ void VersionList::merge(const VersionListPtr &other)      {          qWarning() << "Empty list loaded ...";      } -    for (const VersionPtr &version : other->m_versions) +    for (const Version::Ptr &version : other->m_versions)      {          // we already have the version. merge the contents          if (m_lookup.contains(version->version())) @@ -235,7 +235,7 @@ void VersionList::merge(const VersionListPtr &other)      endResetModel();  } -void VersionList::setupAddedVersion(const int row, const VersionPtr &version) +void VersionList::setupAddedVersion(const int row, const Version::Ptr &version)  {      // FIXME: do not disconnect from everythin, disconnect only the lambdas here      version->disconnect(); @@ -244,7 +244,7 @@ void VersionList::setupAddedVersion(const int row, const VersionPtr &version)      connect(version.get(), &Version::typeChanged, this, [this, row]() { emit dataChanged(index(row), index(row), QVector<int>() << TypeRole); });  } -BaseVersionPtr VersionList::getRecommended() const +BaseVersion::Ptr VersionList::getRecommended() const  {      return m_recommended;  } diff --git a/launcher/meta/VersionList.h b/launcher/meta/VersionList.h index a6db2fd7..a4d5603d 100644 --- a/launcher/meta/VersionList.h +++ b/launcher/meta/VersionList.h @@ -20,10 +20,10 @@  #include <QJsonObject>  #include <memory> +#include "meta/Version.h" +  namespace Meta  { -using VersionPtr = std::shared_ptr<class Version>; -using VersionListPtr = std::shared_ptr<class VersionList>;  class VersionList : public BaseVersionList, public BaseEntity  { @@ -33,6 +33,8 @@ class VersionList : public BaseVersionList, public BaseEntity  public:      explicit VersionList(const QString &uid, QObject *parent = nullptr); +    using Ptr = std::shared_ptr<VersionList>; +      enum Roles      {          UidRole = Qt::UserRole + 100, @@ -43,11 +45,11 @@ public:      Task::Ptr getLoadTask() override;      bool isLoaded() override; -    const BaseVersionPtr at(int i) const override; +    const BaseVersion::Ptr at(int i) const override;      int count() const override;      void sortVersions() override; -    BaseVersionPtr getRecommended() const override; +    BaseVersion::Ptr getRecommended() const override;      QVariant data(const QModelIndex &index, int role) const override;      RoleList providesRoles() const override; @@ -65,38 +67,38 @@ public:      }      QString humanReadable() const; -    VersionPtr getVersion(const QString &version); +    Version::Ptr getVersion(const QString &version);      bool hasVersion(QString version) const; -    QVector<VersionPtr> versions() const +    QVector<Version::Ptr> versions() const      {          return m_versions;      }  public: // for usage only by parsers      void setName(const QString &name); -    void setVersions(const QVector<VersionPtr> &versions); -    void merge(const VersionListPtr &other); -    void mergeFromIndex(const VersionListPtr &other); +    void setVersions(const QVector<Version::Ptr> &versions); +    void merge(const VersionList::Ptr &other); +    void mergeFromIndex(const VersionList::Ptr &other);      void parse(const QJsonObject &obj) override;  signals:      void nameChanged(const QString &name);  protected slots: -    void updateListData(QList<BaseVersionPtr>) override +    void updateListData(QList<BaseVersion::Ptr>) override      {      }  private: -    QVector<VersionPtr> m_versions; -    QHash<QString, VersionPtr> m_lookup; +    QVector<Version::Ptr> m_versions; +    QHash<QString, Version::Ptr> m_lookup;      QString m_uid;      QString m_name; -    VersionPtr m_recommended; +    Version::Ptr m_recommended; -    void setupAddedVersion(const int row, const VersionPtr &version); +    void setupAddedVersion(const int row, const Version::Ptr &version);  };  } -Q_DECLARE_METATYPE(Meta::VersionListPtr) +Q_DECLARE_METATYPE(Meta::VersionList::Ptr) diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 3a820951..de22b365 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -43,7 +43,6 @@  #include "settings/SettingsObject.h"  #include "Application.h" -#include "MMCStrings.h"  #include "pathmatcher/RegexpMatcher.h"  #include "pathmatcher/MultiMatcher.h"  #include "FileSystem.h" diff --git a/launcher/minecraft/VanillaInstanceCreationTask.cpp b/launcher/minecraft/VanillaInstanceCreationTask.cpp index c45daa9a..0bb92e87 100644 --- a/launcher/minecraft/VanillaInstanceCreationTask.cpp +++ b/launcher/minecraft/VanillaInstanceCreationTask.cpp @@ -7,7 +7,7 @@  #include "minecraft/PackProfile.h"  #include "settings/INISettingsObject.h" -VanillaCreationTask::VanillaCreationTask(BaseVersionPtr version, QString loader, BaseVersionPtr loader_version) +VanillaCreationTask::VanillaCreationTask(BaseVersion::Ptr version, QString loader, BaseVersion::Ptr loader_version)      : InstanceCreationTask(), m_version(std::move(version)), m_using_loader(true), m_loader(std::move(loader)), m_loader_version(std::move(loader_version))  {} diff --git a/launcher/minecraft/VanillaInstanceCreationTask.h b/launcher/minecraft/VanillaInstanceCreationTask.h index 7a37bbd6..d1b81682 100644 --- a/launcher/minecraft/VanillaInstanceCreationTask.h +++ b/launcher/minecraft/VanillaInstanceCreationTask.h @@ -7,16 +7,16 @@  class VanillaCreationTask final : public InstanceCreationTask {      Q_OBJECT     public: -    VanillaCreationTask(BaseVersionPtr version) : InstanceCreationTask(), m_version(std::move(version)) {} -    VanillaCreationTask(BaseVersionPtr version, QString loader, BaseVersionPtr loader_version); +    VanillaCreationTask(BaseVersion::Ptr version) : InstanceCreationTask(), m_version(std::move(version)) {} +    VanillaCreationTask(BaseVersion::Ptr version, QString loader, BaseVersion::Ptr loader_version);      bool createInstance() override;     private:      // Version to update to / create of the instance. -    BaseVersionPtr m_version; +    BaseVersion::Ptr m_version;      bool m_using_loader = false;      QString m_loader; -    BaseVersionPtr m_loader_version; +    BaseVersion::Ptr m_loader_version;  }; diff --git a/launcher/minecraft/launch/VerifyJavaInstall.cpp b/launcher/minecraft/launch/VerifyJavaInstall.cpp index 99809f82..6ae666b4 100644 --- a/launcher/minecraft/launch/VerifyJavaInstall.cpp +++ b/launcher/minecraft/launch/VerifyJavaInstall.cpp @@ -71,5 +71,7 @@ void VerifyJavaInstall::executeTask() {      {          emit logLine(tr("Java version %1").arg(major), MessageLevel::Error);      } +    emit logLine(tr("Go to instance Java settings to change your Java version or disable the Java compatibility check if you know what you're doing."), MessageLevel::Error); +      emitFailed(QString("Incompatible Java major version"));  } diff --git a/launcher/minecraft/mod/tasks/LocalModUpdateTask.cpp b/launcher/minecraft/mod/tasks/LocalModUpdateTask.cpp index 4b878918..cc4e252c 100644 --- a/launcher/minecraft/mod/tasks/LocalModUpdateTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalModUpdateTask.cpp @@ -36,7 +36,7 @@ LocalModUpdateTask::LocalModUpdateTask(QDir index_dir, ModPlatform::IndexedPack&      }  #ifdef Q_OS_WIN32 -    SetFileAttributesA(index_dir.path().toStdString().c_str(), FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED); +    SetFileAttributesW(index_dir.path().toStdWString().c_str(), FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED);  #endif  } diff --git a/launcher/modplatform/ModAPI.h b/launcher/modplatform/ModAPI.h index c7408835..703de143 100644 --- a/launcher/modplatform/ModAPI.h +++ b/launcher/modplatform/ModAPI.h @@ -39,7 +39,7 @@  #include <QList>  #include <list> -#include "Version.h" +#include "../Version.h"  #include "net/NetJob.h"  namespace ModPlatform { diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp index 68d75943..291ad916 100644 --- a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp +++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp @@ -58,7 +58,7 @@  namespace ATLauncher { -static Meta::VersionPtr getComponentVersion(const QString& uid, const QString& version); +static Meta::Version::Ptr getComponentVersion(const QString& uid, const QString& version);  PackInstallTask::PackInstallTask(UserInteractionSupport *support, QString packName, QString version, InstallMode installMode)  { @@ -1037,7 +1037,7 @@ void PackInstallTask::install()      emitSucceeded();  } -static Meta::VersionPtr getComponentVersion(const QString& uid, const QString& version) +static Meta::Version::Ptr getComponentVersion(const QString& uid, const QString& version)  {      auto vlist = APPLICATION->metadataIndex()->get(uid);      if (!vlist) diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.h b/launcher/modplatform/atlauncher/ATLPackInstallTask.h index 78cd87fb..90e25ae2 100644 --- a/launcher/modplatform/atlauncher/ATLPackInstallTask.h +++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.h @@ -68,7 +68,7 @@ public:       * Requests a user interaction to select a component version from a given version list       * and constrained to a given Minecraft version.       */ -    virtual QString chooseVersion(Meta::VersionListPtr vlist, QString minecraftVersion) = 0; +    virtual QString chooseVersion(Meta::VersionList::Ptr vlist, QString minecraftVersion) = 0;      /**       * Requests a user interaction to display a message. @@ -137,8 +137,8 @@ private:      QString archivePath;      QStringList jarmods; -    Meta::VersionPtr minecraftVersion; -    QMap<QString, Meta::VersionPtr> componentsToInstall; +    Meta::Version::Ptr minecraftVersion; +    QMap<QString, Meta::Version::Ptr> componentsToInstall;      QFuture<std::optional<QStringList>> m_extractFuture;      QFutureWatcher<std::optional<QStringList>> m_extractFutureWatcher; diff --git a/launcher/modplatform/helpers/HashUtils.cpp b/launcher/modplatform/helpers/HashUtils.cpp index bf53aa0e..f1e4759e 100644 --- a/launcher/modplatform/helpers/HashUtils.cpp +++ b/launcher/modplatform/helpers/HashUtils.cpp @@ -4,6 +4,7 @@  #include <QFile>  #include "FileSystem.h" +#include "StringUtils.h"  #include <MurmurHash2.h> @@ -78,7 +79,7 @@ void FlameHasher::executeTask()      // CF-specific      auto should_filter_out = [](char c) { return (c == 9 || c == 10 || c == 13 || c == 32); }; -    std::ifstream file_stream(m_path.toStdString(), std::ifstream::binary); +    std::ifstream file_stream(StringUtils::toStdString(m_path), std::ifstream::binary);      // TODO: This is very heavy work, but apparently QtConcurrent can't use move semantics, so we can't boop this to another thread.      // How do we make this non-blocking then?      m_hash = QString::number(MurmurHash2(std::move(file_stream), 4 * MiB, should_filter_out)); diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp index a4620df9..96f54067 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp @@ -140,7 +140,7 @@ auto loadIndexedVersion(QJsonObject &obj) -> ModpackVersion      for (auto file_iter : files) {          File indexed_file;          auto parent = Json::requireObject(file_iter); -        auto is_primary = Json::ensureBoolean(parent, "primary", false); +        auto is_primary = Json::ensureBoolean(parent, (const QString)QStringLiteral("primary"), false);          if (!is_primary) {              auto filename = Json::ensureString(parent, "filename");              // Checking suffix here is fine because it's the response from Modrinth, diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index b1fe963e..0ed29311 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -22,10 +22,14 @@  #include <QDir>  #include <QObject> -#include <toml++/toml.h> +#include "FileSystem.h" +#include "StringUtils.h" +  #include "minecraft/mod/Mod.h"  #include "modplatform/ModIndex.h" +#include <toml++/toml.h> +  namespace Packwiz {  auto getRealIndexName(QDir& index_dir, QString normalized_fname, bool should_find_match) -> QString @@ -63,22 +67,22 @@ static inline auto indexFileName(QString const& mod_slug) -> QString  static ModPlatform::ProviderCapabilities ProviderCaps;  // Helper functions for extracting data from the TOML file -auto stringEntry(toml::table table, const std::string entry_name) -> QString +auto stringEntry(toml::table table, QString entry_name) -> QString  { -    auto node = table[entry_name]; +    auto node = table[StringUtils::toStdString(entry_name)];      if (!node) { -        qCritical() << QString::fromStdString("Failed to read str property '" + entry_name + "' in mod metadata."); +        qCritical() << "Failed to read str property '" + entry_name + "' in mod metadata.";          return {};      } -    return QString::fromStdString(node.value_or("")); +    return node.value_or("");  } -auto intEntry(toml::table table, const std::string entry_name) -> int +auto intEntry(toml::table table, QString entry_name) -> int  { -    auto node = table[entry_name]; +    auto node = table[StringUtils::toStdString(entry_name)];      if (!node) { -        qCritical() << QString::fromStdString("Failed to read int property '" + entry_name + "' in mod metadata."); +        qCritical() << "Failed to read int property '" + entry_name + "' in mod metadata.";          return {};      } @@ -145,6 +149,8 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod)      // they want to do!      if (index_file.exists()) {          index_file.remove(); +    } else { +        FS::ensureFilePathExists(index_file.fileName());      }      if (!index_file.open(QIODevice::ReadWrite)) { @@ -228,14 +234,14 @@ auto V1::getIndexForMod(QDir& index_dir, QString slug) -> Mod      toml::table table;  #if TOML_EXCEPTIONS      try { -        table = toml::parse_file(index_dir.absoluteFilePath(real_fname).toStdString()); +        table = toml::parse_file(StringUtils::toStdString(index_dir.absoluteFilePath(real_fname)));      } catch (const toml::parse_error& err) {          qWarning() << QString("Could not open file %1!").arg(normalized_fname);          qWarning() << "Reason: " << QString(err.what());          return {};      }  #else -    table = toml::parse_file(index_dir.absoluteFilePath(real_fname).toStdString()); +    table = toml::parse_file(StringUtils::toStdString(index_dir.absoluteFilePath(real_fname)));      if (!table) {          qWarning() << QString("Could not open file %1!").arg(normalized_fname);          qWarning() << "Reason: " << QString(table.error().what()); diff --git a/launcher/modplatform/packwiz/Packwiz.h b/launcher/modplatform/packwiz/Packwiz.h index 3ec80377..9754e5c4 100644 --- a/launcher/modplatform/packwiz/Packwiz.h +++ b/launcher/modplatform/packwiz/Packwiz.h @@ -24,7 +24,6 @@  #include <QUrl>  #include <QVariant> -struct toml_table_t;  class QDir;  // Mod from launcher/minecraft/mod/Mod.h @@ -34,9 +33,6 @@ namespace Packwiz {  auto getRealIndexName(QDir& index_dir, QString normalized_index_name, bool should_match = false) -> QString; -auto stringEntry(toml_table_t* parent, const char* entry_name) -> QString; -auto intEntry(toml_table_t* parent, const char* entry_name) -> int; -  class V1 {     public:      struct Mod { diff --git a/launcher/net/HttpMetaCache.cpp b/launcher/net/HttpMetaCache.cpp index 42198b71..e242dcf4 100644 --- a/launcher/net/HttpMetaCache.cpp +++ b/launcher/net/HttpMetaCache.cpp @@ -242,7 +242,7 @@ void HttpMetaCache::Load()          foo->local_changed_timestamp = Json::ensureDouble(element_obj, "last_changed_timestamp");          foo->remote_changed_timestamp = Json::ensureString(element_obj, "remote_changed_timestamp"); -        foo->makeEternal(Json::ensureBoolean(element_obj, "eternal", false)); +        foo->makeEternal(Json::ensureBoolean(element_obj, (const QString)QStringLiteral("eternal"), false));          if (!foo->isEternal()) {              foo->current_age = Json::ensureDouble(element_obj, "current_age");              foo->max_age = Json::ensureDouble(element_obj, "max_age"); diff --git a/launcher/resources/backgrounds/backgrounds.qrc b/launcher/resources/backgrounds/backgrounds.qrc index 52921512..e55faf15 100644 --- a/launcher/resources/backgrounds/backgrounds.qrc +++ b/launcher/resources/backgrounds/backgrounds.qrc @@ -1,8 +1,17 @@  <!DOCTYPE RCC>  <RCC version="1.0">      <qresource prefix="/backgrounds"> -        <file alias="kitteh">catbgrnd2.png</file> -        <file alias="catmas">catmas.png</file> -        <file alias="cattiversary">cattiversary.png</file> +        <file alias="kitteh">kitteh.png</file> +        <file alias="kitteh-xmas">kitteh-xmas.png</file> +        <file alias="kitteh-bday">kitteh-bday.png</file> +        <file alias="kitteh-spooky">kitteh-spooky.png</file> +        <file alias="rory">rory.png</file> +        <file alias="rory-xmas">rory-xmas.png</file> +        <file alias="rory-bday">rory-bday.png</file> +        <file alias="rory-spooky">rory-spooky.png</file> +        <file alias="rory-flat">rory-flat.png</file> +        <file alias="rory-flat-xmas">rory-flat-xmas.png</file> +        <file alias="rory-flat-bday">rory-flat-bday.png</file> +        <file alias="rory-flat-spooky">rory-flat-spooky.png</file>      </qresource>  </RCC> diff --git a/launcher/resources/backgrounds/cattiversary.png b/launcher/resources/backgrounds/kitteh-bday.pngBinary files differ index 09a36566..09a36566 100644 --- a/launcher/resources/backgrounds/cattiversary.png +++ b/launcher/resources/backgrounds/kitteh-bday.png diff --git a/launcher/resources/backgrounds/kitteh-spooky.png b/launcher/resources/backgrounds/kitteh-spooky.pngBinary files differ new file mode 100644 index 00000000..deb0bebb --- /dev/null +++ b/launcher/resources/backgrounds/kitteh-spooky.png diff --git a/launcher/resources/backgrounds/catmas.png b/launcher/resources/backgrounds/kitteh-xmas.pngBinary files differ index 8bdb1d5c..8bdb1d5c 100644 --- a/launcher/resources/backgrounds/catmas.png +++ b/launcher/resources/backgrounds/kitteh-xmas.png diff --git a/launcher/resources/backgrounds/catbgrnd2.png b/launcher/resources/backgrounds/kitteh.pngBinary files differ index e9de7f27..e9de7f27 100644 --- a/launcher/resources/backgrounds/catbgrnd2.png +++ b/launcher/resources/backgrounds/kitteh.png diff --git a/launcher/resources/backgrounds/rory-bday.png b/launcher/resources/backgrounds/rory-bday.pngBinary files differ new file mode 100644 index 00000000..66b88094 --- /dev/null +++ b/launcher/resources/backgrounds/rory-bday.png diff --git a/launcher/resources/backgrounds/rory-flat-bday.png b/launcher/resources/backgrounds/rory-flat-bday.pngBinary files differ new file mode 100644 index 00000000..8a6e366d --- /dev/null +++ b/launcher/resources/backgrounds/rory-flat-bday.png diff --git a/launcher/resources/backgrounds/rory-flat-spooky.png b/launcher/resources/backgrounds/rory-flat-spooky.pngBinary files differ new file mode 100644 index 00000000..6360c612 --- /dev/null +++ b/launcher/resources/backgrounds/rory-flat-spooky.png diff --git a/launcher/resources/backgrounds/rory-flat-xmas.png b/launcher/resources/backgrounds/rory-flat-xmas.pngBinary files differ new file mode 100644 index 00000000..96c3ae38 --- /dev/null +++ b/launcher/resources/backgrounds/rory-flat-xmas.png diff --git a/launcher/resources/backgrounds/rory-flat.png b/launcher/resources/backgrounds/rory-flat.pngBinary files differ new file mode 100644 index 00000000..ccec0662 --- /dev/null +++ b/launcher/resources/backgrounds/rory-flat.png diff --git a/launcher/resources/backgrounds/rory-spooky.png b/launcher/resources/backgrounds/rory-spooky.pngBinary files differ new file mode 100644 index 00000000..a727619b --- /dev/null +++ b/launcher/resources/backgrounds/rory-spooky.png diff --git a/launcher/resources/backgrounds/rory-xmas.png b/launcher/resources/backgrounds/rory-xmas.pngBinary files differ new file mode 100644 index 00000000..107feb78 --- /dev/null +++ b/launcher/resources/backgrounds/rory-xmas.png diff --git a/launcher/resources/backgrounds/rory.png b/launcher/resources/backgrounds/rory.pngBinary files differ new file mode 100644 index 00000000..577f4dce --- /dev/null +++ b/launcher/resources/backgrounds/rory.png diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index eda234df..85b00b67 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -262,6 +262,8 @@ public:      TranslatedAction actionNoAccountsAdded;      TranslatedAction actionNoDefaultAccount; +    TranslatedAction actionLockToolbars; +      QVector<TranslatedToolButton *> all_toolbuttons;      QWidget *centralWidget = nullptr; @@ -420,6 +422,12 @@ public:          actionManageAccounts->setCheckable(false);          actionManageAccounts->setIcon(APPLICATION->getThemedIcon("accounts"));          all_actions.append(&actionManageAccounts); + +        actionLockToolbars = TranslatedAction(MainWindow); +        actionLockToolbars->setObjectName(QStringLiteral("actionLockToolbars")); +        actionLockToolbars.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Lock Toolbars")); +        actionLockToolbars->setCheckable(true); +        all_actions.append(&actionLockToolbars);      }      void createMainToolbar(QMainWindow *MainWindow) @@ -427,7 +435,6 @@ public:          mainToolBar = TranslatedToolbar(MainWindow);          mainToolBar->setVisible(menuBar->isNativeMenuBar() || !APPLICATION->settings()->get("MenuBarInsteadOfToolBar").toBool());          mainToolBar->setObjectName(QStringLiteral("mainToolBar")); -        mainToolBar->setMovable(true);          mainToolBar->setAllowedAreas(Qt::TopToolBarArea | Qt::BottomToolBarArea);          mainToolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);          mainToolBar->setFloatable(false); @@ -524,6 +531,8 @@ public:          viewMenu->addAction(actionCAT);          viewMenu->addSeparator(); +        viewMenu->addAction(actionLockToolbars); +          menuBar->addMenu(foldersMenu);          profileMenu = menuBar->addMenu(tr("&Accounts")); @@ -601,7 +610,6 @@ public:      {          newsToolBar = TranslatedToolbar(MainWindow);          newsToolBar->setObjectName(QStringLiteral("newsToolBar")); -        newsToolBar->setMovable(true);          newsToolBar->setAllowedAreas(Qt::TopToolBarArea | Qt::BottomToolBarArea);          newsToolBar->setIconSize(QSize(16, 16));          newsToolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); @@ -736,7 +744,6 @@ public:          instanceToolBar->setObjectName(QStringLiteral("instanceToolBar"));          // disabled until we have an instance selected          instanceToolBar->setEnabled(false); -        instanceToolBar->setMovable(true);          // Qt doesn't like vertical moving toolbars, so we have to force them...          // See https://github.com/PolyMC/PolyMC/issues/493          connect(instanceToolBar, &QToolBar::orientationChanged, [=](Qt::Orientation){ instanceToolBar->setOrientation(Qt::Vertical); }); @@ -918,6 +925,14 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new MainWindow          connect(ui->actionCAT.operator->(), SIGNAL(toggled(bool)), SLOT(onCatToggled(bool)));          setCatBackground(cat_enable);      } + +    // Lock toolbars +    { +        bool toolbarsLocked = APPLICATION->settings()->get("ToolbarsLocked").toBool(); +        ui->actionLockToolbars->setChecked(toolbarsLocked); +        connect(ui->actionLockToolbars, &QAction::toggled, this, &MainWindow::lockToolbars); +        lockToolbars(toolbarsLocked); +    }      // start instance when double-clicked      connect(view, &InstanceView::activated, this, &MainWindow::instanceActivated); @@ -1073,8 +1088,19 @@ QMenu * MainWindow::createPopupMenu()  {      QMenu* filteredMenu = QMainWindow::createPopupMenu();      filteredMenu->removeAction( ui->mainToolBar->toggleViewAction() ); + +    filteredMenu->addAction(ui->actionLockToolbars); +      return filteredMenu;  } +void MainWindow::lockToolbars(bool state) +{ +    ui->mainToolBar->setMovable(!state); +    ui->instanceToolBar->setMovable(!state); +    ui->newsToolBar->setMovable(!state); +    APPLICATION->settings()->set("ToolbarsLocked", state); +} +  void MainWindow::konamiTriggered()  { @@ -1560,15 +1586,14 @@ void MainWindow::setCatBackground(bool enabled)          QDateTime now = QDateTime::currentDateTime();          QDateTime birthday(QDate(now.date().year(), 11, 30), QTime(0, 0));          QDateTime xmas(QDate(now.date().year(), 12, 25), QTime(0, 0)); -        QString cat; -        if(non_stupid_abs(now.daysTo(xmas)) <= 4) { -            cat = "catmas"; -        } -        else if (non_stupid_abs(now.daysTo(birthday)) <= 12) { -            cat = "cattiversary"; -        } -        else { -            cat = "kitteh"; +        QDateTime halloween(QDate(now.date().year(), 10, 31), QTime(0, 0)); +        QString cat = APPLICATION->settings()->get("BackgroundCat").toString(); +        if (non_stupid_abs(now.daysTo(xmas)) <= 4) { +            cat += "-xmas"; +        } else if (non_stupid_abs(now.daysTo(halloween)) <= 4) { +            cat += "-spooky"; +        } else if (non_stupid_abs(now.daysTo(birthday)) <= 12) { +            cat += "-bday";          }          view->setStyleSheet(QString(R"(  InstanceView @@ -1576,10 +1601,11 @@ InstanceView      background-image: url(:/backgrounds/%1);      background-attachment: fixed;      background-clip: padding; -    background-position: top right; +    background-position: bottom left;      background-repeat: none;      background-color:palette(base); -})").arg(cat)); +})") +                                .arg(cat));      }      else      { @@ -1625,7 +1651,7 @@ void MainWindow::on_actionCopyInstance_triggered()      if (!copyInstDlg.exec())          return; -    auto copyTask = new InstanceCopyTask(m_selectedInstance, copyInstDlg.shouldCopySaves(), copyInstDlg.shouldKeepPlaytime()); +    auto copyTask = new InstanceCopyTask(m_selectedInstance, copyInstDlg.getChosenOptions());      copyTask->setName(copyInstDlg.instName());      copyTask->setGroup(copyInstDlg.instGroup());      copyTask->setIcon(copyInstDlg.iconKey()); @@ -1900,6 +1926,7 @@ void MainWindow::on_actionReportBug_triggered()  void MainWindow::on_actionClearMetadata_triggered()  {      APPLICATION->metacache()->evictAll(); +    APPLICATION->metacache()->SaveNow();  }  void MainWindow::on_actionOpenWiki_triggered() diff --git a/launcher/ui/MainWindow.h b/launcher/ui/MainWindow.h index cb8cb4aa..f9d1f1c7 100644 --- a/launcher/ui/MainWindow.h +++ b/launcher/ui/MainWindow.h @@ -203,6 +203,8 @@ private slots:      void globalSettingsClosed(); +    void lockToolbars(bool); +  #ifndef Q_OS_MAC      void keyReleaseEvent(QKeyEvent *event) override;  #endif diff --git a/launcher/ui/dialogs/AboutDialog.cpp b/launcher/ui/dialogs/AboutDialog.cpp index 52d6baef..a36e4a3d 100644 --- a/launcher/ui/dialogs/AboutDialog.cpp +++ b/launcher/ui/dialogs/AboutDialog.cpp @@ -73,17 +73,12 @@ QString getCreditsHtml()      stream << "<h3>" << QObject::tr("%1 Developers", "About Credits").arg(BuildConfig.LAUNCHER_DISPLAYNAME) << "</h3>\n";      stream << QString("<p>Sefa Eyeoglu (Scrumplex) %1</p>\n")   .arg(getWebsite("https://scrumplex.net"));      stream << QString("<p>dada513 %1</p>\n")                    .arg(getGitHub("dada513")); -    stream << QString("<p>txtsd %1</p>\n")                      .arg(getGitHub("txtsd")); +    stream << QString("<p>txtsd %1</p>\n")                      .arg(getWebsite("https://ihavea.quest"));      stream << QString("<p>timoreo %1</p>\n")                    .arg(getGitHub("timoreo22"));      stream << QString("<p>Ezekiel Smith (ZekeSmith) %1</p>\n")  .arg(getGitHub("ZekeSmith"));      stream << QString("<p>cozyGalvinism %1</p>\n")              .arg(getGitHub("cozyGalvinism")); -    stream << "<br />\n"; - -    //: %1 is the name of the launcher, determined at build time, e.g. "Prism Launcher Contributors" -    stream << "<h3>" << QObject::tr("%1 Contributors", "About Credits").arg(BuildConfig.LAUNCHER_DISPLAYNAME) << "</h3>\n";      stream << QString("<p>DioEgizio %1</p>\n")      .arg(getGitHub("DioEgizio"));      stream << QString("<p>flowln %1</p>\n")         .arg(getGitHub("flowln")); -    stream << QString("<p>swirl %1</p>\n")          .arg(getWebsite("https://swurl.xyz/"));      stream << "<br />\n";      // TODO: possibly retrieve from git history at build time? diff --git a/launcher/ui/dialogs/CopyInstanceDialog.cpp b/launcher/ui/dialogs/CopyInstanceDialog.cpp index 9ec341bc..3f5122f6 100644 --- a/launcher/ui/dialogs/CopyInstanceDialog.cpp +++ b/launcher/ui/dialogs/CopyInstanceDialog.cpp @@ -44,7 +44,6 @@  #include "BaseVersion.h"  #include "icons/IconList.h" -#include "tasks/Task.h"  #include "BaseInstance.h"  #include "InstanceList.h" @@ -78,8 +77,14 @@ CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)      }      ui->groupBox->setCurrentIndex(index);      ui->groupBox->lineEdit()->setPlaceholderText(tr("No group")); -    ui->copySavesCheckbox->setChecked(m_copySaves); -    ui->keepPlaytimeCheckbox->setChecked(m_keepPlaytime); +    ui->copySavesCheckbox->setChecked(m_selectedOptions.isCopySavesEnabled()); +    ui->keepPlaytimeCheckbox->setChecked(m_selectedOptions.isKeepPlaytimeEnabled()); +    ui->copyGameOptionsCheckbox->setChecked(m_selectedOptions.isCopyGameOptionsEnabled()); +    ui->copyResPacksCheckbox->setChecked(m_selectedOptions.isCopyResourcePacksEnabled()); +    ui->copyShaderPacksCheckbox->setChecked(m_selectedOptions.isCopyShaderPacksEnabled()); +    ui->copyServersCheckbox->setChecked(m_selectedOptions.isCopyServersEnabled()); +    ui->copyModsCheckbox->setChecked(m_selectedOptions.isCopyModsEnabled()); +    ui->copyScreenshotsCheckbox->setChecked(m_selectedOptions.isCopyScreenshotsEnabled());  }  CopyInstanceDialog::~CopyInstanceDialog() @@ -117,6 +122,31 @@ QString CopyInstanceDialog::instGroup() const      return ui->groupBox->currentText();  } +const InstanceCopyPrefs& CopyInstanceDialog::getChosenOptions() const +{ +    return m_selectedOptions; +} + +void CopyInstanceDialog::checkAllCheckboxes(const bool& b) +{ +    ui->keepPlaytimeCheckbox->setChecked(b); +    ui->copySavesCheckbox->setChecked(b); +    ui->copyGameOptionsCheckbox->setChecked(b); +    ui->copyResPacksCheckbox->setChecked(b); +    ui->copyShaderPacksCheckbox->setChecked(b); +    ui->copyServersCheckbox->setChecked(b); +    ui->copyModsCheckbox->setChecked(b); +    ui->copyScreenshotsCheckbox->setChecked(b); +} + +// Check the "Select all" checkbox if all options are already selected: +void CopyInstanceDialog::updateSelectAllCheckbox() +{ +    ui->selectAllCheckbox->blockSignals(true); +    ui->selectAllCheckbox->setChecked(m_selectedOptions.allTrue()); +    ui->selectAllCheckbox->blockSignals(false); +} +  void CopyInstanceDialog::on_iconButton_clicked()  {      IconPickerDialog dlg(this); @@ -129,42 +159,64 @@ void CopyInstanceDialog::on_iconButton_clicked()      }  } +  void CopyInstanceDialog::on_instNameTextBox_textChanged(const QString &arg1)  {      updateDialogState();  } -bool CopyInstanceDialog::shouldCopySaves() const +void CopyInstanceDialog::on_selectAllCheckbox_stateChanged(int state)  { -    return m_copySaves; +    bool checked; +    checked = (state == Qt::Checked); +    checkAllCheckboxes(checked);  }  void CopyInstanceDialog::on_copySavesCheckbox_stateChanged(int state)  { -    if(state == Qt::Unchecked) -    { -        m_copySaves = false; -    } -    else if(state == Qt::Checked) -    { -        m_copySaves = true; -    } +    m_selectedOptions.enableCopySaves(state == Qt::Checked); +    updateSelectAllCheckbox();  } -bool CopyInstanceDialog::shouldKeepPlaytime() const + +void CopyInstanceDialog::on_keepPlaytimeCheckbox_stateChanged(int state)  { -    return m_keepPlaytime; +    m_selectedOptions.enableKeepPlaytime(state == Qt::Checked); +    updateSelectAllCheckbox();  } +void CopyInstanceDialog::on_copyGameOptionsCheckbox_stateChanged(int state) +{ +    m_selectedOptions.enableCopyGameOptions(state == Qt::Checked); +    updateSelectAllCheckbox(); +} -void CopyInstanceDialog::on_keepPlaytimeCheckbox_stateChanged(int state) +void CopyInstanceDialog::on_copyResPacksCheckbox_stateChanged(int state)  { -    if(state == Qt::Unchecked) -    { -        m_keepPlaytime = false; -    } -    else if(state == Qt::Checked) -    { -        m_keepPlaytime = true; -    } +    m_selectedOptions.enableCopyResourcePacks(state == Qt::Checked); +    updateSelectAllCheckbox(); +} + +void CopyInstanceDialog::on_copyShaderPacksCheckbox_stateChanged(int state) +{ +    m_selectedOptions.enableCopyShaderPacks(state == Qt::Checked); +    updateSelectAllCheckbox(); +} + +void CopyInstanceDialog::on_copyServersCheckbox_stateChanged(int state) +{ +    m_selectedOptions.enableCopyServers(state == Qt::Checked); +    updateSelectAllCheckbox(); +} + +void CopyInstanceDialog::on_copyModsCheckbox_stateChanged(int state) +{ +    m_selectedOptions.enableCopyMods(state == Qt::Checked); +    updateSelectAllCheckbox(); +} + +void CopyInstanceDialog::on_copyScreenshotsCheckbox_stateChanged(int state) +{ +    m_selectedOptions.enableCopyScreenshots(state == Qt::Checked); +    updateSelectAllCheckbox();  } diff --git a/launcher/ui/dialogs/CopyInstanceDialog.h b/launcher/ui/dialogs/CopyInstanceDialog.h index bf3cd920..884501d1 100644 --- a/launcher/ui/dialogs/CopyInstanceDialog.h +++ b/launcher/ui/dialogs/CopyInstanceDialog.h @@ -17,7 +17,7 @@  #include <QDialog>  #include "BaseVersion.h" -#include <BaseInstance.h> +#include "InstanceCopyPrefs.h"  class BaseInstance; @@ -39,20 +39,29 @@ public:      QString instName() const;      QString instGroup() const;      QString iconKey() const; -    bool shouldCopySaves() const; -    bool shouldKeepPlaytime() const; +    const InstanceCopyPrefs& getChosenOptions() const;  private  slots:      void on_iconButton_clicked();      void on_instNameTextBox_textChanged(const QString &arg1); +    // Checkboxes +    void on_selectAllCheckbox_stateChanged(int state);      void on_copySavesCheckbox_stateChanged(int state);      void on_keepPlaytimeCheckbox_stateChanged(int state); +    void on_copyGameOptionsCheckbox_stateChanged(int state); +    void on_copyResPacksCheckbox_stateChanged(int state); +    void on_copyShaderPacksCheckbox_stateChanged(int state); +    void on_copyServersCheckbox_stateChanged(int state); +    void on_copyModsCheckbox_stateChanged(int state); +    void on_copyScreenshotsCheckbox_stateChanged(int state);  private: +    void checkAllCheckboxes(const bool& b); +    void updateSelectAllCheckbox(); +    /* data */      Ui::CopyInstanceDialog *ui;      QString InstIconKey;      InstancePtr m_original; -    bool m_copySaves = true; -    bool m_keepPlaytime = true; +    InstanceCopyPrefs m_selectedOptions;  }; diff --git a/launcher/ui/dialogs/CopyInstanceDialog.ui b/launcher/ui/dialogs/CopyInstanceDialog.ui index f4b191e2..b7828fe3 100644 --- a/launcher/ui/dialogs/CopyInstanceDialog.ui +++ b/launcher/ui/dialogs/CopyInstanceDialog.ui @@ -9,8 +9,8 @@     <rect>      <x>0</x>      <y>0</y> -    <width>345</width> -    <height>323</height> +    <width>341</width> +    <height>399</height>     </rect>    </property>    <property name="windowTitle"> @@ -33,7 +33,7 @@         </property>         <property name="sizeHint" stdset="0">          <size> -         <width>40</width> +         <width>60</width>           <height>20</height>          </size>         </property> @@ -60,7 +60,7 @@         </property>         <property name="sizeHint" stdset="0">          <size> -         <width>40</width> +         <width>60</width>           <height>20</height>          </size>         </property> @@ -83,7 +83,10 @@      </widget>     </item>     <item> -    <layout class="QGridLayout" name="gridLayout"> +    <layout class="QGridLayout" name="groupDropdownLayout"> +     <property name="verticalSpacing"> +      <number>6</number> +     </property>       <item row="0" column="0">        <widget class="QLabel" name="labelVersion_3">         <property name="text"> @@ -110,18 +113,96 @@      </layout>     </item>     <item> -    <widget class="QCheckBox" name="copySavesCheckbox"> -     <property name="text"> -      <string>Copy saves</string> -     </property> -    </widget> +    <layout class="QHBoxLayout" name="selectAllButtonLayout"> +     <item> +      <widget class="QCheckBox" name="selectAllCheckbox"> +       <property name="sizePolicy"> +        <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> +         <horstretch>0</horstretch> +         <verstretch>0</verstretch> +        </sizepolicy> +       </property> +       <property name="layoutDirection"> +        <enum>Qt::LeftToRight</enum> +       </property> +       <property name="text"> +        <string>Select all</string> +       </property> +       <property name="checked"> +        <bool>false</bool> +       </property> +      </widget> +     </item> +    </layout>     </item>     <item> -    <widget class="QCheckBox" name="keepPlaytimeCheckbox"> -     <property name="text"> -      <string>Keep play time</string> -     </property> -    </widget> +    <layout class="QGridLayout" name="copyOptionsLayout"> +     <item row="6" column="1"> +      <widget class="QCheckBox" name="copyModsCheckbox"> +       <property name="toolTip"> +        <string>Disabling this will still keep the mod loader (ex: Fabric, Quilt, etc.) but erase the mods folder and their configs.</string> +       </property> +       <property name="text"> +        <string>Copy mods</string> +       </property> +      </widget> +     </item> +     <item row="5" column="0"> +      <widget class="QCheckBox" name="copyGameOptionsCheckbox"> +       <property name="toolTip"> +        <string>Copy the in-game options like FOV, max framerate, etc.</string> +       </property> +       <property name="text"> +        <string>Copy game options</string> +       </property> +      </widget> +     </item> +     <item row="3" column="0"> +      <widget class="QCheckBox" name="copySavesCheckbox"> +       <property name="text"> +        <string>Copy saves</string> +       </property> +      </widget> +     </item> +     <item row="3" column="1"> +      <widget class="QCheckBox" name="copyShaderPacksCheckbox"> +       <property name="text"> +        <string>Copy shader packs</string> +       </property> +      </widget> +     </item> +     <item row="5" column="1"> +      <widget class="QCheckBox" name="copyServersCheckbox"> +       <property name="text"> +        <string>Copy servers</string> +       </property> +      </widget> +     </item> +     <item row="6" column="0"> +      <widget class="QCheckBox" name="copyResPacksCheckbox"> +       <property name="enabled"> +        <bool>true</bool> +       </property> +       <property name="text"> +        <string>Copy resource packs</string> +       </property> +      </widget> +     </item> +     <item row="1" column="0"> +      <widget class="QCheckBox" name="keepPlaytimeCheckbox"> +       <property name="text"> +        <string>Keep play time</string> +       </property> +      </widget> +     </item> +     <item row="1" column="1"> +      <widget class="QCheckBox" name="copyScreenshotsCheckbox"> +       <property name="text"> +        <string>Copy screenshots</string> +       </property> +      </widget> +     </item> +    </layout>     </item>     <item>      <widget class="QDialogButtonBox" name="buttonBox"> @@ -139,8 +220,6 @@    <tabstop>iconButton</tabstop>    <tabstop>instNameTextBox</tabstop>    <tabstop>groupBox</tabstop> -  <tabstop>copySavesCheckbox</tabstop> -  <tabstop>keepPlaytimeCheckbox</tabstop>   </tabstops>   <resources>    <include location="../../graphics.qrc"/> @@ -153,8 +232,8 @@     <slot>accept()</slot>     <hints>      <hint type="sourcelabel"> -     <x>248</x> -     <y>254</y> +     <x>254</x> +     <y>316</y>      </hint>      <hint type="destinationlabel">       <x>157</x> @@ -169,8 +248,8 @@     <slot>reject()</slot>     <hints>      <hint type="sourcelabel"> -     <x>316</x> -     <y>260</y> +     <x>322</x> +     <y>316</y>      </hint>      <hint type="destinationlabel">       <x>286</x> diff --git a/launcher/ui/dialogs/ExportInstanceDialog.cpp b/launcher/ui/dialogs/ExportInstanceDialog.cpp index 9f32dd8e..88552b23 100644 --- a/launcher/ui/dialogs/ExportInstanceDialog.cpp +++ b/launcher/ui/dialogs/ExportInstanceDialog.cpp @@ -39,13 +39,12 @@  #include <MMCZip.h>  #include <QFileDialog>  #include <QMessageBox> -#include <qfilesystemmodel.h> +#include <QFileSystemModel>  #include <QSortFilterProxyModel>  #include <QDebug> -#include <qstack.h>  #include <QSaveFile> -#include "MMCStrings.h" +#include "StringUtils.h"  #include "SeparatorPrefixTree.h"  #include "Application.h"  #include <icons/IconList.h> @@ -85,7 +84,7 @@ public:          // sort and proxy model breaks the original model...          if (sortColumn() == 0)          { -            return Strings::naturalCompare(leftFileInfo.fileName(), rightFileInfo.fileName(), +            return StringUtils::naturalCompare(leftFileInfo.fileName(), rightFileInfo.fileName(),                                             Qt::CaseInsensitive) < 0;          }          if (sortColumn() == 1) @@ -94,7 +93,7 @@ public:              auto rightSize = rightFileInfo.size();              if ((leftSize == rightSize) || (leftFileInfo.isDir() && rightFileInfo.isDir()))              { -                return Strings::naturalCompare(leftFileInfo.fileName(), +                return StringUtils::naturalCompare(leftFileInfo.fileName(),                                                 rightFileInfo.fileName(),                                                 Qt::CaseInsensitive) < 0                             ? asc diff --git a/launcher/ui/dialogs/ModDownloadDialog.cpp b/launcher/ui/dialogs/ModDownloadDialog.cpp index d740c8cb..24d23ba9 100644 --- a/launcher/ui/dialogs/ModDownloadDialog.cpp +++ b/launcher/ui/dialogs/ModDownloadDialog.cpp @@ -1,7 +1,8 @@  // SPDX-License-Identifier: GPL-3.0-only  /* - *  PolyMC - Minecraft Launcher + *  Prism Launcher - Minecraft Launcher   *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> + *  Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>   *   *  This program is free software: you can redistribute it and/or modify   *  it under the terms of the GNU General Public License as published by @@ -131,6 +132,8 @@ QList<BasePage*> ModDownloadDialog::getPages()      if (APPLICATION->capabilities() & Application::SupportsFlame)          pages.append(FlameModPage::create(this, m_instance)); +    m_selectedPage = dynamic_cast<ModPage*>(pages[0]); +      return pages;  } @@ -178,12 +181,22 @@ void ModDownloadDialog::selectedPageChanged(BasePage* previous, BasePage* select          return;      } -    auto* selected_page = dynamic_cast<ModPage*>(selected); -    if (!selected_page) { +    m_selectedPage = dynamic_cast<ModPage*>(selected); +    if (!m_selectedPage) {          qCritical() << "Page '" << selected->displayName() << "' in ModDownloadDialog is not a ModPage!";          return;      }      // Same effect as having a global search bar -    selected_page->setSearchTerm(prev_page->getSearchTerm()); +    m_selectedPage->setSearchTerm(prev_page->getSearchTerm()); +} + +bool ModDownloadDialog::selectPage(QString pageId) +{ +    return m_container->selectPage(pageId); +} + +ModPage* ModDownloadDialog::getSelectedPage() +{ +    return m_selectedPage;  } diff --git a/launcher/ui/dialogs/ModDownloadDialog.h b/launcher/ui/dialogs/ModDownloadDialog.h index 18a5f0f3..fcf6f4fc 100644 --- a/launcher/ui/dialogs/ModDownloadDialog.h +++ b/launcher/ui/dialogs/ModDownloadDialog.h @@ -1,7 +1,8 @@  // SPDX-License-Identifier: GPL-3.0-only  /* - *  PolyMC - Minecraft Launcher + *  Prism Launcher - Minecraft Launcher   *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> + *  Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>   *   *  This program is free software: you can redistribute it and/or modify   *  it under the terms of the GNU General Public License as published by @@ -32,13 +33,14 @@ class ModDownloadDialog;  class PageContainer;  class QDialogButtonBox; +class ModPage;  class ModrinthModPage;  class ModDownloadDialog final : public QDialog, public BasePageProvider  {      Q_OBJECT -public: +   public:      explicit ModDownloadDialog(const std::shared_ptr<ModFolderModel>& mods, QWidget* parent, BaseInstance* instance);      ~ModDownloadDialog() override = default; @@ -51,22 +53,26 @@ public:      bool isModSelected(QString name) const;      const QList<ModDownloadTask*> getTasks(); -    const std::shared_ptr<ModFolderModel> &mods; +    const std::shared_ptr<ModFolderModel>& mods; -public slots: +    bool selectPage(QString pageId); +    ModPage* getSelectedPage(); + +   public slots:      void confirm();      void accept() override;      void reject() override; -private slots: +   private slots:      void selectedPageChanged(BasePage* previous, BasePage* selected); -private: -    Ui::ModDownloadDialog *ui = nullptr; -    PageContainer * m_container = nullptr; -    QDialogButtonBox * m_buttons = nullptr; -    QVBoxLayout *m_verticalLayout = nullptr; +   private: +    Ui::ModDownloadDialog* ui = nullptr; +    PageContainer* m_container = nullptr; +    QDialogButtonBox* m_buttons = nullptr; +    QVBoxLayout* m_verticalLayout = nullptr; +    ModPage* m_selectedPage = nullptr;      QHash<QString, ModDownloadTask*> modTask; -    BaseInstance *m_instance; +    BaseInstance* m_instance;  }; diff --git a/launcher/ui/dialogs/VersionSelectDialog.cpp b/launcher/ui/dialogs/VersionSelectDialog.cpp index 70ef72d6..d7880334 100644 --- a/launcher/ui/dialogs/VersionSelectDialog.cpp +++ b/launcher/ui/dialogs/VersionSelectDialog.cpp @@ -120,7 +120,7 @@ void VersionSelectDialog::selectRecommended()      m_versionWidget->selectRecommended();  } -BaseVersionPtr VersionSelectDialog::selectedVersion() const +BaseVersion::Ptr VersionSelectDialog::selectedVersion() const  {      return m_versionWidget->selectedVersion();  } diff --git a/launcher/ui/dialogs/VersionSelectDialog.h b/launcher/ui/dialogs/VersionSelectDialog.h index ed30d3f3..18a50cdb 100644 --- a/launcher/ui/dialogs/VersionSelectDialog.h +++ b/launcher/ui/dialogs/VersionSelectDialog.h @@ -44,7 +44,7 @@ public:      int exec() override; -    BaseVersionPtr selectedVersion() const; +    BaseVersion::Ptr selectedVersion() const;      void setCurrentVersion(const QString & version);      void setFuzzyFilter(BaseVersionList::ModelRoles role, QString filter); diff --git a/launcher/ui/pages/global/LauncherPage.cpp b/launcher/ui/pages/global/LauncherPage.cpp index 4ae7509c..6661bf0f 100644 --- a/launcher/ui/pages/global/LauncherPage.cpp +++ b/launcher/ui/pages/global/LauncherPage.cpp @@ -1,8 +1,9 @@  // SPDX-License-Identifier: GPL-3.0-only  /* - *  PolyMC - Minecraft Launcher + *  Prism Launcher - Minecraft Launcher   *  Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>   *  Copyright (c) 2022 dada513 <dada513@protonmail.com> + *  Copyright (C) 2022 Tayou <tayou@gmx.net>   *   *  This program is free software: you can redistribute it and/or modify   *  it under the terms of the GNU General Public License as published by @@ -334,6 +335,18 @@ void LauncherPage::applySettings()          APPLICATION->setApplicationTheme(newAppTheme, false);      } +    switch (ui->themeBackgroundCat->currentIndex()) { +    case 0: // original cat +        s->set("BackgroundCat", "kitteh"); +        break; +    case 1: // rory the cat +        s->set("BackgroundCat", "rory"); +        break; +    case 2: // rory the cat flat edition +        s->set("BackgroundCat", "rory-flat"); +        break; +    } +      s->set("MenuBarInsteadOfToolBar", ui->preferMenuBarCheckBox->isChecked());      // Console settings @@ -384,45 +397,16 @@ void LauncherPage::loadSettings()      m_currentUpdateChannel = s->get("UpdateChannel").toString();      //FIXME: make generic      auto theme = s->get("IconTheme").toString(); -    if (theme == "pe_colored") -    { -        ui->themeComboBox->setCurrentIndex(0); -    } -    else if (theme == "pe_light") -    { -        ui->themeComboBox->setCurrentIndex(1); -    } -    else if (theme == "pe_dark") -    { -        ui->themeComboBox->setCurrentIndex(2); -    } -    else if (theme == "pe_blue") -    { -        ui->themeComboBox->setCurrentIndex(3); -    } -    else if (theme == "OSX") -    { -        ui->themeComboBox->setCurrentIndex(4); -    } -    else if (theme == "iOS") -    { -        ui->themeComboBox->setCurrentIndex(5); -    } -    else if (theme == "flat") -    { -        ui->themeComboBox->setCurrentIndex(6); -    } -    else if (theme == "flat_white") -    { -        ui->themeComboBox->setCurrentIndex(7); -    } -    else if (theme == "multimc") -    { -        ui->themeComboBox->setCurrentIndex(8); -    } -    else if (theme == "custom") -    { -        ui->themeComboBox->setCurrentIndex(9); +    QStringList iconThemeOptions{"pe_colored", "pe_light", "pe_dark", "pe_blue", "OSX", "iOS", "flat", "flat_white", "multimc", "custom"}; +    ui->themeComboBox->setCurrentIndex(iconThemeOptions.indexOf(theme)); + +    auto cat = s->get("BackgroundCat").toString(); +    if (cat == "kitteh") { +        ui->themeBackgroundCat->setCurrentIndex(0); +    } else if (cat == "rory") { +        ui->themeBackgroundCat->setCurrentIndex(1); +    } else if (cat == "rory-flat") { +        ui->themeBackgroundCat->setCurrentIndex(2);      }      { diff --git a/launcher/ui/pages/global/LauncherPage.ui b/launcher/ui/pages/global/LauncherPage.ui index 76a25f2e..6de644ee 100644 --- a/launcher/ui/pages/global/LauncherPage.ui +++ b/launcher/ui/pages/global/LauncherPage.ui @@ -340,6 +340,44 @@              </property>             </widget>            </item> +          <item row="2" column="0"> +           <widget class="QLabel" name="label_5"> +            <property name="text"> +             <string>C&at</string> +            </property> +            <property name="buddy"> +             <cstring>themeBackgroundCat</cstring> +            </property> +           </widget> +          </item> +          <item row="2" column="1"> +           <widget class="QComboBox" name="themeBackgroundCat"> +            <property name="sizePolicy"> +             <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> +              <horstretch>0</horstretch> +              <verstretch>0</verstretch> +             </sizepolicy> +            </property> +            <property name="focusPolicy"> +             <enum>Qt::StrongFocus</enum> +            </property> +            <item> +             <property name="text"> +              <string>Background Cat (from MultiMC)</string> +             </property> +            </item> +            <item> +             <property name="text"> +              <string>Rory ID 11 (drawn by Ashtaka)</string> +             </property> +            </item> +            <item> +             <property name="text"> +              <string>Rory ID 11 (flat edition, drawn by Ashtaka)</string> +             </property> +            </item> +           </widget> +          </item>           </layout>          </widget>         </item> diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp index f2c1746f..234f9f36 100644 --- a/launcher/ui/pages/modplatform/ModPage.cpp +++ b/launcher/ui/pages/modplatform/ModPage.cpp @@ -1,7 +1,8 @@  // SPDX-License-Identifier: GPL-3.0-only  /* - *  PolyMC - Minecraft Launcher + *  Prism Launcher - Minecraft Launcher   *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> + *  Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>   *   *  This program is free software: you can redistribute it and/or modify   *  it under the terms of the GNU General Public License as published by @@ -37,7 +38,9 @@  #include "Application.h"  #include "ui_ModPage.h" +#include <QDesktopServices>  #include <QKeyEvent> +#include <QRegularExpression>  #include <memory>  #include <HoeDown.h> @@ -80,6 +83,8 @@ ModPage::ModPage(ModDownloadDialog* dialog, BaseInstance* instance, ModAPI* api)      ui->packView->setItemDelegate(new ProjectItemDelegate(this));      ui->packView->installEventFilter(this); + +    connect(ui->packDescription, &QTextBrowser::anchorClicked, this, &ModPage::openUrl);  }  ModPage::~ModPage() @@ -158,8 +163,8 @@ void ModPage::triggerSearch()  {      auto changed = m_filter_widget->changed();      m_filter = m_filter_widget->getFilter(); -     -    if(changed){ + +    if (changed) {          ui->packView->clearSelection();          ui->packDescription->clear();          ui->versionSelectionBox->clear(); @@ -241,6 +246,79 @@ void ModPage::onModSelected()      ui->packView->adjustSize();  } +static const QRegularExpression modrinth(QRegularExpression::anchoredPattern("(?:www\\.)?modrinth\\.com\\/mod\\/([^\\/]+)\\/?")); +static const QRegularExpression curseForge(QRegularExpression::anchoredPattern("(?:www\\.)?curseforge\\.com\\/minecraft\\/mc-mods\\/([^\\/]+)\\/?")); +static const QRegularExpression curseForgeOld(QRegularExpression::anchoredPattern("minecraft\\.curseforge\\.com\\/projects\\/([^\\/]+)\\/?")); + +void ModPage::openUrl(const QUrl& url) +{ +    // do not allow other url schemes for security reasons +    if (!(url.scheme() == "http" || url.scheme() == "https")) { +        qWarning() << "Unsupported scheme" << url.scheme(); +        return; +    } + +    // detect mod URLs and search instead + +    const QString address = url.host() + url.path(); +    QRegularExpressionMatch match; +    const char* page; + +    match = modrinth.match(address); +    if (match.hasMatch()) +        page = "modrinth"; +    else if (APPLICATION->capabilities() & Application::SupportsFlame) { +        match = curseForge.match(address); +        if (!match.hasMatch()) +            match = curseForgeOld.match(address); + +        if (match.hasMatch()) +            page = "curseforge"; +    } + +    if (match.hasMatch()) { +        const QString slug = match.captured(1); + +        // ensure the user isn't opening the same mod +        if (slug != current.slug) { +            dialog->selectPage(page); + +            ModPage* newPage = dialog->getSelectedPage(); + +            QLineEdit* searchEdit = newPage->ui->searchEdit; +            ModPlatform::ListModel* model = newPage->listModel; +            QListView* view = newPage->ui->packView; + +            auto jump = [url, slug, model, view] { +                for (int row = 0; row < model->rowCount({}); row++) { +                    const QModelIndex index = model->index(row); +                    const auto pack = model->data(index, Qt::UserRole).value<ModPlatform::IndexedPack>(); + +                    if (pack.slug == slug) { +                        view->setCurrentIndex(index); +                        return; +                    } +                } + +                // The final fallback. +                QDesktopServices::openUrl(url); +            }; + +            searchEdit->setText(slug); +            newPage->triggerSearch(); + +            if (model->activeJob()) +                connect(model->activeJob(), &Task::finished, jump); +            else +                jump(); + +            return; +        } +    } + +    // open in the user's web browser +    QDesktopServices::openUrl(url); +}  /******** Make changes to the UI ********/ @@ -270,8 +348,8 @@ void ModPage::updateModVersions(int prev_count)          if ((valid || m_filter->versions.empty()) && !optedOut(version))              ui->versionSelectionBox->addItem(version.version, QVariant(i));      } -    if (ui->versionSelectionBox->count() == 0 && prev_count != 0) {  -        ui->versionSelectionBox->addItem(tr("No valid version found!"), QVariant(-1));  +    if (ui->versionSelectionBox->count() == 0 && prev_count != 0) { +        ui->versionSelectionBox->addItem(tr("No valid version found!"), QVariant(-1));          ui->modSelectionButton->setText(tr("Cannot select invalid version :("));      } @@ -317,8 +395,7 @@ void ModPage::updateUi()          text += "<br>" + tr(" by ") + authorStrs.join(", ");      } -     -    if(current.extraDataLoaded) { +    if (current.extraDataLoaded) {          if (!current.extraData.donate.isEmpty()) {              text += "<br><br>" + tr("Donate information: ");              auto donateToStr = [](ModPlatform::DonationData& donate) -> QString { diff --git a/launcher/ui/pages/modplatform/ModPage.h b/launcher/ui/pages/modplatform/ModPage.h index ae3d7e77..c9ccbaf2 100644 --- a/launcher/ui/pages/modplatform/ModPage.h +++ b/launcher/ui/pages/modplatform/ModPage.h @@ -82,6 +82,7 @@ class ModPage : public QWidget, public BasePage {      void onSelectionChanged(QModelIndex first, QModelIndex second);      void onVersionSelectionChanged(QString data);      void onModSelected(); +    virtual void openUrl(const QUrl& url);     protected:      Ui::ModPage* ui = nullptr; diff --git a/launcher/ui/pages/modplatform/ModPage.ui b/launcher/ui/pages/modplatform/ModPage.ui index 943f02aa..94365aa5 100644 --- a/launcher/ui/pages/modplatform/ModPage.ui +++ b/launcher/ui/pages/modplatform/ModPage.ui @@ -16,10 +16,10 @@       <item row="1" column="2">        <widget class="ProjectDescriptionPage" name="packDescription">         <property name="openExternalLinks"> -        <bool>true</bool> +        <bool>false</bool>         </property>         <property name="openLinks"> -        <bool>true</bool> +        <bool>false</bool>         </property>        </widget>       </item> diff --git a/launcher/ui/pages/modplatform/VanillaPage.cpp b/launcher/ui/pages/modplatform/VanillaPage.cpp index 99190f31..29fecb85 100644 --- a/launcher/ui/pages/modplatform/VanillaPage.cpp +++ b/launcher/ui/pages/modplatform/VanillaPage.cpp @@ -187,12 +187,12 @@ void VanillaPage::retranslate()      ui->retranslateUi(this);  } -BaseVersionPtr VanillaPage::selectedVersion() const +BaseVersion::Ptr VanillaPage::selectedVersion() const  {      return m_selectedVersion;  } -BaseVersionPtr VanillaPage::selectedLoaderVersion() const +BaseVersion::Ptr VanillaPage::selectedLoaderVersion() const  {      return m_selectedLoaderVersion;  } @@ -227,14 +227,14 @@ void VanillaPage::suggestCurrent()      dialog->setSuggestedIcon("default");  } -void VanillaPage::setSelectedVersion(BaseVersionPtr version) +void VanillaPage::setSelectedVersion(BaseVersion::Ptr version)  {      m_selectedVersion = version;      suggestCurrent();      loaderFilterChanged();  } -void VanillaPage::setSelectedLoaderVersion(BaseVersionPtr version) +void VanillaPage::setSelectedLoaderVersion(BaseVersion::Ptr version)  {      m_selectedLoaderVersion = version;      suggestCurrent(); diff --git a/launcher/ui/pages/modplatform/VanillaPage.h b/launcher/ui/pages/modplatform/VanillaPage.h index 7193597d..39aba760 100644 --- a/launcher/ui/pages/modplatform/VanillaPage.h +++ b/launcher/ui/pages/modplatform/VanillaPage.h @@ -76,13 +76,13 @@ public:      void openedImpl() override; -    BaseVersionPtr selectedVersion() const; -    BaseVersionPtr selectedLoaderVersion() const; +    BaseVersion::Ptr selectedVersion() const; +    BaseVersion::Ptr selectedLoaderVersion() const;      QString selectedLoader() const;  public slots: -    void setSelectedVersion(BaseVersionPtr version); -    void setSelectedLoaderVersion(BaseVersionPtr version); +    void setSelectedVersion(BaseVersion::Ptr version); +    void setSelectedLoaderVersion(BaseVersion::Ptr version);  private slots:      void filterChanged(); @@ -98,7 +98,7 @@ private:      NewInstanceDialog *dialog = nullptr;      Ui::VanillaPage *ui = nullptr;      bool m_versionSetByUser = false; -    BaseVersionPtr m_selectedVersion; -    BaseVersionPtr m_selectedLoaderVersion; +    BaseVersion::Ptr m_selectedVersion; +    BaseVersion::Ptr m_selectedLoaderVersion;      QString m_selectedLoader;  }; diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlFilterModel.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlFilterModel.cpp index c1ab166b..0887ebee 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlFilterModel.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlFilterModel.cpp @@ -20,7 +20,8 @@  #include <modplatform/atlauncher/ATLPackIndex.h>  #include <Version.h> -#include <MMCStrings.h> + +#include "StringUtils.h"  namespace Atl { @@ -86,7 +87,7 @@ bool FilterModel::lessThan(const QModelIndex &left, const QModelIndex &right) co          return lv < rv;      }      else if (currentSorting == ByName) { -        return Strings::naturalCompare(leftPack.name, rightPack.name, Qt::CaseSensitive) >= 0; +        return StringUtils::naturalCompare(leftPack.name, rightPack.name, Qt::CaseSensitive) >= 0;      }      // Invalid sorting set, somehow... diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlUserInteractionSupportImpl.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlUserInteractionSupportImpl.cpp index c68e40ba..f5f50cae 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlUserInteractionSupportImpl.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlUserInteractionSupportImpl.cpp @@ -53,7 +53,7 @@ std::optional<QVector<QString>> AtlUserInteractionSupportImpl::chooseOptionalMod      return optionalModDialog.getResult();  } -QString AtlUserInteractionSupportImpl::chooseVersion(Meta::VersionListPtr vlist, QString minecraftVersion) +QString AtlUserInteractionSupportImpl::chooseVersion(Meta::VersionList::Ptr vlist, QString minecraftVersion)  {      VersionSelectDialog vselect(vlist.get(), "Choose Version", m_parent, false);      if (minecraftVersion != nullptr) { diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlUserInteractionSupportImpl.h b/launcher/ui/pages/modplatform/atlauncher/AtlUserInteractionSupportImpl.h index 3b37c9be..37010b3f 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlUserInteractionSupportImpl.h +++ b/launcher/ui/pages/modplatform/atlauncher/AtlUserInteractionSupportImpl.h @@ -46,7 +46,7 @@ public:      AtlUserInteractionSupportImpl(QWidget* parent);  private: -    QString chooseVersion(Meta::VersionListPtr vlist, QString minecraftVersion) override; +    QString chooseVersion(Meta::VersionList::Ptr vlist, QString minecraftVersion) override;      std::optional<QVector<QString>> chooseOptionalMods(ATLauncher::PackVersion version, QVector<ATLauncher::VersionMod> mods) override;      void displayMessage(QString message) override; diff --git a/launcher/ui/pages/modplatform/flame/FlameModPage.cpp b/launcher/ui/pages/modplatform/flame/FlameModPage.cpp index fd6e32ff..bad78c97 100644 --- a/launcher/ui/pages/modplatform/flame/FlameModPage.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameModPage.cpp @@ -1,7 +1,8 @@  // SPDX-License-Identifier: GPL-3.0-only  /* - *  PolyMC - Minecraft Launcher + *  Prism Launcher - Minecraft Launcher   *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> + *  Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>   *   *  This program is free software: you can redistribute it and/or modify   *  it under the terms of the GNU General Public License as published by @@ -39,7 +40,7 @@  #include "FlameModModel.h"  #include "ui/dialogs/ModDownloadDialog.h" -FlameModPage::FlameModPage(ModDownloadDialog* dialog, BaseInstance* instance)  +FlameModPage::FlameModPage(ModDownloadDialog* dialog, BaseInstance* instance)      : ModPage(dialog, instance, new FlameAPI())  {      listModel = new FlameMod::ListModel(this); @@ -53,7 +54,7 @@ FlameModPage::FlameModPage(ModDownloadDialog* dialog, BaseInstance* instance)      ui->sortByBox->addItem(tr("Sort by Author"));      ui->sortByBox->addItem(tr("Sort by Downloads")); -    // sometimes Qt just ignores virtual slots and doesn't work as intended it seems,  +    // sometimes Qt just ignores virtual slots and doesn't work as intended it seems,      // so it's best not to connect them in the parent's contructor...      connect(ui->sortByBox, SIGNAL(currentIndexChanged(int)), this, SLOT(triggerSearch()));      connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &FlameModPage::onSelectionChanged); @@ -78,3 +79,19 @@ bool FlameModPage::optedOut(ModPlatform::IndexedVersion& ver) const  // other mod providers start loading before being selected, at least with  // my Qt, so we need to implement this in every derived class...  auto FlameModPage::shouldDisplay() const -> bool { return true; } + +void FlameModPage::openUrl(const QUrl& url) +{ +    if (url.scheme().isEmpty()) { +        QString query = url.query(QUrl::FullyDecoded); + +        if (query.startsWith("remoteUrl=")) { +            // attempt to resolve url from warning page +            query.remove(0, 10); +            ModPage::openUrl({QUrl::fromPercentEncoding(query.toUtf8())}); // double decoding is necessary +            return; +        } +    } + +    ModPage::openUrl(url); +} diff --git a/launcher/ui/pages/modplatform/flame/FlameModPage.h b/launcher/ui/pages/modplatform/flame/FlameModPage.h index 50dedd6f..58479ab9 100644 --- a/launcher/ui/pages/modplatform/flame/FlameModPage.h +++ b/launcher/ui/pages/modplatform/flame/FlameModPage.h @@ -1,7 +1,8 @@  // SPDX-License-Identifier: GPL-3.0-only  /* - *  PolyMC - Minecraft Launcher + *  Prism Launcher - Minecraft Launcher   *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> + *  Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>   *   *  This program is free software: you can redistribute it and/or modify   *  it under the terms of the GNU General Public License as published by @@ -64,4 +65,6 @@ class FlameModPage : public ModPage {      bool optedOut(ModPlatform::IndexedVersion& ver) const override;      auto shouldDisplay() const -> bool override; + +    void openUrl(const QUrl& url) override;  }; diff --git a/launcher/ui/pages/modplatform/flame/FlameModel.cpp b/launcher/ui/pages/modplatform/flame/FlameModel.cpp index 9f8605eb..debae8c3 100644 --- a/launcher/ui/pages/modplatform/flame/FlameModel.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameModel.cpp @@ -3,7 +3,6 @@  #include "Application.h"  #include "ui/widgets/ProjectItem.h" -#include <MMCStrings.h>  #include <Version.h>  #include <QtMath> diff --git a/launcher/ui/pages/modplatform/ftb/FtbFilterModel.cpp b/launcher/ui/pages/modplatform/ftb/FtbFilterModel.cpp index cbf347fc..e2b548f2 100644 --- a/launcher/ui/pages/modplatform/ftb/FtbFilterModel.cpp +++ b/launcher/ui/pages/modplatform/ftb/FtbFilterModel.cpp @@ -19,7 +19,8 @@  #include <QDebug>  #include "modplatform/modpacksch/FTBPackManifest.h" -#include <MMCStrings.h> + +#include "StringUtils.h"  namespace Ftb { @@ -81,7 +82,7 @@ bool FilterModel::lessThan(const QModelIndex &left, const QModelIndex &right) co          return leftPack.installs < rightPack.installs;      }      else if (currentSorting == ByName) { -        return Strings::naturalCompare(leftPack.name, rightPack.name, Qt::CaseSensitive) >= 0; +        return StringUtils::naturalCompare(leftPack.name, rightPack.name, Qt::CaseSensitive) >= 0;      }      // Invalid sorting set, somehow... diff --git a/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp b/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp index 2d135e59..6f11cc95 100644 --- a/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp +++ b/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp @@ -36,7 +36,7 @@  #include "ListModel.h"  #include "Application.h" -#include <MMCStrings.h> +#include "StringUtils.h"  #include <Version.h>  #include <QtMath> @@ -66,7 +66,7 @@ bool FilterModel::lessThan(const QModelIndex &left, const QModelIndex &right) co          return lv < rv;      } else if(currentSorting == Sorting::ByName) { -        return Strings::naturalCompare(leftPack.name, rightPack.name, Qt::CaseSensitive) >= 0; +        return StringUtils::naturalCompare(leftPack.name, rightPack.name, Qt::CaseSensitive) >= 0;      }      //UHM, some inavlid value set?! diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModPage.cpp index 62e417c8..c531ea90 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModPage.cpp @@ -53,7 +53,7 @@ ModrinthModPage::ModrinthModPage(ModDownloadDialog* dialog, BaseInstance* instan      ui->sortByBox->addItem(tr("Sort by Last Updated"));      ui->sortByBox->addItem(tr("Sort by Newest")); -    // sometimes Qt just ignores virtual slots and doesn't work as intended it seems,  +    // sometimes Qt just ignores virtual slots and doesn't work as intended it seems,      // so it's best not to connect them in the parent's constructor...      connect(ui->sortByBox, SIGNAL(currentIndexChanged(int)), this, SLOT(triggerSearch()));      connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &ModrinthModPage::onSelectionChanged); diff --git a/launcher/ui/themes/CustomTheme.cpp b/launcher/ui/themes/CustomTheme.cpp index 3e3e27de..3ad61668 100644 --- a/launcher/ui/themes/CustomTheme.cpp +++ b/launcher/ui/themes/CustomTheme.cpp @@ -1,48 +1,81 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + *  Prism Launcher - Minecraft Launcher + *  Copyright (C) 2022 Tayou <tayou@gmx.net> + * + *  This program is free software: you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation, version 3. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program.  If not, see <https://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + *      Copyright 2013-2021 MultiMC Contributors + * + *      Licensed under the Apache License, Version 2.0 (the "License"); + *      you may not use this file except in compliance with the License. + *      You may obtain a copy of the License at + * + *          http://www.apache.org/licenses/LICENSE-2.0 + * + *      Unless required by applicable law or agreed to in writing, software + *      distributed under the License is distributed on an "AS IS" BASIS, + *      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + *      See the License for the specific language governing permissions and + *      limitations under the License. + */  #include "CustomTheme.h" -#include <QDir> -#include <Json.h>  #include <FileSystem.h> +#include <Json.h> +#include "ThemeManager.h" -const char * themeFile = "theme.json"; -const char * styleFile = "themeStyle.css"; +const char* themeFile = "theme.json"; -static bool readThemeJson(const QString &path, QPalette &palette, double &fadeAmount, QColor &fadeColor, QString &name, QString &widgets) +static bool readThemeJson(const QString& path, +                          QPalette& palette, +                          double& fadeAmount, +                          QColor& fadeColor, +                          QString& name, +                          QString& widgets, +                          QString& qssFilePath, +                          bool& dataIncomplete)  {      QFileInfo pathInfo(path); -    if(pathInfo.exists() && pathInfo.isFile()) -    { -        try -        { +    if (pathInfo.exists() && pathInfo.isFile()) { +        try {              auto doc = Json::requireDocument(path, "Theme JSON file");              const QJsonObject root = doc.object(); +            dataIncomplete = !root.contains("qssFilePath");              name = Json::requireString(root, "name", "Theme name");              widgets = Json::requireString(root, "widgets", "Qt widget theme"); +            qssFilePath = Json::ensureString(root, "qssFilePath", "themeStyle.css");              auto colorsRoot = Json::requireObject(root, "colors", "colors object"); -            auto readColor = [&](QString colorName) -> QColor -            { +            auto readColor = [&](QString colorName) -> QColor {                  auto colorValue = Json::ensureString(colorsRoot, colorName, QString()); -                if(!colorValue.isEmpty()) -                { +                if (!colorValue.isEmpty()) {                      QColor color(colorValue); -                    if(!color.isValid()) -                    { -                        qWarning() << "Color value" << colorValue << "for" << colorName << "was not recognized."; +                    if (!color.isValid()) { +                        themeWarningLog() << "Color value" << colorValue << "for" << colorName << "was not recognized.";                          return QColor();                      }                      return color;                  }                  return QColor();              }; -            auto readAndSetColor = [&](QPalette::ColorRole role, QString colorName) -            { +            auto readAndSetColor = [&](QPalette::ColorRole role, QString colorName) {                  auto color = readColor(colorName); -                if(color.isValid()) -                { +                if (color.isValid()) {                      palette.setColor(role, color); -                } -                else -                { -                    qDebug() << "Color value for" << colorName << "was not present."; +                } else { +                    themeDebugLog() << "Color value for" << colorName << "was not present.";                  }              }; @@ -61,36 +94,36 @@ static bool readThemeJson(const QString &path, QPalette &palette, double &fadeAm              readAndSetColor(QPalette::Highlight, "Highlight");              readAndSetColor(QPalette::HighlightedText, "HighlightedText"); -            //fade +            // fade              fadeColor = readColor("fadeColor");              fadeAmount = Json::ensureDouble(colorsRoot, "fadeAmount", 0.5, "fade amount"); -        } -        catch (const Exception &e) -        { -            qWarning() << "Couldn't load theme json: " << e.cause(); +        } catch (const Exception& e) { +            themeWarningLog() << "Couldn't load theme json: " << e.cause();              return false;          } -    } -    else -    { -        qDebug() << "No theme json present."; +    } else { +        themeDebugLog() << "No theme json present.";          return false;      }      return true;  } -static bool writeThemeJson(const QString &path, const QPalette &palette, double fadeAmount, QColor fadeColor, QString name, QString widgets) +static bool writeThemeJson(const QString& path, +                           const QPalette& palette, +                           double fadeAmount, +                           QColor fadeColor, +                           QString name, +                           QString widgets, +                           QString qssFilePath)  {      QJsonObject rootObj;      rootObj.insert("name", name);      rootObj.insert("widgets", widgets); +    rootObj.insert("qssFilePath", qssFilePath);      QJsonObject colorsObj; -    auto insertColor = [&](QPalette::ColorRole role, QString colorName) -    { -        colorsObj.insert(colorName, palette.color(role).name()); -    }; +    auto insertColor = [&](QPalette::ColorRole role, QString colorName) { colorsObj.insert(colorName, palette.color(role).name()); };      // palette      insertColor(QPalette::Window, "Window"); @@ -112,82 +145,95 @@ static bool writeThemeJson(const QString &path, const QPalette &palette, double      colorsObj.insert("fadeAmount", fadeAmount);      rootObj.insert("colors", colorsObj); -    try -    { +    try {          Json::write(rootObj, path);          return true; -    } -    catch (const Exception &e) -    { -        qWarning() << "Failed to write theme json to" << path; +    } catch (const Exception& e) { +        themeWarningLog() << "Failed to write theme json to" << path;          return false;      }  } -CustomTheme::CustomTheme(ITheme* baseTheme, QString folder) +/// @param baseTheme Base Theme +/// @param fileInfo FileInfo object for file to load +/// @param isManifest whether to load a theme manifest or a qss file +CustomTheme::CustomTheme(ITheme* baseTheme, QFileInfo& fileInfo, bool isManifest)  { -    m_id = folder; -    QString path = FS::PathCombine("themes", m_id); -    QString pathResources = FS::PathCombine("themes", m_id, "resources"); +    if (isManifest) { +        m_id = fileInfo.dir().dirName(); -    qDebug() << "Loading theme" << m_id; +        QString path = FS::PathCombine("themes", m_id); +        QString pathResources = FS::PathCombine("themes", m_id, "resources"); -    if(!FS::ensureFolderPathExists(path) || !FS::ensureFolderPathExists(pathResources)) -    { -        qWarning() << "couldn't create folder for theme!"; -        m_palette = baseTheme->colorScheme(); -        m_styleSheet = baseTheme->appStyleSheet(); -        return; -    } +        if (!FS::ensureFolderPathExists(path) || !FS::ensureFolderPathExists(pathResources)) { +            themeWarningLog() << "couldn't create folder for theme!"; +            m_palette = baseTheme->colorScheme(); +            m_styleSheet = baseTheme->appStyleSheet(); +            return; +        } + +        auto themeFilePath = FS::PathCombine(path, themeFile); -    auto themeFilePath = FS::PathCombine(path, themeFile); +        bool jsonDataIncomplete = false; -    m_palette = baseTheme->colorScheme(); -    if (!readThemeJson(themeFilePath, m_palette, m_fadeAmount, m_fadeColor, m_name, m_widgets)) -    { -        m_name = "Custom";          m_palette = baseTheme->colorScheme(); -        m_fadeColor = baseTheme->fadeColor(); -        m_fadeAmount = baseTheme->fadeAmount(); -        m_widgets = baseTheme->qtTheme(); - -        QFileInfo info(themeFilePath); -        if(!info.exists()) -        { -            writeThemeJson(themeFilePath, m_palette, m_fadeAmount, m_fadeColor, "Custom", m_widgets); +        if (!readThemeJson(themeFilePath, m_palette, m_fadeAmount, m_fadeColor, m_name, m_widgets, m_qssFilePath, jsonDataIncomplete)) { +            themeDebugLog() << "Did not read theme json file correctly, writing new one to: " << themeFilePath; +            m_name = "Custom"; +            m_palette = baseTheme->colorScheme(); +            m_fadeColor = baseTheme->fadeColor(); +            m_fadeAmount = baseTheme->fadeAmount(); +            m_widgets = baseTheme->qtTheme(); +            m_qssFilePath = "themeStyle.css"; +        } else { +            m_palette = fadeInactive(m_palette, m_fadeAmount, m_fadeColor);          } -    } -    else -    { -        m_palette = fadeInactive(m_palette, m_fadeAmount, m_fadeColor); -    } -    auto cssFilePath = FS::PathCombine(path, styleFile); -    QFileInfo info (cssFilePath); -    if(info.isFile()) -    { -        try -        { -            // TODO: validate css? -            m_styleSheet = QString::fromUtf8(FS::read(cssFilePath)); +        if (jsonDataIncomplete) { +            writeThemeJson(fileInfo.absoluteFilePath(), m_palette, m_fadeAmount, m_fadeColor, m_name, m_widgets, m_qssFilePath);          } -        catch (const Exception &e) -        { -            qWarning() << "Couldn't load css:" << e.cause() << "from" << cssFilePath; + +        auto qssFilePath = FS::PathCombine(path, m_qssFilePath); +        QFileInfo info(qssFilePath); +        if (info.isFile()) { +            try { +                // TODO: validate css? +                m_styleSheet = QString::fromUtf8(FS::read(qssFilePath)); +            } catch (const Exception& e) { +                themeWarningLog() << "Couldn't load css:" << e.cause() << "from" << qssFilePath; +                m_styleSheet = baseTheme->appStyleSheet(); +            } +        } else { +            themeDebugLog() << "No theme css present.";              m_styleSheet = baseTheme->appStyleSheet(); +            try { +                FS::write(qssFilePath, m_styleSheet.toUtf8()); +            } catch (const Exception& e) { +                themeWarningLog() << "Couldn't write css:" << e.cause() << "to" << qssFilePath; +            }          } -    } -    else -    { -        qDebug() << "No theme css present."; -        m_styleSheet = baseTheme->appStyleSheet(); -        try -        { -            FS::write(cssFilePath, m_styleSheet.toUtf8()); +    } else { +        m_id = fileInfo.fileName(); +        m_name = fileInfo.baseName(); +        QString path = fileInfo.filePath(); +        // themeDebugLog << "Theme ID: " << m_id; +        // themeDebugLog << "Theme Name: " << m_name; +        // themeDebugLog << "Theme Path: " << path; + +        if (!FS::ensureFilePathExists(path)) { +            themeWarningLog() << m_name << " Theme file path doesn't exist!"; +            m_palette = baseTheme->colorScheme(); +            m_styleSheet = baseTheme->appStyleSheet(); +            return;          } -        catch (const Exception &e) -        { -            qWarning() << "Couldn't write css:" << e.cause() << "to" << cssFilePath; + +        m_palette = baseTheme->colorScheme(); +        try { +            // TODO: validate qss? +            m_styleSheet = QString::fromUtf8(FS::read(path)); +        } catch (const Exception& e) { +            themeWarningLog() << "Couldn't load qss:" << e.cause() << "from" << path; +            m_styleSheet = baseTheme->appStyleSheet();          }      }  } @@ -197,7 +243,6 @@ QStringList CustomTheme::searchPaths()      return { FS::PathCombine("themes", m_id, "resources") };  } -  QString CustomTheme::id()  {      return m_id; diff --git a/launcher/ui/themes/CustomTheme.h b/launcher/ui/themes/CustomTheme.h index d216895d..f2b1b06e 100644 --- a/launcher/ui/themes/CustomTheme.h +++ b/launcher/ui/themes/CustomTheme.h @@ -1,11 +1,45 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + *  Prism Launcher - Minecraft Launcher + *  Copyright (C) 2022 Tayou <tayou@gmx.net> + * + *  This program is free software: you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation, version 3. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program.  If not, see <https://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + *      Copyright 2013-2021 MultiMC Contributors + * + *      Licensed under the Apache License, Version 2.0 (the "License"); + *      you may not use this file except in compliance with the License. + *      You may obtain a copy of the License at + * + *          http://www.apache.org/licenses/LICENSE-2.0 + * + *      Unless required by applicable law or agreed to in writing, software + *      distributed under the License is distributed on an "AS IS" BASIS, + *      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + *      See the License for the specific language governing permissions and + *      limitations under the License. + */  #pragma once +#include <QFileInfo>  #include "ITheme.h" -class CustomTheme: public ITheme -{ -public: -    CustomTheme(ITheme * baseTheme, QString folder); +class CustomTheme : public ITheme { +   public: +    CustomTheme(ITheme* baseTheme, QFileInfo& file, bool isManifest);      virtual ~CustomTheme() {}      QString id() override; @@ -19,7 +53,7 @@ public:      QString qtTheme() override;      QStringList searchPaths() override; -private: /* data */ +   private: /* data */      QPalette m_palette;      QColor m_fadeColor;      double m_fadeAmount; @@ -27,5 +61,5 @@ private: /* data */      QString m_name;      QString m_id;      QString m_widgets; +    QString m_qssFilePath;  }; - diff --git a/launcher/ui/themes/SystemTheme.cpp b/launcher/ui/themes/SystemTheme.cpp index 49b1afaa..a63d1741 100644 --- a/launcher/ui/themes/SystemTheme.cpp +++ b/launcher/ui/themes/SystemTheme.cpp @@ -1,30 +1,65 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + *  Prism Launcher - Minecraft Launcher + *  Copyright (C) 2022 Tayou <tayou@gmx.net> + * + *  This program is free software: you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation, version 3. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program.  If not, see <https://www.gnu.org/licenses/>. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + *      Copyright 2013-2021 MultiMC Contributors + * + *      Licensed under the Apache License, Version 2.0 (the "License"); + *      you may not use this file except in compliance with the License. + *      You may obtain a copy of the License at + * + *          http://www.apache.org/licenses/LICENSE-2.0 + * + *      Unless required by applicable law or agreed to in writing, software + *      distributed under the License is distributed on an "AS IS" BASIS, + *      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + *      See the License for the specific language governing permissions and + *      limitations under the License. + */  #include "SystemTheme.h"  #include <QApplication>  #include <QStyle>  #include <QStyleFactory>  #include <QDebug> +#include "ThemeManager.h"  SystemTheme::SystemTheme()  { -    qDebug() << "Determining System Theme..."; +    themeDebugLog() << "Determining System Theme...";      const auto & style = QApplication::style();      systemPalette = style->standardPalette();      QString lowerThemeName = style->objectName(); -    qDebug() << "System theme seems to be:" << lowerThemeName; +    themeDebugLog() << "System theme seems to be:" << lowerThemeName;      QStringList styles = QStyleFactory::keys();      for(auto &st: styles)      { -        qDebug() << "Considering theme from theme factory:" << st.toLower(); +        themeDebugLog() << "Considering theme from theme factory:" << st.toLower();          if(st.toLower() == lowerThemeName)          {              systemTheme = st; -            qDebug() << "System theme has been determined to be:" << systemTheme; +            themeDebugLog() << "System theme has been determined to be:" << systemTheme;              return;          }      }      // fall back to fusion if we can't find the current theme.      systemTheme = "Fusion"; -    qDebug() << "System theme not found, defaulted to Fusion"; +    themeDebugLog() << "System theme not found, defaulted to Fusion";  }  void SystemTheme::apply(bool initial) diff --git a/launcher/ui/themes/ThemeManager.cpp b/launcher/ui/themes/ThemeManager.cpp new file mode 100644 index 00000000..01a38a86 --- /dev/null +++ b/launcher/ui/themes/ThemeManager.cpp @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: GPL-3.0-only
 +/*
 + *  Prism Launcher - Minecraft Launcher
 + *  Copyright (C) 2022 Tayou <tayou@gmx.net>
 + *
 + *  This program is free software: you can redistribute it and/or modify
 + *  it under the terms of the GNU General Public License as published by
 + *  the Free Software Foundation, version 3.
 + *
 + *  This program is distributed in the hope that it will be useful,
 + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 + *  GNU General Public License for more details.
 + *
 + *  You should have received a copy of the GNU General Public License
 + *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
 + */
 +#include "ThemeManager.h"
 +
 +#include <QApplication>
 +#include <QDir>
 +#include <QDirIterator>
 +#include <QIcon>
 +#include "ui/themes/BrightTheme.h"
 +#include "ui/themes/CustomTheme.h"
 +#include "ui/themes/DarkTheme.h"
 +#include "ui/themes/SystemTheme.h"
 +
 +#include "Application.h"
 +
 +#ifdef Q_OS_WIN
 +#include <windows.h>
 +// this is needed for versionhelpers.h, it is also included in WinDarkmode, but we can't rely on that.
 +// Ultimately this should be included in versionhelpers, but that is outside of the project.
 +#include "ui/WinDarkmode.h"
 +#include <versionhelpers.h>
 +#endif
 +
 +ThemeManager::ThemeManager(MainWindow* mainWindow)
 +{
 +    m_mainWindow = mainWindow;
 +    InitializeThemes();
 +}
 +
 +/// @brief Adds the Theme to the list of themes
 +/// @param theme The Theme to add
 +/// @return Theme ID
 +QString ThemeManager::AddTheme(std::unique_ptr<ITheme> theme)
 +{
 +    QString id = theme->id();
 +    m_themes.emplace(id, std::move(theme));
 +    return id;
 +}
 +
 +/// @brief Gets the Theme from the List via ID
 +/// @param themeId Theme ID of theme to fetch
 +/// @return Theme at themeId
 +ITheme* ThemeManager::GetTheme(QString themeId)
 +{
 +    return m_themes[themeId].get();
 +}
 +
 +void ThemeManager::InitializeThemes()
 +{
 +    // Icon themes
 +    {
 +        // TODO: icon themes and instance icons do not mesh well together. Rearrange and fix discrepancies!
 +        // set icon theme search path!
 +        auto searchPaths = QIcon::themeSearchPaths();
 +        searchPaths.append("iconthemes");
 +        QIcon::setThemeSearchPaths(searchPaths);
 +        themeDebugLog() << "<> Icon themes initialized.";
 +    }
 +
 +    // Initialize widget themes
 +    {
 +        themeDebugLog() << "<> Initializing Widget Themes";
 +        themeDebugLog() << "Loading Built-in Theme:" << AddTheme(std::make_unique<SystemTheme>());
 +        auto darkThemeId = AddTheme(std::make_unique<DarkTheme>());
 +        themeDebugLog() << "Loading Built-in Theme:" << darkThemeId;
 +        themeDebugLog() << "Loading Built-in Theme:" << AddTheme(std::make_unique<BrightTheme>());
 +
 +        // TODO: need some way to differentiate same name themes in different subdirectories (maybe smaller grey text next to theme name in
 +        // dropdown?)
 +        QString themeFolder = QDir("./themes/").absoluteFilePath("");
 +        themeDebugLog() << "Theme Folder Path: " << themeFolder;
 +
 +        QDirIterator directoryIterator(themeFolder, QDir::Dirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
 +        while (directoryIterator.hasNext()) {
 +            QDir dir(directoryIterator.next());
 +            QFileInfo themeJson(dir.absoluteFilePath("theme.json"));
 +            if (themeJson.exists()) {
 +                // Load "theme.json" based themes
 +                themeDebugLog() << "Loading JSON Theme from:" << themeJson.absoluteFilePath();
 +                AddTheme(std::make_unique<CustomTheme>(GetTheme(darkThemeId), themeJson, true));
 +            } else {
 +                // Load pure QSS Themes
 +                QDirIterator stylesheetFileIterator(dir.absoluteFilePath(""), { "*.qss", "*.css" }, QDir::Files);
 +                while (stylesheetFileIterator.hasNext()) {
 +                    QFile customThemeFile(stylesheetFileIterator.next());
 +                    QFileInfo customThemeFileInfo(customThemeFile);
 +                    themeDebugLog() << "Loading QSS Theme from:" << customThemeFileInfo.absoluteFilePath();
 +                    AddTheme(std::make_unique<CustomTheme>(GetTheme(darkThemeId), customThemeFileInfo, false));
 +                }
 +            }
 +        }
 +
 +        themeDebugLog() << "<> Widget themes initialized.";
 +    }
 +}
 +
 +QList<ITheme*> ThemeManager::getValidApplicationThemes()
 +{
 +    QList<ITheme*> ret;
 +    ret.reserve(m_themes.size());
 +    for (auto&& [id, theme] : m_themes) {
 +        ret.append(theme.get());
 +    }
 +    return ret;
 +}
 +
 +void ThemeManager::setIconTheme(const QString& name)
 +{
 +    QIcon::setThemeName(name);
 +}
 +
 +void ThemeManager::applyCurrentlySelectedTheme()
 +{
 +    setIconTheme(APPLICATION->settings()->get("IconTheme").toString());
 +    themeDebugLog() << "<> Icon theme set.";
 +    setApplicationTheme(APPLICATION->settings()->get("ApplicationTheme").toString(), true);
 +    themeDebugLog() << "<> Application theme set.";
 +}
 +
 +void ThemeManager::setApplicationTheme(const QString& name, bool initial)
 +{
 +    auto systemPalette = qApp->palette();
 +    auto themeIter = m_themes.find(name);
 +    if (themeIter != m_themes.end()) {
 +        auto& theme = themeIter->second;
 +        themeDebugLog() << "applying theme" << theme->name();
 +        theme->apply(initial);
 +#ifdef Q_OS_WIN
 +        if (m_mainWindow && IsWindows10OrGreater()) {
 +            if (QString::compare(theme->id(), "dark") == 0) {
 +                WinDarkmode::setDarkWinTitlebar(m_mainWindow->winId(), true);
 +            } else {
 +                WinDarkmode::setDarkWinTitlebar(m_mainWindow->winId(), false);
 +            }
 +        }
 +#endif
 +    } else {
 +        themeWarningLog() << "Tried to set invalid theme:" << name;
 +    }
 +}
 diff --git a/launcher/ui/themes/ThemeManager.h b/launcher/ui/themes/ThemeManager.h new file mode 100644 index 00000000..b85cb742 --- /dev/null +++ b/launcher/ui/themes/ThemeManager.h @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-3.0-only
 +/*
 + *  Prism Launcher - Minecraft Launcher
 + *  Copyright (C) 2022 Tayou <tayou@gmx.net>
 + *
 + *  This program is free software: you can redistribute it and/or modify
 + *  it under the terms of the GNU General Public License as published by
 + *  the Free Software Foundation, version 3.
 + *
 + *  This program is distributed in the hope that it will be useful,
 + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 + *  GNU General Public License for more details.
 + *
 + *  You should have received a copy of the GNU General Public License
 + *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
 + */
 +#pragma once
 +
 +#include <QString>
 +
 +#include "ui/MainWindow.h"
 +#include "ui/themes/ITheme.h"
 +
 +inline auto themeDebugLog()
 +{
 +    return qDebug() << "[Theme]";
 +}
 +inline auto themeWarningLog()
 +{
 +    return qWarning() << "[Theme]";
 +}
 +
 +class ThemeManager {
 +   public:
 +    ThemeManager(MainWindow* mainWindow);
 +
 +    // maybe make private? Or put in ctor?
 +    void InitializeThemes();
 +
 +    QList<ITheme*> getValidApplicationThemes();
 +    void setIconTheme(const QString& name);
 +    void applyCurrentlySelectedTheme();
 +    void setApplicationTheme(const QString& name, bool initial);
 +
 +   private:
 +    std::map<QString, std::unique_ptr<ITheme>> m_themes;
 +    MainWindow* m_mainWindow;
 +
 +    QString AddTheme(std::unique_ptr<ITheme> theme);
 +    ITheme* GetTheme(QString themeId);
 +};
 diff --git a/launcher/ui/widgets/JavaSettingsWidget.cpp b/launcher/ui/widgets/JavaSettingsWidget.cpp index 314a126e..c7c4dbbd 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.cpp +++ b/launcher/ui/widgets/JavaSettingsWidget.cpp @@ -245,7 +245,7 @@ void JavaSettingsWidget::memoryValueChanged(int)      }  } -void JavaSettingsWidget::javaVersionSelected(BaseVersionPtr version) +void JavaSettingsWidget::javaVersionSelected(BaseVersion::Ptr version)  {      auto java = std::dynamic_pointer_cast<JavaInstall>(version);      if(!java) diff --git a/launcher/ui/widgets/JavaSettingsWidget.h b/launcher/ui/widgets/JavaSettingsWidget.h index 0d280daf..5344e2cd 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.h +++ b/launcher/ui/widgets/JavaSettingsWidget.h @@ -60,7 +60,7 @@ public:  protected slots:      void memoryValueChanged(int);      void javaPathEdited(const QString &path); -    void javaVersionSelected(BaseVersionPtr version); +    void javaVersionSelected(BaseVersion::Ptr version);      void on_javaBrowseBtn_clicked();      void on_javaStatusBtn_clicked();      void checkFinished(JavaCheckResult result); diff --git a/launcher/ui/widgets/ModFilterWidget.h b/launcher/ui/widgets/ModFilterWidget.h index 958a1e2b..706ffd21 100644 --- a/launcher/ui/widgets/ModFilterWidget.h +++ b/launcher/ui/widgets/ModFilterWidget.h @@ -49,7 +49,7 @@ public:      auto getFilter() -> std::shared_ptr<Filter>;      auto changed() const -> bool { return m_last_version_id != m_version_id; } -    Meta::VersionListPtr versionList() { return m_version_list; } +    Meta::VersionList::Ptr versionList() { return m_version_list; }  private:      ModFilterWidget(Version def, QWidget* parent = nullptr); @@ -73,7 +73,7 @@ private:  /* Version stuff */      QButtonGroup m_mcVersion_buttons; -    Meta::VersionListPtr m_version_list; +    Meta::VersionList::Ptr m_version_list;      /* Used to tell if the filter was changed since the last getFilter() call */      VersionButtonID m_last_version_id = VersionButtonID::Strict; diff --git a/launcher/ui/widgets/VersionSelectWidget.cpp b/launcher/ui/widgets/VersionSelectWidget.cpp index cc4fc6a2..404860d9 100644 --- a/launcher/ui/widgets/VersionSelectWidget.cpp +++ b/launcher/ui/widgets/VersionSelectWidget.cpp @@ -142,7 +142,7 @@ void VersionSelectWidget::changeProgress(qint64 current, qint64 total)  void VersionSelectWidget::currentRowChanged(const QModelIndex& current, const QModelIndex&)  {      auto variant = m_proxyModel->data(current, BaseVersionList::VersionPointerRole); -    emit selectedVersionChanged(variant.value<BaseVersionPtr>()); +    emit selectedVersionChanged(variant.value<BaseVersion::Ptr>());  }  void VersionSelectWidget::preselect() @@ -186,11 +186,11 @@ bool VersionSelectWidget::hasVersions() const      return m_proxyModel->rowCount(QModelIndex()) != 0;  } -BaseVersionPtr VersionSelectWidget::selectedVersion() const +BaseVersion::Ptr VersionSelectWidget::selectedVersion() const  {      auto currentIndex = listView->selectionModel()->currentIndex();      auto variant = m_proxyModel->data(currentIndex, BaseVersionList::VersionPointerRole); -    return variant.value<BaseVersionPtr>(); +    return variant.value<BaseVersion::Ptr>();  }  void VersionSelectWidget::setExactFilter(BaseVersionList::ModelRoles role, QString filter) diff --git a/launcher/ui/widgets/VersionSelectWidget.h b/launcher/ui/widgets/VersionSelectWidget.h index f56daa8a..e75efc6f 100644 --- a/launcher/ui/widgets/VersionSelectWidget.h +++ b/launcher/ui/widgets/VersionSelectWidget.h @@ -40,7 +40,7 @@ public:      void loadList();      bool hasVersions() const; -    BaseVersionPtr selectedVersion() const; +    BaseVersion::Ptr selectedVersion() const;      void selectRecommended();      void selectCurrent(); @@ -54,7 +54,7 @@ public:      void setResizeOn(int column);  signals: -    void selectedVersionChanged(BaseVersionPtr version); +    void selectedVersionChanged(BaseVersion::Ptr version);  protected:      virtual void closeEvent ( QCloseEvent* ); | 
