aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSefa Eyeoglu <contact@scrumplex.net>2022-11-27 20:05:21 +0100
committerSefa Eyeoglu <contact@scrumplex.net>2022-11-27 20:05:21 +0100
commitf3279a069760d4f6ba02a52ea53aa5aeccef3935 (patch)
tree586e145a1cc92cdac7f55a80b064a616dea6cd6c
parent8cac61f0bea17036346438761728c3b7bef4fbeb (diff)
parentcc92e1b0b6293a5c198b34554e68440a82e6074c (diff)
downloadPrismLauncher-f3279a069760d4f6ba02a52ea53aa5aeccef3935.tar.gz
PrismLauncher-f3279a069760d4f6ba02a52ea53aa5aeccef3935.tar.bz2
PrismLauncher-f3279a069760d4f6ba02a52ea53aa5aeccef3935.zip
Merge remote-tracking branch 'upstream/develop' into import-resource-pack-dialog-uwu
-rw-r--r--.github/workflows/build.yml35
-rw-r--r--.github/workflows/trigger_builds.yml3
-rw-r--r--CMakeLists.txt6
-rw-r--r--flake.nix4
-rw-r--r--launcher/FileSystem.cpp189
-rw-r--r--launcher/FileSystem.h5
-rw-r--r--launcher/resources/OSX/OSX.qrc1
-rw-r--r--launcher/resources/OSX/scalable/shortcut.svg14
-rw-r--r--launcher/resources/breeze_dark/breeze_dark.qrc1
-rw-r--r--launcher/resources/breeze_dark/scalable/shortcut.svg18
-rw-r--r--launcher/resources/breeze_light/breeze_light.qrc1
-rw-r--r--launcher/resources/breeze_light/scalable/shortcut.svg18
-rw-r--r--launcher/resources/flat/flat.qrc1
-rw-r--r--launcher/resources/flat/scalable/shortcut.svg3
-rw-r--r--launcher/resources/flat_white/flat_white.qrc1
-rw-r--r--launcher/resources/flat_white/scalable/shortcut.svg3
-rw-r--r--launcher/resources/iOS/iOS.qrc1
-rw-r--r--launcher/resources/iOS/scalable/shortcut.svg13
-rw-r--r--launcher/resources/multimc/multimc.qrc9
-rw-r--r--launcher/resources/multimc/scalable/delete.svg282
-rw-r--r--launcher/resources/multimc/scalable/export.svg466
-rw-r--r--launcher/resources/multimc/scalable/launch.svg96
-rw-r--r--launcher/resources/multimc/scalable/rename.svg437
-rw-r--r--launcher/resources/multimc/scalable/shortcut.svg157
-rw-r--r--launcher/resources/multimc/scalable/tag.svg398
-rw-r--r--launcher/resources/pe_blue/pe_blue.qrc1
-rw-r--r--launcher/resources/pe_blue/scalable/shortcut.svg41
-rw-r--r--launcher/resources/pe_colored/pe_colored.qrc1
-rw-r--r--launcher/resources/pe_colored/scalable/shortcut.svg13
-rw-r--r--launcher/resources/pe_dark/pe_dark.qrc1
-rw-r--r--launcher/resources/pe_dark/scalable/shortcut.svg41
-rw-r--r--launcher/resources/pe_light/pe_light.qrc1
-rw-r--r--launcher/resources/pe_light/scalable/shortcut.svg41
-rw-r--r--launcher/ui/MainWindow.cpp138
-rw-r--r--launcher/ui/MainWindow.h2
-rw-r--r--nix/default.nix95
36 files changed, 2455 insertions, 82 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index c0e5b50a..f07a86e6 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -15,6 +15,9 @@ on:
SPARKLE_ED25519_KEY:
description: Private key for signing Sparkle updates
required: false
+ CACHIX_AUTH_TOKEN:
+ description: Private token for authenticating against Cachix cache
+ required: false
jobs:
build:
@@ -230,7 +233,7 @@ jobs:
- name: Configure CMake (Windows MSVC)
if: runner.os == 'Windows' && matrix.msystem == ''
run: |
- cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}}
+ cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON
# https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix)
if ("${{ env.CCACHE_VAR }}")
{
@@ -526,3 +529,33 @@ jobs:
bundle: "Prism Launcher.flatpak"
manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml
cache-key: flatpak-${{ github.sha }}-x86_64
+
+ nix:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ package:
+ - prismlauncher
+ - prismlauncher-qt5
+ steps:
+ - name: Clone repository
+ if: inputs.build_type == 'Debug'
+ uses: actions/checkout@v3
+ with:
+ submodules: 'true'
+ - name: Install nix
+ if: inputs.build_type == 'Debug'
+ uses: cachix/install-nix-action@v18
+ with:
+ install_url: https://nixos.org/nix/install
+ extra_nix_config: |
+ auto-optimise-store = true
+ experimental-features = nix-command flakes
+ - uses: cachix/cachix-action@v12
+ if: inputs.build_type == 'Debug'
+ with:
+ name: prismlauncher
+ authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
+ - name: Build
+ if: inputs.build_type == 'Debug'
+ run: nix build .#${{ matrix.package }} --print-build-logs
diff --git a/.github/workflows/trigger_builds.yml b/.github/workflows/trigger_builds.yml
index 44751fbc..a08193a0 100644
--- a/.github/workflows/trigger_builds.yml
+++ b/.github/workflows/trigger_builds.yml
@@ -8,7 +8,6 @@ on:
- '**.md'
- '**/LICENSE'
- 'flake.lock'
- - '**.nix'
- 'packages/**'
- '.github/ISSUE_TEMPLATE/**'
- '.markdownlint**'
@@ -17,7 +16,6 @@ on:
- '**.md'
- '**/LICENSE'
- 'flake.lock'
- - '**.nix'
- 'packages/**'
- '.github/ISSUE_TEMPLATE/**'
- '.markdownlint**'
@@ -33,3 +31,4 @@ jobs:
is_qt_cached: true
secrets:
SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }}
+ CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8fc0d326..f04b733b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -368,11 +368,11 @@ if(NOT ZLIB_FOUND)
set(SKIP_INSTALL_ALL ON)
add_subdirectory(libraries/zlib EXCLUDE_FROM_ALL)
- set(ZLIB_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries/zlib" "${CMAKE_CURRENT_BINARY_DIR}/libraries/zlib")
+ set(ZLIB_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries/zlib" "${CMAKE_CURRENT_BINARY_DIR}/libraries/zlib" CACHE STRING "")
set_target_properties(zlibstatic PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${ZLIB_INCLUDE_DIR}")
add_library(ZLIB::ZLIB ALIAS zlibstatic)
- set(ZLIB_LIBRARY ZLIB::ZLIB)
- set(ZLIB_FOUND true)
+ set(ZLIB_LIBRARY ZLIB::ZLIB CACHE STRING "zlib library name")
+
find_package(ZLIB REQUIRED)
else()
message(STATUS "Using system zlib")
diff --git a/flake.nix b/flake.nix
index d4a25338..b1e07c91 100644
--- a/flake.nix
+++ b/flake.nix
@@ -23,8 +23,8 @@
pkgs = forAllSystems (system: nixpkgs.legacyPackages.${system});
packagesFn = pkgs: rec {
- prismlauncher = pkgs.libsForQt5.callPackage ./nix { inherit version self libnbtplusplus tomlplusplus; };
- prismlauncher-qt6 = pkgs.qt6Packages.callPackage ./nix { inherit version self libnbtplusplus tomlplusplus; };
+ prismlauncher-qt5 = pkgs.libsForQt5.callPackage ./nix { inherit version self libnbtplusplus tomlplusplus; };
+ prismlauncher = pkgs.qt6Packages.callPackage ./nix { inherit version self libnbtplusplus tomlplusplus; };
};
in
{
diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp
index 0c6527b1..1da50e21 100644
--- a/launcher/FileSystem.cpp
+++ b/launcher/FileSystem.cpp
@@ -49,6 +49,7 @@
#include "StringUtils.h"
#if defined Q_OS_WIN32
+#define WIN32_LEAN_AND_MEAN
#include <objbase.h>
#include <objidl.h>
#include <shlguid.h>
@@ -188,6 +189,8 @@ bool copy::operator()(const QString& offset, bool dryRun)
qDebug() << "Source file:" << src_path;
qDebug() << "Destination file:" << dst_path;
}
+ m_copied++;
+ emit fileCopied(relative_dst_path);
};
// We can't use copy_opts::recursive because we need to take into account the
@@ -341,12 +344,37 @@ QString getDesktopDir()
}
// Cross-platform Shortcut creation
-bool createShortCut(QString location, QString dest, QStringList args, QString name, QString icon)
+bool createShortcut(QString destination, QString target, QStringList args, QString name, QString icon)
{
-#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
- location = PathCombine(location, name + ".desktop");
+#if defined(Q_OS_MACOS)
+ destination += ".command";
- QFile f(location);
+ QFile f(destination);
+ f.open(QIODevice::WriteOnly | QIODevice::Text);
+ QTextStream stream(&f);
+
+ QString argstring;
+ if (!args.empty())
+ argstring = " \"" + args.join("\" \"") + "\"";
+
+ stream << "#!/bin/bash"
+ << "\n";
+ stream << "\""
+ << target
+ << "\" "
+ << argstring
+ << "\n";
+
+ stream.flush();
+ f.close();
+
+ f.setPermissions(f.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeGroup | QFileDevice::ExeOther);
+
+ return true;
+#elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
+ destination += ".desktop";
+
+ QFile f(destination);
f.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream stream(&f);
@@ -358,10 +386,12 @@ bool createShortCut(QString location, QString dest, QStringList args, QString na
<< "\n";
stream << "Type=Application"
<< "\n";
- stream << "TryExec=" << dest.toLocal8Bit() << "\n";
- stream << "Exec=" << dest.toLocal8Bit() << argstring.toLocal8Bit() << "\n";
+ stream << "Exec=\"" << target.toLocal8Bit() << "\"" << argstring.toLocal8Bit() << "\n";
stream << "Name=" << name.toLocal8Bit() << "\n";
- stream << "Icon=" << icon.toLocal8Bit() << "\n";
+ if (!icon.isEmpty())
+ {
+ stream << "Icon=" << icon.toLocal8Bit() << "\n";
+ }
stream.flush();
f.close();
@@ -369,25 +399,132 @@ bool createShortCut(QString location, QString dest, QStringList args, QString na
f.setPermissions(f.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeGroup | QFileDevice::ExeOther);
return true;
-#elif defined Q_OS_WIN
- // TODO: Fix
- // QFile file(PathCombine(location, name + ".lnk"));
- // WCHAR *file_w;
- // WCHAR *dest_w;
- // WCHAR *args_w;
- // file.fileName().toWCharArray(file_w);
- // dest.toWCharArray(dest_w);
-
- // QString argStr;
- // for (int i = 0; i < args.count(); i++)
- // {
- // argStr.append(args[i]);
- // argStr.append(" ");
- // }
- // argStr.toWCharArray(args_w);
-
- // return SUCCEEDED(CreateLink(file_w, dest_w, args_w));
- return false;
+#elif defined(Q_OS_WIN)
+ QFileInfo targetInfo(target);
+
+ if (!targetInfo.exists())
+ {
+ qWarning() << "Target file does not exist!";
+ return false;
+ }
+
+ target = targetInfo.absoluteFilePath();
+
+ if (target.length() >= MAX_PATH)
+ {
+ qWarning() << "Target file path is too long!";
+ return false;
+ }
+
+ if (!icon.isEmpty() && icon.length() >= MAX_PATH)
+ {
+ qWarning() << "Icon path is too long!";
+ return false;
+ }
+
+ destination += ".lnk";
+
+ if (destination.length() >= MAX_PATH)
+ {
+ qWarning() << "Destination path is too long!";
+ return false;
+ }
+
+ QString argStr;
+ int argCount = args.count();
+ for (int i = 0; i < argCount; i++)
+ {
+ if (args[i].contains(' '))
+ {
+ argStr.append('"').append(args[i]).append('"');
+ }
+ else
+ {
+ argStr.append(args[i]);
+ }
+
+ if (i < argCount - 1)
+ {
+ argStr.append(" ");
+ }
+ }
+
+ if (argStr.length() >= MAX_PATH)
+ {
+ qWarning() << "Arguments string is too long!";
+ return false;
+ }
+
+ HRESULT hres;
+
+ // ...yes, you need to initialize the entire COM stack just to make a shortcut
+ hres = CoInitialize(nullptr);
+ if (FAILED(hres))
+ {
+ qWarning() << "Failed to initialize COM!";
+ return false;
+ }
+
+ WCHAR wsz[MAX_PATH];
+
+ IShellLink* psl;
+
+ // create an IShellLink instance - this stores the shortcut's attributes
+ hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
+ if (SUCCEEDED(hres))
+ {
+ wmemset(wsz, 0, MAX_PATH);
+ target.toWCharArray(wsz);
+ psl->SetPath(wsz);
+
+ wmemset(wsz, 0, MAX_PATH);
+ argStr.toWCharArray(wsz);
+ psl->SetArguments(wsz);
+
+ wmemset(wsz, 0, MAX_PATH);
+ targetInfo.absolutePath().toWCharArray(wsz);
+ psl->SetWorkingDirectory(wsz); // "Starts in" attribute
+
+ if (!icon.isEmpty())
+ {
+ wmemset(wsz, 0, MAX_PATH);
+ icon.toWCharArray(wsz);
+ psl->SetIconLocation(wsz, 0);
+ }
+
+ // query an IPersistFile interface from our IShellLink instance
+ // this is the interface that will actually let us save the shortcut to disk!
+ IPersistFile* ppf;
+ hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf);
+ if (SUCCEEDED(hres))
+ {
+ wmemset(wsz, 0, MAX_PATH);
+ destination.toWCharArray(wsz);
+ hres = ppf->Save(wsz, TRUE);
+ if (FAILED(hres))
+ {
+ qWarning() << "IPresistFile->Save() failed";
+ qWarning() << "hres = " << hres;
+ }
+ ppf->Release();
+ }
+ else
+ {
+ qWarning() << "Failed to query IPersistFile interface from IShellLink instance";
+ qWarning() << "hres = " << hres;
+ }
+ psl->Release();
+ }
+ else
+ {
+ qWarning() << "Failed to create IShellLink instance";
+ qWarning() << "hres = " << hres;
+ }
+
+ // go away COM, nobody likes you
+ CoUninitialize();
+
+ return SUCCEEDED(hres);
#else
qWarning("Desktop Shortcuts not supported on your platform!");
return false;
diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h
index a9a81123..ac893725 100644
--- a/launcher/FileSystem.h
+++ b/launcher/FileSystem.h
@@ -172,4 +172,9 @@ QString getDesktopDir();
// Overrides one folder with the contents of another, preserving items exclusive to the first folder
// Equivalent to doing QDir::rename, but allowing for overrides
bool overrideFolder(QString overwritten_path, QString override_path);
+
+/**
+ * Creates a shortcut to the specified target file at the specified destination path.
+ */
+bool createShortcut(QString destination, QString target, QStringList args, QString name, QString icon);
}
diff --git a/launcher/resources/OSX/OSX.qrc b/launcher/resources/OSX/OSX.qrc
index 19fe312b..9d4511d1 100644
--- a/launcher/resources/OSX/OSX.qrc
+++ b/launcher/resources/OSX/OSX.qrc
@@ -39,5 +39,6 @@
<file>scalable/export.svg</file>
<file>scalable/rename.svg</file>
<file>scalable/launch.svg</file>
+ <file>scalable/shortcut.svg</file>
</qresource>
</RCC>
diff --git a/launcher/resources/OSX/scalable/shortcut.svg b/launcher/resources/OSX/scalable/shortcut.svg
new file mode 100644
index 00000000..a2b7488e
--- /dev/null
+++ b/launcher/resources/OSX/scalable/shortcut.svg
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Calque_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
+<rect fill="none" width="24" height="24"/>
+<g id="_x35__1_">
+ <g>
+ <path fill="#585858" d="M9.5,9.5C9.8,9.5,10,9.2,10,9l0-2.4l7.6,7.3c0.2,0.2,0.5,0.2,0.7,0c0.2-0.2,0.2-0.5,0-0.7L10.8,6L13,6
+ c0.3,0,0.5-0.2,0.5-0.5S13.3,5,13,5H9.5C9.2,5,9,5.2,9,5.5V9C9,9.2,9.2,9.5,9.5,9.5z M21,5h-5.5v1H21c0.5,0,1,0.5,1,1l0,10
+ c0,0.5-0.4,1-1,1l-10,0c-0.5,0-1-0.5-1-1v-5.5H9V17c0,1.1,1.1,2,2.2,2H21c1.1,0,2-0.9,2-2V7.2C23,6.1,22.1,5,21,5z"/>
+ </g>
+</g>
+</svg>
diff --git a/launcher/resources/breeze_dark/breeze_dark.qrc b/launcher/resources/breeze_dark/breeze_dark.qrc
index 4d7a69b2..97434abc 100644
--- a/launcher/resources/breeze_dark/breeze_dark.qrc
+++ b/launcher/resources/breeze_dark/breeze_dark.qrc
@@ -27,6 +27,7 @@
<file>scalable/refresh.svg</file>
<file>scalable/resourcepacks.svg</file>
<file>scalable/shaderpacks.svg</file>
+ <file>scalable/shortcut.svg</file>
<file>scalable/screenshots.svg</file>
<file>scalable/settings.svg</file>
<file>scalable/status-bad.svg</file>
diff --git a/launcher/resources/breeze_dark/scalable/shortcut.svg b/launcher/resources/breeze_dark/scalable/shortcut.svg
new file mode 100644
index 00000000..5559be1d
--- /dev/null
+++ b/launcher/resources/breeze_dark/scalable/shortcut.svg
@@ -0,0 +1,18 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
+ <defs id="defs3051">
+ <style type="text/css" id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#eff0f1;
+ }
+ </style>
+ </defs>
+ <g
+ transform="translate(-3,-1033.3622)">
+ <path
+ style="fill:currentColor;fill-opacity:1;stroke:none"
+ d="M 4,7 C 3.4459904,7 3,7.4459904 3,8 l 0,6 c 0,0.55401 0.4459904,1 1,1 l 5,0 c 0.55401,0 1,-0.44599 1,-1 l 0,-1 2,0 0,1 c 0,0.554 0.44599,1 1,1 l 5,0 c 0.55401,0 1,-0.446 1,-1 L 19,8 C 19,7.446 18.55401,7 18,7 l -5,0 c -0.55401,0 -1,0.446 -1,1 l 0,1 -2,0 0,-1 C 10,7.4459904 9.55401,7 9,7 Z M 4,8 7,8 9,8 9,9 C 8.4459904,9 8,9.4459904 8,10 l 0,2 c 0,0.55401 0.4459904,1 1,1 l 0,1 -2,0 -3,0 z m 9,0 3,0 2,0 0,6 -2,0 -3,0 0,-1 c 0.55401,0 1,-0.44599 1,-1 l 0,-2 C 14,9.4459904 13.55401,9 13,9 Z m -4,2 4,0 0,2 -4,0 z"
+ transform="translate(0,1030.3622)"
+ id="rect4161"
+ class="ColorScheme-Text" />
+ </g>
+</svg>
diff --git a/launcher/resources/breeze_light/breeze_light.qrc b/launcher/resources/breeze_light/breeze_light.qrc
index 7d9d99f5..6d868b18 100644
--- a/launcher/resources/breeze_light/breeze_light.qrc
+++ b/launcher/resources/breeze_light/breeze_light.qrc
@@ -27,6 +27,7 @@
<file>scalable/refresh.svg</file>
<file>scalable/resourcepacks.svg</file>
<file>scalable/shaderpacks.svg</file>
+ <file>scalable/shortcut.svg</file>
<file>scalable/screenshots.svg</file>
<file>scalable/settings.svg</file>
<file>scalable/status-bad.svg</file>
diff --git a/launcher/resources/breeze_light/scalable/shortcut.svg b/launcher/resources/breeze_light/scalable/shortcut.svg
new file mode 100644
index 00000000..426769d1
--- /dev/null
+++ b/launcher/resources/breeze_light/scalable/shortcut.svg
@@ -0,0 +1,18 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
+ <defs id="defs3051">
+ <style type="text/css" id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#232629;
+ }
+ </style>
+ </defs>
+ <g
+ transform="translate(-3,-1033.3622)">
+ <path
+ style="fill:currentColor;fill-opacity:1;stroke:none"
+ d="M 4,7 C 3.4459904,7 3,7.4459904 3,8 l 0,6 c 0,0.55401 0.4459904,1 1,1 l 5,0 c 0.55401,0 1,-0.44599 1,-1 l 0,-1 2,0 0,1 c 0,0.554 0.44599,1 1,1 l 5,0 c 0.55401,0 1,-0.446 1,-1 L 19,8 C 19,7.446 18.55401,7 18,7 l -5,0 c -0.55401,0 -1,0.446 -1,1 l 0,1 -2,0 0,-1 C 10,7.4459904 9.55401,7 9,7 Z M 4,8 7,8 9,8 9,9 C 8.4459904,9 8,9.4459904 8,10 l 0,2 c 0,0.55401 0.4459904,1 1,1 l 0,1 -2,0 -3,0 z m 9,0 3,0 2,0 0,6 -2,0 -3,0 0,-1 c 0.55401,0 1,-0.44599 1,-1 l 0,-2 C 14,9.4459904 13.55401,9 13,9 Z m -4,2 4,0 0,2 -4,0 z"
+ transform="translate(0,1030.3622)"
+ id="rect4161"
+ class="ColorScheme-Text" />
+ </g>
+</svg>
diff --git a/launcher/resources/flat/flat.qrc b/launcher/resources/flat/flat.qrc
index 508e0a9f..a846bd2d 100644
--- a/launcher/resources/flat/flat.qrc
+++ b/launcher/resources/flat/flat.qrc
@@ -35,6 +35,7 @@
<file>scalable/screenshot-placeholder.svg</file>
<file>scalable/screenshots.svg</file>
<file>scalable/settings.svg</file>
+ <file>scalable/shortcut.svg</file>
<file>scalable/star.svg</file>
<file>scalable/status-bad.svg</file>
<file>scalable/status-good.svg</file>
diff --git a/launcher/resources/flat/scalable/shortcut.svg b/launcher/resources/flat/scalable/shortcut.svg
new file mode 100644
index 00000000..83878d19
--- /dev/null
+++ b/launcher/resources/flat/scalable/shortcut.svg
@@ -0,0 +1,3 @@
+<svg fill="#757575" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
+ <path d="M5 21q-.825 0-1.413-.587Q3 19.825 3 19V5q0-.825.587-1.413Q4.175 3 5 3h7v2H5v14h14v-7h2v7q0 .825-.587 1.413Q19.825 21 19 21Zm4.7-5.3-1.4-1.4L17.6 5H14V3h7v7h-2V6.4Z"/>
+</svg>
diff --git a/launcher/resources/flat_white/flat_white.qrc b/launcher/resources/flat_white/flat_white.qrc
index e11d6316..b0759d8f 100644
--- a/launcher/resources/flat_white/flat_white.qrc
+++ b/launcher/resources/flat_white/flat_white.qrc
@@ -35,6 +35,7 @@
<file>scalable/screenshot-placeholder.svg</file>
<file>scalable/screenshots.svg</file>
<file>scalable/settings.svg</file>
+ <file>scalable/shortcut.svg</file>
<file>scalable/star.svg</file>
<file>scalable/status-bad.svg</file>
<file>scalable/status-good.svg</file>
diff --git a/launcher/resources/flat_white/scalable/shortcut.svg b/launcher/resources/flat_white/scalable/shortcut.svg
new file mode 100644
index 00000000..b419a77d
--- /dev/null
+++ b/launcher/resources/flat_white/scalable/shortcut.svg
@@ -0,0 +1,3 @@
+<svg fill="#D8DEE9" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
+ <path d="M5 21q-.825 0-1.413-.587Q3 19.825 3 19V5q0-.825.587-1.413Q4.175 3 5 3h7v2H5v14h14v-7h2v7q0 .825-.587 1.413Q19.825 21 19 21Zm4.7-5.3-1.4-1.4L17.6 5H14V3h7v7h-2V6.4Z"/>
+</svg>
diff --git a/launcher/resources/iOS/iOS.qrc b/launcher/resources/iOS/iOS.qrc
index aa08d811..0b79efb2 100644
--- a/launcher/resources/iOS/iOS.qrc
+++ b/launcher/resources/iOS/iOS.qrc
@@ -39,5 +39,6 @@
<file>scalable/export.svg</file>
<file>scalable/rename.svg</file>
<file>scalable/launch.svg</file>
+ <file>scalable/shortcut.svg</file>
</qresource>
</RCC>
diff --git a/launcher/resources/iOS/scalable/shortcut.svg b/launcher/resources/iOS/scalable/shortcut.svg
new file mode 100644
index 00000000..16e9fa48
--- /dev/null
+++ b/launcher/resources/iOS/scalable/shortcut.svg
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Calque_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 32 32" enable-background="new 0 0 32 32" xml:space="preserve">
+<g id="_x35__5_">
+ <g>
+ <path fill="#3366CC" d="M3,11c0.6,0,1-0.5,1-1l0-4.8l15.2,14.5c0.4,0.4,1,0.4,1.4,0c0.4-0.4,0.4-1,0-1.4L5.6,4L10,4
+ c0.6,0,1-0.5,1-1s-0.4-1-1-1H3C2.5,2,2,2.4,2,3v7C2,10.5,2.4,11,3,11z M26,2H15v2h11c1.1,0,2,0.9,2,2l0,20.1c0,1.1-0.9,2-2,2L6,28
+ c-1.1,0-2-0.9-2-2V15H2v11c0,2.2,2.2,4,4.4,4h19.7c2.2,0,3.9-1.8,3.9-3.9V6.4C30,4.2,28.2,2,26,2z"/>
+ </g>
+</g>
+</svg>
diff --git a/launcher/resources/multimc/multimc.qrc b/launcher/resources/multimc/multimc.qrc
index 3f3d22fc..9741267c 100644
--- a/launcher/resources/multimc/multimc.qrc
+++ b/launcher/resources/multimc/multimc.qrc
@@ -312,5 +312,14 @@
<file>scalable/instances/fox.svg</file>
<file>scalable/instances/bee.svg</file>
<file>scalable/instances/prismlauncher.svg</file>
+
+ <!-- delete, tag, rename, shortcut CC-BY-SA 3.0, Oxygen icons.-->
+ <file>scalable/delete.svg</file>
+ <file>scalable/tag.svg</file>
+ <file>scalable/rename.svg</file>
+ <file>scalable/shortcut.svg</file>
+
+ <file>scalable/export.svg</file>
+ <file>scalable/launch.svg</file>
</qresource>
</RCC>
diff --git a/launcher/resources/multimc/scalable/delete.svg b/launcher/resources/multimc/scalable/delete.svg
new file mode 100644
index 00000000..414cbd5c
--- /dev/null
+++ b/launcher/resources/multimc/scalable/delete.svg
@@ -0,0 +1,282 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ width="33.866665mm"
+ height="33.866665mm"
+ viewBox="0 0 33.866665 33.866665"
+ version="1.1"
+ id="svg2411"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs2408">
+ <linearGradient
+ xlink:href="#linearGradient3315"
+ id="linearGradient3321"
+ x1="20.961376"
+ y1="70.875"
+ x2="106.96138"
+ y2="70.875"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.26458333,0,0,0.26458333,-152.84859,113.94802)" />
+ <linearGradient
+ id="linearGradient3315">
+ <stop
+ style="stop-color:#bf0303;stop-opacity:1;"
+ offset="0"
+ id="stop3317" />
+ <stop
+ id="stop3323"
+ offset="0.375"
+ style="stop-color:#fc3d3d;stop-opacity:1;" />
+ <stop
+ style="stop-color:#bf0303;stop-opacity:1;"
+ offset="0.75"
+ id="stop3325" />
+ <stop
+ style="stop-color:#bf0303;stop-opacity:1;"
+ offset="1"
+ id="stop3319" />
+ </linearGradient>
+ <linearGradient
+ xlink:href="#linearGradient3335"
+ id="linearGradient3341"
+ x1="22.032"
+ y1="39.036999"
+ x2="105.967"
+ y2="39.036999"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.26458333,0,0,0.26458333,-152.84859,113.94802)" />
+ <linearGradient
+ id="linearGradient3335">
+ <stop
+ style="stop-color:#9c0f0f;stop-opacity:0.28301886;"
+ offset="0"
+ id="stop3337" />
+ <stop
+ id="stop3343"
+ offset="0.5"
+ style="stop-color:#9c0f0f;stop-opacity:1;" />
+ <stop
+ style="stop-color:#9c0f0f;stop-opacity:0.1981132;"
+ offset="1"
+ id="stop3339" />
+ </linearGradient>
+ <linearGradient
+ xlink:href="#linearGradient3347"
+ id="linearGradient3353"
+ x1="12.190286"
+ y1="21.738001"
+ x2="115.80972"
+ y2="21.738001"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.26458333,0,0,0.26458333