Merge branch 'PrismLauncher:develop' into data-packs
This commit is contained in:
@@ -248,6 +248,7 @@ void MinecraftInstance::loadSpecificSettings()
|
||||
m_settings->registerSetting("ExportSummary", "");
|
||||
m_settings->registerSetting("ExportAuthor", "");
|
||||
m_settings->registerSetting("ExportOptionalFiles", true);
|
||||
m_settings->registerSetting("ExportRecommendedRAM");
|
||||
|
||||
auto dataPacksEnabled = m_settings->registerSetting("GlobalDataPacksEnabled", false);
|
||||
auto dataPacksPath = m_settings->registerSetting("GlobalDataPacksPath", "");
|
||||
@@ -1019,7 +1020,6 @@ QMap<QString, QString> MinecraftInstance::createCensorFilterFromSession(AuthSess
|
||||
return filter;
|
||||
}
|
||||
|
||||
|
||||
QStringList MinecraftInstance::getLogFileSearchPaths()
|
||||
{
|
||||
return { FS::PathCombine(gameRoot(), "crash-reports"), FS::PathCombine(gameRoot(), "logs"), gameRoot() };
|
||||
|
||||
237
launcher/minecraft/ShortcutUtils.cpp
Normal file
237
launcher/minecraft/ShortcutUtils.cpp
Normal file
@@ -0,0 +1,237 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||
* Copyright (C) 2025 Yihe Li <winmikedows@hotmail.com>
|
||||
*
|
||||
* parent program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* parent program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with parent program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* parent file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
*
|
||||
* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use parent file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "ShortcutUtils.h"
|
||||
|
||||
#include "FileSystem.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QFileDialog>
|
||||
|
||||
#include <BuildConfig.h>
|
||||
#include <DesktopServices.h>
|
||||
#include <icons/IconList.h>
|
||||
|
||||
namespace ShortcutUtils {
|
||||
|
||||
void createInstanceShortcut(const Shortcut& shortcut, const QString& filePath)
|
||||
{
|
||||
if (!shortcut.instance)
|
||||
return;
|
||||
|
||||
QString appPath = QApplication::applicationFilePath();
|
||||
auto icon = APPLICATION->icons()->icon(shortcut.iconKey.isEmpty() ? shortcut.instance->iconKey() : shortcut.iconKey);
|
||||
if (icon == nullptr) {
|
||||
icon = APPLICATION->icons()->icon("grass");
|
||||
}
|
||||
QString iconPath;
|
||||
QStringList args;
|
||||
#if defined(Q_OS_MACOS)
|
||||
if (appPath.startsWith("/private/var/")) {
|
||||
QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"),
|
||||
QObject::tr("The launcher is in the folder it was extracted from, therefore it cannot create shortcuts."));
|
||||
return;
|
||||
}
|
||||
|
||||
iconPath = FS::PathCombine(shortcut.instance->instanceRoot(), "Icon.icns");
|
||||
|
||||
QFile iconFile(iconPath);
|
||||
if (!iconFile.open(QFile::WriteOnly)) {
|
||||
QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Failed to create icon for application."));
|
||||
return;
|
||||
}
|
||||
|
||||
QIcon iconObj = icon->icon();
|
||||
bool success = iconObj.pixmap(1024, 1024).save(iconPath, "ICNS");
|
||||
iconFile.close();
|
||||
|
||||
if (!success) {
|
||||
iconFile.remove();
|
||||
QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Failed to create icon for application."));
|
||||
return;
|
||||
}
|
||||
#elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
||||
if (appPath.startsWith("/tmp/.mount_")) {
|
||||
// AppImage!
|
||||
appPath = QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE"));
|
||||
if (appPath.isEmpty()) {
|
||||
QMessageBox::critical(
|
||||
shortcut.parent, QObject::tr("Create Shortcut"),
|
||||
QObject::tr("Launcher is running as misconfigured AppImage? ($APPIMAGE environment variable is missing)"));
|
||||
} else if (appPath.endsWith("/")) {
|
||||
appPath.chop(1);
|
||||
}
|
||||
}
|
||||
|
||||
iconPath = FS::PathCombine(shortcut.instance->instanceRoot(), "icon.png");
|
||||
|
||||
QFile iconFile(iconPath);
|
||||
if (!iconFile.open(QFile::WriteOnly)) {
|
||||
QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Failed to create icon for shortcut."));
|
||||
return;
|
||||
}
|
||||
bool success = icon->icon().pixmap(64, 64).save(&iconFile, "PNG");
|
||||
iconFile.close();
|
||||
|
||||
if (!success) {
|
||||
iconFile.remove();
|
||||
QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Failed to create icon for shortcut."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (DesktopServices::isFlatpak()) {
|
||||
appPath = "flatpak";
|
||||
args.append({ "run", BuildConfig.LAUNCHER_APPID });
|
||||
}
|
||||
|
||||
#elif defined(Q_OS_WIN)
|
||||
iconPath = FS::PathCombine(shortcut.instance->instanceRoot(), "icon.ico");
|
||||
|
||||
// part of fix for weird bug involving the window icon being replaced
|
||||
// dunno why it happens, but parent 2-line fix seems to be enough, so w/e
|
||||
auto appIcon = APPLICATION->getThemedIcon("logo");
|
||||
|
||||
QFile iconFile(iconPath);
|
||||
if (!iconFile.open(QFile::WriteOnly)) {
|
||||
QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Failed to create icon for shortcut."));
|
||||
return;
|
||||
}
|
||||
bool success = icon->icon().pixmap(64, 64).save(&iconFile, "ICO");
|
||||
iconFile.close();
|
||||
|
||||
// restore original window icon
|
||||
QGuiApplication::setWindowIcon(appIcon);
|
||||
|
||||
if (!success) {
|
||||
iconFile.remove();
|
||||
QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Failed to create icon for shortcut."));
|
||||
return;
|
||||
}
|
||||
|
||||
#else
|
||||
QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Not supported on your platform!"));
|
||||
return;
|
||||
#endif
|
||||
args.append({ "--launch", shortcut.instance->id() });
|
||||
args.append(shortcut.extraArgs);
|
||||
|
||||
if (!FS::createShortcut(filePath, appPath, args, shortcut.name, iconPath)) {
|
||||
#if not defined(Q_OS_MACOS)
|
||||
iconFile.remove();
|
||||
#endif
|
||||
QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"),
|
||||
QObject::tr("Failed to create %1 shortcut!").arg(shortcut.targetString));
|
||||
}
|
||||
}
|
||||
|
||||
void createInstanceShortcutOnDesktop(const Shortcut& shortcut)
|
||||
{
|
||||
if (!shortcut.instance)
|
||||
return;
|
||||
|
||||
QString desktopDir = FS::getDesktopDir();
|
||||
if (desktopDir.isEmpty()) {
|
||||
QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Couldn't find desktop?!"));
|
||||
return;
|
||||
}
|
||||
|
||||
QString shortcutFilePath = FS::PathCombine(desktopDir, FS::RemoveInvalidFilenameChars(shortcut.name));
|
||||
createInstanceShortcut(shortcut, shortcutFilePath);
|
||||
QMessageBox::information(shortcut.parent, QObject::tr("Create Shortcut"),
|
||||
QObject::tr("Created a shortcut to this %1 on your desktop!").arg(shortcut.targetString));
|
||||
}
|
||||
|
||||
void createInstanceShortcutInApplications(const Shortcut& shortcut)
|
||||
{
|
||||
if (!shortcut.instance)
|
||||
return;
|
||||
|
||||
QString applicationsDir = FS::getApplicationsDir();
|
||||
if (applicationsDir.isEmpty()) {
|
||||
QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Couldn't find applications folder?!"));
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(Q_OS_MACOS) || defined(Q_OS_WIN)
|
||||
applicationsDir = FS::PathCombine(applicationsDir, BuildConfig.LAUNCHER_DISPLAYNAME + " Instances");
|
||||
|
||||
QDir applicationsDirQ(applicationsDir);
|
||||
if (!applicationsDirQ.mkpath(".")) {
|
||||
QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"),
|
||||
QObject::tr("Failed to create instances folder in applications folder!"));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
QString shortcutFilePath = FS::PathCombine(applicationsDir, FS::RemoveInvalidFilenameChars(shortcut.name));
|
||||
createInstanceShortcut(shortcut, shortcutFilePath);
|
||||
QMessageBox::information(shortcut.parent, QObject::tr("Create Shortcut"),
|
||||
QObject::tr("Created a shortcut to this %1 in your applications folder!").arg(shortcut.targetString));
|
||||
}
|
||||
|
||||
void createInstanceShortcutInOther(const Shortcut& shortcut)
|
||||
{
|
||||
if (!shortcut.instance)
|
||||
return;
|
||||
|
||||
QString defaultedDir = FS::getDesktopDir();
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
||||
QString extension = ".desktop";
|
||||
#elif defined(Q_OS_WINDOWS)
|
||||
QString extension = ".lnk";
|
||||
#else
|
||||
QString extension = "";
|
||||
#endif
|
||||
|
||||
QString shortcutFilePath = FS::PathCombine(defaultedDir, FS::RemoveInvalidFilenameChars(shortcut.name) + extension);
|
||||
QFileDialog fileDialog;
|
||||
// workaround to make sure the portal file dialog opens in the desktop directory
|
||||
fileDialog.setDirectoryUrl(defaultedDir);
|
||||
|
||||
shortcutFilePath = fileDialog.getSaveFileName(shortcut.parent, QObject::tr("Create Shortcut"), shortcutFilePath,
|
||||
QObject::tr("Desktop Entries") + " (*" + extension + ")");
|
||||
if (shortcutFilePath.isEmpty())
|
||||
return; // file dialog canceled by user
|
||||
|
||||
if (shortcutFilePath.endsWith(extension))
|
||||
shortcutFilePath = shortcutFilePath.mid(0, shortcutFilePath.length() - extension.length());
|
||||
createInstanceShortcut(shortcut, shortcutFilePath);
|
||||
QMessageBox::information(shortcut.parent, QObject::tr("Create Shortcut"),
|
||||
QObject::tr("Created a shortcut to this %1!").arg(shortcut.targetString));
|
||||
}
|
||||
|
||||
} // namespace ShortcutUtils
|
||||
66
launcher/minecraft/ShortcutUtils.h
Normal file
66
launcher/minecraft/ShortcutUtils.h
Normal file
@@ -0,0 +1,66 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||
* Copyright (C) 2025 Yihe Li <winmikedows@hotmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
*
|
||||
* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "Application.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
|
||||
namespace ShortcutUtils {
|
||||
/// A struct to hold parameters for creating a shortcut
|
||||
struct Shortcut {
|
||||
BaseInstance* instance;
|
||||
QString name;
|
||||
QString targetString;
|
||||
QWidget* parent = nullptr;
|
||||
QStringList extraArgs = {};
|
||||
QString iconKey = "";
|
||||
};
|
||||
|
||||
/// Create an instance shortcut on the specified file path
|
||||
void createInstanceShortcut(const Shortcut& shortcut, const QString& filePath);
|
||||
|
||||
/// Create an instance shortcut on the desktop
|
||||
void createInstanceShortcutOnDesktop(const Shortcut& shortcut);
|
||||
|
||||
/// Create an instance shortcut in the Applications directory
|
||||
void createInstanceShortcutInApplications(const Shortcut& shortcut);
|
||||
|
||||
/// Create an instance shortcut in other directories
|
||||
void createInstanceShortcutInOther(const Shortcut& shortcut);
|
||||
|
||||
} // namespace ShortcutUtils
|
||||
@@ -84,7 +84,7 @@ class Mod : public Resource {
|
||||
|
||||
bool valid() const override;
|
||||
|
||||
[[nodiscard]] int compare(const Resource & other, SortType type) const override;
|
||||
[[nodiscard]] int compare(const Resource& other, SortType type) const override;
|
||||
[[nodiscard]] bool applyFilter(QRegularExpression filter) const override;
|
||||
|
||||
// Delete all the files of this mod
|
||||
|
||||
@@ -48,7 +48,8 @@ TexturePackFolderModel::TexturePackFolderModel(const QDir& dir, BaseInstance* in
|
||||
m_column_names = QStringList({ "Enable", "Image", "Name", "Last Modified", "Provider", "Size" });
|
||||
m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Last Modified"), tr("Provider"), tr("Size") });
|
||||
m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::DATE, SortType::PROVIDER, SortType::SIZE };
|
||||
m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive };
|
||||
m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch,
|
||||
QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive };
|
||||
m_columnsHideable = { false, true, false, true, true, true };
|
||||
m_columnsHiddenByDefault = { false, false, false, false, false, true };
|
||||
}
|
||||
|
||||
@@ -268,6 +268,26 @@ void SkinList::installSkins(const QStringList& iconFiles)
|
||||
installSkin(file);
|
||||
}
|
||||
|
||||
QString getUniqueFile(const QString& root, const QString& file)
|
||||
{
|
||||
auto result = FS::PathCombine(root, file);
|
||||
if (!QFileInfo::exists(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
QString baseName = QFileInfo(file).completeBaseName();
|
||||
QString extension = QFileInfo(file).suffix();
|
||||
int tries = 0;
|
||||
while (QFileInfo::exists(result)) {
|
||||
if (++tries > 256)
|
||||
return {};
|
||||
|
||||
QString key = QString("%1%2.%3").arg(baseName).arg(tries).arg(extension);
|
||||
result = FS::PathCombine(root, key);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
QString SkinList::installSkin(const QString& file, const QString& name)
|
||||
{
|
||||
if (file.isEmpty())
|
||||
@@ -282,7 +302,7 @@ QString SkinList::installSkin(const QString& file, const QString& name)
|
||||
if (fileinfo.suffix() != "png" && !SkinModel(fileinfo.absoluteFilePath()).isValid())
|
||||
return tr("Skin images must be 64x64 or 64x32 pixel PNG files.");
|
||||
|
||||
QString target = FS::PathCombine(m_dir.absolutePath(), name.isEmpty() ? fileinfo.fileName() : name);
|
||||
QString target = getUniqueFile(m_dir.absolutePath(), name.isEmpty() ? fileinfo.fileName() : name);
|
||||
|
||||
return QFile::copy(file, target) ? "" : tr("Unable to copy file");
|
||||
}
|
||||
@@ -371,7 +391,8 @@ bool SkinList::setData(const QModelIndex& idx, const QVariant& value, int role)
|
||||
auto& skin = m_skinList[row];
|
||||
auto newName = value.toString();
|
||||
if (skin.name() != newName) {
|
||||
skin.rename(newName);
|
||||
if (!skin.rename(newName))
|
||||
return false;
|
||||
save();
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -122,7 +122,11 @@ QString SkinModel::name() const
|
||||
bool SkinModel::rename(QString newName)
|
||||
{
|
||||
auto info = QFileInfo(m_path);
|
||||
m_path = FS::PathCombine(info.absolutePath(), newName + ".png");
|
||||
auto new_path = FS::PathCombine(info.absolutePath(), newName + ".png");
|
||||
if (QFileInfo::exists(new_path)) {
|
||||
return false;
|
||||
}
|
||||
m_path = new_path;
|
||||
return FS::move(info.absoluteFilePath(), m_path);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user