Merge branch 'develop' into macos-add-to-path
This commit is contained in:
commit
2c9452efaf
@ -126,7 +126,7 @@ set(Launcher_NEWS_OPEN_URL "https://prismlauncher.org/news" CACHE STRING "URL th
|
|||||||
set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRING "URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help")
|
set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRING "URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help")
|
||||||
|
|
||||||
######## Set version numbers ########
|
######## Set version numbers ########
|
||||||
set(Launcher_VERSION_MAJOR 5)
|
set(Launcher_VERSION_MAJOR 6)
|
||||||
set(Launcher_VERSION_MINOR 0)
|
set(Launcher_VERSION_MINOR 0)
|
||||||
|
|
||||||
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}")
|
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}")
|
||||||
|
28
README.md
28
README.md
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
Prism Launcher is a custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once.
|
Prism Launcher is a custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once.
|
||||||
|
|
||||||
This is a **fork** of the MultiMC Launcher and not endorsed by MultiMC.
|
This is a **fork** of the MultiMC Launcher and is not endorsed by MultiMC.
|
||||||
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
@ -18,35 +18,35 @@ This is a **fork** of the MultiMC Launcher and not endorsed by MultiMC.
|
|||||||
<img src="https://repology.org/badge/vertical-allrepos/prismlauncher.svg" alt="Packaging status" align="right">
|
<img src="https://repology.org/badge/vertical-allrepos/prismlauncher.svg" alt="Packaging status" align="right">
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
- All downloads and instructions for Prism Launcher can be found [on our website](https://prismlauncher.org/download/).
|
- All downloads and instructions for Prism Launcher can be found on our [Website](https://prismlauncher.org/download/).
|
||||||
- Last build status can be found [here](https://github.com/PrismLauncher/PrismLauncher/actions).
|
- Last build status can be found in the [GitHub Actions](https://github.com/PrismLauncher/PrismLauncher/actions).
|
||||||
|
|
||||||
### Development Builds
|
### Development Builds
|
||||||
|
|
||||||
There are development builds available [here](https://github.com/PrismLauncher/PrismLauncher/actions). These have debug information in the binaries, so their file sizes are relatively larger.
|
There are development builds available [here](https://github.com/PrismLauncher/PrismLauncher/actions). These have debug information in the binaries, so their file sizes are relatively larger.
|
||||||
|
|
||||||
Portable builds are provided for Linux, Windows, and macOS.
|
Prebuilt Development builds are provided for **Linux**, **Windows** and **macOS**.
|
||||||
|
|
||||||
For Arch, Debian and Gentoo, respectively, you can use these packages to get compiled development versions:
|
For **Arch**, **Debian**, **Fedora**, **OpenSUSE (Tumbleweed)** and **Gentoo**, respectively, you can use these packages for the latest development versions:
|
||||||
|
|
||||||
[![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--git-blue?style=flat-square)](https://aur.archlinux.org/packages/prismlauncher-qt5-git/) [![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--qt5--git-blue?style=flat-square)](https://aur.archlinux.org/packages/prismlauncher-git/) [![prismlauncher-git](https://img.shields.io/badge/mpr-prismlauncher--git-orange?style=flat-square)](https://mpr.makedeb.org/packages/prismlauncher-git) [![prismlauncher-9999](https://img.shields.io/badge/gentoo-prismlauncher--9999-purple?style=flat-square)](https://packages.gentoo.org/packages/games-action/prismlauncher)
|
[![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--git-1793D1?style=flat-square&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-qt5-git/) [![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--qt5--git-1793D1?style=flat-square&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-git/) [![prismlauncher-git](https://img.shields.io/badge/mpr-prismlauncher--git-A80030?style=flat-square&logo=debian&logoColor=white)](https://mpr.makedeb.org/packages/prismlauncher-git) [![prismlauncher-nightly](https://img.shields.io/badge/copr-prismlauncher--nightly-51A2DA?style=flat-square&logo=fedora&logoColor=white)](https://copr.fedorainfracloud.org/coprs/g3tchoo/prismlauncher/) [![prismlauncher-nightly](https://img.shields.io/badge/OBS-prismlauncher--nightly-3AB6A9?style=flat-square&logo=opensuse&logoColor=white)](https://build.opensuse.org/project/show/home:getchoo) [![prismlauncher-9999](https://img.shields.io/badge/gentoo-prismlauncher--9999-4D4270?style=flat-square&logo=gentoo&logoColor=white)](https://packages.gentoo.org/packages/games-action/prismlauncher)
|
||||||
|
|
||||||
## Help & Support
|
## Community & Support
|
||||||
|
|
||||||
Feel free to create an issue if you need help.
|
Feel free to create a GitHub issue if you find a bug or want to suggest a new feature. We have multiple communities that can also help you.
|
||||||
|
|
||||||
#### Join our Discord server:
|
#### Join our Discord server:
|
||||||
[![Prism Launcher Discord server](https://discordapp.com/api/guilds/1031648380885147709/widget.png?style=banner3)](https://discord.gg/prismlauncher)
|
[![Prism Launcher Discord server](https://discordapp.com/api/guilds/1031648380885147709/widget.png?style=banner2)](https://discord.gg/prismlauncher)
|
||||||
|
|
||||||
#### Join our Matrix space:
|
#### Join our Matrix space (Will be opened at a later date):
|
||||||
[![PrismLauncher Space](https://img.shields.io/matrix/prismlauncher:matrix.org?style=for-the-badge)](https://matrix.to/#/#prismlauncher:matrix.org)
|
[![PrismLauncher Space](https://img.shields.io/matrix/prismlauncher:matrix.org?style=for-the-badge&logo=matrix)](https://matrix.to/#/#prismlauncher:matrix.org)
|
||||||
|
|
||||||
#### Join our SubReddit:
|
#### Join our SubReddit:
|
||||||
[![r/PrismLauncher](https://img.shields.io/reddit/subreddit-subscribers/prismlauncher?style=for-the-badge)](https://www.reddit.com/r/PrismLauncher/)
|
[![r/PrismLauncher](https://img.shields.io/reddit/subreddit-subscribers/prismlauncher?style=for-the-badge&logo=reddit)](https://www.reddit.com/r/PrismLauncher/)
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
If you want to build Prism Launcher yourself, check [Build Instructions](https://prismlauncher.org/wiki/development/build-instructions/) for build instructions.
|
If you want to build Prism Launcher yourself, check the [Build Instructions](https://prismlauncher.org/wiki/development/build-instructions/).
|
||||||
|
|
||||||
## Translations
|
## Translations
|
||||||
|
|
||||||
@ -97,6 +97,6 @@ Thanks to the awesome people over at [MacStadium](https://www.macstadium.com/),
|
|||||||
|
|
||||||
All launcher code is available under the GPL-3.0-only license.
|
All launcher code is available under the GPL-3.0-only license.
|
||||||
|
|
||||||
![https://github.com/PrismLauncher/PrismLauncher/blob/develop/LICENSE](https://img.shields.io/github/license/PrismLauncher/PrismLauncher?style=for-the-badge)
|
![https://github.com/PrismLauncher/PrismLauncher/blob/develop/LICENSE](https://img.shields.io/github/license/PrismLauncher/PrismLauncher?style=for-the-badge&logo=gnu&color=C4282D)
|
||||||
|
|
||||||
The logo and related assets are under the CC BY-SA 4.0 license.
|
The logo and related assets are under the CC BY-SA 4.0 license.
|
||||||
|
@ -563,7 +563,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
|
|
||||||
// Memory
|
// Memory
|
||||||
m_settings->registerSetting({"MinMemAlloc", "MinMemoryAlloc"}, 512);
|
m_settings->registerSetting({"MinMemAlloc", "MinMemoryAlloc"}, 512);
|
||||||
m_settings->registerSetting({"MaxMemAlloc", "MaxMemoryAlloc"}, 4096);
|
m_settings->registerSetting({"MaxMemAlloc", "MaxMemoryAlloc"}, suitableMaxMem());
|
||||||
m_settings->registerSetting("PermGen", 128);
|
m_settings->registerSetting("PermGen", 128);
|
||||||
|
|
||||||
// Java Settings
|
// Java Settings
|
||||||
@ -611,6 +611,8 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
// The cat
|
// The cat
|
||||||
m_settings->registerSetting("TheCat", false);
|
m_settings->registerSetting("TheCat", false);
|
||||||
|
|
||||||
|
m_settings->registerSetting("ToolbarsLocked", false);
|
||||||
|
|
||||||
m_settings->registerSetting("InstSortMode", "Name");
|
m_settings->registerSetting("InstSortMode", "Name");
|
||||||
m_settings->registerSetting("SelectedInstance", QString());
|
m_settings->registerSetting("SelectedInstance", QString());
|
||||||
|
|
||||||
@ -1589,3 +1591,17 @@ QString Application::getUserAgentUncached()
|
|||||||
|
|
||||||
return BuildConfig.USER_AGENT_UNCACHED;
|
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;
|
||||||
|
}
|
||||||
|
@ -200,6 +200,8 @@ public:
|
|||||||
|
|
||||||
void ShowGlobalSettings(class QWidget * parent, QString open_page = QString());
|
void ShowGlobalSettings(class QWidget * parent, QString open_page = QString());
|
||||||
|
|
||||||
|
int suitableMaxMem();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void updateAllowedChanged(bool status);
|
void updateAllowedChanged(bool status);
|
||||||
void globalSettingsAboutToOpen();
|
void globalSettingsAboutToOpen();
|
||||||
|
@ -24,13 +24,15 @@ set(CORE_SOURCES
|
|||||||
NullInstance.h
|
NullInstance.h
|
||||||
MMCZip.h
|
MMCZip.h
|
||||||
MMCZip.cpp
|
MMCZip.cpp
|
||||||
MMCStrings.h
|
StringUtils.h
|
||||||
MMCStrings.cpp
|
StringUtils.cpp
|
||||||
RuntimeContext.h
|
RuntimeContext.h
|
||||||
|
|
||||||
# Basic instance manipulation tasks (derived from InstanceTask)
|
# Basic instance manipulation tasks (derived from InstanceTask)
|
||||||
InstanceCreationTask.h
|
InstanceCreationTask.h
|
||||||
InstanceCreationTask.cpp
|
InstanceCreationTask.cpp
|
||||||
|
InstanceCopyPrefs.h
|
||||||
|
InstanceCopyPrefs.cpp
|
||||||
InstanceCopyTask.h
|
InstanceCopyTask.h
|
||||||
InstanceCopyTask.cpp
|
InstanceCopyTask.cpp
|
||||||
InstanceImportTask.h
|
InstanceImportTask.h
|
||||||
@ -1064,7 +1066,7 @@ if(INSTALL_BUNDLE STREQUAL "full")
|
|||||||
# Image formats
|
# Image formats
|
||||||
install(
|
install(
|
||||||
DIRECTORY "${QT_PLUGINS_DIR}/imageformats"
|
DIRECTORY "${QT_PLUGINS_DIR}/imageformats"
|
||||||
CONFIGURATIONS Debug RelWithDebInfo
|
CONFIGURATIONS Debug RelWithDebInfo ""
|
||||||
DESTINATION ${PLUGIN_DEST_DIR}
|
DESTINATION ${PLUGIN_DEST_DIR}
|
||||||
COMPONENT Runtime
|
COMPONENT Runtime
|
||||||
REGEX "tga|tiff|mng" EXCLUDE
|
REGEX "tga|tiff|mng" EXCLUDE
|
||||||
@ -1082,7 +1084,7 @@ if(INSTALL_BUNDLE STREQUAL "full")
|
|||||||
# Icon engines
|
# Icon engines
|
||||||
install(
|
install(
|
||||||
DIRECTORY "${QT_PLUGINS_DIR}/iconengines"
|
DIRECTORY "${QT_PLUGINS_DIR}/iconengines"
|
||||||
CONFIGURATIONS Debug RelWithDebInfo
|
CONFIGURATIONS Debug RelWithDebInfo ""
|
||||||
DESTINATION ${PLUGIN_DEST_DIR}
|
DESTINATION ${PLUGIN_DEST_DIR}
|
||||||
COMPONENT Runtime
|
COMPONENT Runtime
|
||||||
REGEX "fontawesome" EXCLUDE
|
REGEX "fontawesome" EXCLUDE
|
||||||
@ -1100,7 +1102,7 @@ if(INSTALL_BUNDLE STREQUAL "full")
|
|||||||
# Platform plugins
|
# Platform plugins
|
||||||
install(
|
install(
|
||||||
DIRECTORY "${QT_PLUGINS_DIR}/platforms"
|
DIRECTORY "${QT_PLUGINS_DIR}/platforms"
|
||||||
CONFIGURATIONS Debug RelWithDebInfo
|
CONFIGURATIONS Debug RelWithDebInfo ""
|
||||||
DESTINATION ${PLUGIN_DEST_DIR}
|
DESTINATION ${PLUGIN_DEST_DIR}
|
||||||
COMPONENT Runtime
|
COMPONENT Runtime
|
||||||
REGEX "minimal|linuxfb|offscreen" EXCLUDE
|
REGEX "minimal|linuxfb|offscreen" EXCLUDE
|
||||||
@ -1119,7 +1121,7 @@ if(INSTALL_BUNDLE STREQUAL "full")
|
|||||||
if(EXISTS "${QT_PLUGINS_DIR}/styles")
|
if(EXISTS "${QT_PLUGINS_DIR}/styles")
|
||||||
install(
|
install(
|
||||||
DIRECTORY "${QT_PLUGINS_DIR}/styles"
|
DIRECTORY "${QT_PLUGINS_DIR}/styles"
|
||||||
CONFIGURATIONS Debug RelWithDebInfo
|
CONFIGURATIONS Debug RelWithDebInfo ""
|
||||||
DESTINATION ${PLUGIN_DEST_DIR}
|
DESTINATION ${PLUGIN_DEST_DIR}
|
||||||
COMPONENT Runtime
|
COMPONENT Runtime
|
||||||
)
|
)
|
||||||
@ -1137,7 +1139,7 @@ if(INSTALL_BUNDLE STREQUAL "full")
|
|||||||
if(EXISTS "${QT_PLUGINS_DIR}/tls")
|
if(EXISTS "${QT_PLUGINS_DIR}/tls")
|
||||||
install(
|
install(
|
||||||
DIRECTORY "${QT_PLUGINS_DIR}/tls"
|
DIRECTORY "${QT_PLUGINS_DIR}/tls"
|
||||||
CONFIGURATIONS Debug RelWithDebInfo
|
CONFIGURATIONS Debug RelWithDebInfo ""
|
||||||
DESTINATION ${PLUGIN_DEST_DIR}
|
DESTINATION ${PLUGIN_DEST_DIR}
|
||||||
COMPONENT Runtime
|
COMPONENT Runtime
|
||||||
)
|
)
|
||||||
|
@ -44,7 +44,9 @@
|
|||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
#include "DesktopServices.h"
|
#include "DesktopServices.h"
|
||||||
|
#include "StringUtils.h"
|
||||||
|
|
||||||
#if defined Q_OS_WIN32
|
#if defined Q_OS_WIN32
|
||||||
#include <objbase.h>
|
#include <objbase.h>
|
||||||
@ -79,22 +81,6 @@ namespace fs = std::filesystem;
|
|||||||
namespace fs = ghc::filesystem;
|
namespace fs = ghc::filesystem;
|
||||||
#endif
|
#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 {
|
namespace FS {
|
||||||
|
|
||||||
void ensureExists(const QDir& dir)
|
void ensureExists(const QDir& dir)
|
||||||
@ -163,6 +149,9 @@ bool ensureFolderPathExists(QString foldernamepath)
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @brief Copies a directory and it's contents from src to dest
|
||||||
|
/// @param offset subdirectory form src to copy to dest
|
||||||
|
/// @return if there was an error during the filecopy
|
||||||
bool copy::operator()(const QString& offset)
|
bool copy::operator()(const QString& offset)
|
||||||
{
|
{
|
||||||
using copy_opts = fs::copy_options;
|
using copy_opts = fs::copy_options;
|
||||||
@ -191,7 +180,7 @@ bool copy::operator()(const QString& offset)
|
|||||||
auto dst_path = PathCombine(dst, relative_dst_path);
|
auto dst_path = PathCombine(dst, relative_dst_path);
|
||||||
ensureFilePathExists(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) {
|
if (err) {
|
||||||
qWarning() << "Failed to copy files:" << QString::fromStdString(err.message());
|
qWarning() << "Failed to copy files:" << QString::fromStdString(err.message());
|
||||||
qDebug() << "Source file:" << src_path;
|
qDebug() << "Source file:" << src_path;
|
||||||
@ -213,7 +202,7 @@ bool copy::operator()(const QString& offset)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If the root src is not a directory, the previous iterator won't run.
|
// 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, "");
|
copy_file(src, "");
|
||||||
|
|
||||||
return err.value() == 0;
|
return err.value() == 0;
|
||||||
@ -223,7 +212,7 @@ bool deletePath(QString path)
|
|||||||
{
|
{
|
||||||
std::error_code err;
|
std::error_code err;
|
||||||
|
|
||||||
fs::remove_all(toStdString(path), err);
|
fs::remove_all(StringUtils::toStdString(path), err);
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
qWarning() << "Failed to remove files:" << QString::fromStdString(err.message());
|
qWarning() << "Failed to remove files:" << QString::fromStdString(err.message());
|
||||||
@ -414,7 +403,7 @@ bool overrideFolder(QString overwritten_path, QString override_path)
|
|||||||
fs::copy_options opt = copy_opts::recursive | copy_opts::overwrite_existing;
|
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?
|
// 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) {
|
if (err) {
|
||||||
qCritical() << QString("Failed to apply override from %1 to %2").arg(override_path, overwritten_path);
|
qCritical() << QString("Failed to apply override from %1 to %2").arg(override_path, overwritten_path);
|
||||||
|
@ -75,6 +75,7 @@ bool ensureFilePathExists(QString filenamepath);
|
|||||||
*/
|
*/
|
||||||
bool ensureFolderPathExists(QString filenamepath);
|
bool ensureFolderPathExists(QString filenamepath);
|
||||||
|
|
||||||
|
/// @brief Copies a directory and it's contents from src to dest
|
||||||
class copy {
|
class copy {
|
||||||
public:
|
public:
|
||||||
copy(const QString& src, const QString& dst)
|
copy(const QString& src, const QString& dst)
|
||||||
|
135
launcher/InstanceCopyPrefs.cpp
Normal file
135
launcher/InstanceCopyPrefs.cpp
Normal file
@ -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;
|
||||||
|
}
|
41
launcher/InstanceCopyPrefs.h
Normal file
41
launcher/InstanceCopyPrefs.h
Normal file
@ -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;
|
||||||
|
};
|
@ -5,15 +5,17 @@
|
|||||||
#include "pathmatcher/RegexpMatcher.h"
|
#include "pathmatcher/RegexpMatcher.h"
|
||||||
#include <QtConcurrentRun>
|
#include <QtConcurrentRun>
|
||||||
|
|
||||||
InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, bool copySaves, bool keepPlaytime)
|
InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, const InstanceCopyPrefs& prefs)
|
||||||
{
|
{
|
||||||
m_origInstance = origInstance;
|
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...
|
// FIXME: get this from the original instance type...
|
||||||
auto matcherReal = new RegexpMatcher("[.]?minecraft/saves");
|
auto matcherReal = new RegexpMatcher(filters);
|
||||||
matcherReal->caseSensitive(false);
|
matcherReal->caseSensitive(false);
|
||||||
m_matcher.reset(matcherReal);
|
m_matcher.reset(matcherReal);
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,21 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "tasks/Task.h"
|
|
||||||
#include "net/NetJob.h"
|
|
||||||
#include <QUrl>
|
|
||||||
#include <QFuture>
|
#include <QFuture>
|
||||||
#include <QFutureWatcher>
|
#include <QFutureWatcher>
|
||||||
#include "settings/SettingsObject.h"
|
#include <QUrl>
|
||||||
#include "BaseVersion.h"
|
|
||||||
#include "BaseInstance.h"
|
#include "BaseInstance.h"
|
||||||
|
#include "BaseVersion.h"
|
||||||
|
#include "InstanceCopyPrefs.h"
|
||||||
#include "InstanceTask.h"
|
#include "InstanceTask.h"
|
||||||
|
#include "net/NetJob.h"
|
||||||
|
#include "settings/SettingsObject.h"
|
||||||
|
#include "tasks/Task.h"
|
||||||
|
|
||||||
class InstanceCopyTask : public InstanceTask
|
class InstanceCopyTask : public InstanceTask
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit InstanceCopyTask(InstancePtr origInstance, bool copySaves, bool keepPlaytime);
|
explicit InstanceCopyTask(InstancePtr origInstance, const InstanceCopyPrefs& prefs);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
//! Entry point for tasks.
|
//! Entry point for tasks.
|
||||||
@ -22,7 +23,8 @@ protected:
|
|||||||
void copyFinished();
|
void copyFinished();
|
||||||
void copyAborted();
|
void copyAborted();
|
||||||
|
|
||||||
private: /* data */
|
private:
|
||||||
|
/* data */
|
||||||
InstancePtr m_origInstance;
|
InstancePtr m_origInstance;
|
||||||
QFuture<bool> m_copyFuture;
|
QFuture<bool> m_copyFuture;
|
||||||
QFutureWatcher<bool> m_copyFutureWatcher;
|
QFutureWatcher<bool> m_copyFutureWatcher;
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
#include "JavaCommon.h"
|
#include "JavaCommon.h"
|
||||||
#include "java/JavaUtils.h"
|
#include "java/JavaUtils.h"
|
||||||
#include "ui/dialogs/CustomMessageBox.h"
|
#include "ui/dialogs/CustomMessageBox.h"
|
||||||
#include <MMCStrings.h>
|
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
|
|
||||||
bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget *parent)
|
bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget *parent)
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
|
|
||||||
namespace Strings
|
|
||||||
{
|
|
||||||
int naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs);
|
|
||||||
}
|
|
@ -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
|
/// 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();
|
return (location < s.length()) ? s.at(location) : QChar();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TAKEN FROM Qt, because it doesn't expose it intelligently
|
/// 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
|
// skip spaces, tabs and 0's
|
||||||
QChar c1 = getNextChar(s1, l1);
|
QChar c1 = getNextChar(s1, l1);
|
||||||
while (c1.isSpace())
|
while (c1.isSpace())
|
||||||
c1 = getNextChar(s1, ++l1);
|
c1 = getNextChar(s1, ++l1);
|
||||||
|
|
||||||
QChar c2 = getNextChar(s2, l2);
|
QChar c2 = getNextChar(s2, l2);
|
||||||
while (c2.isSpace())
|
while (c2.isSpace())
|
||||||
c2 = getNextChar(s2, ++l2);
|
c2 = getNextChar(s2, ++l2);
|
||||||
|
|
||||||
if (c1.isDigit() && c2.isDigit())
|
if (c1.isDigit() && c2.isDigit()) {
|
||||||
{
|
|
||||||
while (c1.digitValue() == 0)
|
while (c1.digitValue() == 0)
|
||||||
c1 = getNextChar(s1, ++l1);
|
c1 = getNextChar(s1, ++l1);
|
||||||
while (c2.digitValue() == 0)
|
while (c2.digitValue() == 0)
|
||||||
@ -30,11 +32,8 @@ int Strings::naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensit
|
|||||||
int lookAheadLocation2 = l2;
|
int lookAheadLocation2 = l2;
|
||||||
int currentReturnValue = 0;
|
int currentReturnValue = 0;
|
||||||
// find the last digit, setting currentReturnValue as we go if it isn't equal
|
// find the last digit, setting currentReturnValue as we go if it isn't equal
|
||||||
for (QChar lookAhead1 = c1, lookAhead2 = c2;
|
for (QChar lookAhead1 = c1, lookAhead2 = c2; (lookAheadLocation1 <= s1.length() && lookAheadLocation2 <= s2.length());
|
||||||
(lookAheadLocation1 <= s1.length() && lookAheadLocation2 <= s2.length());
|
lookAhead1 = getNextChar(s1, ++lookAheadLocation1), lookAhead2 = getNextChar(s2, ++lookAheadLocation2)) {
|
||||||
lookAhead1 = getNextChar(s1, ++lookAheadLocation1),
|
|
||||||
lookAhead2 = getNextChar(s2, ++lookAheadLocation2))
|
|
||||||
{
|
|
||||||
bool is1ADigit = !lookAhead1.isNull() && lookAhead1.isDigit();
|
bool is1ADigit = !lookAhead1.isNull() && lookAhead1.isDigit();
|
||||||
bool is2ADigit = !lookAhead2.isNull() && lookAhead2.isDigit();
|
bool is2ADigit = !lookAhead2.isNull() && lookAhead2.isDigit();
|
||||||
if (!is1ADigit && !is2ADigit)
|
if (!is1ADigit && !is2ADigit)
|
||||||
@ -43,14 +42,10 @@ int Strings::naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensit
|
|||||||
return -1;
|
return -1;
|
||||||
if (!is2ADigit)
|
if (!is2ADigit)
|
||||||
return 1;
|
return 1;
|
||||||
if (currentReturnValue == 0)
|
if (currentReturnValue == 0) {
|
||||||
{
|
if (lookAhead1 < lookAhead2) {
|
||||||
if (lookAhead1 < lookAhead2)
|
|
||||||
{
|
|
||||||
currentReturnValue = -1;
|
currentReturnValue = -1;
|
||||||
}
|
} else if (lookAhead1 > lookAhead2) {
|
||||||
else if (lookAhead1 > lookAhead2)
|
|
||||||
{
|
|
||||||
currentReturnValue = 1;
|
currentReturnValue = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -58,19 +53,24 @@ int Strings::naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensit
|
|||||||
if (currentReturnValue != 0)
|
if (currentReturnValue != 0)
|
||||||
return currentReturnValue;
|
return currentReturnValue;
|
||||||
}
|
}
|
||||||
if (cs == Qt::CaseInsensitive)
|
|
||||||
{
|
if (cs == Qt::CaseInsensitive) {
|
||||||
if (!c1.isLower())
|
if (!c1.isLower())
|
||||||
c1 = c1.toLower();
|
c1 = c1.toLower();
|
||||||
if (!c2.isLower())
|
if (!c2.isLower())
|
||||||
c2 = c2.toLower();
|
c2 = c2.toLower();
|
||||||
}
|
}
|
||||||
|
|
||||||
int r = QString::localeAwareCompare(c1, c2);
|
int r = QString::localeAwareCompare(c1, c2);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return -1;
|
return -1;
|
||||||
if (r > 0)
|
if (r > 0)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
l1 += 1;
|
||||||
|
l2 += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The two strings are the same (02 == 2) so fall back to the normal sort
|
// The two strings are the same (02 == 2) so fall back to the normal sort
|
||||||
return QString::compare(s1, s2, cs);
|
return QString::compare(s1, s2, cs);
|
||||||
}
|
}
|
32
launcher/StringUtils.h
Normal file
32
launcher/StringUtils.h
Normal file
@ -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
|
@ -1,9 +1,10 @@
|
|||||||
#include "JavaInstall.h"
|
#include "JavaInstall.h"
|
||||||
#include <MMCStrings.h>
|
|
||||||
|
#include "StringUtils.h"
|
||||||
|
|
||||||
bool JavaInstall::operator<(const JavaInstall &rhs)
|
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)
|
if(archCompare != 0)
|
||||||
return archCompare < 0;
|
return archCompare < 0;
|
||||||
if(id < rhs.id)
|
if(id < rhs.id)
|
||||||
@ -14,7 +15,7 @@ bool JavaInstall::operator<(const JavaInstall &rhs)
|
|||||||
{
|
{
|
||||||
return false;
|
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)
|
bool JavaInstall::operator==(const JavaInstall &rhs)
|
||||||
|
@ -41,7 +41,6 @@
|
|||||||
#include "java/JavaInstallList.h"
|
#include "java/JavaInstallList.h"
|
||||||
#include "java/JavaCheckerJob.h"
|
#include "java/JavaCheckerJob.h"
|
||||||
#include "java/JavaUtils.h"
|
#include "java/JavaUtils.h"
|
||||||
#include "MMCStrings.h"
|
|
||||||
#include "minecraft/VersionFilterData.h"
|
#include "minecraft/VersionFilterData.h"
|
||||||
|
|
||||||
JavaInstallList::JavaInstallList(QObject *parent) : BaseVersionList(parent)
|
JavaInstallList::JavaInstallList(QObject *parent) : BaseVersionList(parent)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "JavaVersion.h"
|
#include "JavaVersion.h"
|
||||||
#include <MMCStrings.h>
|
|
||||||
|
#include "StringUtils.h"
|
||||||
|
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
@ -98,12 +99,12 @@ bool JavaVersion::operator<(const JavaVersion &rhs)
|
|||||||
else if(thisPre && rhsPre)
|
else if(thisPre && rhsPre)
|
||||||
{
|
{
|
||||||
// both are prereleases - use natural compare...
|
// 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
|
// neither is prerelease, so they are the same -> this cannot be less than rhs
|
||||||
return false;
|
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)
|
bool JavaVersion::operator==(const JavaVersion &rhs)
|
||||||
|
@ -37,7 +37,6 @@
|
|||||||
|
|
||||||
#include "launch/LaunchTask.h"
|
#include "launch/LaunchTask.h"
|
||||||
#include "MessageLevel.h"
|
#include "MessageLevel.h"
|
||||||
#include "MMCStrings.h"
|
|
||||||
#include "java/JavaChecker.h"
|
#include "java/JavaChecker.h"
|
||||||
#include "tasks/Task.h"
|
#include "tasks/Task.h"
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
@ -91,5 +91,7 @@ int main(int argc, char *argv[])
|
|||||||
return 1;
|
return 1;
|
||||||
case Application::Succeeded:
|
case Application::Succeeded:
|
||||||
return 0;
|
return 0;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,6 @@
|
|||||||
#include "settings/SettingsObject.h"
|
#include "settings/SettingsObject.h"
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
|
||||||
#include "MMCStrings.h"
|
|
||||||
#include "pathmatcher/RegexpMatcher.h"
|
#include "pathmatcher/RegexpMatcher.h"
|
||||||
#include "pathmatcher/MultiMatcher.h"
|
#include "pathmatcher/MultiMatcher.h"
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
|
@ -36,7 +36,7 @@ LocalModUpdateTask::LocalModUpdateTask(QDir index_dir, ModPlatform::IndexedPack&
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef Q_OS_WIN32
|
#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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -372,13 +372,20 @@ void FlameCreationTask::idResolverSucceeded(QEventLoop& loop)
|
|||||||
auto results = m_mod_id_resolver->getResults();
|
auto results = m_mod_id_resolver->getResults();
|
||||||
|
|
||||||
// first check for blocked mods
|
// first check for blocked mods
|
||||||
QString text;
|
QList<BlockedMod> blocked_mods;
|
||||||
QList<QUrl> urls;
|
|
||||||
auto anyBlocked = false;
|
auto anyBlocked = false;
|
||||||
for (const auto& result : results.files.values()) {
|
for (const auto& result : results.files.values()) {
|
||||||
if (!result.resolved || result.url.isEmpty()) {
|
if (!result.resolved || result.url.isEmpty()) {
|
||||||
text += QString("%1: <a href='%2'>%2</a><br/>").arg(result.fileName, result.websiteUrl);
|
|
||||||
urls.append(QUrl(result.websiteUrl));
|
BlockedMod blocked_mod;
|
||||||
|
blocked_mod.name = result.fileName;
|
||||||
|
blocked_mod.websiteUrl = result.websiteUrl;
|
||||||
|
blocked_mod.hash = result.hash;
|
||||||
|
blocked_mod.matched = false;
|
||||||
|
blocked_mod.localPath = "";
|
||||||
|
|
||||||
|
blocked_mods.append(blocked_mod);
|
||||||
|
|
||||||
anyBlocked = true;
|
anyBlocked = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -388,11 +395,12 @@ void FlameCreationTask::idResolverSucceeded(QEventLoop& loop)
|
|||||||
auto message_dialog = new BlockedModsDialog(m_parent, tr("Blocked mods found"),
|
auto message_dialog = new BlockedModsDialog(m_parent, tr("Blocked mods found"),
|
||||||
tr("The following mods were blocked on third party launchers.<br/>"
|
tr("The following mods were blocked on third party launchers.<br/>"
|
||||||
"You will need to manually download them and add them to the modpack"),
|
"You will need to manually download them and add them to the modpack"),
|
||||||
text,
|
blocked_mods);
|
||||||
urls);
|
|
||||||
message_dialog->setModal(true);
|
message_dialog->setModal(true);
|
||||||
|
|
||||||
if (message_dialog->exec()) {
|
if (message_dialog->exec()) {
|
||||||
|
qDebug() << "Post dialog blocked mods list: " << blocked_mods;
|
||||||
|
copyBlockedMods(blocked_mods);
|
||||||
setupDownloadJob(loop);
|
setupDownloadJob(loop);
|
||||||
} else {
|
} else {
|
||||||
m_mod_id_resolver.reset();
|
m_mod_id_resolver.reset();
|
||||||
@ -404,6 +412,38 @@ void FlameCreationTask::idResolverSucceeded(QEventLoop& loop)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @brief copy the matched blocked mods to the instance staging area
|
||||||
|
/// @param blocked_mods list of the blocked mods and their matched paths
|
||||||
|
void FlameCreationTask::copyBlockedMods(QList<BlockedMod> const& blocked_mods)
|
||||||
|
{
|
||||||
|
setStatus(tr("Copying Blocked Mods..."));
|
||||||
|
setAbortable(false);
|
||||||
|
int i = 0;
|
||||||
|
int total = blocked_mods.length();
|
||||||
|
setProgress(i, total);
|
||||||
|
for (auto const& mod : blocked_mods) {
|
||||||
|
if (!mod.matched) {
|
||||||
|
qDebug() << mod.name << "was not matched to a local file, skipping copy";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto dest_path = FS::PathCombine(m_stagingPath, "minecraft", "mods", mod.name);
|
||||||
|
|
||||||
|
setStatus(tr("Copying Blocked Mods (%1 out of %2 are done)").arg(QString::number(i), QString::number(total)));
|
||||||
|
|
||||||
|
qDebug() << "Will try to copy" << mod.localPath << "to" << dest_path;
|
||||||
|
|
||||||
|
if (!FS::copy(mod.localPath, dest_path)()) {
|
||||||
|
qDebug() << "Copy of" << mod.localPath << "to" << dest_path << "Failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
|
setProgress(i, total);
|
||||||
|
}
|
||||||
|
|
||||||
|
setAbortable(true);
|
||||||
|
}
|
||||||
|
|
||||||
void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
|
void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
|
||||||
{
|
{
|
||||||
m_files_job = new NetJob(tr("Mod download"), APPLICATION->network());
|
m_files_job = new NetJob(tr("Mod download"), APPLICATION->network());
|
||||||
@ -449,7 +489,7 @@ void FlameCreationTask::setupDownloadJob(QEventLoop& loop)
|
|||||||
m_files_job.reset();
|
m_files_job.reset();
|
||||||
setError(reason);
|
setError(reason);
|
||||||
});
|
});
|
||||||
connect(m_files_job.get(), &NetJob::progress, [&](qint64 current, qint64 total) { setProgress(current, total); });
|
connect(m_files_job.get(), &NetJob::progress, this, &FlameCreationTask::setProgress);
|
||||||
connect(m_files_job.get(), &NetJob::finished, &loop, &QEventLoop::quit);
|
connect(m_files_job.get(), &NetJob::finished, &loop, &QEventLoop::quit);
|
||||||
|
|
||||||
setStatus(tr("Downloading mods..."));
|
setStatus(tr("Downloading mods..."));
|
||||||
|
@ -10,6 +10,8 @@
|
|||||||
|
|
||||||
#include "net/NetJob.h"
|
#include "net/NetJob.h"
|
||||||
|
|
||||||
|
#include "ui/dialogs/BlockedModsDialog.h"
|
||||||
|
|
||||||
class FlameCreationTask final : public InstanceCreationTask {
|
class FlameCreationTask final : public InstanceCreationTask {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
@ -29,6 +31,7 @@ class FlameCreationTask final : public InstanceCreationTask {
|
|||||||
private slots:
|
private slots:
|
||||||
void idResolverSucceeded(QEventLoop&);
|
void idResolverSucceeded(QEventLoop&);
|
||||||
void setupDownloadJob(QEventLoop&);
|
void setupDownloadJob(QEventLoop&);
|
||||||
|
void copyBlockedMods(QList<BlockedMod> const& blocked_mods);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QWidget* m_parent = nullptr;
|
QWidget* m_parent = nullptr;
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
|
#include "StringUtils.h"
|
||||||
|
|
||||||
#include <MurmurHash2.h>
|
#include <MurmurHash2.h>
|
||||||
|
|
||||||
@ -35,6 +36,18 @@ Hasher::Ptr createFlameHasher(QString file_path)
|
|||||||
return new FlameHasher(file_path);
|
return new FlameHasher(file_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Hasher::Ptr createBlockedModHasher(QString file_path, ModPlatform::Provider provider)
|
||||||
|
{
|
||||||
|
return new BlockedModHasher(file_path, provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
Hasher::Ptr createBlockedModHasher(QString file_path, ModPlatform::Provider provider, QString type)
|
||||||
|
{
|
||||||
|
auto hasher = new BlockedModHasher(file_path, provider);
|
||||||
|
hasher->useHashType(type);
|
||||||
|
return hasher;
|
||||||
|
}
|
||||||
|
|
||||||
void ModrinthHasher::executeTask()
|
void ModrinthHasher::executeTask()
|
||||||
{
|
{
|
||||||
QFile file(m_path);
|
QFile file(m_path);
|
||||||
@ -66,7 +79,7 @@ void FlameHasher::executeTask()
|
|||||||
// CF-specific
|
// CF-specific
|
||||||
auto should_filter_out = [](char c) { return (c == 9 || c == 10 || c == 13 || c == 32); };
|
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.
|
// 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?
|
// How do we make this non-blocking then?
|
||||||
m_hash = QString::number(MurmurHash2(std::move(file_stream), 4 * MiB, should_filter_out));
|
m_hash = QString::number(MurmurHash2(std::move(file_stream), 4 * MiB, should_filter_out));
|
||||||
@ -78,4 +91,50 @@ void FlameHasher::executeTask()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BlockedModHasher::BlockedModHasher(QString file_path, ModPlatform::Provider provider)
|
||||||
|
: Hasher(file_path), provider(provider) {
|
||||||
|
setObjectName(QString("BlockedModHasher: %1").arg(file_path));
|
||||||
|
hash_type = ProviderCaps.hashType(provider).first();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlockedModHasher::executeTask()
|
||||||
|
{
|
||||||
|
QFile file(m_path);
|
||||||
|
|
||||||
|
try {
|
||||||
|
file.open(QFile::ReadOnly);
|
||||||
|
} catch (FS::FileSystemException& e) {
|
||||||
|
qCritical() << QString("Failed to open JAR file in %1").arg(m_path);
|
||||||
|
qCritical() << QString("Reason: ") << e.cause();
|
||||||
|
|
||||||
|
emitFailed("Failed to open file for hashing.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_hash = ProviderCaps.hash(provider, &file, hash_type);
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
if (m_hash.isEmpty()) {
|
||||||
|
emitFailed("Empty hash!");
|
||||||
|
} else {
|
||||||
|
emitSucceeded();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList BlockedModHasher::getHashTypes() {
|
||||||
|
return ProviderCaps.hashType(provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BlockedModHasher::useHashType(QString type) {
|
||||||
|
auto types = ProviderCaps.hashType(provider);
|
||||||
|
if (types.contains(type)) {
|
||||||
|
hash_type = type;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
qDebug() << "Bad hash type " << type << " for provider";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Hashing
|
} // namespace Hashing
|
||||||
|
@ -40,8 +40,23 @@ class ModrinthHasher : public Hasher {
|
|||||||
void executeTask() override;
|
void executeTask() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class BlockedModHasher : public Hasher {
|
||||||
|
public:
|
||||||
|
BlockedModHasher(QString file_path, ModPlatform::Provider provider);
|
||||||
|
|
||||||
|
void executeTask() override;
|
||||||
|
|
||||||
|
QStringList getHashTypes();
|
||||||
|
bool useHashType(QString type);
|
||||||
|
private:
|
||||||
|
ModPlatform::Provider provider;
|
||||||
|
QString hash_type;
|
||||||
|
};
|
||||||
|
|
||||||
Hasher::Ptr createHasher(QString file_path, ModPlatform::Provider provider);
|
Hasher::Ptr createHasher(QString file_path, ModPlatform::Provider provider);
|
||||||
Hasher::Ptr createFlameHasher(QString file_path);
|
Hasher::Ptr createFlameHasher(QString file_path);
|
||||||
Hasher::Ptr createModrinthHasher(QString file_path);
|
Hasher::Ptr createModrinthHasher(QString file_path);
|
||||||
|
Hasher::Ptr createBlockedModHasher(QString file_path, ModPlatform::Provider provider);
|
||||||
|
Hasher::Ptr createBlockedModHasher(QString file_path, ModPlatform::Provider provider, QString type);
|
||||||
|
|
||||||
} // namespace Hashing
|
} // namespace Hashing
|
||||||
|
@ -176,8 +176,6 @@ void PackInstallTask::resolveMods()
|
|||||||
|
|
||||||
void PackInstallTask::onResolveModsSucceeded()
|
void PackInstallTask::onResolveModsSucceeded()
|
||||||
{
|
{
|
||||||
QString text;
|
|
||||||
QList<QUrl> urls;
|
|
||||||
auto anyBlocked = false;
|
auto anyBlocked = false;
|
||||||
|
|
||||||
Flame::Manifest results = m_mod_id_resolver_task->getResults();
|
Flame::Manifest results = m_mod_id_resolver_task->getResults();
|
||||||
@ -191,11 +189,16 @@ void PackInstallTask::onResolveModsSucceeded()
|
|||||||
|
|
||||||
// First check for blocked mods
|
// First check for blocked mods
|
||||||
if (!results_file.resolved || results_file.url.isEmpty()) {
|
if (!results_file.resolved || results_file.url.isEmpty()) {
|
||||||
QString type(local_file.type);
|
|
||||||
|
|
||||||
type[0] = type[0].toUpper();
|
BlockedMod blocked_mod;
|
||||||
text += QString("%1: %2 - <a href='%3'>%3</a><br/>").arg(type, local_file.name, results_file.websiteUrl);
|
blocked_mod.name = local_file.name;
|
||||||
urls.append(QUrl(results_file.websiteUrl));
|
blocked_mod.websiteUrl = results_file.websiteUrl;
|
||||||
|
blocked_mod.hash = results_file.hash;
|
||||||
|
blocked_mod.matched = false;
|
||||||
|
blocked_mod.localPath = "";
|
||||||
|
|
||||||
|
m_blocked_mods.append(blocked_mod);
|
||||||
|
|
||||||
anyBlocked = true;
|
anyBlocked = true;
|
||||||
} else {
|
} else {
|
||||||
local_file.url = results_file.url.toString();
|
local_file.url = results_file.url.toString();
|
||||||
@ -210,13 +213,16 @@ void PackInstallTask::onResolveModsSucceeded()
|
|||||||
auto message_dialog = new BlockedModsDialog(m_parent, tr("Blocked files found"),
|
auto message_dialog = new BlockedModsDialog(m_parent, tr("Blocked files found"),
|
||||||
tr("The following files are not available for download in third party launchers.<br/>"
|
tr("The following files are not available for download in third party launchers.<br/>"
|
||||||
"You will need to manually download them and add them to the instance."),
|
"You will need to manually download them and add them to the instance."),
|
||||||
text,
|
m_blocked_mods);
|
||||||
urls);
|
|
||||||
|
|
||||||
if (message_dialog->exec() == QDialog::Accepted)
|
if (message_dialog->exec() == QDialog::Accepted) {
|
||||||
|
qDebug() << "Post dialog blocked mods list: " << m_blocked_mods;
|
||||||
createInstance();
|
createInstance();
|
||||||
else
|
}
|
||||||
|
else {
|
||||||
abort();
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
createInstance();
|
createInstance();
|
||||||
}
|
}
|
||||||
@ -320,6 +326,9 @@ void PackInstallTask::downloadPack()
|
|||||||
void PackInstallTask::onModDownloadSucceeded()
|
void PackInstallTask::onModDownloadSucceeded()
|
||||||
{
|
{
|
||||||
m_net_job.reset();
|
m_net_job.reset();
|
||||||
|
if (!m_blocked_mods.isEmpty()) {
|
||||||
|
copyBlockedMods();
|
||||||
|
}
|
||||||
emitSucceeded();
|
emitSucceeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -343,4 +352,35 @@ void PackInstallTask::onModDownloadFailed(QString reason)
|
|||||||
emitFailed(reason);
|
emitFailed(reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @brief copy the matched blocked mods to the instance staging area
|
||||||
|
void PackInstallTask::copyBlockedMods()
|
||||||
|
{
|
||||||
|
setStatus(tr("Copying Blocked Mods..."));
|
||||||
|
setAbortable(false);
|
||||||
|
int i = 0;
|
||||||
|
int total = m_blocked_mods.length();
|
||||||
|
setProgress(i, total);
|
||||||
|
for (auto const& mod : m_blocked_mods) {
|
||||||
|
if (!mod.matched) {
|
||||||
|
qDebug() << mod.name << "was not matched to a local file, skipping copy";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto dest_path = FS::PathCombine(m_stagingPath, ".minecraft", "mods", mod.name);
|
||||||
|
|
||||||
|
setStatus(tr("Copying Blocked Mods (%1 out of %2 are done)").arg(QString::number(i), QString::number(total)));
|
||||||
|
|
||||||
|
qDebug() << "Will try to copy" << mod.localPath << "to" << dest_path;
|
||||||
|
|
||||||
|
if (!FS::copy(mod.localPath, dest_path)()) {
|
||||||
|
qDebug() << "Copy of" << mod.localPath << "to" << dest_path << "Failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
|
setProgress(i, total);
|
||||||
|
}
|
||||||
|
|
||||||
|
setAbortable(true);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ModpacksCH
|
} // namespace ModpacksCH
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
#include "QObjectPtr.h"
|
#include "QObjectPtr.h"
|
||||||
#include "modplatform/flame/FileResolvingTask.h"
|
#include "modplatform/flame/FileResolvingTask.h"
|
||||||
#include "net/NetJob.h"
|
#include "net/NetJob.h"
|
||||||
|
#include "ui/dialogs/BlockedModsDialog.h"
|
||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
@ -76,6 +77,7 @@ private:
|
|||||||
void resolveMods();
|
void resolveMods();
|
||||||
void createInstance();
|
void createInstance();
|
||||||
void downloadPack();
|
void downloadPack();
|
||||||
|
void copyBlockedMods();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NetJob::Ptr m_net_job = nullptr;
|
NetJob::Ptr m_net_job = nullptr;
|
||||||
@ -90,6 +92,7 @@ private:
|
|||||||
Version m_version;
|
Version m_version;
|
||||||
|
|
||||||
QMap<QString, QString> m_files_to_copy;
|
QMap<QString, QString> m_files_to_copy;
|
||||||
|
QList<BlockedMod> m_blocked_mods;
|
||||||
|
|
||||||
//FIXME: nuke
|
//FIXME: nuke
|
||||||
QWidget* m_parent;
|
QWidget* m_parent;
|
||||||
|
@ -22,10 +22,14 @@
|
|||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
#include <toml++/toml.h>
|
#include "FileSystem.h"
|
||||||
|
#include "StringUtils.h"
|
||||||
|
|
||||||
#include "minecraft/mod/Mod.h"
|
#include "minecraft/mod/Mod.h"
|
||||||
#include "modplatform/ModIndex.h"
|
#include "modplatform/ModIndex.h"
|
||||||
|
|
||||||
|
#include <toml++/toml.h>
|
||||||
|
|
||||||
namespace Packwiz {
|
namespace Packwiz {
|
||||||
|
|
||||||
auto getRealIndexName(QDir& index_dir, QString normalized_fname, bool should_find_match) -> QString
|
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;
|
static ModPlatform::ProviderCapabilities ProviderCaps;
|
||||||
|
|
||||||
// Helper functions for extracting data from the TOML file
|
// 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) {
|
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 {};
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
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 {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,6 +149,8 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod)
|
|||||||
// they want to do!
|
// they want to do!
|
||||||
if (index_file.exists()) {
|
if (index_file.exists()) {
|
||||||
index_file.remove();
|
index_file.remove();
|
||||||
|
} else {
|
||||||
|
FS::ensureFilePathExists(index_file.fileName());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!index_file.open(QIODevice::ReadWrite)) {
|
if (!index_file.open(QIODevice::ReadWrite)) {
|
||||||
@ -228,14 +234,14 @@ auto V1::getIndexForMod(QDir& index_dir, QString slug) -> Mod
|
|||||||
toml::table table;
|
toml::table table;
|
||||||
#if TOML_EXCEPTIONS
|
#if TOML_EXCEPTIONS
|
||||||
try {
|
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) {
|
} catch (const toml::parse_error& err) {
|
||||||
qWarning() << QString("Could not open file %1!").arg(normalized_fname);
|
qWarning() << QString("Could not open file %1!").arg(normalized_fname);
|
||||||
qWarning() << "Reason: " << QString(err.what());
|
qWarning() << "Reason: " << QString(err.what());
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
table = toml::parse_file(index_dir.absoluteFilePath(real_fname).toStdString());
|
table = toml::parse_file(StringUtils::toStdString(index_dir.absoluteFilePath(real_fname)));
|
||||||
if (!table) {
|
if (!table) {
|
||||||
qWarning() << QString("Could not open file %1!").arg(normalized_fname);
|
qWarning() << QString("Could not open file %1!").arg(normalized_fname);
|
||||||
qWarning() << "Reason: " << QString(table.error().what());
|
qWarning() << "Reason: " << QString(table.error().what());
|
||||||
|
@ -24,7 +24,6 @@
|
|||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
|
||||||
struct toml_table_t;
|
|
||||||
class QDir;
|
class QDir;
|
||||||
|
|
||||||
// Mod from launcher/minecraft/mod/Mod.h
|
// 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 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 {
|
class V1 {
|
||||||
public:
|
public:
|
||||||
struct Mod {
|
struct Mod {
|
||||||
|
@ -4,12 +4,14 @@
|
|||||||
<file alias="kitteh">kitteh.png</file>
|
<file alias="kitteh">kitteh.png</file>
|
||||||
<file alias="kitteh-xmas">kitteh-xmas.png</file>
|
<file alias="kitteh-xmas">kitteh-xmas.png</file>
|
||||||
<file alias="kitteh-bday">kitteh-bday.png</file>
|
<file alias="kitteh-bday">kitteh-bday.png</file>
|
||||||
<file alias="kitteh-ween">kitteh-ween.png</file>
|
<file alias="kitteh-spooky">kitteh-spooky.png</file>
|
||||||
<file alias="rory">rory.png</file>
|
<file alias="rory">rory.png</file>
|
||||||
<file alias="rory-xmas">rory-xmas.png</file>
|
<file alias="rory-xmas">rory-xmas.png</file>
|
||||||
<file alias="rory-bday">rory-bday.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">rory-flat.png</file>
|
||||||
<file alias="rory-flat-xmas">rory-flat-xmas.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-bday">rory-flat-bday.png</file>
|
||||||
|
<file alias="rory-flat-spooky">rory-flat-spooky.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 92 KiB |
BIN
launcher/resources/backgrounds/rory-flat-spooky.png
Normal file
BIN
launcher/resources/backgrounds/rory-flat-spooky.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 65 KiB |
BIN
launcher/resources/backgrounds/rory-spooky.png
Normal file
BIN
launcher/resources/backgrounds/rory-spooky.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 94 KiB |
@ -266,6 +266,8 @@ public:
|
|||||||
TranslatedAction actionNoAccountsAdded;
|
TranslatedAction actionNoAccountsAdded;
|
||||||
TranslatedAction actionNoDefaultAccount;
|
TranslatedAction actionNoDefaultAccount;
|
||||||
|
|
||||||
|
TranslatedAction actionLockToolbars;
|
||||||
|
|
||||||
QVector<TranslatedToolButton *> all_toolbuttons;
|
QVector<TranslatedToolButton *> all_toolbuttons;
|
||||||
|
|
||||||
QWidget *centralWidget = nullptr;
|
QWidget *centralWidget = nullptr;
|
||||||
@ -432,6 +434,12 @@ public:
|
|||||||
actionManageAccounts->setCheckable(false);
|
actionManageAccounts->setCheckable(false);
|
||||||
actionManageAccounts->setIcon(APPLICATION->getThemedIcon("accounts"));
|
actionManageAccounts->setIcon(APPLICATION->getThemedIcon("accounts"));
|
||||||
all_actions.append(&actionManageAccounts);
|
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)
|
void createMainToolbar(QMainWindow *MainWindow)
|
||||||
@ -439,7 +447,6 @@ public:
|
|||||||
mainToolBar = TranslatedToolbar(MainWindow);
|
mainToolBar = TranslatedToolbar(MainWindow);
|
||||||
mainToolBar->setVisible(menuBar->isNativeMenuBar() || !APPLICATION->settings()->get("MenuBarInsteadOfToolBar").toBool());
|
mainToolBar->setVisible(menuBar->isNativeMenuBar() || !APPLICATION->settings()->get("MenuBarInsteadOfToolBar").toBool());
|
||||||
mainToolBar->setObjectName(QStringLiteral("mainToolBar"));
|
mainToolBar->setObjectName(QStringLiteral("mainToolBar"));
|
||||||
mainToolBar->setMovable(true);
|
|
||||||
mainToolBar->setAllowedAreas(Qt::TopToolBarArea | Qt::BottomToolBarArea);
|
mainToolBar->setAllowedAreas(Qt::TopToolBarArea | Qt::BottomToolBarArea);
|
||||||
mainToolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
mainToolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
||||||
mainToolBar->setFloatable(false);
|
mainToolBar->setFloatable(false);
|
||||||
@ -540,6 +547,8 @@ public:
|
|||||||
viewMenu->addAction(actionCAT);
|
viewMenu->addAction(actionCAT);
|
||||||
viewMenu->addSeparator();
|
viewMenu->addSeparator();
|
||||||
|
|
||||||
|
viewMenu->addAction(actionLockToolbars);
|
||||||
|
|
||||||
menuBar->addMenu(foldersMenu);
|
menuBar->addMenu(foldersMenu);
|
||||||
|
|
||||||
profileMenu = menuBar->addMenu(tr("&Accounts"));
|
profileMenu = menuBar->addMenu(tr("&Accounts"));
|
||||||
@ -620,7 +629,6 @@ public:
|
|||||||
{
|
{
|
||||||
newsToolBar = TranslatedToolbar(MainWindow);
|
newsToolBar = TranslatedToolbar(MainWindow);
|
||||||
newsToolBar->setObjectName(QStringLiteral("newsToolBar"));
|
newsToolBar->setObjectName(QStringLiteral("newsToolBar"));
|
||||||
newsToolBar->setMovable(true);
|
|
||||||
newsToolBar->setAllowedAreas(Qt::TopToolBarArea | Qt::BottomToolBarArea);
|
newsToolBar->setAllowedAreas(Qt::TopToolBarArea | Qt::BottomToolBarArea);
|
||||||
newsToolBar->setIconSize(QSize(16, 16));
|
newsToolBar->setIconSize(QSize(16, 16));
|
||||||
newsToolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
newsToolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
||||||
@ -755,7 +763,6 @@ public:
|
|||||||
instanceToolBar->setObjectName(QStringLiteral("instanceToolBar"));
|
instanceToolBar->setObjectName(QStringLiteral("instanceToolBar"));
|
||||||
// disabled until we have an instance selected
|
// disabled until we have an instance selected
|
||||||
instanceToolBar->setEnabled(false);
|
instanceToolBar->setEnabled(false);
|
||||||
instanceToolBar->setMovable(true);
|
|
||||||
// Qt doesn't like vertical moving toolbars, so we have to force them...
|
// Qt doesn't like vertical moving toolbars, so we have to force them...
|
||||||
// See https://github.com/PolyMC/PolyMC/issues/493
|
// See https://github.com/PolyMC/PolyMC/issues/493
|
||||||
connect(instanceToolBar, &QToolBar::orientationChanged, [=](Qt::Orientation){ instanceToolBar->setOrientation(Qt::Vertical); });
|
connect(instanceToolBar, &QToolBar::orientationChanged, [=](Qt::Orientation){ instanceToolBar->setOrientation(Qt::Vertical); });
|
||||||
@ -937,6 +944,14 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new MainWindow
|
|||||||
connect(ui->actionCAT.operator->(), SIGNAL(toggled(bool)), SLOT(onCatToggled(bool)));
|
connect(ui->actionCAT.operator->(), SIGNAL(toggled(bool)), SLOT(onCatToggled(bool)));
|
||||||
setCatBackground(cat_enable);
|
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
|
// start instance when double-clicked
|
||||||
connect(view, &InstanceView::activated, this, &MainWindow::instanceActivated);
|
connect(view, &InstanceView::activated, this, &MainWindow::instanceActivated);
|
||||||
|
|
||||||
@ -1092,8 +1107,19 @@ QMenu * MainWindow::createPopupMenu()
|
|||||||
{
|
{
|
||||||
QMenu* filteredMenu = QMainWindow::createPopupMenu();
|
QMenu* filteredMenu = QMainWindow::createPopupMenu();
|
||||||
filteredMenu->removeAction( ui->mainToolBar->toggleViewAction() );
|
filteredMenu->removeAction( ui->mainToolBar->toggleViewAction() );
|
||||||
|
|
||||||
|
filteredMenu->addAction(ui->actionLockToolbars);
|
||||||
|
|
||||||
return filteredMenu;
|
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()
|
void MainWindow::konamiTriggered()
|
||||||
{
|
{
|
||||||
@ -1583,8 +1609,8 @@ void MainWindow::setCatBackground(bool enabled)
|
|||||||
QString cat = APPLICATION->settings()->get("BackgroundCat").toString();
|
QString cat = APPLICATION->settings()->get("BackgroundCat").toString();
|
||||||
if (non_stupid_abs(now.daysTo(xmas)) <= 4) {
|
if (non_stupid_abs(now.daysTo(xmas)) <= 4) {
|
||||||
cat += "-xmas";
|
cat += "-xmas";
|
||||||
} else if (cat == "kitteh" && non_stupid_abs(now.daysTo(halloween)) <= 4) {
|
} else if (non_stupid_abs(now.daysTo(halloween)) <= 4) {
|
||||||
cat += "-ween";
|
cat += "-spooky";
|
||||||
} else if (non_stupid_abs(now.daysTo(birthday)) <= 12) {
|
} else if (non_stupid_abs(now.daysTo(birthday)) <= 12) {
|
||||||
cat += "-bday";
|
cat += "-bday";
|
||||||
}
|
}
|
||||||
@ -1644,7 +1670,7 @@ void MainWindow::on_actionCopyInstance_triggered()
|
|||||||
if (!copyInstDlg.exec())
|
if (!copyInstDlg.exec())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto copyTask = new InstanceCopyTask(m_selectedInstance, copyInstDlg.shouldCopySaves(), copyInstDlg.shouldKeepPlaytime());
|
auto copyTask = new InstanceCopyTask(m_selectedInstance, copyInstDlg.getChosenOptions());
|
||||||
copyTask->setName(copyInstDlg.instName());
|
copyTask->setName(copyInstDlg.instName());
|
||||||
copyTask->setGroup(copyInstDlg.instGroup());
|
copyTask->setGroup(copyInstDlg.instGroup());
|
||||||
copyTask->setIcon(copyInstDlg.iconKey());
|
copyTask->setIcon(copyInstDlg.iconKey());
|
||||||
@ -1919,6 +1945,7 @@ void MainWindow::on_actionReportBug_triggered()
|
|||||||
void MainWindow::on_actionClearMetadata_triggered()
|
void MainWindow::on_actionClearMetadata_triggered()
|
||||||
{
|
{
|
||||||
APPLICATION->metacache()->evictAll();
|
APPLICATION->metacache()->evictAll();
|
||||||
|
APPLICATION->metacache()->SaveNow();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef Q_OS_MAC
|
#ifdef Q_OS_MAC
|
||||||
|
@ -207,6 +207,8 @@ private slots:
|
|||||||
|
|
||||||
void globalSettingsClosed();
|
void globalSettingsClosed();
|
||||||
|
|
||||||
|
void lockToolbars(bool);
|
||||||
|
|
||||||
#ifndef Q_OS_MAC
|
#ifndef Q_OS_MAC
|
||||||
void keyReleaseEvent(QKeyEvent *event) override;
|
void keyReleaseEvent(QKeyEvent *event) override;
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,28 +1,186 @@
|
|||||||
#include "BlockedModsDialog.h"
|
#include "BlockedModsDialog.h"
|
||||||
#include "ui_BlockedModsDialog.h"
|
|
||||||
#include <QPushButton>
|
|
||||||
#include <QDialogButtonBox>
|
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
|
#include <QDialogButtonBox>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include "Application.h"
|
||||||
|
#include "ui_BlockedModsDialog.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
|
||||||
BlockedModsDialog::BlockedModsDialog(QWidget *parent, const QString &title, const QString &text, const QString &body, const QList<QUrl> &urls) :
|
BlockedModsDialog::BlockedModsDialog(QWidget* parent, const QString& title, const QString& text, QList<BlockedMod>& mods)
|
||||||
QDialog(parent), ui(new Ui::BlockedModsDialog), urls(urls) {
|
: QDialog(parent), ui(new Ui::BlockedModsDialog), mods(mods)
|
||||||
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
auto openAllButton = ui->buttonBox->addButton(tr("Open All"), QDialogButtonBox::ActionRole);
|
auto openAllButton = ui->buttonBox->addButton(tr("Open All"), QDialogButtonBox::ActionRole);
|
||||||
connect(openAllButton, &QPushButton::clicked, this, &BlockedModsDialog::openAll);
|
connect(openAllButton, &QPushButton::clicked, this, &BlockedModsDialog::openAll);
|
||||||
|
|
||||||
|
connect(&watcher, &QFileSystemWatcher::directoryChanged, this, &BlockedModsDialog::directoryChanged);
|
||||||
|
|
||||||
|
hashing_task = shared_qobject_ptr<ConcurrentTask>(new ConcurrentTask(this, "MakeHashesTask", 10));
|
||||||
|
|
||||||
|
qDebug() << "Mods List: " << mods;
|
||||||
|
|
||||||
|
setupWatch();
|
||||||
|
scanPaths();
|
||||||
|
|
||||||
this->setWindowTitle(title);
|
this->setWindowTitle(title);
|
||||||
ui->label->setText(text);
|
ui->label->setText(text);
|
||||||
ui->textBrowser->setText(body);
|
ui->labelModsFound->setText(tr("Please download the missing mods."));
|
||||||
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockedModsDialog::~BlockedModsDialog() {
|
BlockedModsDialog::~BlockedModsDialog()
|
||||||
|
{
|
||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlockedModsDialog::openAll() {
|
void BlockedModsDialog::openAll()
|
||||||
for(auto &url : urls) {
|
{
|
||||||
QDesktopServices::openUrl(url);
|
for (auto& mod : mods) {
|
||||||
|
QDesktopServices::openUrl(mod.websiteUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @brief update UI with current status of the blocked mod detection
|
||||||
|
void BlockedModsDialog::update()
|
||||||
|
{
|
||||||
|
QString text;
|
||||||
|
QString span;
|
||||||
|
|
||||||
|
for (auto& mod : mods) {
|
||||||
|
if (mod.matched) {
|
||||||
|
// ✔ -> html for HEAVY CHECK MARK : ✔
|
||||||
|
span = QString(tr("<span style=\"color:green\"> ✔ Found at %1 </span>")).arg(mod.localPath);
|
||||||
|
} else {
|
||||||
|
// ✘ -> html for HEAVY BALLOT X : ✘
|
||||||
|
span = QString(tr("<span style=\"color:red\"> ✘ Not Found </span>"));
|
||||||
|
}
|
||||||
|
text += QString(tr("%1: <a href='%2'>%2</a> <p>Hash: %3 %4</p> <br/>")).arg(mod.name, mod.websiteUrl, mod.hash, span);
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->textBrowser->setText(text);
|
||||||
|
|
||||||
|
if (allModsMatched()) {
|
||||||
|
ui->labelModsFound->setText(tr("All mods found ✔"));
|
||||||
|
} else {
|
||||||
|
ui->labelModsFound->setText(tr("Please download the missing mods."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Signal fired when a watched direcotry has changed
|
||||||
|
/// @param path the path to the changed directory
|
||||||
|
void BlockedModsDialog::directoryChanged(QString path)
|
||||||
|
{
|
||||||
|
qDebug() << "Directory changed: " << path;
|
||||||
|
scanPath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief add the user downloads folder and the global mods folder to the filesystem watcher
|
||||||
|
void BlockedModsDialog::setupWatch()
|
||||||
|
{
|
||||||
|
const QString downloadsFolder = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
|
||||||
|
const QString modsFolder = APPLICATION->settings()->get("CentralModsDir").toString();
|
||||||
|
watcher.addPath(downloadsFolder);
|
||||||
|
watcher.addPath(modsFolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief scan all watched folder
|
||||||
|
void BlockedModsDialog::scanPaths()
|
||||||
|
{
|
||||||
|
for (auto& dir : watcher.directories()) {
|
||||||
|
scanPath(dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Scan the directory at path, skip paths that do not contain a file name
|
||||||
|
/// of a blocked mod we are looking for
|
||||||
|
/// @param path the directory to scan
|
||||||
|
void BlockedModsDialog::scanPath(QString path)
|
||||||
|
{
|
||||||
|
QDir scan_dir(path);
|
||||||
|
QDirIterator scan_it(path, QDir::Filter::Files | QDir::Filter::Hidden, QDirIterator::NoIteratorFlags);
|
||||||
|
while (scan_it.hasNext()) {
|
||||||
|
QString file = scan_it.next();
|
||||||
|
|
||||||
|
if (!checkValidPath(file)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto hash_task = Hashing::createBlockedModHasher(file, ModPlatform::Provider::FLAME, "sha1");
|
||||||
|
|
||||||
|
qDebug() << "Creating Hash task for path: " << file;
|
||||||
|
|
||||||
|
connect(hash_task.get(), &Task::succeeded, [this, hash_task, file] { checkMatchHash(hash_task->getResult(), file); });
|
||||||
|
connect(hash_task.get(), &Task::failed, [file] { qDebug() << "Failed to hash path: " << file; });
|
||||||
|
|
||||||
|
hashing_task->addTask(hash_task);
|
||||||
|
}
|
||||||
|
|
||||||
|
hashing_task->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief check if the computed hash for the provided path matches a blocked
|
||||||
|
/// mod we are looking for
|
||||||
|
/// @param hash the computed hash for the provided path
|
||||||
|
/// @param path the path to the local file being compared
|
||||||
|
void BlockedModsDialog::checkMatchHash(QString hash, QString path)
|
||||||
|
{
|
||||||
|
bool match = false;
|
||||||
|
|
||||||
|
qDebug() << "Checking for match on hash: " << hash << "| From path:" << path;
|
||||||
|
|
||||||
|
for (auto& mod : mods) {
|
||||||
|
if (mod.matched) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (mod.hash.compare(hash, Qt::CaseInsensitive) == 0) {
|
||||||
|
mod.matched = true;
|
||||||
|
mod.localPath = path;
|
||||||
|
match = true;
|
||||||
|
|
||||||
|
qDebug() << "Hash match found:" << mod.name << hash << "| From path:" << path;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Check if the name of the file at path matches the name of a blocked mod we are searching for
|
||||||
|
/// @param path the path to check
|
||||||
|
/// @return boolean: did the path match the name of a blocked mod?
|
||||||
|
bool BlockedModsDialog::checkValidPath(QString path)
|
||||||
|
{
|
||||||
|
QFileInfo file = QFileInfo(path);
|
||||||
|
QString filename = file.fileName();
|
||||||
|
|
||||||
|
for (auto& mod : mods) {
|
||||||
|
if (mod.name.compare(filename, Qt::CaseInsensitive) == 0) {
|
||||||
|
qDebug() << "Name match found:" << mod.name << "| From path:" << path;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BlockedModsDialog::allModsMatched()
|
||||||
|
{
|
||||||
|
return std::all_of(mods.begin(), mods.end(), [](auto const& mod) { return mod.matched; });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// qDebug print support for the BlockedMod struct
|
||||||
|
QDebug operator<<(QDebug debug, const BlockedMod& m)
|
||||||
|
{
|
||||||
|
QDebugStateSaver saver(debug);
|
||||||
|
|
||||||
|
debug.nospace() << "{ name: " << m.name << ", websiteUrl: " << m.websiteUrl << ", hash: " << m.hash << ", matched: " << m.matched
|
||||||
|
<< ", localPath: " << m.localPath << "}";
|
||||||
|
|
||||||
|
return debug;
|
||||||
|
}
|
||||||
|
@ -1,7 +1,23 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
|
#include <QString>
|
||||||
|
#include <QList>
|
||||||
|
|
||||||
|
#include <QFileSystemWatcher>
|
||||||
|
|
||||||
|
#include "modplatform/helpers/HashUtils.h"
|
||||||
|
|
||||||
|
#include "tasks/ConcurrentTask.h"
|
||||||
|
|
||||||
|
struct BlockedMod {
|
||||||
|
QString name;
|
||||||
|
QString websiteUrl;
|
||||||
|
QString hash;
|
||||||
|
bool matched;
|
||||||
|
QString localPath;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
namespace Ui { class BlockedModsDialog; }
|
namespace Ui { class BlockedModsDialog; }
|
||||||
@ -11,12 +27,27 @@ class BlockedModsDialog : public QDialog {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BlockedModsDialog(QWidget *parent, const QString &title, const QString &text, const QString &body, const QList<QUrl> &urls);
|
BlockedModsDialog(QWidget *parent, const QString &title, const QString &text, QList<BlockedMod> &mods);
|
||||||
|
|
||||||
~BlockedModsDialog() override;
|
~BlockedModsDialog() override;
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::BlockedModsDialog *ui;
|
Ui::BlockedModsDialog *ui;
|
||||||
const QList<QUrl> &urls;
|
QList<BlockedMod> &mods;
|
||||||
|
QFileSystemWatcher watcher;
|
||||||
|
shared_qobject_ptr<ConcurrentTask> hashing_task;
|
||||||
|
|
||||||
void openAll();
|
void openAll();
|
||||||
|
void update();
|
||||||
|
void directoryChanged(QString path);
|
||||||
|
void setupWatch();
|
||||||
|
void scanPaths();
|
||||||
|
void scanPath(QString path);
|
||||||
|
void checkMatchHash(QString hash, QString path);
|
||||||
|
|
||||||
|
bool checkValidPath(QString path);
|
||||||
|
bool allModsMatched();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
QDebug operator<<(QDebug debug, const BlockedMod &m);
|
||||||
|
@ -13,8 +13,8 @@
|
|||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string notr="true">BlockedModsDialog</string>
|
<string notr="true">BlockedModsDialog</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<item row="0" column="0">
|
<item>
|
||||||
<widget class="QLabel" name="label">
|
<widget class="QLabel" name="label">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string notr="true"/>
|
<string notr="true"/>
|
||||||
@ -24,17 +24,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0">
|
<item>
|
||||||
<widget class="QDialogButtonBox" name="buttonBox">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="standardButtons">
|
|
||||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
|
||||||
<widget class="QTextBrowser" name="textBrowser">
|
<widget class="QTextBrowser" name="textBrowser">
|
||||||
<property name="acceptRichText">
|
<property name="acceptRichText">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
@ -44,6 +34,27 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="bottomBoxH">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="labelModsFound">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<resources/>
|
||||||
|
@ -44,7 +44,6 @@
|
|||||||
|
|
||||||
#include "BaseVersion.h"
|
#include "BaseVersion.h"
|
||||||
#include "icons/IconList.h"
|
#include "icons/IconList.h"
|
||||||
#include "tasks/Task.h"
|
|
||||||
#include "BaseInstance.h"
|
#include "BaseInstance.h"
|
||||||
#include "InstanceList.h"
|
#include "InstanceList.h"
|
||||||
|
|
||||||
@ -78,8 +77,14 @@ CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
|
|||||||
}
|
}
|
||||||
ui->groupBox->setCurrentIndex(index);
|
ui->groupBox->setCurrentIndex(index);
|
||||||
ui->groupBox->lineEdit()->setPlaceholderText(tr("No group"));
|
ui->groupBox->lineEdit()->setPlaceholderText(tr("No group"));
|
||||||
ui->copySavesCheckbox->setChecked(m_copySaves);
|
ui->copySavesCheckbox->setChecked(m_selectedOptions.isCopySavesEnabled());
|
||||||
ui->keepPlaytimeCheckbox->setChecked(m_keepPlaytime);
|
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()
|
CopyInstanceDialog::~CopyInstanceDialog()
|
||||||
@ -117,6 +122,31 @@ QString CopyInstanceDialog::instGroup() const
|
|||||||
return ui->groupBox->currentText();
|
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()
|
void CopyInstanceDialog::on_iconButton_clicked()
|
||||||
{
|
{
|
||||||
IconPickerDialog dlg(this);
|
IconPickerDialog dlg(this);
|
||||||
@ -129,42 +159,64 @@ void CopyInstanceDialog::on_iconButton_clicked()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CopyInstanceDialog::on_instNameTextBox_textChanged(const QString &arg1)
|
void CopyInstanceDialog::on_instNameTextBox_textChanged(const QString &arg1)
|
||||||
{
|
{
|
||||||
updateDialogState();
|
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)
|
void CopyInstanceDialog::on_copySavesCheckbox_stateChanged(int state)
|
||||||
{
|
{
|
||||||
if(state == Qt::Unchecked)
|
m_selectedOptions.enableCopySaves(state == Qt::Checked);
|
||||||
{
|
updateSelectAllCheckbox();
|
||||||
m_copySaves = false;
|
|
||||||
}
|
|
||||||
else if(state == Qt::Checked)
|
|
||||||
{
|
|
||||||
m_copySaves = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CopyInstanceDialog::shouldKeepPlaytime() const
|
|
||||||
{
|
|
||||||
return m_keepPlaytime;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CopyInstanceDialog::on_keepPlaytimeCheckbox_stateChanged(int state)
|
void CopyInstanceDialog::on_keepPlaytimeCheckbox_stateChanged(int state)
|
||||||
{
|
{
|
||||||
if(state == Qt::Unchecked)
|
m_selectedOptions.enableKeepPlaytime(state == Qt::Checked);
|
||||||
{
|
updateSelectAllCheckbox();
|
||||||
m_keepPlaytime = false;
|
}
|
||||||
}
|
|
||||||
else if(state == Qt::Checked)
|
void CopyInstanceDialog::on_copyGameOptionsCheckbox_stateChanged(int state)
|
||||||
{
|
{
|
||||||
m_keepPlaytime = true;
|
m_selectedOptions.enableCopyGameOptions(state == Qt::Checked);
|
||||||
}
|
updateSelectAllCheckbox();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CopyInstanceDialog::on_copyResPacksCheckbox_stateChanged(int state)
|
||||||
|
{
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include "BaseVersion.h"
|
#include "BaseVersion.h"
|
||||||
#include <BaseInstance.h>
|
#include "InstanceCopyPrefs.h"
|
||||||
|
|
||||||
class BaseInstance;
|
class BaseInstance;
|
||||||
|
|
||||||
@ -39,20 +39,29 @@ public:
|
|||||||
QString instName() const;
|
QString instName() const;
|
||||||
QString instGroup() const;
|
QString instGroup() const;
|
||||||
QString iconKey() const;
|
QString iconKey() const;
|
||||||
bool shouldCopySaves() const;
|
const InstanceCopyPrefs& getChosenOptions() const;
|
||||||
bool shouldKeepPlaytime() const;
|
|
||||||
|
|
||||||
private
|
private
|
||||||
slots:
|
slots:
|
||||||
void on_iconButton_clicked();
|
void on_iconButton_clicked();
|
||||||
void on_instNameTextBox_textChanged(const QString &arg1);
|
void on_instNameTextBox_textChanged(const QString &arg1);
|
||||||
|
// Checkboxes
|
||||||
|
void on_selectAllCheckbox_stateChanged(int state);
|
||||||
void on_copySavesCheckbox_stateChanged(int state);
|
void on_copySavesCheckbox_stateChanged(int state);
|
||||||
void on_keepPlaytimeCheckbox_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:
|
private:
|
||||||
|
void checkAllCheckboxes(const bool& b);
|
||||||
|
void updateSelectAllCheckbox();
|
||||||
|
/* data */
|
||||||
Ui::CopyInstanceDialog *ui;
|
Ui::CopyInstanceDialog *ui;
|
||||||
QString InstIconKey;
|
QString InstIconKey;
|
||||||
InstancePtr m_original;
|
InstancePtr m_original;
|
||||||
bool m_copySaves = true;
|
InstanceCopyPrefs m_selectedOptions;
|
||||||
bool m_keepPlaytime = true;
|
|
||||||
};
|
};
|
||||||
|
@ -9,8 +9,8 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>345</width>
|
<width>341</width>
|
||||||
<height>323</height>
|
<height>399</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
@ -33,7 +33,7 @@
|
|||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" stdset="0">
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
<width>40</width>
|
<width>60</width>
|
||||||
<height>20</height>
|
<height>20</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
@ -60,7 +60,7 @@
|
|||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" stdset="0">
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
<width>40</width>
|
<width>60</width>
|
||||||
<height>20</height>
|
<height>20</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
@ -83,7 +83,10 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="groupDropdownLayout">
|
||||||
|
<property name="verticalSpacing">
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
<widget class="QLabel" name="labelVersion_3">
|
<widget class="QLabel" name="labelVersion_3">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -110,18 +113,96 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="copySavesCheckbox">
|
<layout class="QHBoxLayout" name="selectAllButtonLayout">
|
||||||
<property name="text">
|
<item>
|
||||||
<string>Copy saves</string>
|
<widget class="QCheckBox" name="selectAllCheckbox">
|
||||||
</property>
|
<property name="sizePolicy">
|
||||||
</widget>
|
<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>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="keepPlaytimeCheckbox">
|
<layout class="QGridLayout" name="copyOptionsLayout">
|
||||||
<property name="text">
|
<item row="6" column="1">
|
||||||
<string>Keep play time</string>
|
<widget class="QCheckBox" name="copyModsCheckbox">
|
||||||
</property>
|
<property name="toolTip">
|
||||||
</widget>
|
<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>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QDialogButtonBox" name="buttonBox">
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
@ -139,8 +220,6 @@
|
|||||||
<tabstop>iconButton</tabstop>
|
<tabstop>iconButton</tabstop>
|
||||||
<tabstop>instNameTextBox</tabstop>
|
<tabstop>instNameTextBox</tabstop>
|
||||||
<tabstop>groupBox</tabstop>
|
<tabstop>groupBox</tabstop>
|
||||||
<tabstop>copySavesCheckbox</tabstop>
|
|
||||||
<tabstop>keepPlaytimeCheckbox</tabstop>
|
|
||||||
</tabstops>
|
</tabstops>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="../../graphics.qrc"/>
|
<include location="../../graphics.qrc"/>
|
||||||
@ -153,8 +232,8 @@
|
|||||||
<slot>accept()</slot>
|
<slot>accept()</slot>
|
||||||
<hints>
|
<hints>
|
||||||
<hint type="sourcelabel">
|
<hint type="sourcelabel">
|
||||||
<x>248</x>
|
<x>254</x>
|
||||||
<y>254</y>
|
<y>316</y>
|
||||||
</hint>
|
</hint>
|
||||||
<hint type="destinationlabel">
|
<hint type="destinationlabel">
|
||||||
<x>157</x>
|
<x>157</x>
|
||||||
@ -169,8 +248,8 @@
|
|||||||
<slot>reject()</slot>
|
<slot>reject()</slot>
|
||||||
<hints>
|
<hints>
|
||||||
<hint type="sourcelabel">
|
<hint type="sourcelabel">
|
||||||
<x>316</x>
|
<x>322</x>
|
||||||
<y>260</y>
|
<y>316</y>
|
||||||
</hint>
|
</hint>
|
||||||
<hint type="destinationlabel">
|
<hint type="destinationlabel">
|
||||||
<x>286</x>
|
<x>286</x>
|
||||||
|
@ -39,13 +39,12 @@
|
|||||||
#include <MMCZip.h>
|
#include <MMCZip.h>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <qfilesystemmodel.h>
|
#include <QFileSystemModel>
|
||||||
|
|
||||||
#include <QSortFilterProxyModel>
|
#include <QSortFilterProxyModel>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <qstack.h>
|
|
||||||
#include <QSaveFile>
|
#include <QSaveFile>
|
||||||
#include "MMCStrings.h"
|
#include "StringUtils.h"
|
||||||
#include "SeparatorPrefixTree.h"
|
#include "SeparatorPrefixTree.h"
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include <icons/IconList.h>
|
#include <icons/IconList.h>
|
||||||
@ -85,7 +84,7 @@ public:
|
|||||||
// sort and proxy model breaks the original model...
|
// sort and proxy model breaks the original model...
|
||||||
if (sortColumn() == 0)
|
if (sortColumn() == 0)
|
||||||
{
|
{
|
||||||
return Strings::naturalCompare(leftFileInfo.fileName(), rightFileInfo.fileName(),
|
return StringUtils::naturalCompare(leftFileInfo.fileName(), rightFileInfo.fileName(),
|
||||||
Qt::CaseInsensitive) < 0;
|
Qt::CaseInsensitive) < 0;
|
||||||
}
|
}
|
||||||
if (sortColumn() == 1)
|
if (sortColumn() == 1)
|
||||||
@ -94,7 +93,7 @@ public:
|
|||||||
auto rightSize = rightFileInfo.size();
|
auto rightSize = rightFileInfo.size();
|
||||||
if ((leftSize == rightSize) || (leftFileInfo.isDir() && rightFileInfo.isDir()))
|
if ((leftSize == rightSize) || (leftFileInfo.isDir() && rightFileInfo.isDir()))
|
||||||
{
|
{
|
||||||
return Strings::naturalCompare(leftFileInfo.fileName(),
|
return StringUtils::naturalCompare(leftFileInfo.fileName(),
|
||||||
rightFileInfo.fileName(),
|
rightFileInfo.fileName(),
|
||||||
Qt::CaseInsensitive) < 0
|
Qt::CaseInsensitive) < 0
|
||||||
? asc
|
? asc
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* PolyMC - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* 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
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -131,6 +132,8 @@ QList<BasePage*> ModDownloadDialog::getPages()
|
|||||||
if (APPLICATION->capabilities() & Application::SupportsFlame)
|
if (APPLICATION->capabilities() & Application::SupportsFlame)
|
||||||
pages.append(FlameModPage::create(this, m_instance));
|
pages.append(FlameModPage::create(this, m_instance));
|
||||||
|
|
||||||
|
m_selectedPage = dynamic_cast<ModPage*>(pages[0]);
|
||||||
|
|
||||||
return pages;
|
return pages;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,12 +181,22 @@ void ModDownloadDialog::selectedPageChanged(BasePage* previous, BasePage* select
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* selected_page = dynamic_cast<ModPage*>(selected);
|
m_selectedPage = dynamic_cast<ModPage*>(selected);
|
||||||
if (!selected_page) {
|
if (!m_selectedPage) {
|
||||||
qCritical() << "Page '" << selected->displayName() << "' in ModDownloadDialog is not a ModPage!";
|
qCritical() << "Page '" << selected->displayName() << "' in ModDownloadDialog is not a ModPage!";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Same effect as having a global search bar
|
// 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;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* PolyMC - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* 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
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -32,13 +33,14 @@ class ModDownloadDialog;
|
|||||||
|
|
||||||
class PageContainer;
|
class PageContainer;
|
||||||
class QDialogButtonBox;
|
class QDialogButtonBox;
|
||||||
|
class ModPage;
|
||||||
class ModrinthModPage;
|
class ModrinthModPage;
|
||||||
|
|
||||||
class ModDownloadDialog final : public QDialog, public BasePageProvider
|
class ModDownloadDialog final : public QDialog, public BasePageProvider
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ModDownloadDialog(const std::shared_ptr<ModFolderModel>& mods, QWidget* parent, BaseInstance* instance);
|
explicit ModDownloadDialog(const std::shared_ptr<ModFolderModel>& mods, QWidget* parent, BaseInstance* instance);
|
||||||
~ModDownloadDialog() override = default;
|
~ModDownloadDialog() override = default;
|
||||||
|
|
||||||
@ -51,22 +53,26 @@ public:
|
|||||||
bool isModSelected(QString name) const;
|
bool isModSelected(QString name) const;
|
||||||
|
|
||||||
const QList<ModDownloadTask*> getTasks();
|
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 confirm();
|
||||||
void accept() override;
|
void accept() override;
|
||||||
void reject() override;
|
void reject() override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void selectedPageChanged(BasePage* previous, BasePage* selected);
|
void selectedPageChanged(BasePage* previous, BasePage* selected);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::ModDownloadDialog *ui = nullptr;
|
Ui::ModDownloadDialog* ui = nullptr;
|
||||||
PageContainer * m_container = nullptr;
|
PageContainer* m_container = nullptr;
|
||||||
QDialogButtonBox * m_buttons = nullptr;
|
QDialogButtonBox* m_buttons = nullptr;
|
||||||
QVBoxLayout *m_verticalLayout = nullptr;
|
QVBoxLayout* m_verticalLayout = nullptr;
|
||||||
|
ModPage* m_selectedPage = nullptr;
|
||||||
|
|
||||||
QHash<QString, ModDownloadTask*> modTask;
|
QHash<QString, ModDownloadTask*> modTask;
|
||||||
BaseInstance *m_instance;
|
BaseInstance* m_instance;
|
||||||
};
|
};
|
||||||
|
@ -27,11 +27,7 @@
|
|||||||
<item row="4" column="1" colspan="3">
|
<item row="4" column="1" colspan="3">
|
||||||
<layout class="QGridLayout" name="gridLayout_2">
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
<item row="0" column="1">
|
<item row="0" column="1">
|
||||||
<widget class="QLineEdit" name="filterEdit">
|
<widget class="QLineEdit" name="filterEdit"/>
|
||||||
<property name="clearButtonEnabled">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
<widget class="QLabel" name="filterLabel">
|
<widget class="QLabel" name="filterLabel">
|
||||||
|
@ -48,11 +48,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<layout class="QGridLayout" name="gridLayout_2">
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
<item row="0" column="1">
|
<item row="0" column="1">
|
||||||
<widget class="QLineEdit" name="filterEdit">
|
<widget class="QLineEdit" name="filterEdit"/>
|
||||||
<property name="clearButtonEnabled">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
<widget class="QLabel" name="filterLabel">
|
<widget class="QLabel" name="filterLabel">
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* PolyMC - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* 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
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -37,7 +38,9 @@
|
|||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "ui_ModPage.h"
|
#include "ui_ModPage.h"
|
||||||
|
|
||||||
|
#include <QDesktopServices>
|
||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
|
#include <QRegularExpression>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include <HoeDown.h>
|
#include <HoeDown.h>
|
||||||
@ -80,6 +83,8 @@ ModPage::ModPage(ModDownloadDialog* dialog, BaseInstance* instance, ModAPI* api)
|
|||||||
|
|
||||||
ui->packView->setItemDelegate(new ProjectItemDelegate(this));
|
ui->packView->setItemDelegate(new ProjectItemDelegate(this));
|
||||||
ui->packView->installEventFilter(this);
|
ui->packView->installEventFilter(this);
|
||||||
|
|
||||||
|
connect(ui->packDescription, &QTextBrowser::anchorClicked, this, &ModPage::openUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
ModPage::~ModPage()
|
ModPage::~ModPage()
|
||||||
@ -159,7 +164,7 @@ void ModPage::triggerSearch()
|
|||||||
auto changed = m_filter_widget->changed();
|
auto changed = m_filter_widget->changed();
|
||||||
m_filter = m_filter_widget->getFilter();
|
m_filter = m_filter_widget->getFilter();
|
||||||
|
|
||||||
if(changed){
|
if (changed) {
|
||||||
ui->packView->clearSelection();
|
ui->packView->clearSelection();
|
||||||
ui->packDescription->clear();
|
ui->packDescription->clear();
|
||||||
ui->versionSelectionBox->clear();
|
ui->versionSelectionBox->clear();
|
||||||
@ -241,6 +246,79 @@ void ModPage::onModSelected()
|
|||||||
ui->packView->adjustSize();
|
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 ********/
|
/******** Make changes to the UI ********/
|
||||||
|
|
||||||
@ -317,8 +395,7 @@ void ModPage::updateUi()
|
|||||||
text += "<br>" + tr(" by ") + authorStrs.join(", ");
|
text += "<br>" + tr(" by ") + authorStrs.join(", ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (current.extraDataLoaded) {
|
||||||
if(current.extraDataLoaded) {
|
|
||||||
if (!current.extraData.donate.isEmpty()) {
|
if (!current.extraData.donate.isEmpty()) {
|
||||||
text += "<br><br>" + tr("Donate information: ");
|
text += "<br><br>" + tr("Donate information: ");
|
||||||
auto donateToStr = [](ModPlatform::DonationData& donate) -> QString {
|
auto donateToStr = [](ModPlatform::DonationData& donate) -> QString {
|
||||||
|
@ -82,6 +82,7 @@ class ModPage : public QWidget, public BasePage {
|
|||||||
void onSelectionChanged(QModelIndex first, QModelIndex second);
|
void onSelectionChanged(QModelIndex first, QModelIndex second);
|
||||||
void onVersionSelectionChanged(QString data);
|
void onVersionSelectionChanged(QString data);
|
||||||
void onModSelected();
|
void onModSelected();
|
||||||
|
virtual void openUrl(const QUrl& url);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Ui::ModPage* ui = nullptr;
|
Ui::ModPage* ui = nullptr;
|
||||||
|
@ -16,10 +16,10 @@
|
|||||||
<item row="1" column="2">
|
<item row="1" column="2">
|
||||||
<widget class="ProjectDescriptionPage" name="packDescription">
|
<widget class="ProjectDescriptionPage" name="packDescription">
|
||||||
<property name="openExternalLinks">
|
<property name="openExternalLinks">
|
||||||
<bool>true</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
<property name="openLinks">
|
<property name="openLinks">
|
||||||
<bool>true</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -20,7 +20,8 @@
|
|||||||
|
|
||||||
#include <modplatform/atlauncher/ATLPackIndex.h>
|
#include <modplatform/atlauncher/ATLPackIndex.h>
|
||||||
#include <Version.h>
|
#include <Version.h>
|
||||||
#include <MMCStrings.h>
|
|
||||||
|
#include "StringUtils.h"
|
||||||
|
|
||||||
namespace Atl {
|
namespace Atl {
|
||||||
|
|
||||||
@ -86,7 +87,7 @@ bool FilterModel::lessThan(const QModelIndex &left, const QModelIndex &right) co
|
|||||||
return lv < rv;
|
return lv < rv;
|
||||||
}
|
}
|
||||||
else if (currentSorting == ByName) {
|
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...
|
// Invalid sorting set, somehow...
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* PolyMC - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* 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
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -78,3 +79,19 @@ bool FlameModPage::optedOut(ModPlatform::IndexedVersion& ver) const
|
|||||||
// other mod providers start loading before being selected, at least with
|
// other mod providers start loading before being selected, at least with
|
||||||
// my Qt, so we need to implement this in every derived class...
|
// my Qt, so we need to implement this in every derived class...
|
||||||
auto FlameModPage::shouldDisplay() const -> bool { return true; }
|
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);
|
||||||
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* PolyMC - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* 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
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -64,4 +65,6 @@ class FlameModPage : public ModPage {
|
|||||||
bool optedOut(ModPlatform::IndexedVersion& ver) const override;
|
bool optedOut(ModPlatform::IndexedVersion& ver) const override;
|
||||||
|
|
||||||
auto shouldDisplay() const -> bool override;
|
auto shouldDisplay() const -> bool override;
|
||||||
|
|
||||||
|
void openUrl(const QUrl& url) override;
|
||||||
};
|
};
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "ui/widgets/ProjectItem.h"
|
#include "ui/widgets/ProjectItem.h"
|
||||||
|
|
||||||
#include <MMCStrings.h>
|
|
||||||
#include <Version.h>
|
#include <Version.h>
|
||||||
|
|
||||||
#include <QtMath>
|
#include <QtMath>
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
#include "modplatform/modpacksch/FTBPackManifest.h"
|
#include "modplatform/modpacksch/FTBPackManifest.h"
|
||||||
#include <MMCStrings.h>
|
|
||||||
|
#include "StringUtils.h"
|
||||||
|
|
||||||
namespace Ftb {
|
namespace Ftb {
|
||||||
|
|
||||||
@ -81,7 +82,7 @@ bool FilterModel::lessThan(const QModelIndex &left, const QModelIndex &right) co
|
|||||||
return leftPack.installs < rightPack.installs;
|
return leftPack.installs < rightPack.installs;
|
||||||
}
|
}
|
||||||
else if (currentSorting == ByName) {
|
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...
|
// Invalid sorting set, somehow...
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
#include "ListModel.h"
|
#include "ListModel.h"
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
|
||||||
#include <MMCStrings.h>
|
#include "StringUtils.h"
|
||||||
#include <Version.h>
|
#include <Version.h>
|
||||||
|
|
||||||
#include <QtMath>
|
#include <QtMath>
|
||||||
@ -66,7 +66,7 @@ bool FilterModel::lessThan(const QModelIndex &left, const QModelIndex &right) co
|
|||||||
return lv < rv;
|
return lv < rv;
|
||||||
|
|
||||||
} else if(currentSorting == Sorting::ByName) {
|
} 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?!
|
//UHM, some inavlid value set?!
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 4b166b69f28e70a416a1a04a98f365d2aeb90de8
|
Subproject commit cc741c9f5f2a62856a2a2e9e275f61eb0591c09c
|
@ -110,6 +110,142 @@ VIAddVersionKey /LANG=${LANG_ENGLISH} "LegalCopyright" "@Launcher_Copyright@"
|
|||||||
VIAddVersionKey /LANG=${LANG_ENGLISH} "FileVersion" "@Launcher_VERSION_NAME4@"
|
VIAddVersionKey /LANG=${LANG_ENGLISH} "FileVersion" "@Launcher_VERSION_NAME4@"
|
||||||
VIAddVersionKey /LANG=${LANG_ENGLISH} "ProductVersion" "@Launcher_VERSION_NAME4@"
|
VIAddVersionKey /LANG=${LANG_ENGLISH} "ProductVersion" "@Launcher_VERSION_NAME4@"
|
||||||
|
|
||||||
|
|
||||||
|
;--------------------------------
|
||||||
|
; Shell Associate Macros
|
||||||
|
|
||||||
|
!macro APP_SETUP DESCRIPTION ICON APP_ID APP_NAME APP_EXE COMMANDTEXT COMMAND ; VERB APP_COMPANY
|
||||||
|
; setup APP_ID
|
||||||
|
WriteRegStr ShCtx "Software\Classes\${APP_ID}" "" `${DESCRIPTION}`
|
||||||
|
WriteRegStr ShCtx "Software\Classes\${APP_ID}\DefaultIcon" "" `${ICON}`
|
||||||
|
; default open verb
|
||||||
|
WriteRegStr ShCtx "Software\Classes\${APP_ID}\shell" "" "open"
|
||||||
|
WriteRegStr ShCtx "Software\Classes\${APP_ID}\shell\open" "" `${COMMANDTEXT}`
|
||||||
|
WriteRegStr ShCtx "Software\Classes\${APP_ID}\shell\open\command" "" `${COMMAND}`
|
||||||
|
|
||||||
|
; if you want the app to use it's own implementation of a verb
|
||||||
|
;WriteRegStr ShCtx "Software\Classes\${APP_ID}\shell\${VERB}" "" "${DESCRIPTION}"
|
||||||
|
;WriteRegStr ShCtx "Software\Classes\${APP_ID}\shell\${VERB}\command" "" `${COMMAND}`
|
||||||
|
|
||||||
|
WriteRegStr ShCtx "Software\Classes\Applications\${APP_EXE}\shell\open\command" "" `${COMMAND}`
|
||||||
|
WriteRegStr ShCtx "Software\Classes\Applications\${APP_EXE}" "FriendlyAppName" `${APP_NAME}` ; [Optional]
|
||||||
|
;WriteRegStr ShCtx "Software\Classes\Applications\${APP_EXE}" "ApplicationCompany" `${APP_COMPANY}` ; [Optional]
|
||||||
|
;WriteRegNone ShCtx "Software\Classes\Applications\${APP_EXE}\SupportedTypes" ".${EXT}" ; [Optional] Only allow "Open With" with specific extension(s) on WinXP+
|
||||||
|
|
||||||
|
# Register "Default Programs" [Optional]
|
||||||
|
!ifdef REGISTER_DEFAULTPROGRAMS
|
||||||
|
WriteRegStr ShCtx "Software\Classes\Applications\${APP_EXE}\Capabilities" "ApplicationDescription" `${DESCRIPTION}`
|
||||||
|
WriteRegStr ShCtx "Software\RegisteredApplications" `${APP_NAME}` "Software\Classes\Applications\${APP_EXE}\Capabilities"
|
||||||
|
!endif
|
||||||
|
|
||||||
|
!macroend
|
||||||
|
|
||||||
|
!macro APP_ASSOCIATE EXT APP_ID APP_EXE OVERWIRTE
|
||||||
|
; Backup the previously associated file class
|
||||||
|
${If} ${OVERWIRTE} == true
|
||||||
|
ReadRegStr $R0 ShCtx "Software\Classes\${EXT}" ""
|
||||||
|
WriteRegStr ShCtx "Software\Classes\${EXT}" "${APP_ID}_backup" "$R0"
|
||||||
|
WriteRegStr ShCtx "Software\Classes\${EXT}" "" "${APP_ID}"
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
|
WriteRegNone ShCtx "Software\Classes\${EXT}\OpenWithList" "${APP_EXE}" ; Win2000+
|
||||||
|
WriteRegNone ShCtx "Software\Classes\${EXT}\OpenWithProgids" "${APP_ID}" ; WinXP+
|
||||||
|
|
||||||
|
# Register "Default Programs" [Optional]
|
||||||
|
!ifdef REGISTER_DEFAULTPROGRAMS
|
||||||
|
WriteRegStr ShCtx "Software\Classes\Applications\${APP_EXE}\Capabilities\FileAssociations" "${EXT}" "${APP_ID}"
|
||||||
|
!endif
|
||||||
|
|
||||||
|
!macroend
|
||||||
|
|
||||||
|
!macro APP_UNASSOCIATE EXT APP_ID
|
||||||
|
|
||||||
|
# Unregister file type
|
||||||
|
ClearErrors
|
||||||
|
; restore backup
|
||||||
|
ReadRegStr $R1 ShCtx "Software\Classes\${EXT}" ""
|
||||||
|
${If} $R1 == "${APP_ID}"
|
||||||
|
ReadRegStr $R0 ShCtx "Software\Classes\${EXT}" `${APP_ID}_backup`
|
||||||
|
WriteRegStr ShCtx "Software\Classes\${EXT}" "" "$R0"
|
||||||
|
${Else}
|
||||||
|
ReadRegStr $R0 ShCtx "Software\Classes\${EXT}" ""
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
|
DeleteRegKey /IfEmpty ShCtx "Software\Classes\${APP_ID}"
|
||||||
|
${IfNot} ${Errors}
|
||||||
|
${AndIf} $R0 == "${APP_ID}"
|
||||||
|
DeleteRegValue ShCtx "Software\Classes\${EXT}" ""
|
||||||
|
DeleteRegKey /IfEmpty ShCtx "Software\Classes\${EXT}"
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
|
DeleteRegValue ShCtx "Software\Classes\${EXT}\OpenWithList" "${APP_EXE}"
|
||||||
|
DeleteRegKey /IfEmpty ShCtx "Software\Classes\${EXT}\OpenWithList"
|
||||||
|
DeleteRegValue ShCtx "Software\Classes\${EXT}\OpenWithProgids" "${APP_ID}"
|
||||||
|
DeleteRegKey /IfEmpty ShCtx "Software\Classes\${EXT}\OpenWithProgids"
|
||||||
|
DeleteRegKey /IfEmpty ShCtx "Software\Classes\${EXT}"
|
||||||
|
|
||||||
|
# Attempt to clean up junk left behind by the Windows shell
|
||||||
|
DeleteRegValue HKCU "Software\Microsoft\Windows\CurrentVersion\ApplicationAssociationToasts" "${APP_ID}_${EXT}"
|
||||||
|
DeleteRegValue HKCU "Software\Microsoft\Windows\CurrentVersion\ApplicationAssociationToasts" "Applications\${APP_EXE}_${EXT}"
|
||||||
|
DeleteRegValue HKCU "Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\${EXT}\OpenWithProgids" "${APP_ID}"
|
||||||
|
DeleteRegKey /IfEmpty HKCU "Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\${EXT}\OpenWithProgids"
|
||||||
|
DeleteRegKey /IfEmpty HKCU "Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\${EXT}\OpenWithList"
|
||||||
|
DeleteRegKey /IfEmpty HKCU "Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\${EXT}"
|
||||||
|
;DeleteRegKey HKCU "Software\Microsoft\Windows\Roaming\OpenWith\FileExts\.${EXT}"
|
||||||
|
;DeleteRegKey HKCU "Software\Microsoft\Windows\CurrentVersion\Explorer\RecentDocs\.${EXT}"
|
||||||
|
|
||||||
|
!macroend
|
||||||
|
|
||||||
|
!macro APP_TEARDOWN APP_ID APP_NAME APP_EXE
|
||||||
|
|
||||||
|
# Unregister file type
|
||||||
|
ClearErrors
|
||||||
|
DeleteRegKey /IfEmpty ShCtx "Software\Classes\${APP_ID}\shell"
|
||||||
|
${IfNot} ${Errors}
|
||||||
|
DeleteRegKey ShCtx "Software\Classes\${APP_ID}\DefaultIcon"
|
||||||
|
${EndIf}
|
||||||
|
|
||||||
|
# Unregister "Open With"
|
||||||
|
DeleteRegKey ShCtx "Software\Classes\Applications\${APP_EXE}"
|
||||||
|
|
||||||
|
# Unregister "Default Programs"
|
||||||
|
!ifdef REGISTER_DEFAULTPROGRAMS
|
||||||
|
DeleteRegValue ShCtx "Software\RegisteredApplications" `${APP_NAME}`
|
||||||
|
DeleteRegKey ShCtx "Software\Classes\Applications\${APP_EXE}\Capabilities"
|
||||||
|
DeleteRegKey /IfEmpty ShCtx "Software\Classes\Applications\${APP_EXE}"
|
||||||
|
!endif
|
||||||
|
|
||||||
|
DeleteRegKey ShCtx `Software\Classes\${APP_ID}`
|
||||||
|
DeleteRegKey ShCtx "Software\Classes\Applications\${APP_EXE}"
|
||||||
|
|
||||||
|
# Attempt to clean up junk left behind by the Windows shell
|
||||||
|
DeleteRegValue HKCU "Software\Microsoft\Windows\CurrentVersion\Search\JumplistData" "$INSTDIR\${APP_EXE}"
|
||||||
|
DeleteRegValue HKCU "Software\Classes\Local Settings\Software\Microsoft\Windows\Shell\MuiCache" "$INSTDIR\${APP_EXE}.FriendlyAppName"
|
||||||
|
DeleteRegValue HKCU "Software\Classes\Local Settings\Software\Microsoft\Windows\Shell\MuiCache" "$INSTDIR\${APP_EXE}.ApplicationCompany"
|
||||||
|
DeleteRegValue HKCU "Software\Microsoft\Windows\ShellNoRoam\MUICache" "$INSTDIR\${APP_EXE}" ; WinXP
|
||||||
|
DeleteRegValue HKCU "Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Compatibility Assistant\Store" "$INSTDIR\${APP_EXE}"
|
||||||
|
|
||||||
|
!macroend
|
||||||
|
|
||||||
|
; !defines for use with SHChangeNotify
|
||||||
|
!ifdef SHCNE_ASSOCCHANGED
|
||||||
|
!undef SHCNE_ASSOCCHANGED
|
||||||
|
!endif
|
||||||
|
!define SHCNE_ASSOCCHANGED 0x08000000
|
||||||
|
!ifdef SHCNF_FLUSH
|
||||||
|
!undef SHCNF_FLUSH
|
||||||
|
!endif
|
||||||
|
!define SHCNF_FLUSH 0x1000
|
||||||
|
|
||||||
|
|
||||||
|
# ensure this is called at the end of any section that changes shell keys
|
||||||
|
!macro NotifyShell_AssocChanged
|
||||||
|
; Using the system.dll plugin to call the SHChangeNotify Win32 API function so we
|
||||||
|
; can update the shell.
|
||||||
|
System::Call "shell32::SHChangeNotify(i,i,i,i) (${SHCNE_ASSOCCHANGED}, ${SHCNF_FLUSH}, 0, 0)"
|
||||||
|
!macroend
|
||||||
|
|
||||||
|
|
||||||
;--------------------------------
|
;--------------------------------
|
||||||
|
|
||||||
; The stuff to install
|
; The stuff to install
|
||||||
@ -171,6 +307,27 @@ Section /o "Desktop Shortcut" DESKTOP_SHORTCUTS
|
|||||||
|
|
||||||
SectionEnd
|
SectionEnd
|
||||||
|
|
||||||
|
|
||||||
|
!define APP_ID "@Launcher_CommonName@.App"
|
||||||
|
!define APP_EXE "@Launcher_APP_BINARY_NAME@.exe"
|
||||||
|
!define APP_ICON "$INSTDIR\${APP_EXE},0"
|
||||||
|
!define APP_DESCRIPTION "@Launcher_DisplayName@"
|
||||||
|
!define APP_NAME "@Launcher_DisplayName@"
|
||||||
|
!define APP_CMD_TEXT "Minecraft Modpack"
|
||||||
|
|
||||||
|
!define REGISTER_DEFAULTPROGRAMS ; value doesn't matter
|
||||||
|
|
||||||
|
Section -ShellAssoc
|
||||||
|
|
||||||
|
!insertmacro APP_SETUP `${APP_DESCRIPTION}` `${APP_ICON}` `${APP_ID}` `${APP_CMD_TEXT}` `${APP_EXE}` `${APP_CMD_TEXT}` '$INSTDIR\${APP_EXE} -I "%1"'
|
||||||
|
|
||||||
|
!insertmacro APP_ASSOCIATE ".zip" `${APP_ID}` `${APP_EXE}` false
|
||||||
|
!insertmacro APP_ASSOCIATE ".mrpack" `${APP_ID}` `${APP_EXE}` true
|
||||||
|
|
||||||
|
!insertmacro NotifyShell_AssocChanged
|
||||||
|
SectionEnd
|
||||||
|
|
||||||
|
|
||||||
;--------------------------------
|
;--------------------------------
|
||||||
|
|
||||||
; Uninstaller
|
; Uninstaller
|
||||||
@ -202,6 +359,16 @@ Section "Uninstall"
|
|||||||
|
|
||||||
SectionEnd
|
SectionEnd
|
||||||
|
|
||||||
|
Section -un.ShellAssoc
|
||||||
|
|
||||||
|
!insertmacro APP_TEARDOWN `${APP_ID}` `${APP_NAME}` `${APP_EXE}`
|
||||||
|
|
||||||
|
!insertmacro APP_UNASSOCIATE ".zip" `${APP_ID}`
|
||||||
|
!insertmacro APP_UNASSOCIATE ".mrpack" `${APP_ID}`
|
||||||
|
|
||||||
|
!insertmacro NotifyShell_AssocChanged
|
||||||
|
SectionEnd
|
||||||
|
|
||||||
;--------------------------------
|
;--------------------------------
|
||||||
|
|
||||||
; Extra command line parameters
|
; Extra command line parameters
|
||||||
|
Loading…
Reference in New Issue
Block a user