From 69469b4484b88459a2fa74358197ac1dba4896ff Mon Sep 17 00:00:00 2001 From: Yihe Li Date: Sun, 11 May 2025 03:33:03 +0800 Subject: [PATCH] Refactor shortcut creation logic into its own file Signed-off-by: Yihe Li --- launcher/CMakeLists.txt | 2 + launcher/minecraft/ShortcutUtils.cpp | 264 +++++++++++++++++++++++++++ launcher/minecraft/ShortcutUtils.h | 71 +++++++ launcher/ui/MainWindow.cpp | 191 +------------------ launcher/ui/MainWindow.h | 2 - launcher/ui/MainWindow.ui | 2 +- 6 files changed, 345 insertions(+), 187 deletions(-) create mode 100644 launcher/minecraft/ShortcutUtils.cpp create mode 100644 launcher/minecraft/ShortcutUtils.h diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index dbd9dc0bb..918e38df0 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -310,6 +310,8 @@ set(MINECRAFT_SOURCES minecraft/ParseUtils.h minecraft/ProfileUtils.cpp minecraft/ProfileUtils.h + minecraft/ShortcutUtils.cpp + minecraft/ShortcutUtils.h minecraft/Library.cpp minecraft/Library.h minecraft/MojangDownloadInfo.h diff --git a/launcher/minecraft/ShortcutUtils.cpp b/launcher/minecraft/ShortcutUtils.cpp new file mode 100644 index 000000000..7d4faf231 --- /dev/null +++ b/launcher/minecraft/ShortcutUtils.cpp @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2022 Sefa Eyeoglu + * + * 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 . + * + * 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 +#include + +#include +#include +#include + +namespace ShortcutUtils { + +void createInstanceShortcut(BaseInstance* instance, + QString shortcutName, + QString shortcutFilePath, + QString targetString, + QWidget* parent, + const QStringList& extraArgs) +{ + if (!instance) + return; + + QString appPath = QApplication::applicationFilePath(); + QString iconPath; + QStringList args; +#if defined(Q_OS_MACOS) + appPath = QApplication::applicationFilePath(); + if (appPath.startsWith("/private/var/")) { + QMessageBox::critical(parent, QObject::tr("Create Shortcut"), + QObject::tr("The launcher is in the folder it was extracted from, therefore it cannot create shortcuts.")); + return; + } + + auto pIcon = APPLICATION->icons()->icon(instance->iconKey()); + if (pIcon == nullptr) { + pIcon = APPLICATION->icons()->icon("grass"); + } + + iconPath = FS::PathCombine(instance->instanceRoot(), "Icon.icns"); + + QFile iconFile(iconPath); + if (!iconFile.open(QFile::WriteOnly)) { + QMessageBox::critical(parent, QObject::tr("Create Shortcut"), QObject::tr("Failed to create icon for application.")); + return; + } + + QIcon icon = pIcon->icon(); + + bool success = icon.pixmap(1024, 1024).save(iconPath, "ICNS"); + iconFile.close(); + + if (!success) { + iconFile.remove(); + QMessageBox::critical(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( + 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); + } + } + + auto icon = APPLICATION->icons()->icon(instance->iconKey()); + if (icon == nullptr) { + icon = APPLICATION->icons()->icon("grass"); + } + + iconPath = FS::PathCombine(instance->instanceRoot(), "icon.png"); + + QFile iconFile(iconPath); + if (!iconFile.open(QFile::WriteOnly)) { + QMessageBox::critical(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(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) + auto icon = APPLICATION->icons()->icon(instance->iconKey()); + if (icon == nullptr) { + icon = APPLICATION->icons()->icon("grass"); + } + + iconPath = FS::PathCombine(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(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(parent, QObject::tr("Create Shortcut"), QObject::tr("Failed to create icon for shortcut.")); + return; + } + +#else + QMessageBox::critical(parent, QObject::tr("Create Shortcut"), QObject::tr("Not supported on your platform!")); + return; +#endif + args.append({ "--launch", instance->id() }); + args.append(extraArgs); + + if (!FS::createShortcut(std::move(shortcutFilePath), appPath, args, shortcutName, iconPath)) { +#if not defined(Q_OS_MACOS) + iconFile.remove(); +#endif + QMessageBox::critical(parent, QObject::tr("Create Shortcut"), QObject::tr("Failed to create %1 shortcut!").arg(targetString)); + return; + } +} + +void createInstanceShortcutOnDesktop(BaseInstance* instance, + QString shortcutName, + QString targetString, + QWidget* parent, + const QStringList& extraArgs) +{ + if (!instance) + return; + + QString desktopDir = FS::getDesktopDir(); + if (desktopDir.isEmpty()) { + QMessageBox::critical(parent, QObject::tr("Create Shortcut"), QObject::tr("Couldn't find desktop?!")); + return; + } + + QString shortcutFilePath = FS::PathCombine(FS::getDesktopDir(), FS::RemoveInvalidFilenameChars(shortcutName)); + createInstanceShortcut(instance, shortcutName, shortcutFilePath, targetString, parent, extraArgs); + QMessageBox::information(parent, QObject::tr("Create Shortcut"), + QObject::tr("Created a shortcut to this %1 on your desktop!").arg(targetString)); +} + +void createInstanceShortcutInApplications(BaseInstance* instance, + QString shortcutName, + QString targetString, + QWidget* parent, + const QStringList& extraArgs) +{ + if (!instance) + return; + + QString applicationsDir = FS::getApplicationsDir(); + if (applicationsDir.isEmpty()) { + QMessageBox::critical(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(parent, QObject::tr("Create Shortcut"), + QObject::tr("Failed to create instances folder in applications folder!")); + return; + } +#endif + + QString shortcutFilePath = FS::PathCombine(applicationsDir, FS::RemoveInvalidFilenameChars(shortcutName)); + createInstanceShortcut(instance, shortcutName, shortcutFilePath, targetString, parent, extraArgs); + QMessageBox::information(parent, QObject::tr("Create Shortcut"), + QObject::tr("Created a shortcut to this %1 in your applications folder!").arg(targetString)); +} + +void createInstanceShortcutInOther(BaseInstance* instance, + QString shortcutName, + QString targetString, + QWidget* parent, + const QStringList& extraArgs) +{ + if (!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(shortcutName) + extension); + QFileDialog fileDialog; + // workaround to make sure the portal file dialog opens in the desktop directory + fileDialog.setDirectoryUrl(defaultedDir); + + shortcutFilePath = fileDialog.getSaveFileName(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(instance, shortcutName, shortcutFilePath, targetString, parent, extraArgs); + QMessageBox::information(parent, QObject::tr("Create Shortcut"), QObject::tr("Created a shortcut to this %1!").arg(targetString)); +} + +} // namespace ShortcutUtils diff --git a/launcher/minecraft/ShortcutUtils.h b/launcher/minecraft/ShortcutUtils.h new file mode 100644 index 000000000..88800f5e6 --- /dev/null +++ b/launcher/minecraft/ShortcutUtils.h @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (C) 2022 Sefa Eyeoglu + * + * 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 . + * + * 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 + +namespace ShortcutUtils { +/// Create an instance shortcut on the specified file path +void createInstanceShortcut(BaseInstance* instance, + QString shortcutName, + QString shortcutFilePath, + QString targetString, + QWidget* parent = nullptr, + const QStringList& extraArgs = {}); + +/// Create an instance shortcut on the desktop +void createInstanceShortcutOnDesktop(BaseInstance* instance, + QString shortcutName, + QString targetString, + QWidget* parent = nullptr, + const QStringList& extraArgs = {}); + +/// Create an instance shortcut in the Applications directory +void createInstanceShortcutInApplications(BaseInstance* instance, + QString shortcutName, + QString targetString, + QWidget* parent = nullptr, + const QStringList& extraArgs = {}); + +/// Create an instance shortcut in other directories +void createInstanceShortcutInOther(BaseInstance* instance, + QString shortcutName, + QString targetString, + QWidget* parent = nullptr, + const QStringList& extraArgs = {}); + +} // namespace ShortcutUtils diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 232ba45cd..ed9961975 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -109,6 +109,7 @@ #include "ui/widgets/LabeledToolButton.h" #include "minecraft/PackProfile.h" +#include "minecraft/ShortcutUtils.h" #include "minecraft/VersionFile.h" #include "minecraft/WorldList.h" #include "minecraft/mod/ModFolderModel.h" @@ -1545,157 +1546,6 @@ void MainWindow::on_actionKillInstance_triggered() } } -void MainWindow::createInstanceShortcut(QString shortcutFilePath) -{ - if (!m_selectedInstance) - return; - - QString appPath = QApplication::applicationFilePath(); - QString iconPath; - QStringList args; -#if defined(Q_OS_MACOS) - appPath = QApplication::applicationFilePath(); - if (appPath.startsWith("/private/var/")) { - QMessageBox::critical(this, tr("Create instance shortcut"), - tr("The launcher is in the folder it was extracted from, therefore it cannot create shortcuts.")); - return; - } - - auto pIcon = APPLICATION->icons()->icon(m_selectedInstance->iconKey()); - if (pIcon == nullptr) { - pIcon = APPLICATION->icons()->icon("grass"); - } - - iconPath = FS::PathCombine(m_selectedInstance->instanceRoot(), "Icon.icns"); - - QFile iconFile(iconPath); - if (!iconFile.open(QFile::WriteOnly)) { - QMessageBox::critical(this, tr("Create instance Application"), tr("Failed to create icon for Application.")); - return; - } - - QIcon icon = pIcon->icon(); - - bool success = icon.pixmap(1024, 1024).save(iconPath, "ICNS"); - iconFile.close(); - - if (!success) { - iconFile.remove(); - QMessageBox::critical(this, tr("Create instance Application"), tr("Failed to create icon for Application.")); - return; - } -#elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) - if (appPath.startsWith("/tmp/.mount_")) { - // AppImage! - appPath = QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE")); - if (appPath.isEmpty()) { - QMessageBox::critical(this, tr("Create instance shortcut"), - tr("Launcher is running as misconfigured AppImage? ($APPIMAGE environment variable is missing)")); - } else if (appPath.endsWith("/")) { - appPath.chop(1); - } - } - - auto icon = APPLICATION->icons()->icon(m_selectedInstance->iconKey()); - if (icon == nullptr) { - icon = APPLICATION->icons()->icon("grass"); - } - - iconPath = FS::PathCombine(m_selectedInstance->instanceRoot(), "icon.png"); - - QFile iconFile(iconPath); - if (!iconFile.open(QFile::WriteOnly)) { - QMessageBox::critical(this, tr("Create instance shortcut"), 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(this, tr("Create instance shortcut"), tr("Failed to create icon for shortcut.")); - return; - } - - if (DesktopServices::isFlatpak()) { - appPath = "flatpak"; - args.append({ "run", BuildConfig.LAUNCHER_APPID }); - } - -#elif defined(Q_OS_WIN) - auto icon = APPLICATION->icons()->icon(m_selectedInstance->iconKey()); - if (icon == nullptr) { - icon = APPLICATION->icons()->icon("grass"); - } - - iconPath = FS::PathCombine(m_selectedInstance->instanceRoot(), "icon.ico"); - - // part of fix for weird bug involving the window icon being replaced - // dunno why it happens, but this 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(this, tr("Create instance shortcut"), 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(this, tr("Create instance shortcut"), tr("Failed to create icon for shortcut.")); - return; - } - -#else - QMessageBox::critical(this, tr("Create instance shortcut"), tr("Not supported on your platform!")); - return; -#endif - args.append({ "--launch", m_selectedInstance->id() }); - - if (!FS::createShortcut(std::move(shortcutFilePath), appPath, args, m_selectedInstance->name(), iconPath)) { -#if not defined(Q_OS_MACOS) - iconFile.remove(); -#endif - QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create instance shortcut!")); - return; - } -} - -void MainWindow::on_actionCreateInstanceShortcutOther_triggered() -{ - if (!m_selectedInstance) - 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(m_selectedInstance->name()) + extension); - QFileDialog fileDialog; - // workaround to make sure the portal file dialog opens in the desktop directory - fileDialog.setDirectoryUrl(defaultedDir); - - shortcutFilePath = - fileDialog.getSaveFileName(this, tr("Create Shortcut"), shortcutFilePath, 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(shortcutFilePath); - QMessageBox::information(this, tr("Create instance shortcut"), tr("Created a shortcut to this instance!")); -} - void MainWindow::on_actionCreateInstanceShortcut_triggered() { if (!m_selectedInstance) @@ -1709,44 +1559,17 @@ void MainWindow::on_actionCreateInstanceShortcut_triggered() void MainWindow::on_actionCreateInstanceShortcutDesktop_triggered() { - if (!m_selectedInstance) - return; - - QString desktopDir = FS::getDesktopDir(); - if (desktopDir.isEmpty()) { - QMessageBox::critical(this, tr("Create instance shortcut"), tr("Couldn't find desktop?!")); - return; - } - - QString shortcutFilePath = FS::PathCombine(FS::getDesktopDir(), FS::RemoveInvalidFilenameChars(m_selectedInstance->name())); - createInstanceShortcut(shortcutFilePath); - QMessageBox::information(this, tr("Create instance shortcut"), tr("Created a shortcut to this instance on your desktop!")); + ShortcutUtils::createInstanceShortcutOnDesktop(m_selectedInstance.get(), m_selectedInstance->name(), tr("instance"), this); } void MainWindow::on_actionCreateInstanceShortcutApplications_triggered() { - if (!m_selectedInstance) - return; + ShortcutUtils::createInstanceShortcutInApplications(m_selectedInstance.get(), m_selectedInstance->name(), tr("instance"), this); +} - QString applicationsDir = FS::getApplicationsDir(); - if (applicationsDir.isEmpty()) { - QMessageBox::critical(this, tr("Create instance shortcut"), 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(this, tr("Create instance shortcut"), tr("Failed to create instances folder in applications folder!")); - return; - } -#endif - - QString shortcutFilePath = FS::PathCombine(applicationsDir, FS::RemoveInvalidFilenameChars(m_selectedInstance->name())); - createInstanceShortcut(shortcutFilePath); - QMessageBox::information(this, tr("Create instance shortcut"), tr("Created a shortcut to this instance in your applications folder!")); +void MainWindow::on_actionCreateInstanceShortcutOther_triggered() +{ + ShortcutUtils::createInstanceShortcutInOther(m_selectedInstance.get(), m_selectedInstance->name(), tr("instance"), this); } void MainWindow::taskEnd() diff --git a/launcher/ui/MainWindow.h b/launcher/ui/MainWindow.h index f3f2de730..20ab21e67 100644 --- a/launcher/ui/MainWindow.h +++ b/launcher/ui/MainWindow.h @@ -166,7 +166,6 @@ class MainWindow : public QMainWindow { void on_actionEditInstance_triggered(); void on_actionCreateInstanceShortcut_triggered(); - void on_actionCreateInstanceShortcutDesktop_triggered(); void on_actionCreateInstanceShortcutApplications_triggered(); void on_actionCreateInstanceShortcutOther_triggered(); @@ -230,7 +229,6 @@ class MainWindow : public QMainWindow { void setSelectedInstanceById(const QString& id); void updateStatusCenter(); void setInstanceActionsEnabled(bool enabled); - void createInstanceShortcut(QString shortcutDirPath); void runModalTask(Task* task); void instanceFromInstanceTask(InstanceTask* task); diff --git a/launcher/ui/MainWindow.ui b/launcher/ui/MainWindow.ui index 69d31589b..6530e2c5a 100644 --- a/launcher/ui/MainWindow.ui +++ b/launcher/ui/MainWindow.ui @@ -776,7 +776,7 @@ Desktop - Creates an shortcut to this instance on your desktop + Creates a shortcut to this instance on your desktop QAction::TextHeuristicRole