aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/ISSUE_TEMPLATE/bug_report.yml9
-rw-r--r--CMakeLists.txt5
-rw-r--r--COPYING.md28
-rw-r--r--launcher/CMakeLists.txt1
-rw-r--r--launcher/FileSystem.cpp227
-rw-r--r--launcher/FileSystem.h51
-rw-r--r--launcher/InstanceImportTask.h6
-rw-r--r--launcher/InstanceList.cpp428
-rw-r--r--launcher/InstanceList.h13
-rw-r--r--launcher/MMCZip.cpp16
-rw-r--r--launcher/MMCZip.h8
-rw-r--r--launcher/minecraft/World.cpp8
-rw-r--r--launcher/minecraft/World.h6
-rw-r--r--launcher/modplatform/atlauncher/ATLPackInstallTask.h6
-rw-r--r--launcher/modplatform/legacy_ftb/PackInstallTask.h6
-rw-r--r--launcher/modplatform/technic/SingleZipPackInstallTask.h6
-rw-r--r--launcher/ui/MainWindow.cpp32
-rw-r--r--launcher/ui/MainWindow.h1
-rw-r--r--libraries/README.md13
-rw-r--r--libraries/optional-bare/CMakeLists.txt5
-rw-r--r--libraries/optional-bare/LICENSE.txt23
-rw-r--r--libraries/optional-bare/README.md5
-rw-r--r--libraries/optional-bare/include/nonstd/optional508
23 files changed, 394 insertions, 1017 deletions
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index bac73932..ab3c8a29 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -27,7 +27,14 @@ body:
attributes:
label: Version of PolyMC
description: The version of PolyMC used in the bug report.
- placeholder: PolyMC 1.3.2
+ placeholder: PolyMC 1.4.1
+ validations:
+ required: true
+- type: textarea
+ attributes:
+ label: Version of Qt
+ description: The version of Qt used in the bug report. You can find it in Help -> About PolyMC -> About Qt.
+ placeholder: Qt 6.3.0
validations:
required: true
- type: textarea
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 33c53b82..62724323 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -29,10 +29,10 @@ set(CMAKE_JAVA_TARGET_OUTPUT_DIR ${PROJECT_BINARY_DIR}/jars)
######## Set compiler flags ########
set(CMAKE_CXX_STANDARD_REQUIRED true)
set(CMAKE_C_STANDARD_REQUIRED true)
-set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD 17)
set(CMAKE_C_STANDARD 11)
include(GenerateExportHeader)
-set(CMAKE_CXX_FLAGS "-Wall -pedantic -D_GLIBCXX_USE_CXX11_ABI=0 -fstack-protector-strong --param=ssp-buffer-size=4 ${CMAKE_CXX_FLAGS}")
+set(CMAKE_CXX_FLAGS "-Wall -pedantic -fstack-protector-strong --param=ssp-buffer-size=4 ${CMAKE_CXX_FLAGS}")
if(UNIX AND APPLE)
set(CMAKE_CXX_FLAGS "-stdlib=libc++ ${CMAKE_CXX_FLAGS}")
endif()
@@ -319,7 +319,6 @@ endif()
add_subdirectory(libraries/rainbow) # Qt extension for colors
add_subdirectory(libraries/LocalPeer) # fork of a library from Qt solutions
add_subdirectory(libraries/classparser) # class parser library
-add_subdirectory(libraries/optional-bare)
add_subdirectory(libraries/tomlc99) # toml parser
add_subdirectory(libraries/katabasis) # An OAuth2 library that tried to do too much
add_subdirectory(libraries/gamemode)
diff --git a/COPYING.md b/COPYING.md
index 3f9608ff..1dd18e17 100644
--- a/COPYING.md
+++ b/COPYING.md
@@ -295,34 +295,6 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-# optional-bare
-
- Code from https://github.com/martinmoene/optional-bare/
-
- Boost Software License - Version 1.0 - August 17th, 2003
-
- Permission is hereby granted, free of charge, to any person or organization
- obtaining a copy of the software and accompanying documentation covered by
- this license (the "Software") to use, reproduce, display, distribute,
- execute, and transmit the Software, and to prepare derivative works of the
- Software, and to permit third-parties to whom the Software is furnished to
- do so, all subject to the following:
-
- The copyright notices in the Software and this entire statement, including
- the above license grant, this restriction and the following disclaimer,
- must be included in all copies of the Software, in whole or in part, and
- all derivative works of the Software, unless such copies or derivative
- works are solely in the form of machine-executable object code generated by
- a source language processor.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
- SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
- FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
- ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- DEALINGS IN THE SOFTWARE.
-
# tomlc99
MIT License
diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt
index a4a1315d..9c5c2ea8 100644
--- a/launcher/CMakeLists.txt
+++ b/launcher/CMakeLists.txt
@@ -988,7 +988,6 @@ target_link_libraries(Launcher_logic
Launcher_murmur2
nbt++
${ZLIB_LIBRARIES}
- optional-bare
tomlc99
BuildConfig
Katabasis
diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp
index ebb4460d..21edbb48 100644
--- a/launcher/FileSystem.cpp
+++ b/launcher/FileSystem.cpp
@@ -35,76 +35,64 @@
#include "FileSystem.h"
+#include <QDebug>
#include <QDir>
#include <QFile>
-#include <QSaveFile>
#include <QFileInfo>
-#include <QDebug>
-#include <QUrl>
+#include <QSaveFile>
#include <QStandardPaths>
#include <QTextStream>
+#include <QUrl>
#if defined Q_OS_WIN32
- #include <windows.h>
- #include <string>
- #include <sys/utime.h>
- #include <winnls.h>
- #include <shobjidl.h>
- #include <objbase.h>
- #include <objidl.h>
- #include <shlguid.h>
- #include <shlobj.h>
+#include <objbase.h>
+#include <objidl.h>
+#include <shlguid.h>
+#include <shlobj.h>
+#include <shobjidl.h>
+#include <sys/utime.h>
+#include <windows.h>
+#include <winnls.h>
+#include <string>
#else
- #include <utime.h>
+#include <utime.h>
#endif
namespace FS {
-void ensureExists(const QDir &dir)
+void ensureExists(const QDir& dir)
{
- if (!QDir().mkpath(dir.absolutePath()))
- {
- throw FileSystemException("Unable to create folder " + dir.dirName() + " (" +
- dir.absolutePath() + ")");
+ if (!QDir().mkpath(dir.absolutePath())) {
+ throw FileSystemException("Unable to create folder " + dir.dirName() + " (" + dir.absolutePath() + ")");
}
}
-void write(const QString &filename, const QByteArray &data)
+void write(const QString& filename, const QByteArray& data)
{
ensureExists(QFileInfo(filename).dir());
QSaveFile file(filename);
- if (!file.open(QSaveFile::WriteOnly))
- {
- throw FileSystemException("Couldn't open " + filename + " for writing: " +
- file.errorString());
+ if (!file.open(QSaveFile::WriteOnly)) {
+ throw FileSystemException("Couldn't open " + filename + " for writing: " + file.errorString());
}
- if (data.size() != file.write(data))
- {
- throw FileSystemException("Error writing data to " + filename + ": " +
- file.errorString());
+ if (data.size() != file.write(data)) {
+ throw FileSystemException("Error writing data to " + filename + ": " + file.errorString());
}
- if (!file.commit())
- {
- throw FileSystemException("Error while committing data to " + filename + ": " +
- file.errorString());
+ if (!file.commit()) {
+ throw FileSystemException("Error while committing data to " + filename + ": " + file.errorString());
}
}
-QByteArray read(const QString &filename)
+QByteArray read(const QString& filename)
{
QFile file(filename);
- if (!file.open(QFile::ReadOnly))
- {
- throw FileSystemException("Unable to open " + filename + " for reading: " +
- file.errorString());
+ if (!file.open(QFile::ReadOnly)) {
+ throw FileSystemException("Unable to open " + filename + " for reading: " + file.errorString());
}
const qint64 size = file.size();
QByteArray data(int(size), 0);
const qint64 ret = file.read(data.data(), size);
- if (ret == -1 || ret != size)
- {
- throw FileSystemException("Error reading data from " + filename + ": " +
- file.errorString());
+ if (ret == -1 || ret != size) {
+ throw FileSystemException("Error reading data from " + filename + ": " + file.errorString());
}
return data;
}
@@ -138,12 +126,12 @@ bool ensureFolderPathExists(QString foldernamepath)
return success;
}
-bool copy::operator()(const QString &offset)
+bool copy::operator()(const QString& offset)
{
- //NOTE always deep copy on windows. the alternatives are too messy.
- #if defined Q_OS_WIN32
+// NOTE always deep copy on windows. the alternatives are too messy.
+#if defined Q_OS_WIN32
m_followSymlinks = true;
- #endif
+#endif
auto src = PathCombine(m_src.absolutePath(), offset);
auto dst = PathCombine(m_dst.absolutePath(), offset);
@@ -152,52 +140,39 @@ bool copy::operator()(const QString &offset)
if (!currentSrc.exists())
return false;
- if(!m_followSymlinks && currentSrc.isSymLink())
- {
+ if (!m_followSymlinks && currentSrc.isSymLink()) {
qDebug() << "creating symlink" << src << " - " << dst;
- if (!ensureFilePathExists(dst))
- {
+ if (!ensureFilePathExists(dst)) {
qWarning() << "Cannot create path!";
return false;
}
return QFile::link(currentSrc.symLinkTarget(), dst);
- }
- else if(currentSrc.isFile())
- {
+ } else if (currentSrc.isFile()) {
qDebug() << "copying file" << src << " - " << dst;
- if (!ensureFilePathExists(dst))
- {
+ if (!ensureFilePathExists(dst)) {
qWarning() << "Cannot create path!";
return false;
}
return QFile::copy(src, dst);
- }
- else if(currentSrc.isDir())
- {
+ } else if (currentSrc.isDir()) {
qDebug() << "recursing" << offset;
- if (!ensureFolderPathExists(dst))
- {
+ if (!ensureFolderPathExists(dst)) {
qWarning() << "Cannot create path!";
return false;
}
QDir currentDir(src);
- for(auto & f : currentDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System))
- {
+ for (auto& f : currentDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System)) {
auto inner_offset = PathCombine(offset, f);
// ignore and skip stuff that matches the blacklist.
- if(m_blacklist && m_blacklist->matches(inner_offset))
- {
+ if (m_blacklist && m_blacklist->matches(inner_offset)) {
continue;
}
- if(!operator()(inner_offset))
- {
+ if (!operator()(inner_offset)) {
qWarning() << "Failed to copy" << inner_offset;
return false;
}
}
- }
- else
- {
+ } else {
qCritical() << "Copy ERROR: Unknown filesystem object:" << src;
return false;
}
@@ -208,55 +183,41 @@ bool deletePath(QString path)
{
bool OK = true;
QFileInfo finfo(path);
- if(finfo.isFile()) {
+ if (finfo.isFile()) {
return QFile::remove(path);
}
QDir dir(path);
- if (!dir.exists())
- {
+ if (!dir.exists()) {
return OK;
}
- auto allEntries = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden |
- QDir::AllDirs | QDir::Files,
- QDir::DirsFirst);
+ auto allEntries = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden | QDir::AllDirs | QDir::Files, QDir::DirsFirst);
- for(auto & info: allEntries)
- {
+ for (auto& info : allEntries) {
#if defined Q_OS_WIN32
QString nativePath = QDir::toNativeSeparators(info.absoluteFilePath());
auto wString = nativePath.toStdWString();
DWORD dwAttrs = GetFileAttributesW(wString.c_str());
// Windows: check for junctions, reparse points and other nasty things of that sort
- if(dwAttrs & FILE_ATTRIBUTE_REPARSE_POINT)
- {
- if (info.isFile())
- {
+ if (dwAttrs & FILE_ATTRIBUTE_REPARSE_POINT) {
+ if (info.isFile()) {
OK &= QFile::remove(info.absoluteFilePath());
- }
- else if (info.isDir())
- {
+ } else if (info.isDir()) {
OK &= dir.rmdir(info.absoluteFilePath());
}
}
#else
// We do not trust Qt with reparse points, but do trust it with unix symlinks.
- if(info.isSymLink())
- {
+ if (info.isSymLink()) {
OK &= QFile::remove(info.absoluteFilePath());
}
#endif
- else if (info.isDir())
- {
+ else if (info.isDir()) {
OK &= deletePath(info.absoluteFilePath());
- }
- else if (info.isFile())
- {
+ } else if (info.isFile()) {
OK &= QFile::remove(info.absoluteFilePath());
- }
- else
- {
+ } else {
OK = false;
qCritical() << "Delete ERROR: Unknown filesystem object:" << info.absoluteFilePath();
}
@@ -265,22 +226,30 @@ bool deletePath(QString path)
return OK;
}
+bool trash(QString path, QString *pathInTrash = nullptr)
+{
+#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
+ return false;
+#else
+ return QFile::moveToTrash(path, pathInTrash);
+#endif
+}
-QString PathCombine(const QString & path1, const QString & path2)
+QString PathCombine(const QString& path1, const QString& path2)
{
- if(!path1.size())
+ if (!path1.size())
return path2;
- if(!path2.size())
+ if (!path2.size())
return path1;
return QDir::cleanPath(path1 + QDir::separator() + path2);
}
-QString PathCombine(const QString & path1, const QString & path2, const QString & path3)
+QString PathCombine(const QString& path1, const QString& path2, const QString& path3)
{
return PathCombine(PathCombine(path1, path2), path3);
}
-QString PathCombine(const QString & path1, const QString & path2, const QString & path3, const QString & path4)
+QString PathCombine(const QString& path1, const QString& path2, const QString& path3, const QString& path4)
{
return PathCombine(PathCombine(path1, path2, path3), path4);
}
@@ -292,17 +261,14 @@ QString AbsolutePath(QString path)
QString ResolveExecutable(QString path)
{
- if (path.isEmpty())
- {
+ if (path.isEmpty()) {
return QString();
}
- if(!path.contains('/'))
- {
+ if (!path.contains('/')) {
path = QStandardPaths::findExecutable(path);
}
QFileInfo pathInfo(path);
- if(!pathInfo.exists() || !pathInfo.isExecutable())
- {
+ if (!pathInfo.exists() || !pathInfo.isExecutable()) {
return QString();
}
return pathInfo.absoluteFilePath();
@@ -322,12 +288,9 @@ QString NormalizePath(QString path)
QDir b(path);
QString newAbsolute = b.absolutePath();
- if (newAbsolute.startsWith(currentAbsolute))
- {
+ if (newAbsolute.startsWith(currentAbsolute)) {
return a.relativeFilePath(newAbsolute);
- }
- else
- {
+ } else {
return newAbsolute;
}
}
@@ -336,10 +299,8 @@ QString badFilenameChars = "\"\\/?<>:;*|!+\r\n";
QString RemoveInvalidFilenameChars(QString string, QChar replaceWith)
{
- for (int i = 0; i < string.length(); i++)
- {
- if (badFilenameChars.contains(string[i]))
- {
+ for (int i = 0; i < string.length(); i++) {
+ if (badFilenameChars.contains(string[i])) {
string[i] = replaceWith;
}
}
@@ -351,15 +312,12 @@ QString DirNameFromString(QString string, QString inDir)
int num = 0;
QString baseName = RemoveInvalidFilenameChars(string, '-');
QString dirName;
- do
- {
- if(num == 0)
- {
+ do {
+ if (num == 0) {
dirName = baseName;
- }
- else
- {
- dirName = baseName + QString::number(num);;
+ } else {
+ dirName = baseName + QString::number(num);
+ ;
}
// If it's over 9000
@@ -383,36 +341,31 @@ bool checkProblemticPathJava(QDir folder)
bool called_coinit = false;
-HRESULT CreateLink(LPCSTR linkPath, LPCSTR targetPath, LPCSTR args)
+HRESULT CreateLink(LPCCH linkPath, LPCWSTR targetPath, LPCWSTR args)
{
HRESULT hres;
- if (!called_coinit)
- {
+ if (!called_coinit) {
hres = CoInitialize(NULL);
called_coinit = true;
- if (!SUCCEEDED(hres))
- {
+ if (!SUCCEEDED(hres)) {
qWarning("Failed to initialize COM. Error 0x%08lX", hres);
return hres;
}
}
- IShellLinkA *link;
- hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink,
- (LPVOID *)&link);
+ IShellLink* link;
+ hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&link);
- if (SUCCEEDED(hres))
- {
- IPersistFile *persistFile;
+ if (SUCCEEDED(hres)) {
+ IPersistFile* persistFile;
link->SetPath(targetPath);
link->SetArguments(args);
- hres = link->QueryInterface(IID_IPersistFile, (LPVOID *)&persistFile);
- if (SUCCEEDED(hres))
- {
+ hres = link->QueryInterface(IID_IPersistFile, (LPVOID*)&persistFile);
+ if (SUCCEEDED(hres)) {
WCHAR wstr[MAX_PATH];
MultiByteToWideChar(CP_ACP, 0, linkPath, -1, wstr, MAX_PATH);
@@ -433,8 +386,7 @@ QString getDesktopDir()
}
// Cross-platform Shortcut creation
-bool createShortCut(QString location, QString dest, QStringList args, QString name,
- QString icon)
+bool createShortCut(QString location, QString dest, QStringList args, QString name, QString icon)
{
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
location = PathCombine(location, name + ".desktop");
@@ -459,8 +411,7 @@ bool createShortCut(QString location, QString dest, QStringList args, QString na
stream.flush();
f.close();
- f.setPermissions(f.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeGroup |
- QFileDevice::ExeOther);
+ f.setPermissions(f.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeGroup | QFileDevice::ExeOther);
return true;
#elif defined Q_OS_WIN
diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h
index fd305b01..b46f3281 100644
--- a/launcher/FileSystem.h
+++ b/launcher/FileSystem.h
@@ -41,29 +41,27 @@
#include <QDir>
#include <QFlags>
-namespace FS
-{
+namespace FS {
-class FileSystemException : public ::Exception
-{
-public:
- FileSystemException(const QString &message) : Exception(message) {}
+class FileSystemException : public ::Exception {
+ public:
+ FileSystemException(const QString& message) : Exception(message) {}
};
/**
* write data to a file safely
*/
-void write(const QString &filename, const QByteArray &data);
+void write(const QString& filename, const QByteArray& data);
/**
* read data from a file safely\
*/
-QByteArray read(const QString &filename);
+QByteArray read(const QString& filename);
/**
* Update the last changed timestamp of an existing file
*/
-bool updateTimestamp(const QString & filename);
+bool updateTimestamp(const QString& filename);
/**
* Creates all the folders in a path for the specified path
@@ -77,35 +75,31 @@ bool ensureFilePathExists(QString filenamepath);
*/
bool ensureFolderPathExists(QString filenamepath);
-class copy
-{
-public:
- copy(const QString & src, const QString & dst)
+class copy {
+ public:
+ copy(const QString& src, const QString& dst)
{
m_src.setPath(src);
m_dst.setPath(dst);
}
- copy & followSymlinks(const bool follow)
+ copy& followSymlinks(const bool follow)
{
m_followSymlinks = follow;
return *this;
}
- copy & blacklist(const IPathMatcher * filter)
+ copy& blacklist(const IPathMatcher* filter)
{
m_blacklist = filter;
return *this;
}
- bool operator()()
- {
- return operator()(QString());
- }
+ bool operator()() { return operator()(QString()); }
-private:
- bool operator()(const QString &offset);
+ private:
+ bool operator()(const QString& offset);
-private:
+ private:
bool m_followSymlinks = true;
- const IPathMatcher * m_blacklist = nullptr;
+ const IPathMatcher* m_blacklist = nullptr;
QDir m_src;
QDir m_dst;
};
@@ -115,9 +109,14 @@ private:
*/
bool deletePath(QString path);
-QString PathCombine(const QString &path1, const QString &path2);
-QString PathCombine(const QString &path1, const QString &path2, const QString &path3);
-QString PathCombine(const QString &path1, const QString &path2, const QString &path3, const QString &path4);
+/**
+ * Trash a folder / file
+ */
+bool trash(QString path, QString *pathInTrash);
+
+QString PathCombine(const QString& path1, const QString& path2);
+QString PathCombine(const QString& path1, const QString& path2, const QString& path3);
+QString PathCombine(const QString& path1, const QString& path2, const QString& path3, const QString& path4);
QString AbsolutePath(QString path);
diff --git a/launcher/InstanceImportTask.h b/launcher/InstanceImportTask.h
index b67d48f3..48ba2161 100644
--- a/launcher/InstanceImportTask.h
+++ b/launcher/InstanceImportTask.h
@@ -44,7 +44,7 @@
#include "QObjectPtr.h"
#include "modplatform/flame/PackManifest.h"
-#include <nonstd/optional>
+#include <optional>
class QuaZip;
namespace Flame
@@ -90,8 +90,8 @@ private: /* data */
QString m_archivePath;
bool m_downloadRequired = false;
std::unique_ptr<QuaZip> m_packZip;
- QFuture<nonstd::optional<QStringList>> m_extractFuture;
- QFutureWatcher<nonstd::optional<QStringList>> m_extractFutureWatcher;
+ QFuture<std::optional<QStringList>> m_extractFuture;
+ QFutureWatcher<std::optional<QStringList>> m_extractFutureWatcher;
QVector<Flame::File> m_blockedMods;
enum class ModpackType{
Unknown,
diff --git a/launcher/InstanceList.cpp b/launcher/InstanceList.cpp
index fb7103dd..4447a17c 100644
--- a/launcher/InstanceList.cpp
+++ b/launcher/InstanceList.cpp
@@ -33,30 +33,32 @@
* limitations under the License.
*/
+#include <QDebug>
#include <QDir>
#include <QDirIterator>
-#include <QSet>
#include <QFile>
-#include <QThread>
-#include <QTextStream>
-#include <QXmlStreamReader>
-#include <QTimer>
-#include <QDebug>
#include <QFileSystemWatcher>
-#include <QUuid>
#include <QJsonArray>
#include <QJsonDocument>
#include <QMimeData>
+#include <QSet>
+#include <QStack>
+#include <QPair>
+#include <QTextStream>
+#include <QThread>
+#include <QTimer>
+#include <QUuid>
+#include <QXmlStreamReader>
-#include "InstanceList.h"
#include "BaseInstance.h"
+#include "ExponentialSeries.h"
+#include "FileSystem.h"
+#include "InstanceList.h"
#include "InstanceTask.h"
-#include "settings/INISettingsObject.h"
#include "NullInstance.h"
-#include "minecraft/MinecraftInstance.h"
-#include "FileSystem.h"
-#include "ExponentialSeries.h"
#include "WatchLock.h"
+#include "minecraft/MinecraftInstance.h"
+#include "settings/INISettingsObject.h"
#ifdef Q_OS_WIN32
#include <Windows.h>
@@ -64,13 +66,12 @@
const static int GROUP_FILE_FORMAT_VERSION = 1;
-InstanceList::InstanceList(SettingsObjectPtr settings, const QString & instDir, QObject *parent)
+InstanceList::InstanceList(SettingsObjectPtr settings, const QString& instDir, QObject* parent)
: QAbstractListModel(parent), m_globalSettings(settings)
{
resumeWatch();
// Create aand normalize path
- if (!QDir::current().exists(instDir))
- {
+ if (!QDir::current().exists(instDir)) {
QDir::current().mkpath(instDir);
}
@@ -83,9 +84,7 @@ InstanceList::InstanceList(SettingsObjectPtr settings, const QString & instDir,
m_watcher->addPath(m_instDir);
}
-InstanceList::~InstanceList()
-{
-}
+InstanceList::~InstanceList() {}
Qt::DropActions InstanceList::supportedDragActions() const
{
@@ -99,7 +98,7 @@ Qt::DropActions InstanceList::supportedDropActions() const
bool InstanceList::canDropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) const
{
- if(data && data->hasFormat("application/x-instanceid")) {
+ if (data && data->hasFormat("application/x-instanceid")) {
return true;
}
return false;
@@ -107,7 +106,7 @@ bool InstanceList::canDropMimeData(const QMimeData* data, Qt::DropAction action,
bool InstanceList::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent)
{
- if(data && data->hasFormat("application/x-instanceid")) {
+ if (data && data->hasFormat("application/x-instanceid")) {
return true;
}
return false;
@@ -120,35 +119,33 @@ QStringList InstanceList::mimeTypes() const
return types;
}
-QMimeData * InstanceList::mimeData(const QModelIndexList& indexes) const
+QMimeData* InstanceList::mimeData(const QModelIndexList& indexes) const
{
auto mimeData = QAbstractListModel::mimeData(indexes);
- if(indexes.size() == 1) {
+ if (indexes.size() == 1) {
auto instanceId = data(indexes[0], InstanceIDRole).toString();
mimeData->setData("application/x-instanceid", instanceId.toUtf8());
}
return mimeData;
}
-
-int InstanceList::rowCount(const QModelIndex &parent) const
+int InstanceList::rowCount(const QModelIndex& parent) const
{
Q_UNUSED(parent);
return m_instances.count();
}
-QModelIndex InstanceList::index(int row, int column, const QModelIndex &parent) const
+QModelIndex InstanceList::index(int row, int column, const QModelIndex& parent) const
{
Q_UNUSED(parent);
if (row < 0 || row >= m_instances.size())
return QModelIndex();
- return createIndex(row, column, (void *)m_instances.at(row).get());
+ return createIndex(row, column, (void*)m_instances.at(row).get());
}
-QVariant InstanceList::data(const QModelIndex &index, int role) const
+QVariant InstanceList::data(const QModelIndex& index, int role) const
{
- if (!index.isValid())
- {
+ if (!index.isValid()) {
return QVariant();
}
BaseInstance *pdata = static_cast<BaseInstance *>(index.internalPointer());
@@ -193,29 +190,25 @@ QVariant InstanceList::data(const QModelIndex &index, int role) const
bool InstanceList::setData(const QModelIndex& index, const QVariant& value, int role)
{
- if (!index.isValid())
- {
+ if (!index.isValid()) {
return false;
}
- if(role != Qt::EditRole)
- {
+ if (role != Qt::EditRole) {
return false;
}
- BaseInstance *pdata = static_cast<BaseInstance *>(index.internalPointer());
+ BaseInstance* pdata = static_cast<BaseInstance*>(index.internalPointer());
auto newName = value.toString();
- if(pdata->name() == newName)
- {
+ if (pdata->name() == newName) {
return true;
}
pdata->setName(newName);
return true;
}
-Qt::ItemFlags InstanceList::flags(const QModelIndex &index) const
+Qt::ItemFlags InstanceList::flags(const QModelIndex& index) const
{
Qt::ItemFlags f;
- if (index.isValid())
- {
+ if (index.isValid()) {
f |= (Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);
}
return f;
@@ -224,13 +217,11 @@ Qt::ItemFlags InstanceList::flags(const QModelIndex &index) const
GroupId InstanceList::getInstanceGroup(const InstanceId& id) const
{
auto inst = getInstanceById(id);
- if(!inst)
- {
+ if (!inst) {
return GroupId();
}
auto iter = m_instanceGroupIndex.find(inst->id());
- if(iter != m_instanceGroupIndex.end())
- {
+ if (iter != m_instanceGroupIndex.end()) {
return *iter;
}
return GroupId();
@@ -239,33 +230,27 @@ GroupId InstanceList::getInstanceGroup(const InstanceId& id) const
void InstanceList::setInstanceGroup(const InstanceId& id, const GroupId& name)
{
auto inst = getInstanceById(id);
- if(!inst)
- {
+ if (!inst) {
qDebug() << "Attempt to set a null instance's group";
return;
}
bool changed = false;
auto iter = m_instanceGroupIndex.find(inst->id());
- if(iter != m_instanceGroupIndex.end())
- {
- if(*iter != name)
- {
+ if (iter != m_instanceGroupIndex.end()) {
+ if (*iter != name) {
*iter = name;
changed = true;
}
- }
- else
- {
+ } else {
changed = true;
m_instanceGroupIndex[id] = name;
}
- if(changed)
- {
+ if (changed) {
m_groupNameCache.insert(name);
auto idx = getInstIndex(inst.get());
- emit dataChanged(index(idx), index(idx), {GroupRole});
+ emit dataChanged(index(idx), index(idx), { GroupRole });
saveGroupList();
}
}
@@ -279,24 +264,20 @@ void InstanceList::deleteGroup(const QString& name)
{
bool removed = false;
qDebug() << "Delete group" << name;
- for(auto & instance: m_instances)
- {
- const auto & instID = instance->id();
+ for (auto& instance : m_instances) {
+ const auto& instID = instance->id();
auto instGroupName = getInstanceGroup(instID);
- if(instGroupName == name)
- {
+ if (instGroupName == name) {
m_instanceGroupIndex.remove(instID);
qDebug() << "Remove" << instID << "from group" << name;
removed = true;
auto idx = getInstIndex(instance.get());
- if(idx > 0)
- {
- emit dataChanged(index(idx), index(idx), {GroupRole});
+ if (idx > 0) {
+ emit dataChanged(index(idx), index(idx), { GroupRole });
}
}
}
- if(removed)
- {
+ if (removed) {
saveGroupList();
}
}
@@ -306,23 +287,75 @@ bool InstanceList::isGroupCollapsed(const QString& group)
return m_collapsedGroups.contains(group);
}
+bool InstanceList::trashInstance(const InstanceId& id)
+{
+ auto inst = getInstanceById(id);
+ if (!inst) {
+ qDebug() << "Cannot trash instance" << id << ". No such instance is present (deleted externally?).";
+ return false;
+ }
+
+ auto cachedGroupId = m_instanceGroupIndex[id];
+
+ qDebug() << "Will trash instance" << id;
+ QString trashedLoc;
+
+ if (m_instanceGroupIndex.remove(id)) {
+ saveGroupList();
+ }
+
+ if (!FS::trash(inst->instanceRoot(), &trashedLoc)) {
+ qDebug() << "Trash of instance" << id << "has not been completely successfully...";
+ return false;
+ }
+
+ qDebug() << "Instance" << id << "has been trashed by the launcher.";
+ m_trashHistory.push({id, inst->instanceRoot(), trashedLoc, cachedGroupId});
+
+ return true;
+}
+
+bool InstanceList::trashedSomething() {
+ return !m_trashHistory.empty();
+}
+
+void InstanceList::undoTrashInstance() {
+ if (m_trashHistory.empty()) {
+ qWarning() << "Nothing to recover from trash.";
+ return;
+ }
+
+ auto top = m_trashHistory.pop();
+
+ while (QDir(top.polyPath).exists()) {
+ top.id += "1";
+ top.polyPath += "1";
+ }
+
+ qDebug() << "Moving" << top.trashPath << "back to" << top.polyPath;
+ QFile(top.trashPath).rename(top.polyPath);
+
+ m_instanceGroupIndex[top.id] = top.groupName;
+ m_groupNameCache.insert(top.groupName);
+
+ saveGroupList();
+ emit instancesChanged();
+}
+
void InstanceList::deleteInstance(const InstanceId& id)
{
auto inst = getInstanceById(id);
- if(!inst)
- {
+ if (!inst) {
qDebug() << "Cannot delete instance" << id << ". No such instance is present (deleted externally?).";
return;
}
- if(m_instanceGroupIndex.remove(id))
- {
+ if (m_instanceGroupIndex.remove(id)) {
saveGroupList();
}
qDebug() << "Will delete instance" << id;
- if(!FS::deletePath(inst->instanceRoot()))
- {
+ if (!FS::deletePath(inst->instanceRoot())) {
qWarning() << "Deletion of instance" << id << "has not been completely successful ...";
return;
}
@@ -330,15 +363,13 @@ void InstanceList::deleteInstance(const InstanceId& id)
qDebug() << "Instance" << id << "has been deleted by the launcher.";
}
-static QMap<InstanceId, InstanceLocator> getIdMapping(const QList<InstancePtr> &list)
+static QMap<InstanceId, InstanceLocator> getIdMapping(const QList<InstancePtr>& list)
{
QMap<InstanceId, InstanceLocator> out;
int i = 0;
- for(auto & item: list)
- {
+ for (auto& item : list) {
auto id = item->id();
- if(out.contains(id))
- {
+ if (out.contains(id)) {
qWarning() << "Duplicate ID" << id << "in instance list";
}
out[id] = std::make_pair(item, i);
@@ -347,24 +378,21 @@ static QMap<InstanceId, InstanceLocator> getIdMapping(const QList<InstancePtr> &
return out;
}
-QList< InstanceId > InstanceList::discoverInstances()
+QList<InstanceId> InstanceList::discoverInstances()
{
qDebug() << "Discovering instances in" << m_instDir;
QList<InstanceId> out;
QDirIterator iter(m_instDir, QDir::Dirs | QDir::NoDot | QDir::NoDotDot | QDir::Readable | QDir::Hidden, QDirIterator::FollowSymlinks);
- while (iter.hasNext())
- {
+ while (iter.hasNext()) {
QString subDir = iter.next();
QFileInfo dirInfo(subDir);
if (!QFileInfo(FS::PathCombine(subDir, "instance.cfg")).exists())
continue;
// if it is a symlink, ignore it if it goes to the instance folder
- if(dirInfo.isSymLink())
- {
+ if (dirInfo.isSymLink()) {
QFileInfo targetInfo(dirInfo.symLinkTarget());
QFileInfo instDirInfo(m_instDir);
- if(targetInfo.canonicalPath() == instDirInfo.canonicalFilePath())
- {
+ if (targetInfo.canonicalPath() == instDirInfo.canonicalFilePath()) {
qDebug() << "Ignoring symlink" << subDir << "that leads into the instances folder";
continue;
}
@@ -388,74 +416,56 @@ InstanceList::InstListError InstanceList::loadList()
QList<InstancePtr> newList;
- for(auto & id: discoverInstances())
- {
- if(existingIds.contains(id))
- {
+ for (auto& id : discoverInstances()) {
+ if (existingIds.contains(id)) {
auto instPair = existingIds[id];
existingIds.remove(id);
qDebug() << "Should keep and soft-reload" << id;
- }
- else
- {
+ } else {
InstancePtr instPtr = loadInstance(id);
- if(instPtr)
- {
+ if (instPtr) {
newList.append(instPtr);
}
}
}
// TODO: looks like a general algorithm with a few specifics inserted. Do something about it.
- if(!existingIds.isEmpty())
- {
+ if (!existingIds.isEmpty()) {
// get the list of removed instances and sort it by their original index, from last to first
auto deadList = existingIds.values();
- auto orderSortPredicate = [](const InstanceLocator & a, const InstanceLocator & b) -> bool
- {
- return a.second > b.second;
- };
+ auto orderSortPredicate = [](const InstanceLocator& a, const InstanceLocator& b) -> bool { return a.second > b.second; };
std::sort(deadList.begin(), deadList.end(), orderSortPredicate);
// remove the contiguous ranges of rows
int front_bookmark = -1;
int back_bookmark = -1;
int currentItem = -1;
- auto removeNow = [&]()
- {
+ auto removeNow = [&]() {
beginRemoveRows(QModelIndex(), front_bookmark, back_bookmark);
m_instances.erase(m_instances.begin() + front_bookmark, m_instances.begin() + back_bookmark + 1);
endRemoveRows();
front_bookmark = -1;
back_bookmark = currentItem;
};
- for(auto & removedItem: deadList)
- {
+ for (auto& removedItem : deadList) {
auto instPtr = removedItem.first;
instPtr->invalidate();
currentItem = removedItem.second;
- if(back_bookmark == -1)
- {
+ if (back_bookmark == -1) {
// no bookmark yet
back_bookmark = currentItem;
- }
- else if(currentItem == front_bookmark - 1)
- {
+ } else if (currentItem == front_bookmark - 1) {
// part of contiguous sequence, continue
- }
- else
- {
+ } else {
// seam between previous and current item
removeNow();
}
front_bookmark = currentItem;
}
- if(back_bookmark != -1)
- {
+ if (back_bookmark != -1) {
removeNow();
}
}
- if(newList.size())
- {
+ if (newList.size()) {
add(newList);
}
m_dirty = false;
@@ -466,26 +476,23 @@ InstanceList::InstListError InstanceList::loadList()
void InstanceList::updateTotalPlayTime()
{
totalPlayTime = 0;
- for(auto const& itr : m_instances)
- {
+ for (auto const& itr : m_instances) {
totalPlayTime += itr.get()->totalTimePlayed();
}
}
void InstanceList::saveNow()
{
- for(auto & item: m_instances)
- {
+ for (auto& item : m_instances) {
item->saveNow();
}
}
-void InstanceList::add(const QList<InstancePtr> &t)
+void InstanceList::add(const QList<InstancePtr>& t)
{
beginInsertRows(QModelIndex(), m_instances.count(), m_instances.count() + t.size() - 1);
m_instances.append(t);
- for(auto & ptr : t)
- {
+ for (auto& ptr : t) {
connect(ptr.get(), &BaseInstance::propertiesChanged, this, &InstanceList::propertiesChanged);
}
endInsertRows();
@@ -493,69 +500,61 @@ void InstanceList::add(const QList<InstancePtr> &t)
void InstanceList::resumeWatch()
{
- if(m_watchLevel > 0)
- {
+ if (m_watchLevel > 0) {
qWarning() << "Bad suspend level resume in instance list";
return;
}
m_watchLevel++;
- if(m_watchLevel > 0 && m_dirty)
- {
+ if (m_watchLevel > 0 && m_dirty) {
loadList();
}
}
void InstanceList::suspendWatch()
{
- m_watchLevel --;
+ m_watchLevel--;
}
void InstanceList::providerUpdated()
{
m_dirty = true;
- if(m_watchLevel == 1)
- {
+ if (m_watchLevel == 1) {
loadList();
}
}
InstancePtr InstanceList::getInstanceById(QString instId) const
{
- if(instId.isEmpty())
+ if (instId.isEmpty())
return InstancePtr();
- for(auto & inst: m_instances)
- {
- if (inst->id() == instId)
- {
+ for (auto& inst : m_instances) {
+ if (inst->id() == instId) {
return inst;
}
}
return InstancePtr();
}
-QModelIndex InstanceList::getInstanceIndexById(const QString &id) const
+QModelIndex InstanceList::getInstanceIndexById(const QString& id) const
{
return index(getInstIndex(getInstanceById(id).get()));
}
-int InstanceList::getInstIndex(BaseInstance *inst) const
+int InstanceList::getInstIndex(BaseInstance* inst) const
{
int count = m_instances.count();
- for (int i = 0; i < count; i++)
- {
- if (inst == m_instances[i].get())
- {
+ for (int i = 0; i < count; i++) {
+ if (inst == m_instances[i].get()) {
return i;
}
}
return -1;
}
-void InstanceList::propertiesChanged(BaseInstance *inst)
+void InstanceList::propertiesChanged(BaseInstance* inst)
{
int i = getInstIndex(inst);
- if (i != -1)
- {
+ if (i != -1) {
emit dataChanged(index(i), index(i));
updateTotalPlayTime();
}
@@ -563,8 +562,7 @@ void InstanceList::propertiesChanged(BaseInstance *inst)
InstancePtr InstanceList::loadInstance(const InstanceId& id)
{
- if(!m_groupsLoaded)
- {
+ if (!m_groupsLoaded) {
loadGroupList();
}
@@ -592,50 +590,42 @@ InstancePtr InstanceList::loadInstance(const InstanceId& id)
void InstanceList::saveGroupList()
{
qDebug() << "Will save group list now.";
- if(!m_instancesProbed)
- {
+ if (!m_instancesProbed) {
qDebug() << "Group saving prevented because we don't know the full list of instances yet.";
return;
}
WatchLock foo(m_watcher, m_instDir);
QString groupFileName = m_instDir + "/instgroups.json";
QMap<QString, QSet<QString>> reverseGroupMap;
- for (auto iter = m_instanceGroupIndex.begin(); iter != m_instanceGroupIndex.end(); iter++)
- {
+ for (auto iter = m_instanceGroupIndex.begin(); iter != m_instanceGroupIndex.end(); iter++) {
QString id = iter.key();
QString group = iter.value();
if (group.isEmpty())
continue;
- if(!instanceSet.contains(id))
- {
+ if (!instanceSet.contains(id)) {
qDebug() << "Skipping saving missing instance" << id << "to groups list.";
continue;
}
- if (!reverseGroupMap.count(group))
- {
+ if (!reverseGroupMap.count(group)) {
QSet<QString> set;
set.insert(id);
reverseGroupMap[group] = set;
- }
- else
- {
- QSet<QString> &set = reverseGroupMap[group];
+ } else {
+ QSet<QString>& set = reverseGroupMap[group];
set.insert(id);
}
}
QJsonObject toplevel;
toplevel.insert("formatVersion", QJsonValue(QString("1")));
QJsonObject groupsArr;
- for (auto iter = reverseGroupMap.begin(); iter != reverseGroupMap.end(); iter++)
- {
+ for (auto iter = reverseGroupMap.begin(); iter != reverseGroupMap.end(); iter++) {
auto list = iter.value();
auto name = iter.key();
QJsonObject groupObj;
QJsonArray instanceArr;
groupObj.insert("hidden", QJsonValue(m_collapsedGroups.contains(name)));
- for (auto item : list)
- {
+ for (auto item : list) {
instanceArr.append(QJsonValue(item));
}
groupObj.insert("instances", instanceArr);
@@ -643,13 +633,10 @@ void InstanceList::saveGroupList()
}
toplevel.insert("groups", groupsArr);
QJsonDocument doc(toplevel);
- try
- {
+ try {
FS::write(groupFileName, doc.toJson());
qDebug() << "Group list saved.";
- }
- catch (const FS::FileSystemException &e)
- {
+ } catch (const FS::FileSystemException& e) {
qCritical() << "Failed to write instance group file :" << e.cause();
}
}
@@ -665,12 +652,9 @@ void InstanceList::loadGroupList()
return;
QByteArray jsonData;
- try
- {
+ try {
jsonData = FS::read(groupFileName);
- }
- catch (const FS::FileSystemException &e)
- {
+ } catch (const FS::FileSystemException& e) {
qCritical() << "Failed to read instance group file :" << e.cause();
return;
}
@@ -679,17 +663,15 @@ void InstanceList::loadGroupList()
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &error);
// if the json was bad, fail
- if (error.error != QJsonParseError::NoError)
- {
+ if (error.error != QJsonParseError::NoError) {
qCritical() << QString("Failed to parse instance group file: %1 at offset %2")
- .arg(error.errorString(), QString::number(error.offset))
- .toUtf8();
+ .arg(error.errorString(), QString::number(error.offset))
+ .toUtf8();
return;
}
// if the root of the json wasn't an object, fail
- if (!jsonDoc.isObject())
- {
+ if (!jsonDoc.isObject()) {
qWarning() << "Invalid group file. Root entry should be an object.";
return;
}
@@ -701,8 +683,7 @@ void InstanceList::loadGroupList()
return;
// Get the groups. if it's not an object, fail
- if (!rootObj.value("groups").isObject())
- {
+ if (!rootObj.value("groups").isObject()) {
qWarning() << "Invalid group list JSON: 'groups' should be an object.";
return;
}
@@ -712,21 +693,20 @@ void InstanceList::loadGroupList()
// Iterate through all the groups.
QJsonObject groupMapping = rootObj.value("groups").toObject();
- for (QJsonObject::iterator iter = groupMapping.begin(); iter != groupMapping.end(); iter++)
- {
+ for (QJsonObject::iterator iter = groupMapping.begin(); iter != groupMapping.end(); iter++) {
QString groupName = iter.key();
// If not an object, complain and skip to the next one.
- if (!iter.value().isObject())
- {
+ if (!iter.value().isObject()) {
qWarning() << QString("Group '%1' in the group list should be an object.").arg(groupName).toUtf8();
continue;
}
QJsonObject groupObj = iter.value().toObject();
- if (!groupObj.value("instances").isArray())
- {
- qWarning() << QString("Group '%1' in the group list is invalid. It should contain an array called 'instances'.").arg(groupName).toUtf8();
+ if (!groupObj.value("instances").isArray()) {
+ qWarning() << QString("Group '%1' in the group list is invalid. It should contain an array called 'instances'.")
+ .arg(groupName)
+ .toUtf8();
continue;
}
@@ -734,15 +714,14 @@ void InstanceList::loadGroupList()
groupSet.insert(groupName);
auto hidden = groupObj.value("hidden").toBool(false);
- if(hidden) {
+ if (hidden) {
m_collapsedGroups.insert(groupName);
}
// Iterate through the list of instances in the group.
QJsonArray instancesArray = groupObj.value("instances").toArray();
- for (QJsonArray::iterator iter2 = instancesArray.begin(); iter2 != instancesArray.end(); iter2++)
- {
+ for (QJsonArray::iterator iter2 = instancesArray.begin(); iter2 != instancesArray.end(); iter2++) {
m_instanceGroupIndex[(*iter2).toString()] = groupName;
}
}
@@ -757,13 +736,11 @@ void InstanceList::instanceDirContentsChanged(const QString& path)
emit instancesChanged();
}
-void InstanceList::on_InstFolderChanged(const Setting &setting, QVariant value)
+void InstanceList::on_InstFolderChanged(const Setting& setting, QVariant value)
{
QString newInstDir = QDir(value.toString()).canonicalPath();
- if(newInstDir != m_instDir)
- {
- if(m_groupsLoaded)
- {
+ if (newInstDir != m_instDir) {
+ if (m_groupsLoaded) {
saveGroupList();
}
m_instDir = newInstDir;
@@ -775,7 +752,7 @@ void InstanceList::on_InstFolderChanged(const Setting &setting, QVariant value)
void InstanceList::on_GroupStateChanged(const QString& group, bool collapsed)
{
qDebug() << "Group" << group << (collapsed ? "collapsed" : "expanded");
- if(collapsed) {
+ if (collapsed) {
m_collapsedGroups.insert(group);
} else {
m_collapsedGroups.remove(group);
@@ -783,19 +760,14 @@ void InstanceList::on_GroupStateChanged(const QString& group, bool collapsed)
saveGroupList();
}
-class InstanceStaging : public Task
-{
-Q_OBJECT
+class InstanceStaging : public Task {
+ Q_OBJECT
const unsigned minBackoff = 1;
const unsigned maxBackoff = 16;
-public:
- InstanceStaging (
- InstanceList * parent,
- Task * child,
- const QString & stagingPath,
- const QString& instanceName,
- const QString& groupName )
- : backoff(minBackoff, maxBackoff)
+
+ public:
+ InstanceStaging(InstanceList* parent, Task* child, const QString& stagingPath, const QString& instanceName, const QString& groupName)
+ : backoff(minBackoff, maxBackoff)
{
m_parent = parent;
m_child.reset(child);
@@ -810,62 +782,51 @@ public:
connect(&m_backoffTimer, &QTimer::timeout, this, &InstanceStaging::childSucceded);
}
- virtual ~InstanceStaging() {};
-
+ virtual ~InstanceStaging(){};
// FIXME/TODO: add ability to abort during instance commit retries
bool abort() override
{
- if(m_child && m_child->canAbort())
- {
+ if (m_child && m_child->canAbort()) {
return m_child->abort();
}
return false;
}
bool canAbort() const override
{
- if(m_child && m_child->canAbort())
- {
+ if (m_child && m_child->canAbort()) {
return true;
}
return false;
}
-protected:
- virtual void executeTask() override
- {
- m_child->start();
- }
- QStringList warnings() const override
- {
- return m_child->warnings();
- }
+ protected:
+ virtual void executeTask() override { m_child->start(); }
+ QStringList warnings() const override { return m_child->warnings(); }
-private slots:
+ private slots:
void childSucceded()
{
unsigned sleepTime = backoff();
- if(m_parent->commitStagedInstance(m_stagingPath, m_instanceName, m_groupName))
- {
+ if (m_parent->commitStagedInstance(m_stagingPath, m_instanceName, m_groupName)) {
emitSucceeded();
return;
}
// we actually failed, retry?
- if(sleepTime == maxBackoff)
- {
+ if (sleepTime == maxBackoff) {
emitFailed(tr("Failed to commit instance, even after multiple retries. It is being blocked by something."));
return;
}
qDebug() << "Failed to commit instance" << m_instanceName << "Initiating backoff:" << sleepTime;
m_backoffTimer.start(sleepTime * 500);
}
- void childFailed(const QString & reason)
+ void childFailed(const QString& reason)
{
m_parent->destroyStagingPath(m_stagingPath);
emitFailed(reason);
}
-private:
+ private:
/*
* WHY: the whole reason why this uses an exponential backoff retry scheme is antivirus on Windows.
* Basically, it starts messing things up while the launcher is extracting/creating instances
@@ -873,14 +834,14 @@ private:
*/
ExponentialSeries backoff;
QString m_stagingPath;
- InstanceList * m_parent;
+ InstanceList* m_parent;
unique_qobject_ptr<Task> m_child;
QString m_instanceName;
QString m_groupName;
QTimer m_backoffTimer;
};
-Task * InstanceList::wrapInstanceTask(InstanceTask * task)
+Task* InstanceList::wrapInstanceTask(InstanceTask* task)
{
auto stagingPath = getStagedInstancePath();
task->setStagingPath(stagingPath);
@@ -895,8 +856,7 @@ QString InstanceList::getStagedInstancePath()
QString relPath = FS::PathCombine(tempDir, key);
QDir rootPath(m_instDir);
auto path = FS::PathCombine(m_instDir, relPath);
- if(!rootPath.mkpath(relPath))
- {
+ if (!rootPath.mkpath(relPath)) {
return QString();
}
#ifdef Q_OS_WIN32
@@ -913,8 +873,7 @@ bool InstanceList::commitStagedInstance(const QString& path, const QString& inst
{
WatchLock lock(m_watcher, m_instDir);
QString destination = FS::PathCombine(m_instDir, instID);
- if(!dir.rename(path, destination))
- {
+ if (!dir.rename(path, destination)) {
qWarning() << "Failed to move" << path << "to" << destination;
return false;
}
@@ -933,7 +892,8 @@ bool InstanceList::destroyStagingPath(const QString& keyPath)
return FS::deletePath(keyPath);
}
-int InstanceList::getTotalPlayTime() {
+int InstanceList::getTotalPlayTime()
+{
updateTotalPlayTime();
return totalPlayTime;
}
diff --git a/launcher/InstanceList.h b/launcher/InstanceList.h
index bc6c3af0..62282f04 100644
--- a/launcher/InstanceList.h
+++ b/launcher/InstanceList.h
@@ -19,6 +19,8 @@
#include <QAbstractListModel>
#include <QSet>
#include <QList>
+#include <QStack>
+#include <QPair>
#include "BaseInstance.h"
@@ -46,6 +48,12 @@ enum class GroupsState
Dirty
};
+struct TrashHistoryItem {
+ QString id;
+ QString polyPath;
+ QString trashPath;
+ QString groupName;
+};
class InstanceList : public QAbstractListModel
{
@@ -102,6 +110,9 @@ public:
void setInstanceGroup(const InstanceId & id, const GroupId& name);
void deleteGroup(const GroupId & name);
+ bool trashInstance(const InstanceId &id);
+ bool trashedSomething();
+ void undoTrashInstance();
void deleteInstance(const InstanceId & id);
// Wrap an instance creation task in some more task machinery and make it ready to be used
@@ -180,4 +191,6 @@ private:
QSet<InstanceId> instanceSet;
bool m_groupsLoaded = false;
bool m_instancesProbed = false;
+
+ QStack<TrashHistoryItem> m_trashHistory;
};
diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp
index 8518e606..04ca5094 100644
--- a/launcher/MMCZip.cpp
+++ b/launcher/MMCZip.cpp
@@ -268,7 +268,7 @@ bool MMCZip::findFilesInZip(QuaZip * zip, const QString & what, QStringList & re
// ours
-nonstd::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString & subdir, const QString &target)
+std::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString & subdir, const QString &target)
{
QDir directory(target);
QStringList extracted;
@@ -277,7 +277,7 @@ nonstd::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString &
auto numEntries = zip->getEntriesCount();
if(numEntries < 0) {
qWarning() << "Failed to enumerate files in archive";
- return nonstd::nullopt;
+ return std::nullopt;
}
else if(numEntries == 0) {
qDebug() << "Extracting empty archives seems odd...";
@@ -286,7 +286,7 @@ nonstd::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString &
else if (!zip->goToFirstFile())
{
qWarning() << "Failed to seek to first file in zip";
- return nonstd::nullopt;
+ return std::nullopt;
}
do
@@ -323,7 +323,7 @@ nonstd::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString &
{
qWarning() << "Failed to extract file" << original_name << "to" << absFilePath;
JlCompress::removeFile(extracted);
- return nonstd::nullopt;
+ return std::nullopt;
}
extracted.append(absFilePath);
@@ -341,7 +341,7 @@ bool MMCZip::extractRelFile(QuaZip *zip, const QString &file, const QString &tar
}
// ours
-nonstd::optional<QStringList> MMCZip::extractDir(QString fileCompressed, QString dir)
+std::optional<QStringList> MMCZip::extractDir(QString fileCompressed, QString dir)
{
QuaZip zip(fileCompressed);
if (!zip.open(QuaZip::mdUnzip))
@@ -352,13 +352,13 @@ nonstd::optional<QStringList> MMCZip::extractDir(QString fileCompressed, QString
return QStringList();
}
qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();;
- return nonstd::nullopt;
+ return std::nullopt;
}
return MMCZip::extractSubDir(&zip, "", dir);
}
// ours
-nonstd::optional<QStringList> MMCZip::extractDir(QString fileCompressed, QString subdir, QString dir)
+std::optional<QStringList> MMCZip::extractDir(QString fileCompressed, QString subdir, QString dir)
{
QuaZip zip(fileCompressed);
if (!zip.open(QuaZip::mdUnzip))
@@ -369,7 +369,7 @@ nonstd::optional<QStringList> MMCZip::extractDir(QString fileCompressed, QString
return QStringList();
}
qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();;
- return nonstd::nullopt;
+ return std::nullopt;
}
return MMCZip::extractSubDir(&zip, subdir, dir);
}
diff --git a/launcher/MMCZip.h b/launcher/MMCZip.h
index 7f43d158..ce9775bd 100644
--- a/launcher/MMCZip.h
+++ b/launcher/MMCZip.h
@@ -42,7 +42,7 @@
#include <functional>
#include <quazip/JlCompress.h>
-#include <nonstd/optional>
+#include <optional>
namespace MMCZip
{
@@ -95,7 +95,7 @@ namespace MMCZip
/**
* Extract a subdirectory from an archive
*/
- nonstd::optional<QStringList> extractSubDir(QuaZip *zip, const QString & subdir, const QString &target);
+ std::optional<QStringList> extractSubDir(QuaZip *zip, const QString & subdir, const QString &target);
bool extractRelFile(QuaZip *zip, const QString & file, const QString &target);
@@ -106,7 +106,7 @@ namespace MMCZip
* \param dir The directory to extract to, the current directory if left empty.
* \return The list of the full paths of the files extracted, empty on failure.
*/
- nonstd::optional<QStringList> extractDir(QString fileCompressed, QString dir);
+ std::optional<QStringList> extractDir(QString fileCompressed, QString dir);
/**
* Extract a subdirectory from an archive
@@ -116,7 +116,7 @@ namespace MMCZip
* \param dir The directory to extract to, the current directory if left empty.
* \return The list of the full paths of the files extracted, empty on failure.
*/
- nonstd::optional<QStringList> extractDir(QString fileCompressed, QString subdir, QString dir);
+ std::optional<QStringList> extractDir(QString fileCompressed, QString subdir, QString dir);
/**
* Extract a single file from an archive into a directory
diff --git a/launcher/minecraft/World.cpp b/launcher/minecraft/World.cpp
index dfcb43d8..90fcf337 100644
--- a/launcher/minecraft/World.cpp
+++ b/launcher/minecraft/World.cpp
@@ -53,12 +53,12 @@
#include <QCoreApplication>
-#include <nonstd/optional>
+#include <optional>
-using nonstd::optional;
-using nonstd::nullopt;
+using std::optional;
+using std::nullopt;
-GameType::GameType(nonstd::optional<int> original):
+GameType::GameType(std::optional<int> original):
original(original)
{
if(!original) {
diff --git a/launcher/minecraft/World.h b/launcher/minecraft/World.h
index 0f587620..8327253a 100644
--- a/launcher/minecraft/World.h
+++ b/launcher/minecraft/World.h
@@ -16,11 +16,11 @@
#pragma once
#include <QFileInfo>
#include <QDateTime>
-#include <nonstd/optional>
+#include <optional>
struct GameType {
GameType() = default;
- GameType (nonstd::optional<int> original);
+ GameType (std::optional<int> original);
QString toTranslatedString() const;
QString toLogString() const;
@@ -33,7 +33,7 @@ struct GameType {
Adventure,
Spectator
} type = Unknown;
- nonstd::optional<int> original;
+ std::optional<int> original;
};
class World
diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.h b/launcher/modplatform/atlauncher/ATLPackInstallTask.h
index f55873e9..992ba9c5 100644
--- a/launcher/modplatform/atlauncher/ATLPackInstallTask.h
+++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.h
@@ -46,7 +46,7 @@
#include "minecraft/PackProfile.h"
#include "meta/Version.h"
-#include <nonstd/optional>
+#include <optional>
namespace ATLauncher {
@@ -131,8 +131,8 @@ private:
Meta::VersionPtr minecraftVersion;
QMap<QString, Meta::VersionPtr> componentsToInstall;
- QFuture<nonstd::optional<QStringList>> m_extractFuture;
- QFutureWatcher<nonstd::optional<QStringList>> m_extractFutureWatcher;
+ QFuture<std::optional<QStringList>> m_extractFuture;
+ QFutureWatcher<std::optional<QStringList>> m_extractFutureWatcher;
QFuture<bool> m_modExtractFuture;
QFutureWatcher<bool> m_modExtractFutureWatcher;
diff --git a/launcher/modplatform/legacy_ftb/PackInstallTask.h b/launcher/modplatform/legacy_ftb/PackInstallTask.h
index a7395220..da4c0da5 100644
--- a/launcher/modplatform/legacy_ftb/PackInstallTask.h
+++ b/launcher/modplatform/legacy_ftb/PackInstallTask.h
@@ -10,7 +10,7 @@
#include "net/NetJob.h"
-#include <nonstd/optional>
+#include <optional>
namespace LegacyFTB {
@@ -46,8 +46,8 @@ private: /* data */
shared_qobject_ptr<QNetworkAccessManager> m_network;
bool abortable = false;
std::unique_ptr<QuaZip> m_packZip;
- QFuture<nonstd::optional<QStringList>> m_extractFuture;
- QFutureWatcher<nonstd::optional<QStringList>> m_extractFutureWatcher;
+ QFuture<std::optional<QStringList>> m_extractFuture;
+ QFutureWatcher<std::optional<QStringList>> m_extractFutureWatcher;
NetJob::Ptr netJobContainer;
QString archivePath;
diff --git a/launcher/modplatform/technic/SingleZipPackInstallTask.h b/launcher/modplatform/technic/SingleZipPackInstallTask.h
index 4d1fcbff..981ccf8a 100644
--- a/launcher/modplatform/technic/SingleZipPackInstallTask.h
+++ b/launcher/modplatform/technic/SingleZipPackInstallTask.h
@@ -24,7 +24,7 @@
#include <QStringList>
#include <QUrl>
-#include <nonstd/optional>
+#include <optional>
namespace Technic {
@@ -57,8 +57,8 @@ private:
QString m_archivePath;
NetJob::Ptr m_filesNetJob;
std::unique_ptr<QuaZip> m_packZip;
- QFuture<nonstd::optional<QStringList>> m_extractFuture;
- QFutureWatcher<nonstd::optional<QStringList>> m_extractFutureWatcher;
+ QFuture<std::optional<QStringList>> m_extractFuture;
+ QFutureWatcher<std::optional<QStringList>> m_extractFutureWatcher;
};
} // namespace Technic
diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp
index d58f158e..c3d95599 100644
--- a/launcher/ui/MainWindow.cpp
+++ b/launcher/ui/MainWindow.cpp
@@ -252,6 +252,9 @@ public:
TranslatedAction actionViewInstanceFolder;
TranslatedAction actionViewCentralModsFolder;
+ QMenu * editMenu = nullptr;
+ TranslatedAction actionUndoTrashInstance;
+
QMenu * helpMenu = nullptr;
TranslatedToolButton helpMenuButton;
TranslatedAction actionReportBug;
@@ -335,6 +338,14 @@ public:
actionSettings->setShortcut(QKeySequence::Preferences);
all_actions.append(&actionSettings);
+ actionUndoTrashInstance = TranslatedAction(MainWindow);
+ connect(actionUndoTrashInstance, SIGNAL(triggered(bool)), MainWindow, SLOT(undoTrashInstance()));
+ actionUndoTrashInstance->setObjectName(QStringLiteral("actionUndoTrashInstance"));
+ actionUndoTrashInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "&Undo Last Instance Deletion"));
+ actionUndoTrashInstance->setEnabled(APPLICATION->instances()->trashedSomething());
+ actionUndoTrashInstance->setShortcut(QKeySequence("Ctrl+Z"));
+ all_actions.append(&actionUndoTrashInstance);
+
if (!BuildConfig.BUG_TRACKER_URL.isEmpty()) {
actionReportBug = TranslatedAction(MainWindow);
actionReportBug->setObjectName(QStringLiteral("actionReportBug"));
@@ -508,6 +519,9 @@ public:
fileMenu->addSeparator();
fileMenu->addAction(actionSettings);
+ editMenu = menuBar->addMenu(tr("&Edit"));
+ editMenu->addAction(actionUndoTrashInstance);
+
viewMenu = menuBar->addMenu(tr("&View"));
viewMenu->setSeparatorsCollapsible(false);
viewMenu->addAction(actionCAT);
@@ -732,9 +746,10 @@ public:
actionDeleteInstance = TranslatedAction(MainWindow);
actionDeleteInstance->setObjectName(QStringLiteral("actionDeleteInstance"));
- actionDeleteInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Dele&te Instance..."));
+ actionDeleteInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Dele&te Instance"));
actionDeleteInstance.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Delete the selected instance."));
actionDeleteInstance->setShortcuts({QKeySequence(tr("Backspace")), QKeySequence::Delete});
+ actionDeleteInstance->setAutoRepeat(false);
all_actions.append(&actionDeleteInstance);
actionCopyInstance = TranslatedAction(MainWindow);
@@ -1150,6 +1165,11 @@ void MainWindow::showInstanceContextMenu(const QPoint &pos)
connect(actionDeleteGroup, SIGNAL(triggered(bool)), SLOT(deleteGroup()));
actions.append(actionDeleteGroup);
}
+
+ QAction *actionUndoTrashInstance = new QAction("Undo last trash instance", this);
+ connect(actionUndoTrashInstance, SIGNAL(triggered(bool)), SLOT(undoTrashInstance()));
+ actionUndoTrashInstance->setEnabled(APPLICATION->instances()->trashedSomething());
+ actions.append(actionUndoTrashInstance);
}
QMenu myMenu;
myMenu.addActions(actions);
@@ -1832,6 +1852,11 @@ void MainWindow::deleteGroup()
}
}
+void MainWindow::undoTrashInstance()
+{
+ APPLICATION->instances()->undoTrashInstance();
+}
+
void MainWindow::on_actionViewInstanceFolder_triggered()
{
QString str = APPLICATION->settings()->get("InstanceDir").toString();
@@ -1957,7 +1982,12 @@ void MainWindow::on_actionDeleteInstance_triggered()
{
return;
}
+
auto id = m_selectedInstance->id();
+ if (APPLICATION->instances()->trashInstance(id)) {
+ return;
+ }
+
auto response = CustomMessageBox::selectable(
this,
tr("CAREFUL!"),
diff --git a/launcher/ui/MainWindow.h b/launcher/ui/MainWindow.h
index d7930b5a..dde3d02c 100644
--- a/launcher/ui/MainWindow.h
+++ b/launcher/ui/MainWindow.h
@@ -145,6 +145,7 @@ private slots:
void on_actionDeleteInstance_triggered();
void deleteGroup();
+ void undoTrashInstance();
void on_actionExportInstance_triggered();
diff --git a/libraries/README.md b/libraries/README.md
index 946e34d8..f225ade8 100644
--- a/libraries/README.md
+++ b/libraries/README.md
@@ -22,11 +22,6 @@ Hoedown is a revived fork of Sundown, the Markdown parser based on the original
See [github repo](https://github.com/hoedown/hoedown).
-## iconfix
-This was originally part of the razor-qt project and the Qt toolkit, respecitvely. Its sole purpose is to reimplement Qt's icon loading logic to prevent it from using any platform plugins that could break icon loading.
-
-Licensed under LGPL 2.1
-
## javacheck
Simple Java tool that prints the JVM details - version and platform bitness.
@@ -155,14 +150,6 @@ Canonical implementation of the murmur2 hash, taken from [SMHasher](https://gith
Public domain (the author disclaimed the copyright).
-## optional-bare
-
-A simple single-file header-only version of a C++17-like optional for default-constructible, copyable types, for C++98 and later.
-
-Imported from: https://github.com/martinmoene/optional-bare/commit/0bb1d183bcee1e854c4ea196b533252c51f98b81
-
-Boost Software License - Version 1.0
-
## quazip
A zip manipulation library, forked for MultiMC's use.
diff --git a/libraries/optional-bare/CMakeLists.txt b/libraries/optional-bare/CMakeLists.txt
deleted file mode 100644
index 952df6e2..00000000
--- a/libraries/optional-bare/CMakeLists.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-cmake_minimum_required(VERSION 3.9.4)
-project(optional-bare)
-
-add_library(optional-bare INTERFACE)
-target_include_directories(optional-bare INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include")
diff --git a/libraries/optional-bare/LICENSE.txt b/libraries/optional-bare/LICENSE.txt
deleted file mode 100644
index 36b7cd93..00000000
--- a/libraries/optional-bare/LICENSE.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-Boost Software License - Version 1.0 - August 17th, 2003
-
-Permission is hereby granted, free of charge, to any person or organization
-obtaining a copy of the software and accompanying documentation covered by
-this license (the "Software") to use, reproduce, display, distribute,
-execute, and transmit the Software, and to prepare derivative works of the
-Software, and to permit third-parties to whom the Software is furnished to
-do so, all subject to the following:
-
-The copyright notices in the Software and this entire statement, including
-the above license grant, this restriction and the following disclaimer,
-must be included in all copies of the Software, in whole or in part, and
-all derivative works of the Software, unless such copies or derivative
-works are solely in the form of machine-executable object code generated by
-a source language processor.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
-SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
-FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
diff --git a/libraries/optional-bare/README.md b/libraries/optional-bare/README.md
deleted file mode 100644
index e29ff7c1..00000000
--- a/libraries/optional-bare/README.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# optional bare
-
-A simple single-file header-only version of a C++17-like optional for default-constructible, copyable types, for C++98 and later.
-
-Imported from: https://github.com/martinmoene/optional-bare/commit/0bb1d183bcee1e854c4ea196b533252c51f98b81
diff --git a/libraries/optional-bare/include/nonstd/optional b/libraries/optional-bare/include/nonstd/optional
deleted file mode 100644
index ecbfa030..00000000
--- a/libraries/optional-bare/include/nonstd/optional
+++ /dev/null
@@ -1,508 +0,0 @@
-//
-// Copyright 2017-2019 by Martin Moene
-//
-// https://github.com/martinmoene/optional-bare
-//
-// Distributed under the Boost Software License, Version 1.0.
-// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-
-#ifndef NONSTD_OPTIONAL_BARE_HPP
-#define NONSTD_OPTIONAL_BARE_HPP
-
-#define optional_bare_MAJOR 1
-#define optional_bare_MINOR 1
-#define optional_bare_PATCH 0
-
-#define optional_bare_VERSION optional_STRINGIFY(optional_bare_MAJOR) "." optional_STRINGIFY(optional_bare_MINOR) "." optional_STRINGIFY(optional_bare_PATCH)
-
-#define optional_STRINGIFY( x ) optional_STRINGIFY_( x )
-#define optional_STRINGIFY_( x ) #x
-
-// optional-bare configuration:
-
-#define optional_OPTIONAL_DEFAULT 0
-#define optional_OPTIONAL_NONSTD 1
-#define optional_OPTIONAL_STD 2
-
-#if !defined( optional_CONFIG_SELECT_OPTIONAL )
-# define optional_CONFIG_SELECT_OPTIONAL ( optional_HAVE_STD_OPTIONAL ? optional_OPTIONAL_STD : optional_OPTIONAL_NONSTD )
-#endif
-
-// Control presence of exception handling (try and auto discover):
-
-#ifndef optional_CONFIG_NO_EXCEPTIONS
-# if _MSC_VER
-# include <cstddef> // for _HAS_EXCEPTIONS
-# endif
-# if _MSC_VER
-# include <cstddef> // for _HAS_EXCEPTIONS
-# endif
-# if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (_HAS_EXCEPTIONS)
-# define optional_CONFIG_NO_EXCEPTIONS 0
-# else
-# define optional_CONFIG_NO_EXCEPTIONS 1
-# endif
-#endif
-
-// C++ language version detection (C++20 is speculative):
-// Note: VC14.0/1900 (VS2015) lacks too much from C++14.
-
-#ifndef optional_CPLUSPLUS
-# if defined(_MSVC_LANG ) && !defined(__clang__)
-# define optional_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG )
-# else
-# define optional_CPLUSPLUS __cplusplus
-# endif
-#endif
-
-#define optional_CPP98_OR_GREATER ( optional_CPLUSPLUS >= 199711L )
-#define optional_CPP11_OR_GREATER ( optional_CPLUSPLUS >= 201103L )
-#define optional_CPP14_OR_GREATER ( optional_CPLUSPLUS >= 201402L )
-#define optional_CPP17_OR_GREATER ( optional_CPLUSPLUS >= 201703L )
-#define optional_CPP20_OR_GREATER ( optional_CPLUSPLUS >= 202000L )
-
-// C++ language version (represent 98 as 3):
-
-#define optional_CPLUSPLUS_V ( optional_CPLUSPLUS / 100 - (optional_CPLUSPLUS > 200000 ? 2000 : 1994) )
-
-// Use C++17 std::optional if available and requested:
-
-#if optional_CPP17_OR_GREATER && defined(__has_include )
-# if __has_include( <optional> )
-# define optional_HAVE_STD_OPTIONAL 1
-# else
-# define optional_HAVE_STD_OPTIONAL 0
-# endif
-#else
-# define optional_HAVE_STD_OPTIONAL 0
-#endif
-
-#define optional_USES_STD_OPTIONAL ( (optional_CONFIG_SELECT_OPTIONAL == optional_OPTIONAL_STD) || ((optional_CONFIG_SELECT_OPTIONAL == optional_OPTIONAL_DEFAULT) && optional_HAVE_STD_OPTIONAL) )
-
-//
-// Using std::optional:
-//
-
-#if optional_USES_STD_OPTIONAL
-
-#include <optional>
-#include <utility>
-
-namespace nonstd {
-
- using std::in_place;
- using std::in_place_type;
- using std::in_place_index;
- using std::in_place_t;
- using std::in_place_type_t;
- using std::in_place_index_t;
-
- using std::optional;
- using std::bad_optional_access;
- using std::hash;
-
- using std::nullopt;
- using std::nullopt_t;
-
- using std::operator==;
- using std::operator!=;
- using std::operator<;
- using std::operator<=;
- using std::operator>;
- using std::operator>=;
- using std::make_optional;
- using std::swap;
-}
-
-#else // optional_USES_STD_OPTIONAL
-
-#include <cassert>
-
-#if ! optional_CONFIG_NO_EXCEPTIONS
-# include <stdexcept>
-#endif
-
-namespace nonstd { namespace optional_bare {
-
-// type for nullopt
-
-struct nullopt_t
-{
- struct init{};
- nullopt_t( init ) {}
-};
-
-// extra parenthesis to prevent the most vexing parse:
-
-const nullopt_t nullopt(( nullopt_t::init() ));
-
-// optional access error.
-
-#if ! optional_CONFIG_NO_EXCEPTIONS
-
-class bad_optional_access : public std::logic_error
-{
-public:
- explicit bad_optional_access()
- : logic_error( "bad optional access" ) {}
-};
-
-#endif // optional_CONFIG_NO_EXCEPTIONS
-
-// Simplistic optional: requires T to be default constructible, copyable.
-
-template< typename T >
-class optional
-{
-private:
- typedef void (optional::*safe_bool)() const;
-
-public:
- typedef T value_type;
-
- optional()
- : has_value_( false )
- {}
-
- optional( nullopt_t )
- : has_value_( false )
- {}
-
- optional( T const & arg )
- : has_value_( true )
- , value_ ( arg )
- {}
-
- template< class U >
- optional( optional<U> const & other )
- : has_value_( other.has_value() )
- , value_ ( other.value() )
- {}
-
- optional & operator=( nullopt_t )
- {
- reset();
- return *this;
- }
-
- template< class U >
- optional & operator=( optional<U> const & other )
- {
- has_value_ = other.has_value();
- value_ = other.value();
- return *this;
- }
-
- void swap( optional & rhs )
- {
- using std::swap;
- if ( has_value() == true && rhs.has_value() == true ) { swap( **this, *rhs ); }
- else if ( has_value() == false && rhs.has_value() == true ) { initialize( *rhs ); rhs.reset(); }
- else if ( has_value() == true && rhs.has_value() == false ) { rhs.initialize( **this ); reset(); }
- }
-
- // observers
-
- value_type const * operator->() const
- {
- return assert( has_value() ),
- &value_;
- }
-
- value_type * operator->()
- {
- return assert( has_value() ),
- &value_;
- }
-
- value_type const & operator*() const
- {
- return assert( has_value() ),
- value_;
- }
-
- value_type & operator*()
- {
- return assert( has_value() ),
- value_;
- }
-
-#if optional_CPP11_OR_GREATER
- explicit operator bool() const
- {
- return has_value();
- }
-#else
- operator safe_bool() const
- {
- return has_value() ? &optional::this_type_does_not_support_comparisons : 0;
- }
-#endif
-
- bool has_value() const
- {
- return has_value_;
- }
-
- value_type const & value() const
- {
-#if optional_CONFIG_NO_EXCEPTIONS
- assert( has_value() );
-#else
- if ( ! has_value() )
- throw bad_optional_access();
-#endif
- return value_;
- }
-
- value_type & value()
- {
-#if optional_CONFIG_NO_EXCEPTIONS
- assert( has_value() );
-#else
- if ( ! has_value() )
- throw bad_optional_access();
-#endif
- return value_;
- }
-
- template< class U >
- value_type value_or( U const & v ) const
- {
- return has_value() ? value() : static_cast<value_type>( v );
- }
-
- // modifiers
-
- void reset()
- {
- has_value_ = false;
- }
-
-private:
- void this_type_does_not_support_comparisons() const {}
-
- template< typename V >
- void initialize( V const & value )
- {
- assert( ! has_value() );
- value_ = value;
- has_value_ = true;
- }
-
-private:
- bool has_value_;
- value_type value_;
-};
-
-// Relational operators
-
-template< typename T, typename U >
-inline bool operator==( optional<T> const & x, optional<U> const & y )
-{
- return bool(x) != bool(y) ? false : bool(x) == false ? true : *x == *y;
-}
-
-template< typename T, typename U >
-inline bool operator!=( optional<T> const & x, optional<U> const & y )
-{
- return !(x == y);
-}
-
-template< typename T, typename U >
-inline bool operator<( optional<T> const & x, optional<U> const & y )
-{
- return (!y) ? false : (!x) ? true : *x < *y;
-}
-
-template< typename T, typename U >
-inline bool operator>( optional<T> const & x, optional<U> const & y )
-{
- return (y < x);
-}
-
-template< typename T, typename U >
-inline bool operator<=( optional<T> const & x, optional<U> const & y )
-{
- return !(y < x);
-}
-
-template< typename T, typename U >
-inline bool operator>=( optional<T> const & x, optional<U> const & y )
-{
- return !(x < y);
-}
-
-// Comparison with nullopt
-
-template< typename T >
-inline bool operator==( optional<T> const & x, nullopt_t )
-{
- return (!x);
-}
-
-template< typename T >
-inline bool operator==( nullopt_t, optional<T> const & x )
-{
- return (!x);
-}
-
-template< typename T >
-inline bool operator!=( optional<T> const & x, nullopt_t )
-{
- return bool(x);
-}
-
-template< typename T >
-inline bool operator!=( nullopt_t, optional<T> const & x )
-{
- return bool(x);
-}
-
-template< typename T >
-inline bool operator<( optional<T> const &, nullopt_t )
-{
- return false;
-}
-
-template< typename T >
-inline bool operator<( nullopt_t, optional<T> const & x )
-{
- return bool(x);
-}
-
-template< typename T >
-inline bool operator<=( optional<T> const & x, nullopt_t )
-{
- return (!x);
-}
-
-template< typename T >
-inline bool operator<=( nullopt_t, optional<T> const & )
-{
- return true;
-}
-
-template< typename T >
-inline bool operator>( optional<T> const & x, nullopt_t )
-{
- return bool(x);
-}
-
-template< typename T >
-inline bool operator>( nullopt_t, optional<T> const & )
-{
- return false;
-}
-
-template< typename T >
-inline bool operator>=( optional<T> const &, nullopt_t )
-{
- return true;
-}
-
-template< typename T >
-inline bool operator>=( nullopt_t, optional<T> const & x )
-{
- return (!x);
-}
-
-// Comparison with T
-
-template< typename T, typename U >
-inline bool operator==( optional<T> const & x, U const & v )
-{
- return bool(x) ? *x == v : false;
-}
-
-template< typename T, typename U >
-inline bool operator==( U const & v, optional<T> const & x )
-{
- return bool(x) ? v == *x : false;
-}
-
-template< typename T, typename U >
-inline bool operator!=( optional<T> const & x, U const & v )
-{
- return bool(x) ? *x != v : true;
-}
-
-template< typename T, typename U >
-inline bool operator!=( U const & v, optional<T> const & x )
-{
- return bool(x) ? v != *x : true;
-}
-
-template< typename T, typename U >
-inline bool operator<( optional<T> const & x, U const & v )
-{
- return bool(x) ? *x < v : true;
-}
-
-template< typename T, typename U >
-inline bool operator<( U const & v, optional<T> const & x )
-{
- return bool(x) ? v < *x : false;
-}
-
-template< typename T, typename U >
-inline bool operator<=( optional<T> const & x, U const & v )
-{
- return bool(x) ? *x <= v : true;
-}
-
-template< typename T, typename U >
-inline bool operator<=( U const & v, optional<T> const & x )
-{
- return bool(x) ? v <= *x : false;
-}
-
-template< typename T, typename U >
-inline bool operator>( optional<T> const & x, U const & v )
-{
- return bool(x) ? *x > v : false;
-}
-
-template< typename T, typename U >
-inline bool operator>( U const & v, optional<T> const & x )
-{
- return bool(x) ? v > *x : true;
-}
-
-template< typename T, typename U >
-inline bool operator>=( optional<T> const & x, U const & v )
-{
- return bool(x) ? *x >= v : false;
-}
-
-template< typename T, typename U >
-inline bool operator>=( U const & v, optional<T> const & x )
-{
- return bool(x) ? v >= *x : true;
-}
-
-// Specialized algorithms
-
-template< typename T >
-void swap( optional<T> & x, optional<T> & y )
-{
- x.swap( y );
-}
-
-// Convenience function to create an optional.
-
-template< typename T >
-inline optional<T> make_optional( T const & v )
-{
- return optional<T>( v );
-}
-
-} // namespace optional-bare
-
-using namespace optional_bare;
-
-} // namespace nonstd
-
-#endif // optional_USES_STD_OPTIONAL
-
-#endif // NONSTD_OPTIONAL_BARE_HPP