From 5b13704275fc3cf02f6c3f4d4bd77d32425ad2c9 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Thu, 23 Jul 2020 01:32:53 -0400 Subject: moved profile, current game and game edition checks to InstanceManager trying to centralize all of this stuff --- src/instancemanager.h | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'src/instancemanager.h') diff --git a/src/instancemanager.h b/src/instancemanager.h index 649c2195..2331ebfd 100644 --- a/src/instancemanager.h +++ b/src/instancemanager.h @@ -24,18 +24,26 @@ along with Mod Organizer. If not, see . #include #include +namespace MOBase { class IPluginGame; } + +class Settings; +class PluginContainer; class InstanceManager { public: - static InstanceManager &instance(); - QString determineDataPath(); - void clearCurrentInstance(); - void overrideInstance(const QString& instanceName); + void overrideProfile(const QString& profileName); + + QString determineDataPath(); + QString determineProfile(const Settings &settings); + bool determineGameEdition(Settings& settings, MOBase::IPluginGame* game); + MOBase::IPluginGame* determineCurrentGame( + const QString& moPath, Settings& settings, const PluginContainer &plugins); + void clearCurrentInstance(); QString currentInstance() const; bool allowedToChangeInstance() const; @@ -69,4 +77,6 @@ private: bool m_Reset {false}; bool m_overrideInstance{false}; QString m_overrideInstanceName; + bool m_overrideProfile{false}; + QString m_overrideProfileName; }; -- cgit v1.3.1 From 3c791b092054192fcff27b599728d3482688aec8 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Thu, 23 Jul 2020 03:01:00 -0400 Subject: moved registry key from "Tannin" to "Mod Organizer Team" --- src/env.cpp | 160 ++++++++++++++++++++++++++++++++++++++++++++++++ src/env.h | 9 +++ src/instancemanager.cpp | 31 ++++++++-- src/instancemanager.h | 2 + 4 files changed, 196 insertions(+), 6 deletions(-) (limited to 'src/instancemanager.h') diff --git a/src/env.cpp b/src/env.cpp index 9f0acbbd..5bed84c1 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -846,6 +846,166 @@ Association getAssociation(const QFileInfo& targetInfo) } +struct RegistryKeyCloser +{ + using pointer = HKEY; + + void operator()(HKEY key) + { + if (key != 0) { + ::RegCloseKey(key); + } + } +}; + +using RegistryKeyPtr = std::unique_ptr; + +RegistryKeyPtr openRegistryKey(HKEY parent, const wchar_t* name) +{ + HKEY subkey = 0; + + auto r = ::RegOpenKeyExW( + parent, name, + 0, KEY_SET_VALUE|KEY_ENUMERATE_SUB_KEYS|KEY_QUERY_VALUE, + &subkey); + + if (r != ERROR_SUCCESS) { + return {}; + } + + return RegistryKeyPtr(subkey); +} + +bool keyHasValues(HKEY key) +{ + auto name = std::make_unique(1000 + 1); + DWORD nameSize = 1000; + + // note that RegEnumValueW() also enumerates the default value if it exists + auto r = ::RegEnumValueW( + key, 0, name.get(), &nameSize, nullptr, nullptr, nullptr, nullptr); + + if (r != ERROR_NO_MORE_ITEMS) { + return true; + } + + // no values, no default, it's empty + return false; +} + +bool forEachSubKey(HKEY key, std::function f) +{ + auto name = std::make_unique(1000 + 1); + DWORD nameSize = 1000; + + DWORD i = 0; + + // something would be really wrong if it had more than 100 keys + while (i < 100) + { + auto r = ::RegEnumKeyExW( + key, i, name.get(), &nameSize, nullptr, nullptr, nullptr, nullptr); + + if (r == ERROR_NO_MORE_ITEMS) { + // no more subkeys + break; + } + + if (r == ERROR_SUCCESS) { + // a subkey exists + auto subkey = openRegistryKey(key, name.get()); + + if (!subkey) { + // can't open it, stop + return false; + } + + // fire callback + if (!f(name.get())) { + return false; + } + } else { + // something went wrong, stop + return false; + } + + ++i; + } + + return true; +} + +bool isKeyEmpty(HKEY key) +{ + // check for any values + if (keyHasValues(key)) { + return false; + } + + auto r = forEachSubKey(key, [&](const wchar_t* name) { + // a subkey exists, recursively check if it's empty + auto subkey = openRegistryKey(key, name); + + if (!subkey) { + // can't open, stop + return false; + } + + if (!isKeyEmpty(subkey.get())) { + // not empty, stop + return false; + } + + // empty, go on + return true; + }); + + if (!r) { + // something went wrong or some subkey has values + return false; + } + + // key has no values and has either no subkeys or all subkeys are empty + return true; +} + +void deleteRegistryKeyIfEmpty(const QString& name) +{ + if (name.isEmpty()) { + return; + } + + auto key = openRegistryKey(HKEY_CURRENT_USER, name.toStdWString().c_str()); + if (!key) { + return; + } + + if (!isKeyEmpty(key.get())) { + return; + } + + ::RegDeleteTreeW(HKEY_CURRENT_USER, name.toStdWString().c_str()); +} + +bool registryValueExists(const QString& keyName, const QString& valueName) +{ + auto key = openRegistryKey( + HKEY_CURRENT_USER, keyName.toStdWString().c_str()); + + if (!key) { + return false; + } + + DWORD type = 0; + + auto r = ::RegQueryValueExW( + key.get(), valueName.toStdWString().c_str(), + nullptr, &type, nullptr, nullptr); + + return (r == ERROR_SUCCESS); +} + + // returns the filename of the given process or the current one // std::wstring processFilename(HANDLE process=INVALID_HANDLE_VALUE) diff --git a/src/env.h b/src/env.h index a563f2c3..dbbd5cf4 100644 --- a/src/env.h +++ b/src/env.h @@ -301,6 +301,15 @@ struct Association Association getAssociation(const QFileInfo& file); +// returns whether the given value exists +// +bool registryValueExists(const QString& key, const QString& value); + +// deletes a registry key if it's empty or only contains empty keys +// +void deleteRegistryKeyIfEmpty(const QString& name); + + enum class CoreDumpTypes { Mini = 1, diff --git a/src/instancemanager.cpp b/src/instancemanager.cpp index 9bcd0f3c..aa8f1d8c 100644 --- a/src/instancemanager.cpp +++ b/src/instancemanager.cpp @@ -23,6 +23,7 @@ along with Mod Organizer. If not, see . #include "settings.h" #include "shared/appconfig.h" #include "plugincontainer.h" +#include "env.h" #include #include #include @@ -37,14 +38,15 @@ along with Mod Organizer. If not, see . using namespace MOBase; -static const char COMPANY_NAME[] = "Tannin"; -static const char APPLICATION_NAME[] = "Mod Organizer"; -static const char INSTANCE_KEY[] = "CurrentInstance"; +const QString Organization = "Mod Organizer Team"; +const QString Application = "Mod Organizer"; +const QString InstanceValue = "CurrentInstance"; InstanceManager::InstanceManager() - : m_AppSettings(COMPANY_NAME, APPLICATION_NAME) + : m_AppSettings(Organization, Application) { + updateRegistryKey(); } InstanceManager &InstanceManager::instance() @@ -53,6 +55,23 @@ InstanceManager &InstanceManager::instance() return s_Instance; } +void InstanceManager::updateRegistryKey() +{ + const QString OldOrganization = "Tannin"; + const QString OldApplication = "Mod Organizer"; + const QString OldInstanceValue = "CurrentInstance"; + + const QString OldRootKey = "Software\\" + OldOrganization; + + if (env::registryValueExists(OldRootKey + "\\" + OldApplication, OldInstanceValue)) { + QSettings old(OldOrganization, OldApplication); + setCurrentInstance(old.value(OldInstanceValue).toString()); + old.remove(OldInstanceValue); + } + + env::deleteRegistryKeyIfEmpty(OldRootKey); +} + void InstanceManager::overrideInstance(const QString& instanceName) { m_overrideInstanceName = instanceName; @@ -70,7 +89,7 @@ QString InstanceManager::currentInstance() const if (m_overrideInstance) return m_overrideInstanceName; else - return m_AppSettings.value(INSTANCE_KEY, "").toString(); + return m_AppSettings.value(InstanceValue, "").toString(); } void InstanceManager::clearCurrentInstance() @@ -82,7 +101,7 @@ void InstanceManager::clearCurrentInstance() void InstanceManager::setCurrentInstance(const QString &name) { - m_AppSettings.setValue(INSTANCE_KEY, name); + m_AppSettings.setValue(InstanceValue, name); } bool InstanceManager::deleteLocalInstance(const QString &instanceId) const diff --git a/src/instancemanager.h b/src/instancemanager.h index 2331ebfd..7f87045d 100644 --- a/src/instancemanager.h +++ b/src/instancemanager.h @@ -71,6 +71,8 @@ private: bool portableInstall() const; bool portableInstallIsLocked() const; + void updateRegistryKey(); + private: QSettings m_AppSettings; -- cgit v1.3.1 From 3761ac84b586669b08657d67a6d170009a96795f Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Thu, 23 Jul 2020 03:31:09 -0400 Subject: deleteLocalInstance() now uses TaskDialog --- src/instancemanager.cpp | 87 +++++++++++++++++++++++++++++++++---------------- src/instancemanager.h | 3 +- 2 files changed, 61 insertions(+), 29 deletions(-) (limited to 'src/instancemanager.h') diff --git a/src/instancemanager.cpp b/src/instancemanager.cpp index aa8f1d8c..3821495b 100644 --- a/src/instancemanager.cpp +++ b/src/instancemanager.cpp @@ -104,30 +104,67 @@ void InstanceManager::setCurrentInstance(const QString &name) m_AppSettings.setValue(InstanceValue, name); } -bool InstanceManager::deleteLocalInstance(const QString &instanceId) const +bool InstanceManager::deleteLocalInstance(const QString& instanceId) const { - bool result = true; - QString instancePath = QDir::fromNativeSeparators(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + instanceId); + QString dir = instancePath(instanceId); - if (QMessageBox::warning(nullptr, QObject::tr("Deleting folder"), - QObject::tr("I'm about to delete the following folder: \"%1\". Proceed?").arg(instancePath), QMessageBox::No | QMessageBox::Yes, QMessageBox::No) == QMessageBox::No ){ - return false; - } + const auto Recycle = QMessageBox::Save; + const auto Delete = QMessageBox::Yes; + const auto Cancel = QMessageBox::Cancel; + + const auto r = MOBase::TaskDialog() + .title(QObject::tr("Deleting instance folder")) + .main(QObject::tr("This will delete the instance folder.")) + .content(dir) + .icon(QMessageBox::Warning) + .button({QObject::tr("Move the folder to the recycle bin"), Recycle}) + .button({QObject::tr("Delete the folder permanently"), Delete}) + .button({QObject::tr("Cancel"), Cancel}) + .exec(); + + std::wstring error; - if (!MOBase::shellDelete(QStringList(instancePath),true)) + switch (r) { - log::warn( - "Failed to shell-delete \"{}\" (errorcode {}), trying regular delete", - instancePath, ::GetLastError()); + case Recycle: + { + if (MOBase::shellDelete(QStringList(dir), true)) { + return true; + } + + const auto e = GetLastError(); + error = formatSystemMessage(e); + log::warn("failed to move to trash '{}', {}", dir, error); + + break; + } + + case Delete: + { + if (MOBase::shellDelete(QStringList(dir), false)) { + return true; + } + + const auto e = GetLastError(); + error = formatSystemMessage(e); + log::warn("failed to delete '{}', {}", dir, error); + + break; + } - if (!MOBase::removeDir(instancePath)) + default: { - log::warn("regular delete failed too"); - result = false; + return true; } } - return result; + QMessageBox::critical( + nullptr, QObject::tr("Error"), QObject::tr( + "Could not delete instance folder \"%1\".\n\n%2") + .arg(dir).arg(error), + QMessageBox::Ok); + + return false; } QString InstanceManager::manageInstances(const QStringList &instanceList) const @@ -147,17 +184,7 @@ QString InstanceManager::manageInstances(const QStringList &instanceList) const } else { QString choice = selection.getChoiceData().toString(); - { - if (QMessageBox::warning(nullptr, QObject::tr("Are you sure?"), - QObject::tr("Are you really sure you want to delete the Instance \"%1\" with all its files?").arg(choice), QMessageBox::No | QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) - { - if (!deleteLocalInstance(choice)) - { - QMessageBox::warning(nullptr, QObject::tr("Failed to delete Instance"), - QObject::tr("Could not delete Instance \"%1\". \nIf the folder was still in use, restart MO and try again.").arg(choice), QMessageBox::Ok); - } - } - } + deleteLocalInstance(choice); } return(manageInstances(instances())); } @@ -278,8 +305,12 @@ QString InstanceManager::chooseInstance(const QStringList &instanceList) const } } +QString InstanceManager::instancePath(const QString& instanceName) const +{ + return QDir::fromNativeSeparators(instancesPath() + "/" + instanceName); +} -QString InstanceManager::instancePath() const +QString InstanceManager::instancesPath() const { return QDir::fromNativeSeparators( QStandardPaths::writableLocation(QStandardPaths::DataLocation)); @@ -292,7 +323,7 @@ QStringList InstanceManager::instances() const "cache", "qtwebengine", }; - const auto dirs = QDir(instancePath()) + const auto dirs = QDir(instancesPath()) .entryList(QDir::Dirs | QDir::NoDotAndDotDot); QStringList list; diff --git a/src/instancemanager.h b/src/instancemanager.h index 7f87045d..13754f89 100644 --- a/src/instancemanager.h +++ b/src/instancemanager.h @@ -53,7 +53,8 @@ private: InstanceManager(); - QString instancePath() const; + QString instancesPath() const; + QString instancePath(const QString& instanceName) const; QStringList instances() const; -- cgit v1.3.1 From 5513b273c679b9edda951f418ef013a762ca587a Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Thu, 23 Jul 2020 04:53:20 -0400 Subject: added empty instance manager dialog --- src/CMakeLists.txt | 6 +- src/instancemanager.cpp | 30 +- src/instancemanager.h | 9 +- src/instancemanagerdialog.cpp | 36 ++ src/instancemanagerdialog.h | 20 + src/instancemanagerdialog.ui | 378 ++++++++++++++++++ src/organizer_en.ts | 885 +++++++++++++++++++++++------------------- 7 files changed, 940 insertions(+), 424 deletions(-) create mode 100644 src/instancemanagerdialog.cpp create mode 100644 src/instancemanagerdialog.h create mode 100644 src/instancemanagerdialog.ui (limited to 'src/instancemanager.h') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 73254574..1f048be8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -29,7 +29,6 @@ add_filter(NAME src/core GROUPS categories archivefiletree installationmanager - instancemanager loadmechanism nexusinterface nxmaccessmanager @@ -84,6 +83,11 @@ add_filter(NAME src/executables GROUPS editexecutablesdialog ) +add_filter(NAME src/instances GROUPS + instancemanager + instancemanagerdialog +) + add_filter(NAME src/loot GROUPS loot lootdialog diff --git a/src/instancemanager.cpp b/src/instancemanager.cpp index 46936444..98edba47 100644 --- a/src/instancemanager.cpp +++ b/src/instancemanager.cpp @@ -182,14 +182,14 @@ QString InstanceManager::manageInstances(const QStringList &instanceList) const } if (selection.exec() == QDialog::Rejected) { - return (chooseInstance(instances())); + return (chooseInstance(instanceNames())); } else { QString choice = selection.getChoiceData().toString(); deleteLocalInstance(choice); } - return(manageInstances(instances())); + return(manageInstances(instanceNames())); } QString InstanceManager::queryInstanceName(const QStringList &instanceList) const @@ -301,7 +301,7 @@ QString InstanceManager::chooseInstance(const QStringList &instanceList) const case Special::Portable: return QString(); case Special::Manage: { - return(manageInstances(instances())); + return(manageInstances(instanceNames())); } default: throw std::runtime_error("invalid selection"); } @@ -319,27 +319,37 @@ QString InstanceManager::instancesPath() const QStandardPaths::writableLocation(QStandardPaths::DataLocation)); } - -QStringList InstanceManager::instances() const +std::vector InstanceManager::instancePaths() const { const std::set ignore = { "cache", "qtwebengine", }; - const auto dirs = QDir(instancesPath()) - .entryList(QDir::Dirs | QDir::NoDotAndDotDot); + const QDir root(instancesPath()); + const auto dirs = root.entryList(QDir::Dirs | QDir::NoDotAndDotDot); - QStringList list; + std::vector list; for (auto&& d : dirs) { if (!ignore.contains(QFileInfo(d).fileName().toLower())) { - list.push_back(d); + list.push_back(root.filePath(d)); } } return list; } +QStringList InstanceManager::instanceNames() const +{ + QStringList list; + + for (auto&& d : instancePaths()) { + list.push_back(d.dirName()); + } + + return list; +} + bool InstanceManager::isPortablePath(const QString& dataPath) { @@ -402,7 +412,7 @@ QString InstanceManager::determineDataPath() if (!m_overrideInstance && (instanceId.isEmpty() || !QFileInfo::exists(dataPath))) { - instanceId = chooseInstance(instances()); + instanceId = chooseInstance(instanceNames()); setCurrentInstance(instanceId); if (!instanceId.isEmpty()) { dataPath = QDir::fromNativeSeparators( diff --git a/src/instancemanager.h b/src/instancemanager.h index 13754f89..8bbbbee9 100644 --- a/src/instancemanager.h +++ b/src/instancemanager.h @@ -29,8 +29,8 @@ namespace MOBase { class IPluginGame; } class Settings; class PluginContainer; -class InstanceManager { - +class InstanceManager +{ public: static InstanceManager &instance(); @@ -49,6 +49,9 @@ public: bool allowedToChangeInstance() const; static bool isPortablePath(const QString& dataPath); + QStringList instanceNames() const; + std::vector instancePaths() const; + private: InstanceManager(); @@ -56,8 +59,6 @@ private: QString instancesPath() const; QString instancePath(const QString& instanceName) const; - QStringList instances() const; - bool deleteLocalInstance(const QString &instanceId) const; QString manageInstances(const QStringList &instanceList) const; diff --git a/src/instancemanagerdialog.cpp b/src/instancemanagerdialog.cpp new file mode 100644 index 00000000..1b482099 --- /dev/null +++ b/src/instancemanagerdialog.cpp @@ -0,0 +1,36 @@ +#include "instancemanagerdialog.h" +#include "ui_instancemanagerdialog.h" +#include "instancemanager.h" + +class InstanceInfo +{ +public: + InstanceInfo(QDir dir) + : m_dir(std::move(dir)) + { + } + + QString name() const + { + return m_dir.dirName(); + } + +private: + QDir m_dir; +}; + + +InstanceManagerDialog::InstanceManagerDialog(QWidget *parent) + : QDialog(parent), ui(new Ui::InstanceManagerDialog) +{ + ui->setupUi(this); + + auto& m = InstanceManager::instance(); + + for (auto&& d : m.instancePaths()) { + InstanceInfo i(d); + ui->list->addItem(i.name()); + } +} + +InstanceManagerDialog::~InstanceManagerDialog() = default; diff --git a/src/instancemanagerdialog.h b/src/instancemanagerdialog.h new file mode 100644 index 00000000..ea1cfc48 --- /dev/null +++ b/src/instancemanagerdialog.h @@ -0,0 +1,20 @@ +#ifndef MODORGANIZER_INSTANCEMANAGERDIALOG_INCLUDED +#define MODORGANIZER_INSTANCEMANAGERDIALOG_INCLUDED + +#include + +namespace Ui { class InstanceManagerDialog; }; + +class InstanceManagerDialog : public QDialog +{ + Q_OBJECT + +public: + explicit InstanceManagerDialog(QWidget *parent = nullptr); + ~InstanceManagerDialog(); + +private: + std::unique_ptr ui; +}; + +#endif // MODORGANIZER_INSTANCEMANAGERDIALOG_INCLUDED diff --git a/src/instancemanagerdialog.ui b/src/instancemanagerdialog.ui new file mode 100644 index 00000000..b35d2e58 --- /dev/null +++ b/src/instancemanagerdialog.ui @@ -0,0 +1,378 @@ + + + InstanceManagerDialog + + + + 0 + 0 + 531 + 413 + + + + Instance manager + + + + + + + 0 + + + 0 + + + 0 + + + 5 + + + + + Create new instance + + + + :/MO/gui/add:/MO/gui/add + + + + + + + Create portable instance + + + + :/MO/gui/package:/MO/gui/package + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + <html><head/><body><p><a href="https://github.com/ModOrganizer2/modorganizer/wiki/Instance"><span style=" text-decoration: underline; color:#0000ff;">What is an instance?</span></a></p></body></html> + + + true + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + instance name + + + Qt::TextBrowserInteraction + + + + + + + Game: + + + + + + + Name: + + + + + + + Location: + + + + + + + Rename + + + + + + + location path + + + Qt::TextBrowserInteraction + + + + + + + Base folder: + + + + + + + base folder path + + + Qt::TextBrowserInteraction + + + + + + + Explore + + + + + + + Explore + + + + + + + game name + + + Qt::TextBrowserInteraction + + + + + + + + + + Qt::Vertical + + + + 20 + 10 + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Convert to portable + + + + + + + Convert to global + + + + + + + Delete instance + + + + :/MO/gui/remove:/MO/gui/remove + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Switch to this instance + + + + :/MO/gui/next:/MO/gui/next + + + + + + + Cancel + + + true + + + + + + + + + + + + + diff --git a/src/organizer_en.ts b/src/organizer_en.ts index 1ad4650f..c65c946c 100644 --- a/src/organizer_en.ts +++ b/src/organizer_en.ts @@ -110,22 +110,22 @@ - + ...and all other contributors! - + Other Supporters && Contributors - + Tannin (Original Creator) - + Close @@ -178,17 +178,17 @@ p, li { white-space: pre-wrap; } AdvancedConflictListModel - + Overwrites - + File - + Overwritten By @@ -1462,54 +1462,54 @@ Right now the only case I know of where this needs to be overwritten is for the FileTreeModel - + Name - + Mod - + Type - + Size - + Date modified - + Directory - - + + Virtual path - + Real path - + From - - + + Also in @@ -1868,6 +1868,85 @@ This is likely due to a corrupted or incompatible download or unrecognized archi + + InstanceManagerDialog + + + Instance manager + + + + + Create new instance + + + + + Create portable instance + + + + + <html><head/><body><p><a href="https://github.com/ModOrganizer2/modorganizer/wiki/Instance"><span style=" text-decoration: underline; color:#0000ff;">What is an instance?</span></a></p></body></html> + + + + + Game: + + + + + Name: + + + + + Location: + + + + + Rename + + + + + Base folder: + + + + + + Explore + + + + + Convert to portable + + + + + Convert to global + + + + + Delete instance + + + + + Switch to this instance + + + + + Cancel + + + ListDialog @@ -1999,12 +2078,12 @@ This is likely due to a corrupted or incompatible download or unrecognized archi MOApplication - + an error occurred: %1 - + an error occurred @@ -2199,7 +2278,7 @@ p, li { white-space: pre-wrap; } - + Create Backup @@ -2353,7 +2432,7 @@ p, li { white-space: pre-wrap; } - + Refresh @@ -2677,7 +2756,7 @@ p, li { white-space: pre-wrap; } - + Endorse Mod Organizer @@ -2755,829 +2834,833 @@ p, li { white-space: pre-wrap; } - + Toolbar and Menu - + Desktop - + Start Menu - + There is no supported sort mechanism for this game. You will probably have to use a third-party tool. - + Crash on exit - + MO crashed while exiting. Some settings may not be saved. Error: %1 - + There are notifications to read - + There are no notifications - - - + + + Endorse - + Won't Endorse - + Help on UI - + Documentation - + Chat on Discord - + Report Issue - + Tutorials - + About - + About Qt - + Name - + Please enter a name for the new profile - + failed to create profile: %1 - + Show tutorial? - + You are starting Mod Organizer for the first time. Do you want to show a tutorial of its basic features? If you choose no you can always start the tutorial from the "Help"-menu. - + Downloads in progress - + There are still downloads in progress, do you really want to quit? - + Plugin "%1" failed: %2 - + Plugin "%1" failed - + Browse Mod Page - + <Edit...> - + (no executables) - + This bsa is enabled in the ini file so it may be required! - + Activating Network Proxy - + Notice: Your current MO version (%1) is lower than the previously used one (%2). The GUI may not downgrade gracefully, so you may experience oddities. However, there should be no serious issues. - + Choose Mod - + Mod Archive - + Start Tutorial? - + You're about to start a tutorial. For technical reasons it's not possible to end the tutorial early. Continue? - + failed to change origin name: %1 - + failed to move "%1" from mod "%2" to "%3": %4 - + failed to rename mod: %1 - + Overwrite? - + This will replace the existing mod "%1". Continue? - + failed to remove mod "%1" - + failed to rename "%1" to "%2" - - - - - + + + + + Confirm - + Remove the following mods?<br><ul>%1</ul> - + failed to remove mod: %1 - - - + + + Failed - + Installation file no longer exists - + Mods installed with old versions of MO can't be reinstalled in this way. - + Failed to create backup. - + Endorsing multiple mods will take a while. Please wait... - + Unendorsing multiple mods will take a while. Please wait... - + Failed to display overwrite dialog: %1 - + Restore all hidden files in the following mods?<br><ul>%1</ul> - - - + + Are you sure? - + About to restore all hidden files in: - + Opening Nexus Links - + You are trying to open %1 links to Nexus Mods. Are you sure you want to do this? - + Nexus ID for this mod is unknown - - + + Opening Web Pages - - + + You are trying to open %1 Web Pages. Are you sure you want to do this? - + + No valid Web Page for this mod + + + + <table cellspacing="5"><tr><th>Type</th><th>All</th><th>Visible</th><tr><td>Enabled mods:&emsp;</td><td align=right>%1 / %2</td><td align=right>%3 / %4</td></tr><tr><td>Unmanaged/DLCs:&emsp;</td><td align=right>%5</td><td align=right>%6</td></tr><tr><td>Mod backups:&emsp;</td><td align=right>%7</td><td align=right>%8</td></tr><tr><td>Separators:&emsp;</td><td align=right>%9</td><td align=right>%10</td></tr></table> - + <table cellspacing="6"><tr><th>Type</th><th>Active </th><th>Total</th></tr><tr><td>All plugins:</td><td align=right>%1 </td><td align=right>%2</td></tr><tr><td>ESMs:</td><td align=right>%3 </td><td align=right>%4</td></tr><tr><td>ESPs:</td><td align=right>%7 </td><td align=right>%8</td></tr><tr><td>ESMs+ESPs:</td><td align=right>%9 </td><td align=right>%10</td></tr><tr><td>ESLs:</td><td align=right>%5 </td><td align=right>%6</td></tr></table> - - - + + + Create Mod... - + This will create an empty mod. Please enter a name: - - + + A mod with this name already exists - + Create Separator... - + This will create a new separator. Please enter a name: - + A separator with this name already exists - + This will move all files from overwrite into a new, regular mod. Please enter a name: - + Move successful. - + About to recursively delete: - + Continue? - + The versioning scheme decides which version is considered newer than another. This function will guess the versioning scheme under the assumption that the installed version is outdated. - + Sorry - + I don't know a versioning scheme where %1 is newer than %2. - + Really enable all visible mods? - + Really disable all visible mods? - + Export to csv - + CSV (Comma Separated Values) is a format that can be imported in programs like Excel to create a spreadsheet. You can also use online editors and converters instead. - + Select what mods you want export: - + All installed mods - + Only active (checked) mods from your current profile - + All currently visible mods in the mod list - + Choose what Columns to export: - + Mod_Priority - + Mod_Name - + Notes_column - + Mod_Status - + Primary_Category - + Nexus_ID - + Mod_Nexus_URL - + Mod_Version - + Install_Date - + Download_File_Name - + export failed: %1 - + Open Game folder - + Open MyGames folder - + Open INIs folder - + Open Instance folder - + Open Mods folder - + Open Profile folder - + Open Downloads folder - + Open MO2 Install folder - + Open MO2 Plugins folder - + Open MO2 Stylesheets folder - + Open MO2 Logs folder - + Install Mod... - + Create empty mod - + Create Separator - + Enable all visible - + Disable all visible - + Check for updates - + Export to csv... - - + + Send to - - + + Top - - + + Bottom - - + + Priority... - + Separator... - + All Mods - + Sync to Mods... - + Move content to Mod... - + Clear Overwrite... - - - + + + Open in Explorer - + Restore Backup - + Remove Backup... - - + + Ignore missing data - - + + Mark as converted/working - - + + Visit on Nexus - - + + Visit on %1 - - + + Change Categories - - + + Primary Category - + Rename Separator... - + Remove Separator... - - + + Select Color... - - + + Reset Color - + Change versioning scheme - + Force-check updates - + Un-ignore update - + Ignore update - - + + Enable selected - - + + Disable selected - + Rename Mod... - + Reinstall Mod - + Remove Mod... - + Restore hidden files - + Un-Endorse - + Won't endorse - + Endorsement state unknown - + Start tracking - + Stop tracking - + Tracked state unknown - + Information... - - + + Exception: - - + + Unknown exception - + %1 more - + Are you sure you want to remove the following %n save(s)?<br><ul>%1</ul><br>Removed saves will be sent to the Recycle Bin. @@ -3585,12 +3668,12 @@ You can also use online editors and converters instead. - + Enable Mods... - + Delete %n save(s) @@ -3598,242 +3681,227 @@ You can also use online editors and converters instead. - + Restart Mod Organizer - + Mod Organizer must restart to finish configuration changes - + Restart - + Continue - + Some things might be weird. - + Can't change download directory while downloads are in progress! - - + + Set Priority - + Set the priority of the selected plugins - + Update available - + Do you want to endorse Mod Organizer on %1 now? - + Abstain from Endorsing Mod Organizer - + Are you sure you want to abstain from endorsing Mod Organizer 2? You will have to visit the mod page on the %1 Nexus site to change your mind. - + Thank you for endorsing MO2! :) - + Please reconsider endorsing MO2 on Nexus! - + Thank you! - + Thank you for your endorsement! - + Mod ID %1 no longer seems to be available on Nexus. - + Request to Nexus failed: %1 - - + + failed to read %1: %2 - + Error - + failed to extract %1 (errorcode %2) - + Extract BSA - + This archive contains invalid hashes. Some files may be broken. - + Extract... - - This will restart MO, continue? - - - - + <Multiple> - + Remove '%1' from the toolbar - + Enable all - + Disable all - + Unlock load order - + Lock load order - + Open Origin in Explorer - + Open Origin Info... - - Sorting plugins - - - - - Are you sure you want to sort your plugins list? - - - - + Backup of load order created - + Choose backup to restore - + No Backups - + There are no backups to restore - - + + Restore failed - - + + Failed to restore the backup. Errorcode: %1 - + Backup of mod list created - + A file with the same name has already been downloaded. What would you like to do? - + Overwrite - + Rename new file - + Ignore file - + Set the priority of the selected mods @@ -4701,7 +4769,7 @@ p, li { white-space: pre-wrap; } NoConflictListModel - + File @@ -4709,211 +4777,211 @@ p, li { white-space: pre-wrap; } OrganizerCore - + File is write protected - + Invalid file format (probably a bug) - + Unknown error %1 - + Failed to write settings - + An error occurred trying to write back MO settings to %1: %2 - - + + Download started - + Download failed - - + + Installation cancelled - - + + Another installation is currently in progress. - - + + Installation successful - - + + Configure Mod - - + + This mod contains ini tweaks. Do you want to configure them now? - - + + mod not found: %1 - - + + Extraction cancelled - - + + The installation was cancelled while extracting files. If this was prior to a FOMOD setup, this warning may be ignored. However, if this was during installation, the mod will likely be missing files. - + file not found: %1 - + failed to generate preview for %1 - + Sorry - + Sorry, can't preview anything. This function currently does not support extracting from bsas. - + File '%1' not found. - + Failed to generate preview for %1 - + Failed to refresh list of esps: %1 - + Multiple esps/esls activated, please check that they don't conflict. - + You need to be logged in with Nexus - + Download? - + A download has been started but no installed page plugin recognizes it. If you download anyway no information (i.e. version) will be associated with the download. Continue? - - + + failed to update mod list: %1 - - + + login successful - + Login failed - + Login failed, try again? - + login failed: %1. Download will not be associated with an account - + login failed: %1 - + login failed: %1. You need to log-in with Nexus to update MO. - + MO1 "Script Extender" load mechanism has left hook.dll in your game folder - - + + Description missing - + <a href="%1">hook.dll</a> has been found in your game folder (right click to copy the full path). This is most likely a leftover of setting the ModOrganizer 1 load mechanism to "Script Extender", in which case you must remove this file either by changing the load mechanism in ModOrganizer 1 or manually removing the file, otherwise the game is likely to crash and burn. - + failed to save load order: %1 - + Error - + The designated write target "%1" is not enabled. @@ -4921,12 +4989,12 @@ Continue? OverwriteConflictListModel - + File - + Overwritten Mods @@ -5026,12 +5094,12 @@ Continue? OverwrittenConflictListModel - + File - + Providing Mod @@ -5653,9 +5721,10 @@ p, li { white-space: pre-wrap; } + + - - + @@ -5972,137 +6041,134 @@ Destination: - - Deleting folder - - - - - I'm about to delete the following folder: "%1". Proceed? + + Deleting instance folder - - Choose Instance to Delete + + This will delete the instance folder. + The folder will be deleted: "%1" - - Be Careful! Deleting an Instance will remove all your files for that Instance (mods, downloads, profiles, configuration, ...). Custom paths outside of the instance folder for downloads, mods, etc. will be left untoched. + + Move the folder to the recycle bin - - Are you sure? + + Delete the folder permanently - - Are you really sure you want to delete the Instance "%1" with all its files? + + Could not delete instance folder "%1". + +%2 - - Failed to delete Instance + + Select an instance to delete - - Could not delete Instance "%1". -If the folder was still in use, restart MO and try again. + + Deleting an instance will delete all the mods, downloads, profiles (including profile-specific saves) and anything in the overwrite folder.<br><br>Custom paths outside of the instance folder will not be deleted. - + Enter a Name for the new Instance - + Enter a new name or select one from the suggested list: (This is just a name for the Instance and can be whatever you wish, the actual game selection will happen on the next screen regardless of chosen name) - - + + Canceled - + Invalid instance name - + The instance name "%1" is invalid. Use the name "%2" instead? - + The instance "%1" already exists. - + Please choose a different instance name, like: "%1 1" . - + Choose Instance - + Each Instance is a full set of MO data files (mods, downloads, profiles, configuration, ...). You can use multiple instances for different games. Instances are stored in Appdata and can be accessed by all MO installations. If your MO folder is writable, you can also store a single instance locally (called a Portable install, and all the MO data files will be inside the installation folder). - + New - + Create a new instance. - + Portable - + Use MO folder for data. - + Manage Instances - + Delete an Instance. - + failed to create %1 - + Data directory created - + New data directory created at %1. If you don't want to store a lot of data there, reconfigure the storage directories via settings. @@ -6194,112 +6260,112 @@ If the folder was still in use, restart MO and try again. - - + + Failed to create "%1". Your user account probably lacks permission. - + Plugin to handle %1 no longer installed. An antivirus might have deleted files. Plugin to handle %1 no longer installed - - - + + + The configured path to the game directory (%1) appears to be a symbolic (or other) link. This setup is incompatible with MO2's VFS and will not run correctly. - + Could not use configuration settings for game "%1", path "%2". - - - + + + Please select the installation of %1 to manage - - - + + + Please select the game to manage - + Canceled finding %1 in "%2". - + Canceled finding game in "%1". - + %1 not identified in "%2". The directory is required to contain the game binary. - + No game identified in "%1". The directory is required to contain the game binary.<br><br><b>These are the games supported by Mod Organizer:</b><ul>%2</ul> - + Please select the game edition you have (MO can't start the game correctly if this is set incorrectly!) - + failed to start shortcut: %1 - + failed to start application: %1 - + Mod Organizer - + An instance of Mod Organizer is already running - + Failed to set up instance - + <Unmanaged> - + Please use "Help" from the toolbar to get usage instructions to all elements - - + + <Manage...> - + failed to parse profile %1: %2 @@ -6427,7 +6493,7 @@ If the folder was still in use, restart MO and try again. - + One of the configured MO2 directories (profiles, mods, or overwrite) is on a path containing a symbolic (or other) link. This is likely to be incompatible with MO2's virtual filesystem. @@ -6590,6 +6656,7 @@ This program is known to cause issues with Mod Organizer, such as freezing or bl + @@ -7903,22 +7970,22 @@ programs you are intentionally running. SingleInstance - + SHM error: %1 - + failed to connect to running instance: %1 - + failed to communicate with running instance: %1 - + failed to receive data from secondary instance: %1 -- cgit v1.3.1 From 60b59ddf097fffa846a4d28e0d9256630da5149c Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Thu, 23 Jul 2020 07:32:06 -0400 Subject: started create instance dialog allow for multiple instances of Settings fill in information in instance manager --- src/CMakeLists.txt | 1 + src/createinstancedialog.cpp | 176 ++++++++++++ src/createinstancedialog.h | 30 ++ src/createinstancedialog.ui | 655 ++++++++++++++++++++++++++++++++++++++++++ src/instancemanager.h | 2 +- src/instancemanagerdialog.cpp | 102 ++++++- src/instancemanagerdialog.h | 16 +- src/instancemanagerdialog.ui | 116 ++++---- src/main.cpp | 9 + src/settings.cpp | 19 +- src/settings.h | 1 + 11 files changed, 1064 insertions(+), 63 deletions(-) create mode 100644 src/createinstancedialog.cpp create mode 100644 src/createinstancedialog.h create mode 100644 src/createinstancedialog.ui (limited to 'src/instancemanager.h') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1f048be8..af350584 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -84,6 +84,7 @@ add_filter(NAME src/executables GROUPS ) add_filter(NAME src/instances GROUPS + createinstancedialog instancemanager instancemanagerdialog ) diff --git a/src/createinstancedialog.cpp b/src/createinstancedialog.cpp new file mode 100644 index 00000000..d43fba1c --- /dev/null +++ b/src/createinstancedialog.cpp @@ -0,0 +1,176 @@ +#include "createinstancedialog.h" +#include "ui_createinstancedialog.h" +#include "instancemanager.h" + +namespace cid +{ + +class Page +{ +public: + Page(CreateInstanceDialog& dlg, std::size_t i) + : ui(dlg.getUI()), m_dlg(dlg), m_index(i) + { + } + + virtual bool ready() const + { + return true; + } + + void next() + { + m_dlg.next(); + } + +protected: + Ui::CreateInstanceDialog* ui; + +private: + CreateInstanceDialog& m_dlg; + std::size_t m_index; +}; + +class TypePage : public Page +{ +public: + TypePage(CreateInstanceDialog& dlg, std::size_t i) + : Page(dlg, i) + { + ui->createGlobal->setDescription( + ui->createGlobal->description() + .arg(InstanceManager::instance().instancesPath())); + + ui->createPortable->setDescription( + ui->createPortable->description() + .arg(qApp->applicationDirPath())); + + QObject::connect( + ui->createGlobal, &QAbstractButton::clicked, [&]{ global(); }); + + QObject::connect( + ui->createPortable, &QAbstractButton::clicked, [&]{ portable(); }); + } + + bool ready() const override + { + return m_global.has_value(); + } + + void global() + { + m_global = true; + + ui->createGlobal->setChecked(true); + ui->createPortable->setChecked(false); + + next(); + } + + void portable() + { + m_global = false; + + ui->createGlobal->setChecked(false); + ui->createPortable->setChecked(true); + + next(); + } + +private: + std::optional m_global; +}; + + +class GamePage : public Page +{ +public: + GamePage(CreateInstanceDialog& dlg, std::size_t i) + : Page(dlg, i) + { + } +}; + + +class NamePage : public Page +{ +public: + NamePage(CreateInstanceDialog& dlg, std::size_t i) + : Page(dlg, i) + { + } +}; + + +class PathsPage : public Page +{ +public: + PathsPage(CreateInstanceDialog& dlg, std::size_t i) + : Page(dlg, i) + { + } +}; + +} // namespace + + +CreateInstanceDialog::CreateInstanceDialog(QWidget *parent) + : QDialog(parent), ui(new Ui::CreateInstanceDialog) +{ + using namespace cid; + + ui->setupUi(this); + + m_pages.push_back(std::make_unique(*this, 0)); + m_pages.push_back(std::make_unique(*this, 1)); + m_pages.push_back(std::make_unique(*this, 2)); + m_pages.push_back(std::make_unique(*this, 3)); + + ui->pages->setCurrentIndex(0); + + updateNavigationButtons(); + + connect(ui->next, &QPushButton::clicked, [&]{ next(); }); + connect(ui->back, &QPushButton::clicked, [&]{ back(); }); + + // + //SelectionDialog games(tr("Select a game to manage.")); + // + //for (auto* game : m_pc.plugins()) { + // if (game->isInstalled()) { + // games.addChoice(game->gameName(), game->gameDirectory().path(), {}); + // } else { + // games.addChoice(game->gameName(), "", {}); + // } + //} + // + //games.exec(); +} + +CreateInstanceDialog::~CreateInstanceDialog() = default; + +Ui::CreateInstanceDialog* CreateInstanceDialog::getUI() +{ + return ui.get(); +} + +void CreateInstanceDialog::next() +{ + ui->pages->setCurrentIndex(ui->pages->currentIndex() + 1); + updateNavigationButtons(); +} + +void CreateInstanceDialog::back() +{ + ui->pages->setCurrentIndex(ui->pages->currentIndex() - 1); + updateNavigationButtons(); +} + +void CreateInstanceDialog::updateNavigationButtons() +{ + const auto i = ui->pages->currentIndex(); + const auto last = (i == (ui->pages->count() - 1)); + + ui->next->setEnabled(m_pages[i]->ready() && !last); + ui->back->setEnabled(i > 0); +} diff --git a/src/createinstancedialog.h b/src/createinstancedialog.h new file mode 100644 index 00000000..03e9de01 --- /dev/null +++ b/src/createinstancedialog.h @@ -0,0 +1,30 @@ +#ifndef MODORGANIZER_CREATEINSTANCEDIALOG_INCLUDED +#define MODORGANIZER_CREATEINSTANCEDIALOG_INCLUDED + +#include + +namespace Ui { class CreateInstanceDialog; }; +namespace cid { class Page; } + +class CreateInstanceDialog : public QDialog +{ + Q_OBJECT + +public: + explicit CreateInstanceDialog(QWidget *parent = nullptr); + + ~CreateInstanceDialog(); + + Ui::CreateInstanceDialog* getUI(); + + void next(); + void back(); + +private: + std::unique_ptr ui; + std::vector> m_pages; + + void updateNavigationButtons(); +}; + +#endif // MODORGANIZER_CREATEINSTANCEDIALOG_INCLUDED diff --git a/src/createinstancedialog.ui b/src/createinstancedialog.ui new file mode 100644 index 00000000..bcfddb20 --- /dev/null +++ b/src/createinstancedialog.ui @@ -0,0 +1,655 @@ + + + CreateInstanceDialog + + + + 0 + 0 + 493 + 423 + + + + Creating an instance + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + <h2>Creating a new instance</h2> + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + + + + 0 + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + <h3>Select the type of instance to create.</h3> + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Create a global instance + + + true + + + Global instances are stored in %1, but some paths can be changed to be on a different drive if necessary. A single installation of Mod Organizer can manage multiple global instances. + + + + + + + Create a portable instance + + + true + + + A portable instance stores everything in Mod Organizer's installation folder, currently %1. There can only be one portable instance per installation of Mod Organizer. + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + <h3>Select the game to manage.</h3> + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Show all supported games + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + <h3>Pick a name for this instance.</h3> + + + + + + + + + + + 0 + + + + + Instance name + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + <h3>Select a folder where the data should be stored.</h3> + + + + + + + This includes downloads, mods, profiles and overwrite. If there is enough space on this drive, you should use the default folder. + + + true + + + + + + + + + + + + + 1 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + ... + + + + + + + Location + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Overwrite + + + + + + + Mods + + + + + + + Base directory + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Profiles + + + + + + + + + + Downloads + + + + + + + + + + + + + + + + + + + Use <code>%BASE_DIR%</code> to refer to the Base Directory. + + + + + + + ... + + + + + + + ... + + + + + + + ... + + + + + + + ... + + + + + + + ... + + + + + + + + + + + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + 292 + 20 + + + + + + + + Show advanced options + + + + + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + < Back + + + + + + + Next > + + + true + + + + + + + Cancel + + + + + + + + + + + diff --git a/src/instancemanager.h b/src/instancemanager.h index 8bbbbee9..d53f4391 100644 --- a/src/instancemanager.h +++ b/src/instancemanager.h @@ -49,6 +49,7 @@ public: bool allowedToChangeInstance() const; static bool isPortablePath(const QString& dataPath); + QString instancesPath() const; QStringList instanceNames() const; std::vector instancePaths() const; @@ -56,7 +57,6 @@ private: InstanceManager(); - QString instancesPath() const; QString instancePath(const QString& instanceName) const; bool deleteLocalInstance(const QString &instanceId) const; diff --git a/src/instancemanagerdialog.cpp b/src/instancemanagerdialog.cpp index 1b482099..c7042aa8 100644 --- a/src/instancemanagerdialog.cpp +++ b/src/instancemanagerdialog.cpp @@ -1,12 +1,19 @@ #include "instancemanagerdialog.h" #include "ui_instancemanagerdialog.h" #include "instancemanager.h" +#include "createinstancedialog.h" +#include "settings.h" +#include "selectiondialog.h" +#include "plugincontainer.h" +#include "shared/appconfig.h" +#include class InstanceInfo { public: - InstanceInfo(QDir dir) - : m_dir(std::move(dir)) + InstanceInfo(QDir dir) : + m_dir(std::move(dir)), + m_settings(dir.filePath(QString::fromStdWString(AppConfig::iniFileName()))) { } @@ -15,22 +22,105 @@ public: return m_dir.dirName(); } + QString gameName() const + { + if (auto n=m_settings.game().name()) { + if (auto e=m_settings.game().edition()) { + if (!e->isEmpty()) { + return *n + " (" + *e + ")"; + } + } + + return *n; + } else { + return {}; + } + } + + QString gamePath() const + { + if (auto n=m_settings.game().directory()) { + return *n; + } else { + return {}; + } + } + + QString location() const + { + return m_dir.path(); + } + + QString baseDirectory() const + { + return m_settings.paths().base(); + } + private: QDir m_dir; + Settings m_settings; }; -InstanceManagerDialog::InstanceManagerDialog(QWidget *parent) - : QDialog(parent), ui(new Ui::InstanceManagerDialog) +InstanceManagerDialog::InstanceManagerDialog( + const PluginContainer& pc, QWidget *parent) + : QDialog(parent), ui(new Ui::InstanceManagerDialog), m_pc(pc) { ui->setupUi(this); + ui->splitter->setSizes({200, 1}); + ui->splitter->setStretchFactor(0, 0); + ui->splitter->setStretchFactor(1, 1); auto& m = InstanceManager::instance(); for (auto&& d : m.instancePaths()) { - InstanceInfo i(d); - ui->list->addItem(i.name()); + auto ii = std::make_unique(d); + + ui->list->addItem(ii->name()); + m_instances.push_back(std::move(ii)); + } + + if (!m_instances.empty()) { + select(0); } + + connect(ui->createNew, &QPushButton::clicked, [&]{ createNew(); }); + connect(ui->list, &QListWidget::itemSelectionChanged, [&]{ onSelection(); }); } InstanceManagerDialog::~InstanceManagerDialog() = default; + +void InstanceManagerDialog::select(std::size_t i) +{ + if (i >= m_instances.size()) { + return; + } + + const auto& ii = m_instances[i]; + fill(*ii); +} + +void InstanceManagerDialog::onSelection() +{ + const auto sel = ui->list->selectionModel()->selectedIndexes(); + if (sel.size() != 1) { + return; + } + + select(static_cast(sel[0].row())); +} + +void InstanceManagerDialog::createNew() +{ + CreateInstanceDialog dlg(this); + dlg.exec(); +} + +void InstanceManagerDialog::fill(const InstanceInfo& ii) +{ + ui->name->setText(ii.name()); + ui->location->setText(ii.location()); + ui->baseDirectory->setText(ii.baseDirectory()); + ui->gameName->setText(ii.gameName()); + ui->gameDir->setText(ii.gamePath()); +} diff --git a/src/instancemanagerdialog.h b/src/instancemanagerdialog.h index ea1cfc48..c3e947dd 100644 --- a/src/instancemanagerdialog.h +++ b/src/instancemanagerdialog.h @@ -5,16 +5,30 @@ namespace Ui { class InstanceManagerDialog; }; +class InstanceInfo; +class PluginContainer; + class InstanceManagerDialog : public QDialog { Q_OBJECT public: - explicit InstanceManagerDialog(QWidget *parent = nullptr); + explicit InstanceManagerDialog( + const PluginContainer& pc, QWidget *parent = nullptr); + ~InstanceManagerDialog(); + void select(std::size_t i); + private: std::unique_ptr ui; + const PluginContainer& m_pc; + std::vector> m_instances; + + void onSelection(); + void createNew(); + + void fill(const InstanceInfo& ii); }; #endif // MODORGANIZER_INSTANCEMANAGERDIALOG_INCLUDED diff --git a/src/instancemanagerdialog.ui b/src/instancemanagerdialog.ui index b35d2e58..146b5fb5 100644 --- a/src/instancemanagerdialog.ui +++ b/src/instancemanagerdialog.ui @@ -6,7 +6,7 @@ 0 0 - 531 + 689 413 @@ -40,17 +40,6 @@ - - - - Create portable instance - - - - :/MO/gui/package:/MO/gui/package - - - @@ -65,9 +54,9 @@ - + - <html><head/><body><p><a href="https://github.com/ModOrganizer2/modorganizer/wiki/Instance"><span style=" text-decoration: underline; color:#0000ff;">What is an instance?</span></a></p></body></html> + <html><head/><body><p><a href="https://github.com/ModOrganizer2/modorganizer/wiki/Instance">What is an instance?</a></p></body></html> true @@ -134,7 +123,7 @@ - + 0 @@ -148,33 +137,51 @@ 0 - + + + true + + + + + - instance name + Name - - Qt::TextBrowserInteraction + + + + + + Explore - + - Game: + Game - - - - Name: + + + + true + + + + + + + true - Location: + Location @@ -185,30 +192,24 @@ - - + + - location path - - - Qt::TextBrowserInteraction + Game location - Base folder: + Base folder - - - base folder path - - - Qt::TextBrowserInteraction + + + true @@ -219,20 +220,17 @@ - - - - Explore + + + + true - - + + - game name - - - Qt::TextBrowserInteraction + Explore @@ -292,6 +290,19 @@ + + + + Qt::Horizontal + + + + 40 + 20 + + + + @@ -371,6 +382,13 @@ + + + LinkLabel + QLabel +
linklabel.h
+
+
diff --git a/src/main.cpp b/src/main.cpp index 08d70dd9..71fb0bbd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -25,6 +25,7 @@ along with Mod Organizer. If not, see . #include "tutorialmanager.h" #include "nxmaccessmanager.h" #include "instancemanager.h" +#include "instancemanagerdialog.h" #include "organizercore.h" #include "env.h" #include "envmodule.h" @@ -298,6 +299,8 @@ int runApplication( try { Settings settings(dataPath + "/" + QString::fromStdWString(AppConfig::iniFileName())); + settings.setGlobalInstance(); + log::getDefault().setLevel(settings.diagnostics().logLevel()); log::debug("using ini at '{}'", settings.filename()); @@ -429,6 +432,12 @@ int runApplication( tt.stop(); + QTimer::singleShot(std::chrono::milliseconds(1), [&] + { + InstanceManagerDialog dlg(*pluginContainer, &mainWindow); + dlg.exec(); + }); + res = application.exec(); mainWindow.close(); diff --git a/src/settings.cpp b/src/settings.cpp index d37f0d99..43e7a4fa 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -68,17 +68,24 @@ Settings::Settings(const QString& path) : m_Network(m_Settings), m_Nexus(*this, m_Settings), m_Steam(*this, m_Settings), m_Interface(m_Settings), m_Diagnostics(m_Settings) { - if (s_Instance != nullptr) { - throw std::runtime_error("second instance of \"Settings\" created"); - } else { - s_Instance = this; - } } Settings::~Settings() { MOBase::QuestionBoxMemory::setCallbacks({}, {}, {}); - s_Instance = nullptr; + + if (s_Instance == this) { + s_Instance = nullptr; + } +} + +void Settings::setGlobalInstance() +{ + if (s_Instance != nullptr) { + throw std::runtime_error("second instance of \"Settings\" created"); + } else { + s_Instance = this; + } } Settings &Settings::instance() diff --git a/src/settings.h b/src/settings.h index a1b30462..84102c72 100644 --- a/src/settings.h +++ b/src/settings.h @@ -681,6 +681,7 @@ public: ~Settings(); static Settings &instance(); + void setGlobalInstance(); // name of the ini file // -- cgit v1.3.1 From b10bcf4cb72bf3db06735a6b893c93319cead965 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Thu, 23 Jul 2020 10:27:58 -0400 Subject: instance name page --- src/createinstancedialog.cpp | 215 ++++++++++++++++++++++++++++++++++++++----- src/createinstancedialog.h | 16 +++- src/createinstancedialog.ui | 23 ++++- src/instancemanager.cpp | 21 +++++ src/instancemanager.h | 5 +- 5 files changed, 252 insertions(+), 28 deletions(-) (limited to 'src/instancemanager.h') diff --git a/src/createinstancedialog.cpp b/src/createinstancedialog.cpp index 79eaa79a..908c20d6 100644 --- a/src/createinstancedialog.cpp +++ b/src/createinstancedialog.cpp @@ -21,11 +21,40 @@ public: return true; } + virtual bool skip() const + { + // no-op + return false; + } + + virtual void activated() + { + // no-op + } + + void updateNavigation() + { + m_dlg.updateNavigation(); + } + void next() { m_dlg.next(); } + + virtual CreateInstanceDialog::Types selectedType() const + { + // no-op + return CreateInstanceDialog::NoType; + } + + virtual MOBase::IPluginGame* selectedGame() const + { + // no-op + return nullptr; + } + protected: Ui::CreateInstanceDialog* ui; CreateInstanceDialog& m_dlg; @@ -40,7 +69,7 @@ class TypePage : public Page { public: TypePage(CreateInstanceDialog& dlg, std::size_t i) - : Page(dlg, i) + : Page(dlg, i), m_type(CreateInstanceDialog::NoType) { ui->createGlobal->setDescription( ui->createGlobal->description() @@ -59,12 +88,17 @@ public: bool ready() const override { - return m_global.has_value(); + return (m_type != CreateInstanceDialog::NoType); + } + + CreateInstanceDialog::Types selectedType() const + { + return m_type; } void global() { - m_global = true; + m_type = CreateInstanceDialog::Global; ui->createGlobal->setChecked(true); ui->createPortable->setChecked(false); @@ -74,7 +108,7 @@ public: void portable() { - m_global = false; + m_type = CreateInstanceDialog::Portable; ui->createGlobal->setChecked(false); ui->createPortable->setChecked(true); @@ -83,7 +117,7 @@ public: } private: - std::optional m_global; + CreateInstanceDialog::Types m_type; }; @@ -99,26 +133,40 @@ public: QObject::connect(ui->showAllGames, &QCheckBox::clicked, [&]{ fillList(); }); } + bool ready() const override + { + return (m_selection != nullptr); + } + + MOBase::IPluginGame* selectedGame() const override + { + if (!m_selection) { + return nullptr; + } + + return m_selection->game; + } + void select(MOBase::IPluginGame* game) { Game* checked = findGame(game); - if (!checked) { - return; - } - if (!checked->installed) { - const auto path = QFileDialog::getExistingDirectory( - &m_dlg, QObject::tr("Find game installation")); + if (checked) { + if (!checked->installed) { + const auto path = QFileDialog::getExistingDirectory( + &m_dlg, QObject::tr("Find game installation")); - if (path.isEmpty()) { - checked = nullptr; - } else { - checked = checkInstallation(path, checked); + if (path.isEmpty()) { + checked = nullptr; + } else { + checked = checkInstallation(path, checked); + } } } m_selection = checked; selectButton(checked); + updateNavigation(); } void selectCustom() @@ -430,8 +478,90 @@ class NamePage : public Page { public: NamePage(CreateInstanceDialog& dlg, std::size_t i) - : Page(dlg, i) + : Page(dlg, i), m_modified(false), m_okay(false) + { + m_originalLabel = ui->instanceNameLabel->text(); + + QObject::connect( + ui->instanceName, &QLineEdit::textEdited, [&]{ onChanged(); }); + } + + bool ready() const override + { + return m_okay; + } + + bool skip() const override { + return (m_dlg.selectedType() == CreateInstanceDialog::Portable); + } + + void activated() override + { + auto* g = m_dlg.selectedGame(); + if (!g) { + // shouldn't happen, next should be disabled + return; + } + + ui->instanceNameLabel->setText(m_originalLabel.arg(g->gameName())); + + if (!m_modified || ui->instanceName->text().isEmpty()) { + const auto n = InstanceManager::instance().makeUniqueName(g->gameName()); + ui->instanceName->setText(n); + m_modified = false; + } + + updateWarnings(); + } + +private: + QString m_originalLabel; + bool m_modified; + bool m_okay; + + void onChanged() + { + m_modified = true; + updateWarnings(); + } + + void updateWarnings() + { + bool exists = false; + bool invalid = false; + bool empty = false; + + auto& m = InstanceManager::instance(); + + const auto name = ui->instanceName->text().trimmed(); + + if (name.isEmpty()) { + empty = true; + } else { + const auto sanitized = m.sanitizeInstanceName(name); + if (name != sanitized) { + invalid = true; + } else { + exists = m.instanceExists(name); + } + } + + if (exists) { + m_okay = false; + ui->instanceNameExists->setVisible(true); + ui->instanceNameInvalid->setVisible(false); + } else if (invalid) { + m_okay = false; + ui->instanceNameExists->setVisible(false); + ui->instanceNameInvalid->setVisible(true); + } else { + m_okay = !empty; + ui->instanceNameExists->setVisible(false); + ui->instanceNameInvalid->setVisible(false); + } + + updateNavigation(); } }; @@ -463,7 +593,7 @@ CreateInstanceDialog::CreateInstanceDialog( ui->pages->setCurrentIndex(0); - updateNavigationButtons(); + updateNavigation(); connect(ui->next, &QPushButton::clicked, [&]{ next(); }); connect(ui->back, &QPushButton::clicked, [&]{ back(); }); @@ -483,17 +613,35 @@ const PluginContainer& CreateInstanceDialog::pluginContainer() void CreateInstanceDialog::next() { - ui->pages->setCurrentIndex(ui->pages->currentIndex() + 1); - updateNavigationButtons(); + selectPage(ui->pages->currentIndex() + 1); } void CreateInstanceDialog::back() { - ui->pages->setCurrentIndex(ui->pages->currentIndex() - 1); - updateNavigationButtons(); + selectPage(ui->pages->currentIndex() - 1); } -void CreateInstanceDialog::updateNavigationButtons() +void CreateInstanceDialog::selectPage(std::size_t i) +{ + while (i < m_pages.size()) { + if (!m_pages[i]->skip()) { + break; + } + + ++i; + } + + if (i >= m_pages.size()) { + return; + } + + ui->pages->setCurrentIndex(static_cast(i)); + m_pages[i]->activated(); + + updateNavigation(); +} + +void CreateInstanceDialog::updateNavigation() { const auto i = ui->pages->currentIndex(); const auto last = (i == (ui->pages->count() - 1)); @@ -501,3 +649,26 @@ void CreateInstanceDialog::updateNavigationButtons() ui->next->setEnabled(m_pages[i]->ready() && !last); ui->back->setEnabled(i > 0); } + +CreateInstanceDialog::Types CreateInstanceDialog::selectedType() const +{ + for (auto&& p : m_pages) { + const auto t = p->selectedType(); + if (t != NoType) { + return t; + } + } + + return NoType; +} + +MOBase::IPluginGame* CreateInstanceDialog::selectedGame() const +{ + for (auto&& p : m_pages) { + if (auto* g=p->selectedGame()) { + return g; + } + } + + return nullptr; +} diff --git a/src/createinstancedialog.h b/src/createinstancedialog.h index df056f85..3fd19d55 100644 --- a/src/createinstancedialog.h +++ b/src/createinstancedialog.h @@ -3,6 +3,7 @@ #include +namespace MOBase { class IPluginGame; } namespace Ui { class CreateInstanceDialog; }; namespace cid { class Page; } @@ -13,6 +14,13 @@ class CreateInstanceDialog : public QDialog Q_OBJECT public: + enum Types + { + NoType = 0, + Global, + Portable + }; + explicit CreateInstanceDialog( const PluginContainer& pc, QWidget *parent = nullptr); @@ -23,13 +31,17 @@ public: void next(); void back(); + void selectPage(std::size_t i); + + void updateNavigation(); + + Types selectedType() const; + MOBase::IPluginGame* selectedGame() const; private: std::unique_ptr ui; const PluginContainer& m_pc; std::vector> m_pages; - - void updateNavigationButtons(); }; #endif // MODORGANIZER_CREATEINSTANCEDIALOG_INCLUDED diff --git a/src/createinstancedialog.ui b/src/createinstancedialog.ui index 8cad959d..6f215539 100644 --- a/src/createinstancedialog.ui +++ b/src/createinstancedialog.ui @@ -305,9 +305,12 @@ 0 - + - <h3>Pick a name for this instance.</h3> + <h3>Customize the name for this <span style="white-space: nowrap;">%1</span> instance.</h3> + + + true @@ -318,7 +321,7 @@ - 0 + 9 @@ -327,6 +330,20 @@ + + + + There is already an instance with this name. + + + + + + + The name contains invalid characters. It must be a valid folder name. + + + diff --git a/src/instancemanager.cpp b/src/instancemanager.cpp index 98edba47..b135cac1 100644 --- a/src/instancemanager.cpp +++ b/src/instancemanager.cpp @@ -650,6 +650,27 @@ MOBase::IPluginGame* InstanceManager::determineCurrentGame( return nullptr; } +QString InstanceManager::makeUniqueName(const QString& instanceName) const +{ + const QString sanitized = sanitizeInstanceName(instanceName); + + QString name = sanitized; + for (int i=2; i<100; ++i) { + if (!instanceExists(name)) { + return name; + } + + name = QString("%1 (%2)").arg(sanitized).arg(i); + } + + return {}; +} + +bool InstanceManager::instanceExists(const QString& instanceName) const +{ + const QDir root = instancesPath(); + return root.exists(instanceName); +} QString InstanceManager::sanitizeInstanceName(const QString &name) const { diff --git a/src/instancemanager.h b/src/instancemanager.h index d53f4391..0cd5ef4c 100644 --- a/src/instancemanager.h +++ b/src/instancemanager.h @@ -53,6 +53,10 @@ public: QStringList instanceNames() const; std::vector instancePaths() const; + QString sanitizeInstanceName(const QString &name) const; + QString makeUniqueName(const QString& instanceName) const; + bool instanceExists(const QString& instanceName) const; + private: InstanceManager(); @@ -63,7 +67,6 @@ private: QString manageInstances(const QStringList &instanceList) const; - QString sanitizeInstanceName(const QString &name) const; void setCurrentInstance(const QString &name); QString queryInstanceName(const QStringList &instanceList) const; -- cgit v1.3.1 From 4ac8ab98563e223075c1c93852d4112ab6e4579c Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Fri, 24 Jul 2020 15:29:28 -0400 Subject: don't skip paths for portable instance, what am I doing implemented actual creation removed mentions of %BASE_DIR% everywhere, use constant and functions from PathSettings --- modorganizer.natvis | 97 +++++++++++ src/createinstancedialog.cpp | 379 ++++++++++++++++++++++++++++++++++++------- src/createinstancedialog.h | 20 +++ src/createinstancedialog.ui | 16 +- src/instancemanager.h | 3 +- src/settings.cpp | 18 +- src/settings.h | 12 ++ src/settingsdialog.cpp | 2 +- src/settingsdialogpaths.cpp | 12 +- 9 files changed, 484 insertions(+), 75 deletions(-) (limited to 'src/instancemanager.h') diff --git a/modorganizer.natvis b/modorganizer.natvis index fe4a7ce2..6e21e099 100644 --- a/modorganizer.natvis +++ b/modorganizer.natvis @@ -8,5 +8,102 @@ file={m_wsFile} loaded={m_loaded} expanded={m_expanded} }} + + <null> + {fileEntry} + fileEntry + + *((Qt5Core.dll!QSharedData *) this) + fileEntry + + + + + + {*((Qt5Core.dll!QFileInfoPrivate *) d_ptr.d)} + *((Qt5Core.dll!QFileInfoPrivate *) d_ptr.d) + + *((Qt5Core.dll!QFileInfoPrivate *) d_ptr.d) + + + + + + <null> + {m_filePath} + m_filePath + + + + + + <null> + {dirEntry} + dirEntry + + *((Qt5Core.dll!QSharedData *) this) + dirEntry + nameFilters + absoluteDirEntry + + + + + + + {*((Qt5Core.dll!QDirPrivate *) d_ptr.d)} + *((Qt5Core.dll!QDirPrivate *) d_ptr.d) + + *((Qt5Core.dll!QDirPrivate *) d_ptr.d) + + + + + + <null> + {fileName} + fileName + + *((Qt5Core.dll!QFileDevice *) this) + fileName + + + + + + {*((Qt5Core.dll!QFilePrivate *) d_ptr.d)} + *((Qt5Core.dll!QFilePrivate *) d_ptr.d) + + *((Qt5Core.dll!QFilePrivate *) d_ptr.d) + + diff --git a/src/createinstancedialog.cpp b/src/createinstancedialog.cpp index 8c6605ed..5c85aa68 100644 --- a/src/createinstancedialog.cpp +++ b/src/createinstancedialog.cpp @@ -2,13 +2,19 @@ #include "ui_createinstancedialog.h" #include "instancemanager.h" #include "plugincontainer.h" +#include "settings.h" #include "shared/appconfig.h" #include #include +#include + +using namespace MOBase; namespace cid { +using MOBase::TaskDialog; + class PathChecker { public: @@ -153,6 +159,12 @@ private: } }; +QString makeDefaultPath(const std::wstring& dir) +{ + return QDir::toNativeSeparators( + PathSettings::makeDefaultPath(QString::fromStdWString(dir))); +} + class Page { @@ -195,7 +207,7 @@ public: return CreateInstanceDialog::NoType; } - virtual MOBase::IPluginGame* selectedGame() const + virtual IPluginGame* selectedGame() const { // no-op return nullptr; @@ -315,7 +327,7 @@ public: return (m_selection != nullptr); } - MOBase::IPluginGame* selectedGame() const override + IPluginGame* selectedGame() const override { if (!m_selection) { return nullptr; @@ -330,10 +342,10 @@ public: return {}; } - return m_selection->dir; + return QDir::toNativeSeparators(m_selection->dir); } - void select(MOBase::IPluginGame* game) + void select(IPluginGame* game) { Game* checked = findGame(game); @@ -397,12 +409,12 @@ public: private: struct Game { - MOBase::IPluginGame* game = nullptr; + IPluginGame* game = nullptr; QCommandLinkButton* button = nullptr; QString dir; bool installed = false; - Game(MOBase::IPluginGame* g) + Game(IPluginGame* g) : game(g), installed(g->isInstalled()) { if (installed) { @@ -418,11 +430,11 @@ private: Game* m_selection; - std::vector sortedGamePlugins() const + std::vector sortedGamePlugins() const { - std::vector v; + std::vector v; - for (auto* game : m_pc.plugins()) { + for (auto* game : m_pc.plugins()) { v.push_back(game); } @@ -433,7 +445,7 @@ private: return v; } - Game* findGame(MOBase::IPluginGame* game) + Game* findGame(IPluginGame* game) { for (auto& g : m_games) { if (g->game == game) { @@ -598,9 +610,9 @@ private: return g; } - MOBase::IPluginGame* findAnotherGame(const QString& path) + IPluginGame* findAnotherGame(const QString& path) { - for (auto* otherGame : m_pc.plugins()) { + for (auto* otherGame : m_pc.plugins()) { if (otherGame->looksValid(path)) { return otherGame; } @@ -609,9 +621,9 @@ private: return nullptr; } - bool confirmUnknown(const QString& path, MOBase::IPluginGame* game) + bool confirmUnknown(const QString& path, IPluginGame* game) { - const auto r = MOBase::TaskDialog(&m_dlg) + const auto r = TaskDialog(&m_dlg) .title(QObject::tr("Unrecognized game")) .main(QObject::tr("Unrecognized game")) .content(QObject::tr( @@ -632,11 +644,11 @@ private: return (r == QMessageBox::Ignore); } - MOBase::IPluginGame* confirmOtherGame( + IPluginGame* confirmOtherGame( const QString& path, - MOBase::IPluginGame* selectedGame, MOBase::IPluginGame* guessedGame) + IPluginGame* selectedGame, IPluginGame* guessedGame) { - const auto r = MOBase::TaskDialog(&m_dlg) + const auto r = TaskDialog(&m_dlg) .title(QObject::tr("Incorrect game")) .main(QObject::tr("Incorrect game")) .content(QObject::tr( @@ -742,7 +754,7 @@ public: } private: - MOBase::IPluginGame* m_previousGame; + IPluginGame* m_previousGame; std::vector m_buttons; QString m_selection; @@ -868,10 +880,6 @@ public: ui->pathPages->setCurrentIndex(0); } - bool skip() const override - { - return (m_dlg.instanceType() == CreateInstanceDialog::Portable); - } bool ready() const override { @@ -934,7 +942,7 @@ private: bool checkVarPath(QString path) const { - path.replace("%BASE_DIR%", ui->base->text()); + path = PathSettings::resolve(path, ui->base->text()); return checkAdvancedPath(path); } @@ -971,11 +979,6 @@ private: e->setText(path); } } - - QString makeDefaultPath(const std::wstring& dir) - { - return "%BASE_DIR%\\" + QString::fromStdWString(dir); - } }; @@ -990,54 +993,52 @@ public: void activated() override { ui->review->setPlainText(makeReview()); + ui->creationLog->clear(); } - QString makeReview() const + QString toLocalizedString(CreateInstanceDialog::Types t) const { - QStringList lines; - - const auto paths = m_dlg.paths(); - - // type - switch (m_dlg.instanceType()) + switch (t) { case CreateInstanceDialog::Global: - { - lines.push_back(QObject::tr("Instance type: %1").arg(QObject::tr("Global"))); - lines.push_back(QObject::tr("Instance name: %1").arg(m_dlg.instanceName())); - - if (paths.downloads.isEmpty()) { - // simple settings - lines.push_back(QObject::tr("Instance location: %1").arg(paths.base)); - } else { - // advanced settings - lines.push_back(QObject::tr("Instance base folder: %1").arg(paths.base)); - lines.push_back(dirLine(QObject::tr("Downloads"), paths.downloads)); - lines.push_back(dirLine(QObject::tr("Mods"), paths.mods)); - lines.push_back(dirLine(QObject::tr("Profiles"), paths.profiles)); - lines.push_back(dirLine(QObject::tr("Overwrite"), paths.overwrite)); - } - - break; - } + return QObject::tr("Global"); case CreateInstanceDialog::Portable: - { - lines.push_back(QObject::tr("Instance type: %1").arg(QObject::tr("Portable"))); - lines.push_back(QObject::tr("Instance location: %1").arg(qApp->applicationDirPath())); - break; - } + return QObject::tr("Portable"); default: - { - lines.push_back(QObject::tr("Instance type: %1").arg(QObject::tr("?"))); + return QObject::tr("Instance type: %1").arg(QObject::tr("?")); + } + } + + QString makeReview() const + { + QStringList lines; + const auto paths = m_dlg.paths(); + + lines.push_back(QObject::tr("Instance type: %1").arg(toLocalizedString(m_dlg.instanceType()))); + lines.push_back(QObject::tr("Instance location: %1").arg(m_dlg.dataPath())); + + if (m_dlg.instanceType() != CreateInstanceDialog::Portable) { + lines.push_back(QObject::tr("Instance name: %1").arg(m_dlg.instanceName())); + } + + if (paths.downloads.isEmpty()) { + // simple settings + if (paths.base != m_dlg.dataPath()) { + lines.push_back(QObject::tr("Base directory: %1").arg(paths.base)); } + } else { + // advanced settings + lines.push_back(QObject::tr("Base directory: %1").arg(paths.base)); + lines.push_back(dirLine(QObject::tr("Downloads"), paths.downloads)); + lines.push_back(dirLine(QObject::tr("Mods"), paths.mods)); + lines.push_back(dirLine(QObject::tr("Profiles"), paths.profiles)); + lines.push_back(dirLine(QObject::tr("Overwrite"), paths.overwrite)); } // game - MOBase::IPluginGame* game = m_dlg.game(); - - QString name = game->gameName(); + QString name = m_dlg.game()->gameName(); if (!m_dlg.gameEdition().isEmpty()) { name += " (" + m_dlg.gameEdition() + ")"; } @@ -1157,8 +1158,195 @@ void CreateInstanceDialog::changePage(int d) } } + +class Failed {}; + +class DirectoryCreator +{ +public: + DirectoryCreator(const DirectoryCreator&) = delete; + DirectoryCreator& operator=(const DirectoryCreator&) = delete; + + static std::unique_ptr create( + const QDir& target, std::function log) + { + return std::unique_ptr(new DirectoryCreator(target, log)); + } + + ~DirectoryCreator() + { + rollback(); + } + + void commit() + { + m_created.clear(); + } + + void rollback() noexcept + { + try + { + for (auto itor=m_created.rbegin(); itor!=m_created.rend(); ++itor) { + const auto r = shell::DeleteDirectoryRecursive(*itor); + if (!r) { + m_logger(r.toString()); + } + } + + m_created.clear(); + } + catch(...) + { + // eat it + } + } + +private: + std::function m_logger; + + DirectoryCreator(const QDir& target, std::function log) + : m_logger(log) + { + try + { + const QString s = QDir::toNativeSeparators(target.absolutePath()); + const QStringList cs = s.split("\\"); + + if (cs.empty()) { + return; + } + + QDir d(cs[0]); + + for (int i=1; i m_created; +}; + void CreateInstanceDialog::finish() { + ui->creationLog->clear(); + logCreation(tr("Creating instance...")); + + const auto& m = InstanceManager::instance(); + const auto ci = creationInfo(); + + auto logger = [&](QString s) { + logCreation(s); + }; + + auto createDir = [&](QString path) { + return DirectoryCreator::create(path, logger); + }; + + + try + { + std::vector> dirs; + + dirs.push_back(createDir(ci.dataPath)); + dirs.push_back(createDir(ci.paths.base)); + dirs.push_back(createDir(PathSettings::resolve(ci.paths.downloads, ci.paths.base))); + dirs.push_back(createDir(PathSettings::resolve(ci.paths.mods, ci.paths.base))); + dirs.push_back(createDir(PathSettings::resolve(ci.paths.profiles, ci.paths.base))); + dirs.push_back(createDir(PathSettings::resolve(ci.paths.overwrite, ci.paths.base))); + + + Settings s(ci.iniPath); + s.game().setName(ci.game->gameName()); + s.game().setDirectory(ci.gameLocation); + + if (!ci.gameEdition.isEmpty()) { + s.game().setEdition(ci.gameEdition); + } + + if (ci.type == Global) { + if (ci.paths.base != ci.dataPath) { + s.paths().setBase(ci.paths.base); + } + + if (ci.paths.downloads != cid::makeDefaultPath(AppConfig::downloadPath())) { + s.paths().setDownloads(ci.paths.downloads); + } + + if (ci.paths.mods != cid::makeDefaultPath(AppConfig::modsPath())) { + s.paths().setMods(ci.paths.mods); + } + + if (ci.paths.profiles != cid::makeDefaultPath(AppConfig::profilesPath())) { + s.paths().setProfiles(ci.paths.profiles); + } + + if (ci.paths.overwrite != cid::makeDefaultPath(AppConfig::overwritePath())) { + s.paths().setOverwrite(ci.paths.overwrite); + } + } + + + logCreation(tr("Writing %1...").arg(ci.iniPath)); + + const auto r = s.sync(); + if (r != QSettings::NoError) { + switch (r) + { + case QSettings::AccessError: + logCreation(formatSystemMessage(ERROR_ACCESS_DENIED)); + break; + + case QSettings::FormatError: + logCreation(tr("Format error.")); + break; + + default: + logCreation(tr("Error %1.").arg(static_cast(r))); + break; + } + + throw Failed(); + } + + for (auto& d : dirs) { + d->commit(); + } + + logCreation(tr("Done.")); + } + catch(Failed&) + { + } +} + +void CreateInstanceDialog::logCreation(const QString& s) +{ + ui->creationLog->insertPlainText(s + "\n"); +} + +void CreateInstanceDialog::logCreation(const std::wstring& s) +{ + logCreation(QString::fromStdWString(s)); } void CreateInstanceDialog::selectPage(std::size_t i) @@ -1213,7 +1401,72 @@ QString CreateInstanceDialog::instanceName() const return getSelected(&cid::Page::selectedInstanceName); } +QString CreateInstanceDialog::dataPath() const +{ + QString s; + + if (instanceType() == Portable) { + s = QDir(qApp->applicationDirPath()).absolutePath(); + } else { + s = InstanceManager::instance().instancePath(instanceName()); + } + + return QDir::toNativeSeparators(s); +} + CreateInstanceDialog::Paths CreateInstanceDialog::paths() const { return getSelected(&cid::Page::selectedPaths); } + +CreateInstanceDialog::CreationInfo CreateInstanceDialog::creationInfo() const +{ + CreationInfo ci; + + ci.type = getSelected(&cid::Page::selectedInstanceType); + ci.game = getSelected(&cid::Page::selectedGame); + ci.gameLocation = getSelected(&cid::Page::selectedGameLocation); + ci.gameEdition = getSelected(&cid::Page::selectedGameEdition); + ci.instanceName = getSelected(&cid::Page::selectedInstanceName); + ci.paths = getSelected(&cid::Page::selectedPaths); + ci.dataPath = dataPath(); + + ci.paths.base = QDir(ci.paths.base).absolutePath(); + + if (ci.paths.downloads.isEmpty()) { + ci.paths.downloads = cid::makeDefaultPath(AppConfig::downloadPath()); + } else if (!ci.paths.downloads.contains(PathSettings::BaseDirVariable)) { + ci.paths.downloads = QDir(ci.paths.downloads).absolutePath(); + } + + if (ci.paths.mods.isEmpty()) { + ci.paths.mods = cid::makeDefaultPath(AppConfig::modsPath()); + } else if (!ci.paths.mods.contains(PathSettings::BaseDirVariable)) { + ci.paths.mods = QDir(ci.paths.mods).absolutePath(); + } + + if (ci.paths.profiles.isEmpty()) { + ci.paths.profiles = cid::makeDefaultPath(AppConfig::profilesPath()); + } else if (!ci.paths.profiles.contains(PathSettings::BaseDirVariable)) { + ci.paths.profiles = QDir(ci.paths.profiles).absolutePath(); + } + + if (ci.paths.overwrite.isEmpty()) { + ci.paths.overwrite = cid::makeDefaultPath(AppConfig::overwritePath()); + } else if (!ci.paths.overwrite.contains(PathSettings::BaseDirVariable)) { + ci.paths.overwrite = QDir(ci.paths.overwrite).absolutePath(); + } + + ci.iniPath = QFileInfo( + ci.dataPath + "/" + QString::fromStdWString(AppConfig::iniFileName())) + .absoluteFilePath(); + + ci.paths.base = QDir::toNativeSeparators(ci.paths.base); + ci.paths.downloads = QDir::toNativeSeparators(ci.paths.downloads); + ci.paths.mods = QDir::toNativeSeparators(ci.paths.mods); + ci.paths.profiles = QDir::toNativeSeparators(ci.paths.profiles); + ci.paths.overwrite = QDir::toNativeSeparators(ci.paths.overwrite); + ci.paths.ini = QDir::toNativeSeparators(ci.paths.ini); + + return ci; +} diff --git a/src/createinstancedialog.h b/src/createinstancedialog.h index d9c392ca..2f5774ae 100644 --- a/src/createinstancedialog.h +++ b/src/createinstancedialog.h @@ -28,10 +28,24 @@ public: QString mods; QString profiles; QString overwrite; + QString ini; auto operator<=>(const Paths&) const = default; }; + struct CreationInfo + { + Types type; + MOBase::IPluginGame* game; + QString gameLocation; + QString gameEdition; + QString instanceName; + QString dataPath; + QString iniPath; + Paths paths; + }; + + explicit CreateInstanceDialog( const PluginContainer& pc, QWidget *parent = nullptr); @@ -54,8 +68,11 @@ public: QString gameLocation() const; QString gameEdition() const; QString instanceName() const; + QString dataPath() const; Paths paths() const; + CreationInfo creationInfo() const; + private: std::unique_ptr ui; const PluginContainer& m_pc; @@ -74,6 +91,9 @@ private: return T(); } + + void logCreation(const QString& s); + void logCreation(const std::wstring& s); }; #endif // MODORGANIZER_CREATEINSTANCEDIALOG_INCLUDED diff --git a/src/createinstancedialog.ui b/src/createinstancedialog.ui index c899a5b7..cfb343fa 100644 --- a/src/createinstancedialog.ui +++ b/src/createinstancedialog.ui @@ -891,7 +891,21 @@ 0 - + + + QTextEdit::NoWrap + + + + + + + QTextEdit::NoWrap + + + Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + diff --git a/src/instancemanager.h b/src/instancemanager.h index 0cd5ef4c..6a6b52ac 100644 --- a/src/instancemanager.h +++ b/src/instancemanager.h @@ -56,13 +56,12 @@ public: QString sanitizeInstanceName(const QString &name) const; QString makeUniqueName(const QString& instanceName) const; bool instanceExists(const QString& instanceName) const; + QString instancePath(const QString& instanceName) const; private: InstanceManager(); - QString instancePath(const QString& instanceName) const; - bool deleteLocalInstance(const QString &instanceId) const; QString manageInstances(const QStringList &instanceList) const; diff --git a/src/settings.cpp b/src/settings.cpp index 8db9c623..661cf429 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1464,6 +1464,8 @@ QSet PluginSettings::readBlacklist() const } +const QString PathSettings::BaseDirVariable = "%BASE_DIR%"; + PathSettings::PathSettings(QSettings& settings) : m_Settings(settings) { @@ -1511,10 +1513,10 @@ QString PathSettings::getConfigurablePath(const QString &key, bool resolve) const { QString result = QDir::fromNativeSeparators( - get(m_Settings, "Settings", key, QString("%BASE_DIR%/") + def)); + get(m_Settings, "Settings", key, makeDefaultPath(def))); if (resolve) { - result.replace("%BASE_DIR%", base()); + result = PathSettings::resolve(result, base()); } return result; @@ -1529,6 +1531,18 @@ void PathSettings::setConfigurablePath(const QString &key, const QString& path) } } +QString PathSettings::resolve(const QString& path, const QString& baseDir) +{ + QString s = path; + s.replace(BaseDirVariable, baseDir); + return s; +} + +QString PathSettings::makeDefaultPath(const QString dirName) +{ + return BaseDirVariable + "/" + dirName; +} + QString PathSettings::base() const { return QDir::fromNativeSeparators(get(m_Settings, diff --git a/src/settings.h b/src/settings.h index a8c527d6..c723faec 100644 --- a/src/settings.h +++ b/src/settings.h @@ -391,6 +391,9 @@ private: class PathSettings { public: + // %BASE_DIR% + static const QString BaseDirVariable; + PathSettings(QSettings& settings); QString base() const; @@ -418,6 +421,15 @@ public: std::map recent() const; void setRecent(const std::map& map); + + // resolves %BASE_DIR% + // + static QString resolve(const QString& path, const QString& baseDir); + + // returns %BASE_DIR%/dirName + // + static QString makeDefaultPath(const QString dirName); + private: QSettings& m_Settings; diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index 87c7201d..c4a53fcd 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -114,7 +114,7 @@ QString SettingsDialog::getColoredButtonStyleSheet() const void SettingsDialog::accept() { QString newModPath = ui->modDirEdit->text(); - newModPath.replace("%BASE_DIR%", ui->baseDirEdit->text()); + newModPath = PathSettings::resolve(newModPath, ui->baseDirEdit->text()); if ((QDir::fromNativeSeparators(newModPath) != QDir::fromNativeSeparators( diff --git a/src/settingsdialogpaths.cpp b/src/settingsdialogpaths.cpp index 74ba4f25..eb334541 100644 --- a/src/settingsdialogpaths.cpp +++ b/src/settingsdialogpaths.cpp @@ -64,7 +64,7 @@ void PathsSettingsTab::update() std::tie(path, setter, defaultName) = dir; QString realPath = path; - realPath.replace("%BASE_DIR%", ui->baseDirEdit->text()); + realPath = PathSettings::resolve(realPath, ui->baseDirEdit->text()); if (!QDir(realPath).exists()) { if (!QDir().mkpath(realPath)) { @@ -113,7 +113,7 @@ void PathsSettingsTab::on_browseBaseDirBtn_clicked() void PathsSettingsTab::on_browseDownloadDirBtn_clicked() { QString searchPath = ui->downloadDirEdit->text(); - searchPath.replace("%BASE_DIR%", ui->baseDirEdit->text()); + searchPath = PathSettings::resolve(searchPath, ui->baseDirEdit->text()); QString temp = QFileDialog::getExistingDirectory(&dialog(), QObject::tr("Select download directory"), searchPath); if (!temp.isEmpty()) { @@ -124,7 +124,7 @@ void PathsSettingsTab::on_browseDownloadDirBtn_clicked() void PathsSettingsTab::on_browseModDirBtn_clicked() { QString searchPath = ui->modDirEdit->text(); - searchPath.replace("%BASE_DIR%", ui->baseDirEdit->text()); + searchPath = PathSettings::resolve(searchPath, ui->baseDirEdit->text()); QString temp = QFileDialog::getExistingDirectory(&dialog(), QObject::tr("Select mod directory"), searchPath); if (!temp.isEmpty()) { @@ -135,7 +135,7 @@ void PathsSettingsTab::on_browseModDirBtn_clicked() void PathsSettingsTab::on_browseCacheDirBtn_clicked() { QString searchPath = ui->cacheDirEdit->text(); - searchPath.replace("%BASE_DIR%", ui->baseDirEdit->text()); + searchPath = PathSettings::resolve(searchPath, ui->baseDirEdit->text()); QString temp = QFileDialog::getExistingDirectory(&dialog(), QObject::tr("Select cache directory"), searchPath); if (!temp.isEmpty()) { @@ -146,7 +146,7 @@ void PathsSettingsTab::on_browseCacheDirBtn_clicked() void PathsSettingsTab::on_browseProfilesDirBtn_clicked() { QString searchPath = ui->profilesDirEdit->text(); - searchPath.replace("%BASE_DIR%", ui->baseDirEdit->text()); + searchPath = PathSettings::resolve(searchPath, ui->baseDirEdit->text()); QString temp = QFileDialog::getExistingDirectory(&dialog(), QObject::tr("Select profiles directory"), searchPath); if (!temp.isEmpty()) { @@ -157,7 +157,7 @@ void PathsSettingsTab::on_browseProfilesDirBtn_clicked() void PathsSettingsTab::on_browseOverwriteDirBtn_clicked() { QString searchPath = ui->overwriteDirEdit->text(); - searchPath.replace("%BASE_DIR%", ui->baseDirEdit->text()); + searchPath = PathSettings::resolve(searchPath, ui->baseDirEdit->text()); QString temp = QFileDialog::getExistingDirectory(&dialog(), QObject::tr("Select overwrite directory"), searchPath); if (!temp.isEmpty()) { -- cgit v1.3.1 From cfdfa9d40e09d509396f57fbf70b48fa2136c230 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 25 Jul 2020 21:17:24 -0400 Subject: PathChecker is unnecessary, refactored its guts into NamePage and PathsPage made existing paths just a warning, don't check for portable instances --- src/createinstancedialog.cpp | 72 ++++++----- src/createinstancedialog.ui | 8 +- src/createinstancedialogpages.cpp | 250 ++++++++++++++++++++++---------------- src/createinstancedialogpages.h | 39 +++--- src/instancemanager.cpp | 7 +- src/instancemanager.h | 1 + 6 files changed, 205 insertions(+), 172 deletions(-) (limited to 'src/instancemanager.h') diff --git a/src/createinstancedialog.cpp b/src/createinstancedialog.cpp index 63be7346..4b9f24ae 100644 --- a/src/createinstancedialog.cpp +++ b/src/createinstancedialog.cpp @@ -2,10 +2,8 @@ #include "ui_createinstancedialog.h" #include "createinstancedialogpages.h" #include "instancemanager.h" -//#include "plugincontainer.h" #include "settings.h" #include "shared/appconfig.h" -//#include #include #include @@ -80,6 +78,9 @@ void CreateInstanceDialog::changePage(int d) { std::size_t i = static_cast(ui->pages->currentIndex()); + // goes back or forwards until an unskippable page is reached, or the + // first/last page + if (d > 0) { for (;;) { ++i; @@ -359,7 +360,7 @@ QString CreateInstanceDialog::dataPath() const QString s; if (instanceType() == Portable) { - s = QDir(qApp->applicationDirPath()).absolutePath(); + s = QDir(InstanceManager::portablePath()).absolutePath(); } else { s = InstanceManager::instance().instancePath(instanceName()); } @@ -372,8 +373,31 @@ CreateInstanceDialog::Paths CreateInstanceDialog::paths() const return getSelected(&cid::Page::selectedPaths); } +void fixVarDir(QString& path, const std::wstring& defaultDir) +{ + if (path.isEmpty()) { + path = cid::makeDefaultPath(defaultDir); + } else if (!path.contains(PathSettings::BaseDirVariable)) { + path = QDir(path).absolutePath(); + } + + path = QDir::toNativeSeparators(path); +} + +void fixDirPath(QString& path) +{ + path = QDir::toNativeSeparators(QDir(path).absolutePath()); +} + +void fixFilePath(QString& path) +{ + path = QDir::toNativeSeparators(QFileInfo(path).absolutePath()); +} + CreateInstanceDialog::CreationInfo CreateInstanceDialog::creationInfo() const { + const auto iniFilename = QString::fromStdWString(AppConfig::iniFileName()); + CreationInfo ci; ci.type = getSelected(&cid::Page::selectedInstanceType); @@ -383,43 +407,15 @@ CreateInstanceDialog::CreationInfo CreateInstanceDialog::creationInfo() const ci.instanceName = getSelected(&cid::Page::selectedInstanceName); ci.paths = getSelected(&cid::Page::selectedPaths); ci.dataPath = dataPath(); + ci.iniPath = ci.dataPath + "/" + iniFilename; - ci.paths.base = QDir(ci.paths.base).absolutePath(); - - if (ci.paths.downloads.isEmpty()) { - ci.paths.downloads = cid::makeDefaultPath(AppConfig::downloadPath()); - } else if (!ci.paths.downloads.contains(PathSettings::BaseDirVariable)) { - ci.paths.downloads = QDir(ci.paths.downloads).absolutePath(); - } - - if (ci.paths.mods.isEmpty()) { - ci.paths.mods = cid::makeDefaultPath(AppConfig::modsPath()); - } else if (!ci.paths.mods.contains(PathSettings::BaseDirVariable)) { - ci.paths.mods = QDir(ci.paths.mods).absolutePath(); - } - - if (ci.paths.profiles.isEmpty()) { - ci.paths.profiles = cid::makeDefaultPath(AppConfig::profilesPath()); - } else if (!ci.paths.profiles.contains(PathSettings::BaseDirVariable)) { - ci.paths.profiles = QDir(ci.paths.profiles).absolutePath(); - } - - if (ci.paths.overwrite.isEmpty()) { - ci.paths.overwrite = cid::makeDefaultPath(AppConfig::overwritePath()); - } else if (!ci.paths.overwrite.contains(PathSettings::BaseDirVariable)) { - ci.paths.overwrite = QDir(ci.paths.overwrite).absolutePath(); - } - - ci.iniPath = QFileInfo( - ci.dataPath + "/" + QString::fromStdWString(AppConfig::iniFileName())) - .absoluteFilePath(); + fixDirPath(ci.paths.base); + fixFilePath(ci.paths.ini); - ci.paths.base = QDir::toNativeSeparators(ci.paths.base); - ci.paths.downloads = QDir::toNativeSeparators(ci.paths.downloads); - ci.paths.mods = QDir::toNativeSeparators(ci.paths.mods); - ci.paths.profiles = QDir::toNativeSeparators(ci.paths.profiles); - ci.paths.overwrite = QDir::toNativeSeparators(ci.paths.overwrite); - ci.paths.ini = QDir::toNativeSeparators(ci.paths.ini); + fixVarDir(ci.paths.downloads, AppConfig::downloadPath()); + fixVarDir(ci.paths.mods, AppConfig::modsPath()); + fixVarDir(ci.paths.profiles, AppConfig::profilesPath()); + fixVarDir(ci.paths.overwrite, AppConfig::overwritePath()); return ci; } diff --git a/src/createinstancedialog.ui b/src/createinstancedialog.ui index cfb343fa..4d9d8b06 100644 --- a/src/createinstancedialog.ui +++ b/src/createinstancedialog.ui @@ -588,7 +588,7 @@ - This folder already exists. + Warning: This folder already exists. @@ -602,7 +602,7 @@ - The folder contains invalid characters. + Warning: The folder contains invalid characters. @@ -706,7 +706,7 @@ - The folder %1 already exists. + Warning: The folder %1 already exists. true @@ -778,7 +778,7 @@ - The folder %1 contains invalid characters. + Warning: The folder %1 contains invalid characters. true diff --git a/src/createinstancedialogpages.cpp b/src/createinstancedialogpages.cpp index ab020c66..4d2c5b8b 100644 --- a/src/createinstancedialogpages.cpp +++ b/src/createinstancedialogpages.cpp @@ -15,19 +15,11 @@ using MOBase::TaskDialog; QString makeDefaultPath(const std::wstring& dir) { - return QDir::toNativeSeparators( - PathSettings::makeDefaultPath(QString::fromStdWString(dir))); + return QDir::toNativeSeparators(PathSettings::makeDefaultPath( + QString::fromStdWString(dir))); } - -PathChecker::PathChecker(QLabel* existsLabel, QLabel* invalidLabel) - : m_exists(existsLabel), m_invalid(invalidLabel) -{ - m_existsOriginal = m_exists->text(); - m_invalidOriginal = m_invalid->text(); -} - -QString PathChecker::sanitizeFileName(const QString& name) const +QString sanitizeFileName(const QString& name) { QString new_name = name; @@ -48,7 +40,7 @@ QString PathChecker::sanitizeFileName(const QString& name) const // same thing as above, but allows path separators and colons // -QString PathChecker::sanitizePath(const QString& path) const +QString sanitizePath(const QString& path) { QString new_name = path; @@ -67,93 +59,31 @@ QString PathChecker::sanitizePath(const QString& path) const return new_name; } -bool PathChecker::checkName(QString parentDir, QString name) const +void setPossiblePlaceholder( + QLabel* label, const QString& s, const QString& arg) { - bool exists = false; - bool invalid = false; - bool empty = false; - - name = name.trimmed(); - - if (name.isEmpty()) { - empty = true; - } else { - const QString sanitized = sanitizeFileName(name); - - if (name != sanitized) { - invalid = true; - } else { - exists = QDir(parentDir).exists(name); - } - } +} - bool okay = false; - if (exists) { - m_exists->setVisible(true); - setPossiblePlaceholder(m_exists, m_existsOriginal, QDir(parentDir).filePath(name)); - m_invalid->setVisible(false); - } else if (invalid) { - m_exists->setVisible(false); - m_invalid->setVisible(true); - setPossiblePlaceholder(m_invalid, m_invalidOriginal, name); - } else { - okay = !empty; - m_exists->setVisible(false); - m_invalid->setVisible(false); - } - - return okay; +PlaceholderLabel::PlaceholderLabel(QLabel* label) + : m_label(label), m_original(label->text()) +{ } -bool PathChecker::checkPath(QString path) const +void PlaceholderLabel::setText(const QString& arg) { - bool exists = false; - bool invalid = false; - bool empty = false; - - path = path.trimmed(); - - if (path.isEmpty()) { - empty = true; - } else { - const QString sanitized = sanitizePath(path); - - if (path != sanitized) { - invalid = true; - } else { - exists = QDir(path).exists(); - } - } - - bool okay = false; - - if (exists) { - m_exists->setVisible(true); - setPossiblePlaceholder(m_exists, m_existsOriginal, path); - m_invalid->setVisible(false); - } else if (invalid) { - m_exists->setVisible(false); - m_invalid->setVisible(true); - setPossiblePlaceholder(m_invalid, m_invalidOriginal, path); - } else { - okay = !empty; - m_exists->setVisible(false); - m_invalid->setVisible(false); + if (m_original.contains("%1")) { + m_label->setText(m_original.arg(arg)); } - - return okay; } -void PathChecker::setPossiblePlaceholder( - QLabel* label, const QString& s, const QString& arg) const +void PlaceholderLabel::setVisible(bool b) { - if (label->text().contains("%1")) { - label->setText(s.arg(arg)); - } + m_label->setVisible(b); } + Page::Page(CreateInstanceDialog& dlg) : ui(dlg.getUI()), m_dlg(dlg), m_pc(dlg.pluginContainer()) { @@ -238,7 +168,7 @@ TypePage::TypePage(CreateInstanceDialog& dlg) ui->createPortable->setDescription( ui->createPortable->description() - .arg(qApp->applicationDirPath())); + .arg(InstanceManager::portablePath())); QObject::connect( ui->createGlobal, &QAbstractButton::clicked, [&]{ global(); }); @@ -727,10 +657,9 @@ void EditionsPage::fillList() NamePage::NamePage(CreateInstanceDialog& dlg) : Page(dlg), m_modified(false), m_okay(false), - m_checker(ui->instanceNameExists, ui->instanceNameInvalid) + m_label(ui->instanceNameLabel), m_exists(ui->instanceNameExists), + m_invalid(ui->instanceNameInvalid) { - m_originalLabel = ui->instanceNameLabel->text(); - QObject::connect( ui->instanceName, &QLineEdit::textEdited, [&]{ onChanged(); }); } @@ -753,7 +682,7 @@ void NamePage::activated() return; } - ui->instanceNameLabel->setText(m_originalLabel.arg(g->gameName())); + m_label.setText(g->gameName()); if (!m_modified || ui->instanceName->text().isEmpty()) { const auto n = InstanceManager::instance().makeUniqueName(g->gameName()); @@ -771,7 +700,7 @@ QString NamePage::selectedInstanceName() const } const auto text = ui->instanceName->text().trimmed(); - return m_checker.sanitizeFileName(text); + return sanitizeFileName(text); } void NamePage::onChanged() @@ -784,15 +713,55 @@ void NamePage::updateWarnings() { const auto root = InstanceManager::instance().instancesPath(); - m_okay = m_checker.checkName(root, ui->instanceName->text()); + m_okay = checkName(root, ui->instanceName->text()); updateNavigation(); } +bool NamePage::checkName(QString parentDir, QString name) +{ + bool exists = false; + bool invalid = false; + bool empty = false; + + name = name.trimmed(); + + if (name.isEmpty()) { + empty = true; + } else { + const QString sanitized = sanitizeFileName(name); + + if (name != sanitized) { + invalid = true; + } else { + exists = QDir(parentDir).exists(name); + } + } + + bool okay = false; + + if (exists) { + m_exists.setVisible(true); + m_exists.setText(QDir(parentDir).filePath(name)); + m_invalid.setVisible(false); + } else if (invalid) { + m_exists.setVisible(false); + m_invalid.setVisible(true); + m_invalid.setText(name); + } else { + okay = !empty; + m_exists.setVisible(false); + m_invalid.setVisible(false); + } + + return okay; +} + PathsPage::PathsPage(CreateInstanceDialog& dlg) : - Page(dlg), - m_checker(ui->locationExists, ui->locationInvalid), - m_advancedChecker(ui->advancedDirExists, ui->advancedDirInvalid) + Page(dlg), m_lastType(CreateInstanceDialog::NoType), + m_simpleExists(ui->locationExists), m_simpleInvalid(ui->locationInvalid), + m_advancedExists(ui->advancedDirExists), + m_advancedInvalid(ui->advancedDirInvalid) { QObject::connect(ui->location, &QLineEdit::textEdited, [&]{ onChanged(); }); QObject::connect(ui->base, &QLineEdit::textEdited, [&]{ onChanged(); }); @@ -816,12 +785,16 @@ bool PathsPage::ready() const void PathsPage::activated() { const auto name = m_dlg.instanceName(); + const auto type = m_dlg.instanceType(); - setPaths(name, (m_lastInstanceName != name)); + const bool changed = (m_lastInstanceName != name) || (m_lastType != type); + + setPaths(name, changed); checkPaths(); updateNavigation(); m_lastInstanceName = name; + m_lastType = type; } CreateInstanceDialog::Paths PathsPage::selectedPaths() const @@ -852,21 +825,23 @@ bool PathsPage::checkPaths() const if (ui->advancedPathOptions->isChecked()) { return checkAdvancedPath(ui->base->text()) && - checkVarPath(ui->downloads->text()); + checkAdvancedPath(resolve(ui->downloads->text())) && + checkAdvancedPath(resolve(ui->mods->text())) && + checkAdvancedPath(resolve(ui->profiles->text())) && + checkAdvancedPath(resolve(ui->overwrite->text())); } else { - return m_checker.checkPath(ui->location->text()); + return checkPath(ui->location->text(), m_simpleExists, m_simpleInvalid); } } bool PathsPage::checkAdvancedPath(const QString& path) const { - return m_advancedChecker.checkPath(path); + return checkPath(path, m_advancedExists, m_advancedInvalid); } -bool PathsPage::checkVarPath(QString path) const +QString PathsPage::resolve(const QString& path) const { - path = PathSettings::resolve(path, ui->base->text()); - return checkAdvancedPath(path); + return PathSettings::resolve(path, ui->base->text()); } void PathsPage::onAdvanced() @@ -884,12 +859,20 @@ void PathsPage::onAdvanced() void PathsPage::setPaths(const QString& name, bool force) { - const auto root = InstanceManager::instance().instancesPath(); - const auto path = QDir::toNativeSeparators(root + "/" + name); + QString path; - setIfEmpty(ui->location, path, force); + if (m_dlg.instanceType() == CreateInstanceDialog::Portable) { + path = InstanceManager::portablePath(); + } else { + const auto root = InstanceManager::instance().instancesPath(); + path = root + "/" + name; + } + path = QDir::toNativeSeparators(QDir(path).canonicalPath()); + + setIfEmpty(ui->location, path, force); setIfEmpty(ui->base, path, force); + setIfEmpty(ui->downloads, makeDefaultPath(AppConfig::downloadPath()), force); setIfEmpty(ui->mods, makeDefaultPath(AppConfig::modsPath()), force); setIfEmpty(ui->profiles, makeDefaultPath(AppConfig::profilesPath()), force); @@ -903,6 +886,61 @@ void PathsPage::setIfEmpty(QLineEdit* e, const QString& path, bool force) } } +bool PathsPage::checkPath( + QString path, + PlaceholderLabel& existsLabel, PlaceholderLabel& invalidLabel) const +{ + bool exists = false; + bool invalid = false; + bool empty = false; + + path = QDir::toNativeSeparators(path.trimmed()); + + if (path.isEmpty()) { + empty = true; + } else { + const QString sanitized = sanitizePath(path); + + if (path != sanitized) { + invalid = true; + } else { + if (m_dlg.instanceType() == CreateInstanceDialog::Portable) { + // the default data path for a portable instance is the application + // directory, so it's not an error if it exists + if (QDir(path) != InstanceManager::instance().portablePath()) { + exists = QDir(path).exists(); + } + } else { + exists = QDir(path).exists(); + } + } + } + + bool okay = true; + + if (invalid) { + okay = false; + existsLabel.setVisible(false); + invalidLabel.setVisible(true); + invalidLabel.setText(path); + } else if (empty) { + okay = false; + existsLabel.setVisible(false); + invalidLabel.setVisible(false); + } else if (exists) { + // this is just a warning + existsLabel.setVisible(true); + existsLabel.setText(path); + invalidLabel.setVisible(false); + } else { + okay = true; + existsLabel.setVisible(false); + invalidLabel.setVisible(false); + } + + return okay; +} + ConfirmationPage::ConfirmationPage(CreateInstanceDialog& dlg) : Page(dlg) diff --git a/src/createinstancedialogpages.h b/src/createinstancedialogpages.h index 0baff666..0d210493 100644 --- a/src/createinstancedialogpages.h +++ b/src/createinstancedialogpages.h @@ -13,29 +13,17 @@ namespace cid QString makeDefaultPath(const std::wstring& dir); -class PathChecker + +class PlaceholderLabel { public: - PathChecker(QLabel* existsLabel, QLabel* invalidLabel); - - QString sanitizeFileName(const QString& name) const; - - // same thing as above, but allows path separators and colons - // - QString sanitizePath(const QString& path) const; - - bool checkName(QString parentDir, QString name) const; - bool checkPath(QString path) const; + PlaceholderLabel(QLabel* label); + void setText(const QString& arg); + void setVisible(bool b); private: - QLabel* m_exists; - QString m_existsOriginal; - - QLabel* m_invalid; - QString m_invalidOriginal; - - void setPossiblePlaceholder( - QLabel* label, const QString& s, const QString& arg) const; + QLabel* m_label; + QString m_original; }; @@ -168,13 +156,13 @@ public: QString selectedInstanceName() const override; private: - PathChecker m_checker; - QString m_originalLabel; + mutable PlaceholderLabel m_label, m_exists, m_invalid; bool m_modified; bool m_okay; void onChanged(); void updateWarnings(); + bool checkName(QString parentDir, QString name); }; @@ -189,16 +177,21 @@ public: CreateInstanceDialog::Paths selectedPaths() const override; private: - PathChecker m_checker, m_advancedChecker; QString m_lastInstanceName; + CreateInstanceDialog::Types m_lastType; + mutable PlaceholderLabel m_simpleExists, m_simpleInvalid; + mutable PlaceholderLabel m_advancedExists, m_advancedInvalid; void onChanged(); bool checkPaths() const; bool checkAdvancedPath(const QString& path) const; - bool checkVarPath(QString path) const; + QString resolve(const QString& path) const; void onAdvanced(); void setPaths(const QString& name, bool force); void setIfEmpty(QLineEdit* e, const QString& path, bool force); + bool checkPath( + QString path, + PlaceholderLabel& existsLabel, PlaceholderLabel& invalidLabel) const; }; diff --git a/src/instancemanager.cpp b/src/instancemanager.cpp index b135cac1..849cdb5e 100644 --- a/src/instancemanager.cpp +++ b/src/instancemanager.cpp @@ -353,7 +353,12 @@ QStringList InstanceManager::instanceNames() const bool InstanceManager::isPortablePath(const QString& dataPath) { - return (dataPath == qApp->applicationDirPath()); + return (dataPath == portablePath()); +} + +QString InstanceManager::portablePath() +{ + return qApp->applicationDirPath(); } bool InstanceManager::portableInstall() const diff --git a/src/instancemanager.h b/src/instancemanager.h index 6a6b52ac..33a751c2 100644 --- a/src/instancemanager.h +++ b/src/instancemanager.h @@ -48,6 +48,7 @@ public: bool allowedToChangeInstance() const; static bool isPortablePath(const QString& dataPath); + static QString portablePath(); QString instancesPath() const; QStringList instanceNames() const; -- cgit v1.3.1 From dfbcf8ec4c6da4d2d098403a01e7ec19b587e836 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 25 Jul 2020 22:08:43 -0400 Subject: restart when finished, added launch instance checkbox --- src/createinstancedialog.cpp | 14 ++++++++++++++ src/createinstancedialog.ui | 38 ++++++++++++++++++++++++++++++++++++++ src/instancemanager.h | 3 +-- 3 files changed, 53 insertions(+), 2 deletions(-) (limited to 'src/instancemanager.h') diff --git a/src/createinstancedialog.cpp b/src/createinstancedialog.cpp index 4b9f24ae..63df9cd7 100644 --- a/src/createinstancedialog.cpp +++ b/src/createinstancedialog.cpp @@ -3,6 +3,7 @@ #include "createinstancedialogpages.h" #include "instancemanager.h" #include "settings.h" +#include "shared/util.h" #include "shared/appconfig.h" #include #include @@ -27,6 +28,7 @@ CreateInstanceDialog::CreateInstanceDialog( m_pages.push_back(std::make_unique(*this)); ui->pages->setCurrentIndex(0); + ui->launch->setChecked(true); updateNavigation(); @@ -287,6 +289,14 @@ void CreateInstanceDialog::finish() } logCreation(tr("Done.")); + + if (ui->launch->isChecked()) { + InstanceManager::instance().setCurrentInstance(ci.instanceName); + ExitModOrganizer(Exit::Restart); + } else { + ui->next->setEnabled(false); + ui->cancel->setText(tr("Close")); + } } catch(Failed&) { @@ -328,6 +338,10 @@ void CreateInstanceDialog::updateNavigation() } else { ui->next->setText(m_originalNext); } + + // this may have been changed by finish() if the launch checkbox wasn't + // checked + ui->cancel->setText(tr("Cancel")); } CreateInstanceDialog::Types CreateInstanceDialog::instanceType() const diff --git a/src/createinstancedialog.ui b/src/createinstancedialog.ui index 41703f5e..bcd2eb03 100644 --- a/src/createinstancedialog.ui +++ b/src/createinstancedialog.ui @@ -910,6 +910,44 @@ + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + 304 + 20 + + + + + + + + Launch the new instance + + + + + + diff --git a/src/instancemanager.h b/src/instancemanager.h index 33a751c2..7467e2fa 100644 --- a/src/instancemanager.h +++ b/src/instancemanager.h @@ -45,6 +45,7 @@ public: void clearCurrentInstance(); QString currentInstance() const; + void setCurrentInstance(const QString &name); bool allowedToChangeInstance() const; static bool isPortablePath(const QString& dataPath); @@ -67,8 +68,6 @@ private: QString manageInstances(const QStringList &instanceList) const; - void setCurrentInstance(const QString &name); - QString queryInstanceName(const QStringList &instanceList) const; QString chooseInstance(const QStringList &instanceList) const; -- cgit v1.3.1 From 0f0313874aa90c66acaac9082f5ba6fbd29300ef Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 25 Jul 2020 22:47:34 -0400 Subject: new GlobalSettings class to manage the registry close the create instance dialog when launch is unchecked --- src/createinstancedialog.cpp | 7 +------ src/createinstancedialog.ui | 3 +++ src/instancemanager.cpp | 30 +++--------------------------- src/instancemanager.h | 4 ---- src/settings.cpp | 36 ++++++++++++++++++++++++++++++++++++ src/settings.h | 16 ++++++++++++++++ 6 files changed, 59 insertions(+), 37 deletions(-) (limited to 'src/instancemanager.h') diff --git a/src/createinstancedialog.cpp b/src/createinstancedialog.cpp index 63df9cd7..5a38aa50 100644 --- a/src/createinstancedialog.cpp +++ b/src/createinstancedialog.cpp @@ -294,8 +294,7 @@ void CreateInstanceDialog::finish() InstanceManager::instance().setCurrentInstance(ci.instanceName); ExitModOrganizer(Exit::Restart); } else { - ui->next->setEnabled(false); - ui->cancel->setText(tr("Close")); + close(); } } catch(Failed&) @@ -338,10 +337,6 @@ void CreateInstanceDialog::updateNavigation() } else { ui->next->setText(m_originalNext); } - - // this may have been changed by finish() if the launch checkbox wasn't - // checked - ui->cancel->setText(tr("Cancel")); } CreateInstanceDialog::Types CreateInstanceDialog::instanceType() const diff --git a/src/createinstancedialog.ui b/src/createinstancedialog.ui index bcd2eb03..1f9a8ce1 100644 --- a/src/createinstancedialog.ui +++ b/src/createinstancedialog.ui @@ -943,6 +943,9 @@ Launch the new instance + + true + diff --git a/src/instancemanager.cpp b/src/instancemanager.cpp index 849cdb5e..bd35cb47 100644 --- a/src/instancemanager.cpp +++ b/src/instancemanager.cpp @@ -23,7 +23,6 @@ along with Mod Organizer. If not, see . #include "settings.h" #include "shared/appconfig.h" #include "plugincontainer.h" -#include "env.h" #include #include #include @@ -38,15 +37,9 @@ along with Mod Organizer. If not, see . using namespace MOBase; -const QString Organization = "Mod Organizer Team"; -const QString Application = "Mod Organizer"; -const QString InstanceValue = "CurrentInstance"; - - InstanceManager::InstanceManager() - : m_AppSettings(Organization, Application) { - updateRegistryKey(); + GlobalSettings::updateRegistryKey(); } InstanceManager &InstanceManager::instance() @@ -55,23 +48,6 @@ InstanceManager &InstanceManager::instance() return s_Instance; } -void InstanceManager::updateRegistryKey() -{ - const QString OldOrganization = "Tannin"; - const QString OldApplication = "Mod Organizer"; - const QString OldInstanceValue = "CurrentInstance"; - - const QString OldRootKey = "Software\\" + OldOrganization; - - if (env::registryValueExists(OldRootKey + "\\" + OldApplication, OldInstanceValue)) { - QSettings old(OldOrganization, OldApplication); - setCurrentInstance(old.value(OldInstanceValue).toString()); - old.remove(OldInstanceValue); - } - - env::deleteRegistryKeyIfEmpty(OldRootKey); -} - void InstanceManager::overrideInstance(const QString& instanceName) { m_overrideInstanceName = instanceName; @@ -89,7 +65,7 @@ QString InstanceManager::currentInstance() const if (m_overrideInstance) return m_overrideInstanceName; else - return m_AppSettings.value(InstanceValue, "").toString(); + return GlobalSettings::currentInstance(); } void InstanceManager::clearCurrentInstance() @@ -101,7 +77,7 @@ void InstanceManager::clearCurrentInstance() void InstanceManager::setCurrentInstance(const QString &name) { - m_AppSettings.setValue(InstanceValue, name); + GlobalSettings::setCurrentInstance(name); } bool InstanceManager::deleteLocalInstance(const QString& instanceId) const diff --git a/src/instancemanager.h b/src/instancemanager.h index 7467e2fa..f53543df 100644 --- a/src/instancemanager.h +++ b/src/instancemanager.h @@ -75,11 +75,7 @@ private: bool portableInstall() const; bool portableInstallIsLocked() const; - void updateRegistryKey(); - private: - - QSettings m_AppSettings; bool m_Reset {false}; bool m_overrideInstance{false}; QString m_overrideInstanceName; diff --git a/src/settings.cpp b/src/settings.cpp index 661cf429..76378af9 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -2165,3 +2165,39 @@ void DiagnosticsSettings::setSpawnDelay(std::chrono::seconds t) { set(m_Settings, "Settings", "spawn_delay", t.count()); } + + +void GlobalSettings::updateRegistryKey() +{ + const QString OldOrganization = "Tannin"; + const QString OldApplication = "Mod Organizer"; + const QString OldInstanceValue = "CurrentInstance"; + + const QString OldRootKey = "Software\\" + OldOrganization; + + if (env::registryValueExists(OldRootKey + "\\" + OldApplication, OldInstanceValue)) { + QSettings old(OldOrganization, OldApplication); + setCurrentInstance(old.value(OldInstanceValue).toString()); + old.remove(OldInstanceValue); + } + + env::deleteRegistryKeyIfEmpty(OldRootKey); +} + +QString GlobalSettings::currentInstance() +{ + return settings().value("CurrentInstance", "").toString(); +} + +void GlobalSettings::setCurrentInstance(const QString& s) +{ + settings().setValue("CurrentInstance", s); +} + +QSettings GlobalSettings::settings() +{ + const QString Organization = "Mod Organizer Team"; + const QString Application = "Mod Organizer"; + + return QSettings(Organization, Application); +} diff --git a/src/settings.h b/src/settings.h index c723faec..54f1bb5b 100644 --- a/src/settings.h +++ b/src/settings.h @@ -827,6 +827,22 @@ private: }; +// manages global settings in the registry +// +class GlobalSettings +{ +public: + // migrates the old settings from the Tannin key to the new one + static void updateRegistryKey(); + + static QString currentInstance(); + static void setCurrentInstance(const QString& s); + +private: + static QSettings settings(); +}; + + // helper class that calls restoreGeometry() in the constructor and // saveGeometry() in the destructor // -- cgit v1.3.1 From a97638249de0a9a4c17dc28805c35df489a64e26 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sun, 26 Jul 2020 21:07:13 -0400 Subject: moved switch to instance to InstanceManager double-click an instance to switch to it (disabled for now, not sure I want that) --- src/createinstancedialog.cpp | 3 +-- src/instancemanager.cpp | 7 +++++++ src/instancemanager.h | 4 ++++ src/instancemanagerdialog.cpp | 27 ++++++++++++++++++++++++--- src/instancemanagerdialog.h | 4 ++++ 5 files changed, 40 insertions(+), 5 deletions(-) (limited to 'src/instancemanager.h') diff --git a/src/createinstancedialog.cpp b/src/createinstancedialog.cpp index 2edfee88..75ccc774 100644 --- a/src/createinstancedialog.cpp +++ b/src/createinstancedialog.cpp @@ -300,8 +300,7 @@ void CreateInstanceDialog::finish() } if (ui->launch->isChecked()) { - InstanceManager::instance().setCurrentInstance(ci.instanceName); - ExitModOrganizer(Exit::Restart); + InstanceManager::instance().switchToInstance(ci.instanceName); } else { close(); } diff --git a/src/instancemanager.cpp b/src/instancemanager.cpp index bd35cb47..2c8d8fb3 100644 --- a/src/instancemanager.cpp +++ b/src/instancemanager.cpp @@ -23,6 +23,7 @@ along with Mod Organizer. If not, see . #include "settings.h" #include "shared/appconfig.h" #include "plugincontainer.h" +#include "shared/util.h" #include #include #include @@ -75,6 +76,12 @@ void InstanceManager::clearCurrentInstance() m_overrideInstance = false; } +void InstanceManager::switchToInstance(const QString& instanceName) +{ + setCurrentInstance(instanceName); + ExitModOrganizer(Exit::Restart); +} + void InstanceManager::setCurrentInstance(const QString &name) { GlobalSettings::setCurrentInstance(name); diff --git a/src/instancemanager.h b/src/instancemanager.h index f53543df..f038678a 100644 --- a/src/instancemanager.h +++ b/src/instancemanager.h @@ -34,6 +34,10 @@ class InstanceManager public: static InstanceManager &instance(); + // restarts MO + // + void switchToInstance(const QString& instanceName); + void overrideInstance(const QString& instanceName); void overrideProfile(const QString& profileName); diff --git a/src/instancemanagerdialog.cpp b/src/instancemanagerdialog.cpp index 26e8eae1..a038d3c2 100644 --- a/src/instancemanagerdialog.cpp +++ b/src/instancemanagerdialog.cpp @@ -92,6 +92,7 @@ InstanceManagerDialog::InstanceManagerDialog( connect(ui->createNew, &QPushButton::clicked, [&]{ createNew(); }); connect(ui->list, &QListWidget::itemSelectionChanged, [&]{ onSelection(); }); + //connect(ui->list, &QListWidget::itemActivated, [&]{ openSelectedInstance(); }); } InstanceManagerDialog::~InstanceManagerDialog() = default; @@ -106,14 +107,24 @@ void InstanceManagerDialog::select(std::size_t i) fill(*ii); } +void InstanceManagerDialog::openSelectedInstance() +{ + const auto i = singleSelection(); + if (i == NoSelection) { + return; + } + + InstanceManager::instance().switchToInstance(m_instances[i]->name()); +} + void InstanceManagerDialog::onSelection() { - const auto sel = ui->list->selectionModel()->selectedIndexes(); - if (sel.size() != 1) { + const auto i = singleSelection(); + if (i == NoSelection) { return; } - select(static_cast(sel[0].row())); + select(i); } void InstanceManagerDialog::createNew() @@ -122,6 +133,16 @@ void InstanceManagerDialog::createNew() dlg.exec(); } +std::size_t InstanceManagerDialog::singleSelection() const +{ + const auto sel = ui->list->selectionModel()->selectedIndexes(); + if (sel.size() != 1) { + return NoSelection; + } + + return static_cast(sel[0].row()); +} + void InstanceManagerDialog::fill(const InstanceInfo& ii) { ui->name->setText(ii.name()); diff --git a/src/instancemanagerdialog.h b/src/instancemanagerdialog.h index c3e947dd..6dbf015f 100644 --- a/src/instancemanagerdialog.h +++ b/src/instancemanagerdialog.h @@ -19,8 +19,11 @@ public: ~InstanceManagerDialog(); void select(std::size_t i); + void openSelectedInstance(); private: + static const std::size_t NoSelection = -1; + std::unique_ptr ui; const PluginContainer& m_pc; std::vector> m_instances; @@ -28,6 +31,7 @@ private: void onSelection(); void createNew(); + std::size_t singleSelection() const; void fill(const InstanceInfo& ii); }; -- cgit v1.3.1 From 6f21f3a19fbd82bc43c97dcab877959220ce2f22 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Thu, 30 Jul 2020 01:17:30 -0400 Subject: disable portable instance button if one exists --- src/createinstancedialog.ui | 18 ++++++++++++++---- src/createinstancedialogpages.cpp | 12 ++++++++++-- src/instancemanager.cpp | 5 ++--- src/instancemanager.h | 2 +- 4 files changed, 27 insertions(+), 10 deletions(-) (limited to 'src/instancemanager.h') diff --git a/src/createinstancedialog.ui b/src/createinstancedialog.ui index ebaef84d..a3c3db92 100644 --- a/src/createinstancedialog.ui +++ b/src/createinstancedialog.ui @@ -181,6 +181,16 @@ + + + + A portable instance already exists. + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + @@ -266,8 +276,8 @@ 0 0 - 63 - 16 + 455 + 286 @@ -402,8 +412,8 @@ 0 0 - 63 - 16 + 455 + 278 diff --git a/src/createinstancedialogpages.cpp b/src/createinstancedialogpages.cpp index b2e3612f..127243c6 100644 --- a/src/createinstancedialogpages.cpp +++ b/src/createinstancedialogpages.cpp @@ -171,11 +171,19 @@ TypePage::TypePage(CreateInstanceDialog& dlg) { ui->createGlobal->setDescription( ui->createGlobal->description() - .arg(InstanceManager::instance().instancesPath())); + .arg(InstanceManager::instance().instancesPath())); ui->createPortable->setDescription( ui->createPortable->description() - .arg(InstanceManager::portablePath())); + .arg(InstanceManager::portablePath())); + + if (InstanceManager::instance().portableInstanceExists()) { + ui->createPortable->setEnabled(false); + ui->portableExistsLabel->setVisible(true); + } else { + ui->createPortable->setEnabled(true); + ui->portableExistsLabel->setVisible(false); + } QObject::connect( ui->createGlobal, &QAbstractButton::clicked, [&]{ global(); }); diff --git a/src/instancemanager.cpp b/src/instancemanager.cpp index 2c8d8fb3..b6798fa9 100644 --- a/src/instancemanager.cpp +++ b/src/instancemanager.cpp @@ -344,7 +344,7 @@ QString InstanceManager::portablePath() return qApp->applicationDirPath(); } -bool InstanceManager::portableInstall() const +bool InstanceManager::portableInstanceExists() const { return QFile::exists(qApp->applicationDirPath() + "/" + QString::fromStdWString(AppConfig::iniFileName())); @@ -357,7 +357,6 @@ bool InstanceManager::portableInstallIsLocked() const QString::fromStdWString(AppConfig::portableLockFileName())); } - bool InstanceManager::allowedToChangeInstance() const { return !portableInstallIsLocked(); @@ -388,7 +387,7 @@ QString InstanceManager::determineDataPath() { instanceId.clear(); } - if (instanceId.isEmpty() && !m_Reset && (m_overrideInstance || portableInstall())) + if (instanceId.isEmpty() && !m_Reset && (m_overrideInstance || portableInstanceExists())) { // startup, apparently using portable mode before return qApp->applicationDirPath(); diff --git a/src/instancemanager.h b/src/instancemanager.h index f038678a..9250ffe9 100644 --- a/src/instancemanager.h +++ b/src/instancemanager.h @@ -54,6 +54,7 @@ public: bool allowedToChangeInstance() const; static bool isPortablePath(const QString& dataPath); static QString portablePath(); + bool portableInstanceExists() const; QString instancesPath() const; QStringList instanceNames() const; @@ -76,7 +77,6 @@ private: QString chooseInstance(const QStringList &instanceList) const; void createDataPath(const QString &dataPath) const; - bool portableInstall() const; bool portableInstallIsLocked() const; private: -- cgit v1.3.1 From ea4485857c09fd3ab6879966e7377d3c56951a85 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Thu, 30 Jul 2020 04:36:12 -0400 Subject: filter, explore buttons, instance rename --- src/createinstancedialog.ui | 21 +++++- src/createinstancedialogpages.cpp | 62 ++------------- src/instancemanager.cpp | 9 +++ src/instancemanager.h | 1 + src/instancemanagerdialog.cpp | 154 ++++++++++++++++++++++++++++++++++++-- src/instancemanagerdialog.h | 12 ++- src/instancemanagerdialog.ui | 27 ++++++- 7 files changed, 221 insertions(+), 65 deletions(-) (limited to 'src/instancemanager.h') diff --git a/src/createinstancedialog.ui b/src/createinstancedialog.ui index a3c3db92..b7f4f502 100644 --- a/src/createinstancedialog.ui +++ b/src/createinstancedialog.ui @@ -77,9 +77,12 @@ - + - Never show this again + <html><head/><body><p><a href="https://github.com/ModOrganizer2/modorganizer/wiki/Instance">More information</a></p></body></html> + + + true @@ -96,6 +99,13 @@ + + + + Never show this page again + + + @@ -1202,6 +1212,13 @@ + + + LinkLabel + QLabel +
linklabel.h
+
+
diff --git a/src/createinstancedialogpages.cpp b/src/createinstancedialogpages.cpp index 127243c6..8d304635 100644 --- a/src/createinstancedialogpages.cpp +++ b/src/createinstancedialogpages.cpp @@ -21,50 +21,6 @@ QString makeDefaultPath(const std::wstring& dir) QString::fromStdWString(dir))); } -QString sanitizeFileName(const QString& name) -{ - QString new_name = name; - - // Restrict the allowed characters - new_name = new_name.remove(QRegExp("[^A-Za-z0-9 _=+;!@#$%^'\\-\\.\\[\\]\\{\\}\\(\\)]")); - - // Don't end in spaces and periods - new_name = new_name.remove(QRegExp("\\.*$")); - new_name = new_name.remove(QRegExp(" *$")); - - // Recurse until stuff stops changing - if (new_name != name) { - return sanitizeFileName(new_name); - } - - return new_name; -} - -// same thing as above, but allows path separators and colons -// -QString sanitizePath(const QString& path) -{ - QString new_name = path; - - // Restrict the allowed characters - new_name = new_name.remove(QRegExp("[^\\\\\\/A-Za-z0-9 _=+;!@#$%^:'\\-\\.\\[\\]\\{\\}\\(\\)]")); - - // Don't end in spaces and periods - new_name = new_name.remove(QRegExp("\\.*$")); - new_name = new_name.remove(QRegExp(" *$")); - - // Recurse until stuff stops changing - if (new_name != path) { - return sanitizeFileName(new_name); - } - - return new_name; -} - -void setPossiblePlaceholder( - QLabel* label, const QString& s, const QString& arg) -{ -} PlaceholderLabel::PlaceholderLabel(QLabel* label) @@ -762,7 +718,7 @@ QString NamePage::selectedInstanceName() const } const auto text = ui->instanceName->text().trimmed(); - return sanitizeFileName(text); + return InstanceManager::instance().sanitizeInstanceName(text); } void NamePage::onChanged() @@ -790,12 +746,10 @@ bool NamePage::checkName(QString parentDir, QString name) if (name.isEmpty()) { empty = true; } else { - const QString sanitized = sanitizeFileName(name); - - if (name != sanitized) { - invalid = true; - } else { + if (InstanceManager::instance().validInstanceName(name)) { exists = QDir(parentDir).exists(name); + } else { + invalid = true; } } @@ -963,11 +917,9 @@ bool PathsPage::checkPath( if (path.isEmpty()) { empty = true; } else { - const QString sanitized = sanitizePath(path); + const QDir d(path); - if (path != sanitized) { - invalid = true; - } else { + if (InstanceManager::instance().validInstanceName(d.dirName())) { if (m_dlg.instanceType() == CreateInstanceDialog::Portable) { // the default data path for a portable instance is the application // directory, so it's not an error if it exists @@ -977,6 +929,8 @@ bool PathsPage::checkPath( } else { exists = QDir(path).exists(); } + } else { + invalid = true; } } diff --git a/src/instancemanager.cpp b/src/instancemanager.cpp index b6798fa9..111f948b 100644 --- a/src/instancemanager.cpp +++ b/src/instancemanager.cpp @@ -676,3 +676,12 @@ QString InstanceManager::sanitizeInstanceName(const QString &name) const } return new_name; } + +bool InstanceManager::validInstanceName(const QString& instanceName) const +{ + if (instanceName.isEmpty()) { + return false; + } + + return (instanceName == sanitizeInstanceName(instanceName)); +} diff --git a/src/instancemanager.h b/src/instancemanager.h index 9250ffe9..33e115a7 100644 --- a/src/instancemanager.h +++ b/src/instancemanager.h @@ -63,6 +63,7 @@ public: QString sanitizeInstanceName(const QString &name) const; QString makeUniqueName(const QString& instanceName) const; bool instanceExists(const QString& instanceName) const; + bool validInstanceName(const QString& instanceName) const; QString instancePath(const QString& instanceName) const; private: diff --git a/src/instancemanagerdialog.cpp b/src/instancemanagerdialog.cpp index a038d3c2..aa6bdb04 100644 --- a/src/instancemanagerdialog.cpp +++ b/src/instancemanagerdialog.cpp @@ -8,8 +8,12 @@ #include "shared/appconfig.h" #include +namespace shell = MOBase::shell; + void openInstanceManager(PluginContainer& pc, QWidget* parent) { + //CreateInstanceDialog dlg(pc, parent); + //dlg.exec(); InstanceManagerDialog dlg(pc, parent); dlg.exec(); } @@ -77,12 +81,20 @@ InstanceManagerDialog::InstanceManagerDialog( ui->splitter->setStretchFactor(0, 0); ui->splitter->setStretchFactor(1, 1); + auto* model = new QStandardItemModel; + ui->list->setModel(model); + + m_filter.setEdit(ui->filter); + m_filter.setList(ui->list); + m_filter.setUpdateDelay(false); + m_filter.setFilteredBorder(false); + auto& m = InstanceManager::instance(); for (auto&& d : m.instancePaths()) { auto ii = std::make_unique(d); - ui->list->addItem(ii->name()); + model->appendRow(new QStandardItem(ii->name())); m_instances.push_back(std::move(ii)); } @@ -91,8 +103,12 @@ InstanceManagerDialog::InstanceManagerDialog( } connect(ui->createNew, &QPushButton::clicked, [&]{ createNew(); }); - connect(ui->list, &QListWidget::itemSelectionChanged, [&]{ onSelection(); }); + connect(ui->list->selectionModel(), &QItemSelectionModel::selectionChanged, [&]{ onSelection(); }); //connect(ui->list, &QListWidget::itemActivated, [&]{ openSelectedInstance(); }); + connect(ui->rename, &QPushButton::clicked, [&]{ rename(); }); + connect(ui->exploreLocation, &QPushButton::clicked, [&]{ exploreLocation(); }); + connect(ui->exploreBaseDirectory, &QPushButton::clicked, [&]{ exploreBaseDirectory(); }); + connect(ui->exploreGame, &QPushButton::clicked, [&]{ exploreGame(); }); } InstanceManagerDialog::~InstanceManagerDialog() = default; @@ -105,11 +121,15 @@ void InstanceManagerDialog::select(std::size_t i) const auto& ii = m_instances[i]; fill(*ii); + + ui->list->selectionModel()->select( + m_filter.mapFromSource(m_filter.sourceModel()->index(i, 0)), + QItemSelectionModel::ClearAndSelect); } void InstanceManagerDialog::openSelectedInstance() { - const auto i = singleSelection(); + const auto i = singleSelectionIndex(); if (i == NoSelection) { return; } @@ -117,9 +137,107 @@ void InstanceManagerDialog::openSelectedInstance() InstanceManager::instance().switchToInstance(m_instances[i]->name()); } +void InstanceManagerDialog::rename() +{ + auto* i = singleSelection(); + if (!i) { + return; + } + + auto& m = InstanceManager::instance(); + if (m.currentInstance() == i->name()) { + QMessageBox::information(this, + tr("Rename instance"), tr("The active instance cannot be renamed")); + return; + } + + QDialog dlg(this); + dlg.setWindowTitle(tr("Rename instance")); + + auto* ly = new QVBoxLayout(&dlg); + + auto* bb = new QDialogButtonBox( + QDialogButtonBox::Cancel | QDialogButtonBox::Ok); + + auto* text = new QLineEdit(i->name()); + text->selectAll(); + + auto* error = new QLabel; + + ly->addWidget(new QLabel(tr("Instance name"))); + ly->addWidget(text); + ly->addWidget(error); + ly->addStretch(); + ly->addWidget(bb); + + connect(text, &QLineEdit::textChanged, [&] { + bool okay = false; + + if (!m.validInstanceName(text->text())) { + error->setText(tr("The instance name must be a valid folder name.")); + } else { + const auto name = m.sanitizeInstanceName(text->text()); + + if ((name != i->name()) && m.instanceExists(text->text())) { + error->setText(tr("An instance with this name already exists.")); + } else { + okay = true; + } + } + + error->setVisible(!okay); + bb->button(QDialogButtonBox::Ok)->setEnabled(okay); + }); + + connect(bb, &QDialogButtonBox::accepted, [&]{ dlg.accept(); }); + connect(bb, &QDialogButtonBox::rejected, [&]{ dlg.reject(); }); + + dlg.resize({400, 120}); + if (dlg.exec() != QDialog::Accepted) { + return; + } + + + const QString newName = m.sanitizeInstanceName(text->text()); + const QString src = QDir::toNativeSeparators(i->location()); + const QString dest = QDir::toNativeSeparators( + QFileInfo(i->location()).dir().path() + "/" + newName); + + const auto r = shell::Rename(src, dest, false); + if (!r) { + QMessageBox::critical( + this, tr("Error"), + tr("Failed to rename \"%1\" to \"%2\": %3") + .arg(src).arg(dest).arg(r.toString())); + + return; + } +} + +void InstanceManagerDialog::exploreLocation() +{ + if (const auto* i=singleSelection()) { + shell::Explore(i->location()); + } +} + +void InstanceManagerDialog::exploreBaseDirectory() +{ + if (const auto* i=singleSelection()) { + shell::Explore(i->baseDirectory()); + } +} + +void InstanceManagerDialog::exploreGame() +{ + if (const auto* i=singleSelection()) { + shell::Explore(i->gamePath()); + } +} + void InstanceManagerDialog::onSelection() { - const auto i = singleSelection(); + const auto i = singleSelectionIndex(); if (i == NoSelection) { return; } @@ -133,14 +251,36 @@ void InstanceManagerDialog::createNew() dlg.exec(); } -std::size_t InstanceManagerDialog::singleSelection() const +std::size_t InstanceManagerDialog::singleSelectionIndex() const { - const auto sel = ui->list->selectionModel()->selectedIndexes(); + const auto sel = m_filter.mapSelectionToSource( + ui->list->selectionModel()->selection()); + if (sel.size() != 1) { return NoSelection; } - return static_cast(sel[0].row()); + return static_cast(sel.indexes()[0].row()); +} + +InstanceInfo* InstanceManagerDialog::singleSelection() +{ + const auto i = singleSelectionIndex(); + if (i == NoSelection) { + return nullptr; + } + + return m_instances[i].get(); +} + +const InstanceInfo* InstanceManagerDialog::singleSelection() const +{ + const auto i = singleSelectionIndex(); + if (i == NoSelection) { + return nullptr; + } + + return m_instances[i].get(); } void InstanceManagerDialog::fill(const InstanceInfo& ii) diff --git a/src/instancemanagerdialog.h b/src/instancemanagerdialog.h index 6dbf015f..529a99b8 100644 --- a/src/instancemanagerdialog.h +++ b/src/instancemanagerdialog.h @@ -1,6 +1,7 @@ #ifndef MODORGANIZER_INSTANCEMANAGERDIALOG_INCLUDED #define MODORGANIZER_INSTANCEMANAGERDIALOG_INCLUDED +#include #include namespace Ui { class InstanceManagerDialog; }; @@ -21,17 +22,26 @@ public: void select(std::size_t i); void openSelectedInstance(); + void rename(); + void exploreLocation(); + void exploreBaseDirectory(); + void exploreGame(); + private: static const std::size_t NoSelection = -1; std::unique_ptr ui; const PluginContainer& m_pc; std::vector> m_instances; + MOBase::FilterWidget m_filter; void onSelection(); void createNew(); - std::size_t singleSelection() const; + std::size_t singleSelectionIndex() const; + InstanceInfo* singleSelection(); + const InstanceInfo* singleSelection() const; + void fill(const InstanceInfo& ii); }; diff --git a/src/instancemanagerdialog.ui b/src/instancemanagerdialog.ui index 146b5fb5..08894ec1 100644 --- a/src/instancemanagerdialog.ui +++ b/src/instancemanagerdialog.ui @@ -103,7 +103,32 @@ 0 - + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Filter + + + + + -- cgit v1.3.1 From b10436d60f7db3eedd838cbfad93cb41ddf1fd86 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 31 Oct 2020 17:44:25 -0400 Subject: game icons in the instance list --- src/instancemanager.cpp | 59 +++++++++++++++++++++++++++++++++++++++++++ src/instancemanager.h | 3 +++ src/instancemanagerdialog.cpp | 19 +++++++++++++- src/settings.cpp | 5 ++++ src/settings.h | 4 +++ 5 files changed, 89 insertions(+), 1 deletion(-) (limited to 'src/instancemanager.h') diff --git a/src/instancemanager.cpp b/src/instancemanager.cpp index 111f948b..4ad099ed 100644 --- a/src/instancemanager.cpp +++ b/src/instancemanager.cpp @@ -637,6 +637,65 @@ MOBase::IPluginGame* InstanceManager::determineCurrentGame( return nullptr; } +const MOBase::IPluginGame* InstanceManager::gamePluginForDirectory( + const QDir& instanceDir, const PluginContainer& plugins) const +{ + const QString ini = + QDir(instanceDir).filePath(QString::fromStdWString(AppConfig::iniFileName())); + + + Settings s(ini); + + if (s.iniStatus() != QSettings::NoError) + { + log::error("failed to load settings from {}", ini); + return nullptr; + } + + const auto instanceGameName = s.game().name(); + + if (instanceGameName && !instanceGameName->isEmpty()) + { + for (const IPluginGame* game : plugins.plugins()) { + if (instanceGameName->compare(game->gameName(), Qt::CaseInsensitive) == 0) { + return game; + } + } + + log::error( + "no plugin reports game name '{}' found in ini {}", + *instanceGameName, ini); + } + else + { + log::error("no game name found in ini {}", ini); + } + + + log::error("falling back on looksValid check"); + + const auto gameDir = s.game().directory(); + + if (gameDir && !gameDir->isEmpty()) + { + for (const IPluginGame* game : plugins.plugins()) { + if (game->looksValid(*gameDir)) { + return game; + } + } + + log::error( + "no plugins appear to support game directory '{}' from ini {}", + *gameDir, ini); + } + else + { + log::error("no game directory found in ini {}", ini); + } + + return nullptr; +} + QString InstanceManager::makeUniqueName(const QString& instanceName) const { const QString sanitized = sanitizeInstanceName(instanceName); diff --git a/src/instancemanager.h b/src/instancemanager.h index 33e115a7..c2d1e0f4 100644 --- a/src/instancemanager.h +++ b/src/instancemanager.h @@ -47,6 +47,9 @@ public: MOBase::IPluginGame* determineCurrentGame( const QString& moPath, Settings& settings, const PluginContainer &plugins); + const MOBase::IPluginGame* gamePluginForDirectory( + const QDir& dir, const PluginContainer& plugins) const; + void clearCurrentInstance(); QString currentInstance() const; void setCurrentInstance(const QString &name); diff --git a/src/instancemanagerdialog.cpp b/src/instancemanagerdialog.cpp index 7b8522ee..545b5c71 100644 --- a/src/instancemanagerdialog.cpp +++ b/src/instancemanagerdialog.cpp @@ -112,6 +112,19 @@ public: return makeIniFile(m_dir); } + QIcon icon(const PluginContainer& plugins) const + { + const auto* game = InstanceManager::instance().gamePluginForDirectory( + m_dir, plugins); + + if (game) + return game->gameIcon(); + + QPixmap empty(32, 32); + empty.fill(QColor(0, 0, 0, 0)); + return QIcon(empty); + } + bool isPortable() const { return m_portable; @@ -376,7 +389,11 @@ void InstanceManagerDialog::updateList() for (std::size_t i=0; iappendRow(new QStandardItem(ii.name())); + + auto* item = new QStandardItem(ii.name()); + item->setIcon(ii.icon(m_pc)); + + m_model->appendRow(item); if (&ii == prevSel) { sel = i; diff --git a/src/settings.cpp b/src/settings.cpp index 593d66bf..99b41e1f 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -465,6 +465,11 @@ QSettings::Status Settings::sync() const return m_Settings.status(); } +QSettings::Status Settings::iniStatus() const +{ + return m_Settings.status(); +} + void Settings::dump() const { static const QStringList ignore({ diff --git a/src/settings.h b/src/settings.h index f71949d2..c8325ba2 100644 --- a/src/settings.h +++ b/src/settings.h @@ -778,6 +778,10 @@ public: // QSettings::Status sync() const; + // last status of the ini file + // + QSettings::Status iniStatus() const; + void dump() const; public slots: -- cgit v1.3.1 From 1d97d914f1de0404b5b4db1bfdeff35ed94ea422 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 3 Nov 2020 10:57:26 -0500 Subject: InstanceManager now returns new Instance struct instead of instance name moved most of the figuring out of instance parameters from InstanceManager to Instance, separated all the ui from it and put it in main.cpp added ways to show single pages in the create instance dialog so they can be used when info is missing --- src/createinstancedialog.cpp | 69 +++- src/createinstancedialog.h | 37 +- src/createinstancedialogpages.cpp | 67 ++-- src/createinstancedialogpages.h | 32 +- src/envshortcut.cpp | 2 +- src/instancemanager.cpp | 707 ++++++++++++++------------------------ src/instancemanager.h | 66 ++-- src/instancemanagerdialog.cpp | 50 ++- src/instancemanagerdialog.h | 3 + src/main.cpp | 265 ++++++++++---- src/organizercore.cpp | 8 +- src/processrunner.cpp | 13 +- src/shared/appconfig.inc | 1 + src/statusbar.cpp | 7 +- 14 files changed, 697 insertions(+), 630 deletions(-) (limited to 'src/instancemanager.h') diff --git a/src/createinstancedialog.cpp b/src/createinstancedialog.cpp index 0f62fcf6..d67e3451 100644 --- a/src/createinstancedialog.cpp +++ b/src/createinstancedialog.cpp @@ -13,7 +13,7 @@ using namespace MOBase; CreateInstanceDialog::CreateInstanceDialog( const PluginContainer& pc, Settings* s, QWidget *parent) : QDialog(parent), ui(new Ui::CreateInstanceDialog), m_pc(pc), m_settings(s), - m_switching(false) + m_switching(false), m_singlePage(false) { using namespace cid; @@ -23,7 +23,7 @@ CreateInstanceDialog::CreateInstanceDialog( m_pages.push_back(std::make_unique(*this)); m_pages.push_back(std::make_unique(*this)); m_pages.push_back(std::make_unique(*this)); - m_pages.push_back(std::make_unique(*this)); + m_pages.push_back(std::make_unique(*this)); m_pages.push_back(std::make_unique(*this)); m_pages.push_back(std::make_unique(*this)); m_pages.push_back(std::make_unique(*this)); @@ -32,6 +32,12 @@ CreateInstanceDialog::CreateInstanceDialog( ui->pages->setCurrentIndex(0); ui->launch->setChecked(true); + if (!m_settings) + { + // first run of MO, there are no instances yet, force launch + ui->launch->setEnabled(false); + } + if (m_pages[0]->skip()) { next(); } @@ -77,7 +83,12 @@ void CreateInstanceDialog::next() const auto last = isOnLastPage(); if (last) { - finish(); + if (m_singlePage) { + // just close the dialog + accept(); + } else { + finish(); + } } else { changePage(+1); } @@ -88,6 +99,15 @@ void CreateInstanceDialog::back() changePage(-1); } +void CreateInstanceDialog::setSinglePageImpl() +{ + m_singlePage = true; + + if (m_pages[ui->pages->currentIndex()]->skip()) { + next(); + } +} + void CreateInstanceDialog::changePage(int d) { std::size_t i = static_cast(ui->pages->currentIndex()); @@ -247,8 +267,8 @@ void CreateInstanceDialog::finish() s.game().setName(ci.game->gameName()); s.game().setDirectory(ci.gameLocation); - if (!ci.gameEdition.isEmpty()) { - s.game().setEdition(ci.gameEdition); + if (!ci.gameVariant.isEmpty()) { + s.game().setEdition(ci.gameVariant); } if (ci.type == Global) { @@ -307,7 +327,8 @@ void CreateInstanceDialog::finish() } if (ui->launch->isChecked()) { - InstanceManager::instance().switchToInstance(ci.instanceName); + InstanceManager::instance().setCurrentInstance(ci.instanceName); + ExitModOrganizer(Exit::Restart); m_switching = true; } else { accept(); @@ -345,8 +366,8 @@ void CreateInstanceDialog::updateNavigation() const auto i = ui->pages->currentIndex(); const auto last = isOnLastPage(); - ui->next->setEnabled(m_pages[i]->ready()); - ui->back->setEnabled(i > 0); + ui->next->setEnabled(canNext()); + ui->back->setEnabled(canBack()); if (last) { ui->next->setText(tr("Finish")); @@ -355,6 +376,32 @@ void CreateInstanceDialog::updateNavigation() } } +bool CreateInstanceDialog::canNext() const +{ + const auto i = ui->pages->currentIndex(); + return m_pages[i]->ready(); +} + +bool CreateInstanceDialog::canBack() const +{ + auto i = ui->pages->currentIndex(); + + for (;;) + { + if (i == 0) { + break; + } + + --i; + + if (!m_pages[i]->skip()) { + return true; + } + } + + return false; +} + CreateInstanceDialog::Types CreateInstanceDialog::instanceType() const { return getSelected(&cid::Page::selectedInstanceType); @@ -370,9 +417,9 @@ QString CreateInstanceDialog::gameLocation() const return getSelected(&cid::Page::selectedGameLocation); } -QString CreateInstanceDialog::gameEdition() const +QString CreateInstanceDialog::gameVariant() const { - return getSelected(&cid::Page::selectedGameEdition); + return getSelected(&cid::Page::selectedGameVariant); } QString CreateInstanceDialog::instanceName() const @@ -433,7 +480,7 @@ CreateInstanceDialog::CreationInfo CreateInstanceDialog::creationInfo() const ci.type = getSelected(&cid::Page::selectedInstanceType); ci.game = getSelected(&cid::Page::selectedGame); ci.gameLocation = getSelected(&cid::Page::selectedGameLocation); - ci.gameEdition = getSelected(&cid::Page::selectedGameEdition); + ci.gameVariant = getSelected(&cid::Page::selectedGameVariant); ci.instanceName = getSelected(&cid::Page::selectedInstanceName); ci.paths = getSelected(&cid::Page::selectedPaths); ci.dataPath = dataPath(); diff --git a/src/createinstancedialog.h b/src/createinstancedialog.h index 0841ef29..6947f2e2 100644 --- a/src/createinstancedialog.h +++ b/src/createinstancedialog.h @@ -39,7 +39,7 @@ public: Types type; MOBase::IPluginGame* game; QString gameLocation; - QString gameEdition; + QString gameVariant; QString instanceName; QString dataPath; QString iniPath; @@ -57,6 +57,32 @@ public: const PluginContainer& pluginContainer(); Settings* settings(); + template + void setSinglePage() + { + for (auto&& p : m_pages) { + if (auto* tp=dynamic_cast(p.get())) { + tp->setSkip(false); + } else { + p->setSkip(true); + } + } + + setSinglePageImpl(); + } + + template + Page* getPage() + { + for (auto&& p : m_pages) { + if (auto* tp=dynamic_cast(p.get())) { + return tp; + } + } + + return nullptr; + } + void next(); void back(); void selectPage(std::size_t i); @@ -69,7 +95,7 @@ public: Types instanceType() const; MOBase::IPluginGame* game() const; QString gameLocation() const; - QString gameEdition() const; + QString gameVariant() const; QString instanceName() const; QString dataPath() const; Paths paths() const; @@ -84,6 +110,10 @@ private: std::vector> m_pages; QString m_originalNext; bool m_switching; + bool m_singlePage; + + + void setSinglePageImpl(); template T getSelected(T (cid::Page::*mf)() const) const @@ -100,6 +130,9 @@ private: void logCreation(const QString& s); void logCreation(const std::wstring& s); + + bool canNext() const; + bool canBack() const; }; #endif // MODORGANIZER_CREATEINSTANCEDIALOG_INCLUDED diff --git a/src/createinstancedialogpages.cpp b/src/createinstancedialogpages.cpp index d809079d..00d49b99 100644 --- a/src/createinstancedialogpages.cpp +++ b/src/createinstancedialogpages.cpp @@ -43,7 +43,7 @@ void PlaceholderLabel::setVisible(bool b) Page::Page(CreateInstanceDialog& dlg) - : ui(dlg.getUI()), m_dlg(dlg), m_pc(dlg.pluginContainer()) + : ui(dlg.getUI()), m_dlg(dlg), m_pc(dlg.pluginContainer()), m_skip(false) { } @@ -54,7 +54,11 @@ bool Page::ready() const bool Page::skip() const { - // no-op + return m_skip || doSkip(); +} + +bool Page::doSkip() const +{ return false; } @@ -63,6 +67,11 @@ void Page::activated() // no-op } +void Page::setSkip(bool b) +{ + m_skip = b; +} + void Page::updateNavigation() { m_dlg.updateNavigation(); @@ -92,7 +101,7 @@ QString Page::selectedGameLocation() const return {}; } -QString Page::selectedGameEdition() const +QString Page::selectedGameVariant() const { // no-op return {}; @@ -116,7 +125,7 @@ IntroPage::IntroPage(CreateInstanceDialog& dlg) { } -bool IntroPage::skip() const +bool IntroPage::doSkip() const { return GlobalSettings::hideCreateInstanceIntro(); } @@ -223,19 +232,24 @@ QString GamePage::selectedGameLocation() const return QDir::toNativeSeparators(m_selection->dir); } -void GamePage::select(IPluginGame* game) +void GamePage::select(IPluginGame* game, const QString& dir) { Game* checked = findGame(game); if (checked) { if (!checked->installed) { - const auto path = QFileDialog::getExistingDirectory( - &m_dlg, QObject::tr("Find game installation")); - - if (path.isEmpty()) { - checked = nullptr; + if (dir.isEmpty()) { + const auto path = QFileDialog::getExistingDirectory( + &m_dlg, QObject::tr("Find game installation")); + + if (path.isEmpty()) { + checked = nullptr; + } else { + checked = checkInstallation(path, checked); + } } else { - checked = checkInstallation(path, checked); + checked->dir = dir; + checked->installed = true; } } } @@ -417,7 +431,6 @@ void GamePage::fillList() const bool showAll = ui->showAllGames->isChecked(); clearButtons(); - addButton(createCustomButton()); for (auto& g : m_games) { g->button = nullptr; @@ -434,6 +447,8 @@ void GamePage::fillList() createGameButton(g.get()); addButton(g->button); } + + addButton(createCustomButton()); } void GamePage::clearButtons() @@ -584,17 +599,17 @@ IPluginGame* GamePage::confirmOtherGame( } -EditionsPage::EditionsPage(CreateInstanceDialog& dlg) +VariantsPage::VariantsPage(CreateInstanceDialog& dlg) : Page(dlg), m_previousGame(nullptr) { } -bool EditionsPage::ready() const +bool VariantsPage::ready() const { return !m_selection.isEmpty(); } -bool EditionsPage::skip() const +bool VariantsPage::doSkip() const { auto* g = m_dlg.game(); if (!g) { @@ -606,7 +621,7 @@ bool EditionsPage::skip() const return (variants.size() < 2); } -void EditionsPage::activated() +void VariantsPage::activated() { auto* g = m_dlg.game(); @@ -617,7 +632,7 @@ void EditionsPage::activated() } } -void EditionsPage::select(const QString& variant) +void VariantsPage::select(const QString& variant) { for (auto* b : m_buttons) { if (b->text() == variant) { @@ -629,9 +644,13 @@ void EditionsPage::select(const QString& variant) } updateNavigation(); + + if (!m_selection.isEmpty()) { + next(); + } } -QString EditionsPage::selectedGameEdition() const +QString VariantsPage::selectedGameVariant() const { auto* g = m_dlg.game(); if (!g) { @@ -647,7 +666,7 @@ QString EditionsPage::selectedGameEdition() const } } -void EditionsPage::fillList() +void VariantsPage::fillList() { ui->editions->clear(); m_buttons.clear(); @@ -665,7 +684,7 @@ void EditionsPage::fillList() QObject::connect(b, &QAbstractButton::clicked, [v, this] { select(v); - }); + }); ui->editions->addButton(b, QDialogButtonBox::AcceptRole); m_buttons.push_back(b); @@ -687,7 +706,7 @@ bool NamePage::ready() const return m_okay; } -bool NamePage::skip() const +bool NamePage::doSkip() const { return (m_dlg.instanceType() == CreateInstanceDialog::Portable); } @@ -983,7 +1002,7 @@ bool NexusPage::ready() const return true; } -bool NexusPage::skip() const +bool NexusPage::doSkip() const { return m_skip; } @@ -1047,8 +1066,8 @@ QString ConfirmationPage::makeReview() const // game QString name = m_dlg.game()->gameName(); - if (!m_dlg.gameEdition().isEmpty()) { - name += " (" + m_dlg.gameEdition() + ")"; + if (!m_dlg.gameVariant().isEmpty()) { + name += " (" + m_dlg.gameVariant() + ")"; } lines.push_back(QObject::tr("Game: %1").arg(name)); diff --git a/src/createinstancedialogpages.h b/src/createinstancedialogpages.h index 412f2d84..e239c196 100644 --- a/src/createinstancedialogpages.h +++ b/src/createinstancedialogpages.h @@ -37,16 +37,18 @@ public: Page(CreateInstanceDialog& dlg); virtual bool ready() const; - virtual bool skip() const; virtual void activated(); + void setSkip(bool b); + bool skip() const; + void updateNavigation(); void next(); virtual CreateInstanceDialog::Types selectedInstanceType() const; virtual MOBase::IPluginGame* selectedGame() const; virtual QString selectedGameLocation() const; - virtual QString selectedGameEdition() const; + virtual QString selectedGameVariant() const; virtual QString selectedInstanceName() const; virtual CreateInstanceDialog::Paths selectedPaths() const; @@ -54,6 +56,9 @@ protected: Ui::CreateInstanceDialog* ui; CreateInstanceDialog& m_dlg; const PluginContainer& m_pc; + bool m_skip; + + virtual bool doSkip() const; }; @@ -62,7 +67,8 @@ class IntroPage : public Page public: IntroPage(CreateInstanceDialog& dlg); - bool skip() const override; +protected: + bool doSkip() const override; }; @@ -91,7 +97,7 @@ public: MOBase::IPluginGame* selectedGame() const override; QString selectedGameLocation() const override; - void select(MOBase::IPluginGame* game); + void select(MOBase::IPluginGame* game, const QString& dir={}); void selectCustom(); void warnUnrecognized(const QString& path); @@ -133,18 +139,20 @@ private: }; -class EditionsPage : public Page +class VariantsPage : public Page { public: - EditionsPage(CreateInstanceDialog& dlg); + VariantsPage(CreateInstanceDialog& dlg); bool ready() const override; - bool skip() const override; void activated() override; - QString selectedGameEdition() const override; + QString selectedGameVariant() const override; void select(const QString& variant); +protected: + bool doSkip() const override; + private: MOBase::IPluginGame* m_previousGame; std::vector m_buttons; @@ -160,10 +168,12 @@ public: NamePage(CreateInstanceDialog& dlg); bool ready() const override; - bool skip() const override; void activated() override; QString selectedInstanceName() const override; +protected: + bool doSkip() const override; + private: mutable PlaceholderLabel m_label, m_exists, m_invalid; bool m_modified; @@ -212,9 +222,11 @@ public: ~NexusPage(); bool ready() const override; - bool skip() const override; void activated() override; +protected: + bool doSkip() const override; + private: std::unique_ptr m_connectionUI; bool m_skip; diff --git a/src/envshortcut.cpp b/src/envshortcut.cpp index 99495c39..5222665b 100644 --- a/src/envshortcut.cpp +++ b/src/envshortcut.cpp @@ -149,7 +149,7 @@ Shortcut::Shortcut(const Executable& exe) m_target = QFileInfo(qApp->applicationFilePath()).absoluteFilePath(); m_arguments = QString("\"moshortcut://%1:%2\"") - .arg(InstanceManager::instance().currentInstance()) + .arg(InstanceManager::instance().currentInstance()->name()) .arg(exe.title()); m_description = QString("Run %1 with ModOrganizer").arg(exe.title()); diff --git a/src/instancemanager.cpp b/src/instancemanager.cpp index 4ad099ed..c79c5254 100644 --- a/src/instancemanager.cpp +++ b/src/instancemanager.cpp @@ -38,257 +38,320 @@ along with Mod Organizer. If not, see . using namespace MOBase; -InstanceManager::InstanceManager() +Instance::Instance(QDir dir, bool portable, QString profileName) : + m_dir(std::move(dir)), m_portable(portable), m_plugin(nullptr), + m_profile(std::move(profileName)) { - GlobalSettings::updateRegistryKey(); } -InstanceManager &InstanceManager::instance() +QString Instance::name() const { - static InstanceManager s_Instance; - return s_Instance; + if (isPortable()) + return QObject::tr("Portable"); + else + return m_dir.dirName(); } -void InstanceManager::overrideInstance(const QString& instanceName) +QString Instance::gameName() const { - m_overrideInstanceName = instanceName; - m_overrideInstance = true; + return m_gameName; } -void InstanceManager::overrideProfile(const QString& profileName) +QString Instance::gameDirectory() const { - m_overrideProfileName = profileName; - m_overrideProfile = true; + return m_gameDir; } -QString InstanceManager::currentInstance() const +QDir Instance::directory() const { - if (m_overrideInstance) - return m_overrideInstanceName; - else - return GlobalSettings::currentInstance(); + return m_dir; } -void InstanceManager::clearCurrentInstance() +MOBase::IPluginGame* Instance::gamePlugin() const { - setCurrentInstance(""); - m_Reset = true; - m_overrideInstance = false; + return m_plugin; } -void InstanceManager::switchToInstance(const QString& instanceName) +QString Instance::profileName() const { - setCurrentInstance(instanceName); - ExitModOrganizer(Exit::Restart); + return m_profile; } -void InstanceManager::setCurrentInstance(const QString &name) +QString Instance::iniPath() const { - GlobalSettings::setCurrentInstance(name); + return InstanceManager::iniPath(m_dir); } -bool InstanceManager::deleteLocalInstance(const QString& instanceId) const +bool Instance::isPortable() const { - QString dir = instancePath(instanceId); + return m_portable; +} - const auto Recycle = QMessageBox::Save; - const auto Delete = QMessageBox::Yes; - const auto Cancel = QMessageBox::Cancel; +Instance::SetupResults Instance::setup(PluginContainer& plugins) +{ + Settings s(iniPath()); - const auto r = MOBase::TaskDialog() - .title(QObject::tr("Deleting instance folder")) - .main(QObject::tr("This will delete the instance folder.")) - .content(dir) - .icon(QMessageBox::Warning) - .button({QObject::tr("Move the folder to the recycle bin"), Recycle}) - .button({QObject::tr("Delete the folder permanently"), Delete}) - .button({QObject::tr("Cancel"), Cancel}) - .exec(); + if (s.iniStatus() != QSettings::NoError) { + log::error("can't read ini {}", iniPath()); + return SetupResults::BadIni; + } - std::wstring error; + if (m_gameName.isEmpty()) { + if (auto v=s.game().name()) + m_gameName = *v; + } - switch (r) - { - case Recycle: - { - if (MOBase::shellDelete(QStringList(dir), true)) { - return true; - } + if (m_gameDir.isEmpty()) { + if (auto v=s.game().directory()) + m_gameDir = *v; + } - const auto e = GetLastError(); - error = formatSystemMessage(e); - log::warn("failed to move to trash '{}', {}", dir, error); + const auto r = getGamePlugin(plugins); + if (r != SetupResults::Ok) { + return r; + } - break; + if (m_gameVariant.isEmpty()) { + if (auto v=s.game().edition()) { + m_gameVariant = *v; } + } - case Delete: - { - if (MOBase::shellDelete(QStringList(dir), false)) { - return true; - } + if (m_gameVariant.isEmpty() && m_plugin->gameVariants().size() > 1) { + return SetupResults::MissingVariant; + } else { + m_plugin->setGameVariant(m_gameVariant); + } - const auto e = GetLastError(); - error = formatSystemMessage(e); - log::warn("failed to delete '{}', {}", dir, error); + getProfile(s); - break; - } + s.game().setName(m_gameName); + s.game().setDirectory(m_gameDir); + s.game().setSelectedProfileName(m_profile); - default: - { - return true; - } - } + if (!m_gameVariant.isEmpty()) + s.game().setEdition(m_gameVariant); - QMessageBox::critical( - nullptr, QObject::tr("Error"), QObject::tr( - "Could not delete instance folder \"%1\".\n\n%2") - .arg(dir).arg(error), - QMessageBox::Ok); + m_plugin->setGamePath(m_gameDir); - return false; + return SetupResults::Ok; } -QString InstanceManager::manageInstances(const QStringList &instanceList) const +void Instance::setGame(const QString& name, const QString& dir) { - SelectionDialog selection(QString("

%1


%2") - .arg(QObject::tr("Select an instance to delete")) - .arg(QObject::tr( - "Deleting an instance will delete all the mods, downloads, profiles " - "(including profile-specific saves) and anything in the overwrite " - "folder.

" - "Custom paths outside of the instance folder will not be deleted."))); + m_gameName = name; + m_gameDir = dir; +} - for (const QString &instance : instanceList) { - selection.addChoice(QIcon(":/MO/gui/multiply_red"), instance, "", instance); - } +void Instance::setVariant(const QString& name) +{ + m_gameVariant = name; +} - if (selection.exec() == QDialog::Rejected) { - return (chooseInstance(instanceNames())); - } - else { - QString choice = selection.getChoiceData().toString(); - deleteLocalInstance(choice); +Instance::SetupResults Instance::getGamePlugin(PluginContainer& plugins) +{ + if (!m_gameName.isEmpty() && !m_gameDir.isEmpty()) + { + // normal case: both the name and dir are in the ini + + // find the plugin by name + for (IPluginGame* game : plugins.plugins()) { + if (m_gameName.compare(game->gameName(), Qt::CaseInsensitive) == 0) { + // plugin found, check if the game directory is valid + + if (!game->looksValid(m_gameDir)) { + // the directory from the ini is not valid anymore + log::warn( + "game plugin {} says dir {} from ini {} is not valid", + game->gameName(), m_gameDir, iniPath()); + + // note that some plugins return true for isInstalled() if a path + // is found in the registry, but without actually checking if it's + // valid + + if (game->isInstalled() && game->looksValid(game->gameDirectory())) { + // bad game directory but the plugin reports there's a valid one + // somewhere; take it instead + log::warn( + "game plugin {} found a game at {}, taking it", + game->gameName(), game->gameDirectory().absolutePath()); + + m_gameDir = game->gameDirectory().absolutePath(); + } else { + // game seems to be gone completely + log::warn("game plugin {} found no game installation at all", game->gameName()); + return SetupResults::GameGone; + } + } + + m_plugin = game; + return SetupResults::Ok; + } + } + + log::warn("game plugin {} not found", m_gameName); + return SetupResults::PluginGone; } + else if (m_gameName.isEmpty() && !m_gameDir.isEmpty()) + { + // the name is missing, but there's a directory; find a plugin that can + // handle it - return(manageInstances(instanceNames())); -} + log::warn( + "game name is missing from ini {} but dir {} is available", + iniPath(), m_gameDir); -QString InstanceManager::queryInstanceName(const QStringList &instanceList) const -{ - QString instanceId; - QString dialogText; - while (instanceId.isEmpty()) { - QInputDialog dialog; + for (IPluginGame* game : plugins.plugins()) { + if (game->looksValid(m_gameDir)) { + // take it + log::warn("found plugin {} that can use dir {}", game->gameName(), m_gameDir); - dialog.setWindowTitle(QObject::tr("Enter a Name for the new Instance")); - dialog.setLabelText(QObject::tr("Enter a new name or select one from the suggested list: \n" - "(This is just a name for the Instance and can be whatever you wish,\n" - " the actual game selection will happen on the next screen regardless of chosen name)")); - // would be neat if we could take the names from the game plugins but - // the required initialization order requires the ini file to be - // available *before* we load plugins - dialog.setComboBoxItems({ "NewName", "Fallout 4", "SkyrimSE", "Skyrim", "SkyrimVR", "Fallout 3", - "Fallout NV", "TTW", "FO4VR", "Oblivion", "Morrowind", "Enderal" }); - dialog.setComboBoxEditable(true); + m_plugin = game; + m_gameName = game->gameName(); - if (dialog.exec() == QDialog::Rejected) { - throw MOBase::MyException(QObject::tr("Canceled")); + return SetupResults::Ok; + } } - dialogText = dialog.textValue(); - instanceId = sanitizeInstanceName(dialogText); - if (instanceId != dialogText) { - if (QMessageBox::question( nullptr, - QObject::tr("Invalid instance name"), - QObject::tr("The instance name \"%1\" is invalid. Use the name \"%2\" instead?").arg(dialogText,instanceId), - QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) { - instanceId=""; - continue; + + log::error("no plugins can use dir {}", m_gameDir); + return SetupResults::GameGone; + } + else if (!m_gameName.isEmpty() && m_gameDir.isEmpty()) + { + // dir is missing, find a plugin with the correct name and use the install + // dir it detected + + log::warn( + "game dir is missing from ini {} but name {} is available", + iniPath(), m_gameName); + + for (IPluginGame* game : plugins.plugins()) { + if (m_gameName.compare(game->gameName(), Qt::CaseInsensitive) == 0) { + // plugin found, use its detected installation dir + + if (game->isInstalled()) { + log::warn( + "found plugin {} that matches name in ini {}, using auto detected " + "game dir {}", + game->gameName(), iniPath(), game->gameDirectory().absolutePath()); + + m_plugin = game; + m_gameDir = game->gameDirectory().absolutePath(); + + return SetupResults::Ok; + } else { + log::warn( + "found plugin {} that matches name in ini {}, but no game install " + "detected by plugin", + game->gameName(), iniPath()); + + return SetupResults::GameGone; } + } } - bool alreadyExists=false; - for (const QString &instance : instanceList) { - if(instanceId==instance) - alreadyExists=true; - } - if(alreadyExists) - { - QMessageBox msgBox; - msgBox.setText( QObject::tr("The instance \"%1\" already exists.").arg(instanceId) ); - msgBox.setInformativeText(QObject::tr("Please choose a different instance name, like: \"%1 1\" .").arg(instanceId)); - msgBox.exec(); - instanceId=""; - } + // plugin seems to be gone + log::error("no plugin matches name {}", m_gameName); + return SetupResults::PluginGone; + } + else + { + // can't do anything with these two missing + log::error("both game name and dir are missing from ini {}", iniPath()); + return SetupResults::IniMissingGame; } - return instanceId; } -QString InstanceManager::chooseInstance(const QStringList &instanceList) const +void Instance::getProfile(const Settings& s) { - if (portableInstallIsLocked()) { - return QString(); + if (!m_profile.isEmpty()) { + // there's already a profile set up, probably an override + return; } - enum class Special : uint8_t { - NewInstance, - Portable, - Manage - }; - - SelectionDialog selection( - QString("

%1


%2") - .arg(QObject::tr("Choose Instance")) - .arg(QObject::tr( - "Each Instance is a full set of MO data files (mods, " - "downloads, profiles, configuration, ...). You can use multiple " - "instances for different games. Instances are stored in Appdata and can be accessed by all MO installations. " - "If your MO folder is writable, you can also store a single instance locally (called " - "a Portable install, and all the MO data files will be inside the installation folder).")), - nullptr); - selection.disableCancel(); - for (const QString &instance : instanceList) { - selection.addChoice(instance, "", instance); + if (auto name=s.game().selectedProfileName()) { + // use last profile + m_profile = *name; + return; } - selection.addChoice(QIcon(":/MO/gui/add"), QObject::tr("New"), - QObject::tr("Create a new instance."), - static_cast(Special::NewInstance)); + // profile missing from ini, use the default + m_profile = QString::fromStdWString(AppConfig::defaultProfileName()); - if (QFileInfo(qApp->applicationDirPath()).isWritable()) { - selection.addChoice(QIcon(":/MO/gui/package"), QObject::tr("Portable"), - QObject::tr("Use MO folder for data."), - static_cast(Special::Portable)); - } + log::warn( + "no profile found in ini {}, using default '{}'", + iniPath(), m_profile); +} - selection.addChoice(QIcon(":/MO/gui/remove"), QObject::tr("Manage Instances"), - QObject::tr("Delete an Instance."), - static_cast(Special::Manage)); - selection.setWindowFlags(selection.windowFlags() | Qt::WindowStaysOnTopHint); +InstanceManager::InstanceManager() +{ + GlobalSettings::updateRegistryKey(); +} + +InstanceManager &InstanceManager::instance() +{ + static InstanceManager s_Instance; + return s_Instance; +} - if (selection.exec() == QDialog::Rejected) { - log::debug("rejected"); - throw MOBase::MyException(QObject::tr("Canceled")); +void InstanceManager::overrideInstance(const QString& instanceName) +{ + m_overrideInstanceName = instanceName; + m_overrideInstance = true; +} + +void InstanceManager::overrideProfile(const QString& profileName) +{ + m_overrideProfileName = profileName; + m_overrideProfile = true; +} + +std::optional InstanceManager::currentInstance() const +{ + const QString profile = m_overrideProfile ? m_overrideProfileName : ""; + + if (portableInstallIsLocked()) { + // force portable instance + return Instance(QDir(portablePath()), true, profile); } - QVariant choice = selection.getChoiceData(); + QString name; - if (choice.type() == QVariant::String) { - return choice.toString(); - } else { - switch (static_cast(choice.value())) { - case Special::NewInstance: return queryInstanceName(instanceList); - case Special::Portable: return QString(); - case Special::Manage: { + if (m_overrideInstance) + name = m_overrideInstanceName; + else + name = GlobalSettings::currentInstance(); - return(manageInstances(instanceNames())); - } - default: throw std::runtime_error("invalid selection"); + if (name.isEmpty()) { + if (portableInstanceExists()) { + // use portable + return Instance(QDir(portablePath()), true, profile); + } else { + // no instance set + return {}; } } + + QString path = instancePath(name); + if (!QFileInfo::exists(path)) { + // the previously used instance doesn't exist anymore + return {}; + } + + return Instance(QDir(path), false, profile); +} + +void InstanceManager::clearCurrentInstance() +{ + setCurrentInstance(""); + m_overrideInstance = false; +} + +void InstanceManager::setCurrentInstance(const QString &name) +{ + GlobalSettings::setCurrentInstance(name); } QString InstanceManager::instancePath(const QString& instanceName) const @@ -302,6 +365,11 @@ QString InstanceManager::instancesPath() const QStandardPaths::writableLocation(QStandardPaths::DataLocation)); } +QString InstanceManager::iniPath(const QDir& instanceDir) +{ + return instanceDir.filePath(QString::fromStdWString(AppConfig::iniFileName())); +} + std::vector InstanceManager::instancePaths() const { const std::set ignore = { @@ -362,287 +430,10 @@ bool InstanceManager::allowedToChangeInstance() const return !portableInstallIsLocked(); } - -void InstanceManager::createDataPath(const QString &dataPath) const -{ - if (!QDir(dataPath).exists()) { - if (!QDir().mkpath(dataPath)) { - throw MOBase::MyException( - QObject::tr("failed to create %1").arg(dataPath)); - } else { - QMessageBox::information( - nullptr, QObject::tr("Data directory created"), - QObject::tr("New data directory created at %1. If you don't want to " - "store a lot of data there, reconfigure the storage " - "directories via settings.").arg(dataPath)); - } - } -} - - -QString InstanceManager::determineDataPath() -{ - QString instanceId = currentInstance(); - if (portableInstallIsLocked()) - { - instanceId.clear(); - } - if (instanceId.isEmpty() && !m_Reset && (m_overrideInstance || portableInstanceExists())) - { - // startup, apparently using portable mode before - return qApp->applicationDirPath(); - } - - QString dataPath = QDir::fromNativeSeparators( - QStandardPaths::writableLocation(QStandardPaths::DataLocation) - + "/" + instanceId); - - - if (!m_overrideInstance && (instanceId.isEmpty() || !QFileInfo::exists(dataPath))) { - instanceId = chooseInstance(instanceNames()); - setCurrentInstance(instanceId); - if (!instanceId.isEmpty()) { - dataPath = QDir::fromNativeSeparators( - QStandardPaths::writableLocation(QStandardPaths::DataLocation) - + "/" + instanceId); - } - } - - if (instanceId.isEmpty()) { - return qApp->applicationDirPath(); - } else { - createDataPath(dataPath); - - return dataPath; - } -} - -QString InstanceManager::determineProfile(const Settings &settings) -{ - auto selectedProfileName = settings.game().selectedProfileName(); - - if (m_overrideProfile) { - log::debug("profile overwritten on command line"); - selectedProfileName = m_overrideProfileName; - } - - if (!selectedProfileName) { - log::debug("no configured profile"); - selectedProfileName = "Default"; - } - - return *selectedProfileName; -} - -bool InstanceManager::determineGameEdition( - Settings& settings, IPluginGame* game) -{ - QString edition; - - if (auto v=settings.game().edition()) { - edition = *v; - } else { - QStringList editions = game->gameVariants(); - if (editions.size() < 2) { - edition = ""; - return true; - } - - SelectionDialog selection( - QObject::tr("Please select the game edition you have (MO can't " - "start the game correctly if this is set " - "incorrectly!)"), - nullptr); - - selection.setWindowFlag(Qt::WindowStaysOnTopHint, true); - - int index = 0; - for (const QString &edition : editions) { - selection.addChoice(edition, "", index++); - } - - if (selection.exec() == QDialog::Rejected) { - return false; - } - - edition = selection.getChoiceString(); - settings.game().setEdition(edition); - } - - game->setGameVariant(edition); - - return true; -} - -MOBase::IPluginGame *selectGame( - Settings &settings, QDir const &gamePath, MOBase::IPluginGame *game) -{ - settings.game().setName(game->gameName()); - - QString gameDir = gamePath.absolutePath(); - game->setGamePath(gameDir); - - settings.game().setDirectory(gameDir); - - return game; -} - -MOBase::IPluginGame* InstanceManager::determineCurrentGame( - const QString& moPath, Settings& settings, const PluginContainer &plugins) -{ - //Determine what game we are running where. Be very paranoid in case the - //user has done something odd. - - //If the game name has been set up, try to use that. - const auto gameName = settings.game().name(); - const bool gameConfigured = (gameName.has_value() && *gameName != ""); - - if (gameConfigured) { - MOBase::IPluginGame *game = plugins.managedGame(*gameName); - if (game == nullptr) { - reportError( - QObject::tr("Plugin to handle %1 no longer installed. An antivirus might have deleted files.") - .arg(*gameName)); - - return nullptr; - } - - auto gamePath = settings.game().directory(); - if (!gamePath || *gamePath == "") { - gamePath = game->gameDirectory().absolutePath(); - } - - QDir gameDir(*gamePath); - QFileInfo directoryInfo(gameDir.path()); - - if (directoryInfo.isSymLink()) { - reportError(QObject::tr("The configured path to the game directory (%1) appears to be a symbolic (or other) link. " - "This setup is incompatible with MO2's VFS and will not run correctly.").arg(*gamePath)); - } - - if (game->looksValid(gameDir)) { - return selectGame(settings, gameDir, game); - } - } - - //If we've made it this far and the instance is already configured for a game, something has gone wrong. - //Tell the user about it. - if (gameConfigured) { - const auto gamePath = settings.game().directory(); - - reportError( - QObject::tr("Could not use configuration settings for game \"%1\", path \"%2\".") - .arg(*gameName).arg(gamePath ? *gamePath : "")); - } - - SelectionDialog selection(gameConfigured ? - QObject::tr("Please select the installation of %1 to manage").arg(*gameName) : - QObject::tr("Please select the game to manage"), nullptr, QSize(32, 32)); - - for (IPluginGame *game : plugins.plugins()) { - //If a game is already configured, skip any plugins that are not for that game - if (gameConfigured && gameName->compare(game->gameName(), Qt::CaseInsensitive) != 0) - continue; - - //Only add games that are installed - if (game->isInstalled()) { - QString path = game->gameDirectory().absolutePath(); - selection.addChoice(game->gameIcon(), game->gameName(), path, QVariant::fromValue(game)); - } - } - - selection.addChoice(QString("Browse..."), QString(), QVariant::fromValue(static_cast(nullptr))); - - while (selection.exec() != QDialog::Rejected) { - IPluginGame * game = selection.getChoiceData().value(); - QString gamePath = selection.getChoiceDescription(); - QFileInfo directoryInfo(gamePath); - if (directoryInfo.isSymLink()) { - reportError(QObject::tr("The configured path to the game directory (%1) appears to be a symbolic (or other) link. " - "This setup is incompatible with MO2's VFS and will not run correctly.").arg(gamePath)); - } - if (game != nullptr) { - return selectGame(settings, game->gameDirectory(), game); - } - - gamePath = QFileDialog::getExistingDirectory(nullptr, gameConfigured ? - QObject::tr("Please select the installation of %1 to manage").arg(*gameName) : - QObject::tr("Please select the game to manage"), - QString(), QFileDialog::ShowDirsOnly); - - if (!gamePath.isEmpty()) { - QDir gameDir(gamePath); - QFileInfo directoryInfo(gamePath); - if (directoryInfo.isSymLink()) { - reportError(QObject::tr("The configured path to the game directory (%1) appears to be a symbolic (or other) link. " - "This setup is incompatible with MO2's VFS and will not run correctly.").arg(gamePath)); - } - QList possibleGames; - for (IPluginGame * const game : plugins.plugins()) { - //If a game is already configured, skip any plugins that are not for that game - if (gameConfigured && gameName->compare(game->gameName(), Qt::CaseInsensitive) != 0) - continue; - - //Only try plugins that look valid for this directory - if (game->looksValid(gameDir)) { - possibleGames.append(game); - } - } - - if (possibleGames.count() > 1) { - SelectionDialog browseSelection(gameConfigured ? - QObject::tr("Please select the installation of %1 to manage").arg(*gameName) : - QObject::tr("Please select the game to manage"), - nullptr, QSize(32, 32)); - - for (IPluginGame *game : possibleGames) { - browseSelection.addChoice(game->gameIcon(), game->gameName(), gamePath, QVariant::fromValue(game)); - } - - if (browseSelection.exec() == QDialog::Accepted) { - return selectGame(settings, gameDir, browseSelection.getChoiceData().value()); - } else { - reportError(gameConfigured ? - QObject::tr("Canceled finding %1 in \"%2\".").arg(*gameName).arg(gamePath) : - QObject::tr("Canceled finding game in \"%1\".").arg(gamePath)); - } - } else if(possibleGames.count() == 1) { - return selectGame(settings, gameDir, possibleGames[0]); - } else { - if (gameConfigured) { - reportError( - QObject::tr("%1 not identified in \"%2\". The directory is required to contain the game binary.") - .arg(*gameName).arg(gamePath)); - } else { - QString supportedGames; - - for (IPluginGame * const game : plugins.plugins()) { - supportedGames += "
  • " + game->gameName() + "
  • "; - } - - QString text = QObject::tr( - "No game identified in \"%1\". The directory is required to " - "contain the game binary.

    " - "These are the games supported by Mod Organizer:" - "
      %2
    ") - .arg(gamePath) - .arg(supportedGames); - - reportError(text); - } - } - } - } - - return nullptr; -} - const MOBase::IPluginGame* InstanceManager::gamePluginForDirectory( const QDir& instanceDir, const PluginContainer& plugins) const { - const QString ini = - QDir(instanceDir).filePath(QString::fromStdWString(AppConfig::iniFileName())); - + const QString ini = iniPath(instanceDir); Settings s(ini); diff --git a/src/instancemanager.h b/src/instancemanager.h index c2d1e0f4..ddab4a2e 100644 --- a/src/instancemanager.h +++ b/src/instancemanager.h @@ -29,29 +29,61 @@ namespace MOBase { class IPluginGame; } class Settings; class PluginContainer; + +class Instance +{ +public: + enum class SetupResults + { + Ok, + BadIni, + IniMissingGame, + PluginGone, + GameGone, + MissingVariant + }; + + Instance(QDir dir, bool portable, QString profileName={}); + + SetupResults setup(PluginContainer& plugins); + + void setGame(const QString& name, const QString& dir); + void setVariant(const QString& name); + + QString name() const; + QString gameName() const; + QString gameDirectory() const; + QDir directory() const; + MOBase::IPluginGame* gamePlugin() const; + QString profileName() const; + QString iniPath() const; + bool isPortable() const; + +private: + QDir m_dir; + bool m_portable; + QString m_gameName, m_gameDir, m_gameVariant; + MOBase::IPluginGame* m_plugin; + QString m_profile; + + SetupResults getGamePlugin(PluginContainer& plugins); + void getProfile(const Settings& s); +}; + + class InstanceManager { public: static InstanceManager &instance(); - // restarts MO - // - void switchToInstance(const QString& instanceName); - void overrideInstance(const QString& instanceName); void overrideProfile(const QString& profileName); - QString determineDataPath(); - QString determineProfile(const Settings &settings); - bool determineGameEdition(Settings& settings, MOBase::IPluginGame* game); - MOBase::IPluginGame* determineCurrentGame( - const QString& moPath, Settings& settings, const PluginContainer &plugins); - const MOBase::IPluginGame* gamePluginForDirectory( const QDir& dir, const PluginContainer& plugins) const; void clearCurrentInstance(); - QString currentInstance() const; + std::optional currentInstance() const; void setCurrentInstance(const QString &name); bool allowedToChangeInstance() const; @@ -68,23 +100,13 @@ public: bool instanceExists(const QString& instanceName) const; bool validInstanceName(const QString& instanceName) const; QString instancePath(const QString& instanceName) const; + static QString iniPath(const QDir& instanceDir); private: - InstanceManager(); - - bool deleteLocalInstance(const QString &instanceId) const; - - QString manageInstances(const QStringList &instanceList) const; - - QString queryInstanceName(const QStringList &instanceList) const; - QString chooseInstance(const QStringList &instanceList) const; - - void createDataPath(const QString &dataPath) const; bool portableInstallIsLocked() const; private: - bool m_Reset {false}; bool m_overrideInstance{false}; QString m_overrideInstanceName; bool m_overrideProfile{false}; diff --git a/src/instancemanagerdialog.cpp b/src/instancemanagerdialog.cpp index 545b5c71..f662082f 100644 --- a/src/instancemanagerdialog.cpp +++ b/src/instancemanagerdialog.cpp @@ -6,6 +6,7 @@ #include "selectiondialog.h" #include "plugincontainer.h" #include "shared/appconfig.h" +#include "shared/util.h" #include #include #include @@ -20,11 +21,6 @@ void openInstanceManager(PluginContainer& pc, QWidget* parent) dlg.exec(); } -QString makeIniFile(const QDir& dir) -{ - return dir.filePath(QString::fromStdWString(AppConfig::iniFileName())); -} - class InstanceInfo { @@ -61,7 +57,7 @@ public: void setDir(const QDir& dir) { m_dir = dir; - m_settings.reset(new Settings(makeIniFile(dir))); + m_settings.reset(new Settings(InstanceManager::iniPath(dir))); } QString name() const @@ -109,7 +105,7 @@ public: QString iniFile() const { - return makeIniFile(m_dir); + return InstanceManager::iniPath(m_dir); } QIcon icon(const PluginContainer& plugins) const @@ -134,10 +130,13 @@ public: { auto& m = InstanceManager::instance(); - if (m_portable && m.currentInstance() == "") { - return true; - } else if (m.currentInstance() == name()) { - return true; + if (auto i=m.currentInstance()) + { + if (m_portable) { + return i->isPortable(); + } else { + return (i->name() == name()); + } } return false; @@ -316,7 +315,7 @@ InstanceManagerDialog::~InstanceManagerDialog() = default; InstanceManagerDialog::InstanceManagerDialog( const PluginContainer& pc, QWidget *parent) : QDialog(parent), ui(new Ui::InstanceManagerDialog), m_pc(pc), - m_model(nullptr) + m_model(nullptr), m_restartOnSelect(true) { ui->setupUi(this); @@ -449,14 +448,16 @@ void InstanceManagerDialog::selectActiveInstance() { const auto active = InstanceManager::instance().currentInstance(); - for (std::size_t i=0; iname() == active) { - select(i); + if (active) { + for (std::size_t i=0; iname() == active->name()) { + select(i); - ui->list->scrollTo( - m_filter.mapFromSource(m_filter.sourceModel()->index(i, 0))); + ui->list->scrollTo( + m_filter.mapFromSource(m_filter.sourceModel()->index(i, 0))); - return; + return; + } } } @@ -470,7 +471,13 @@ void InstanceManagerDialog::openSelectedInstance() return; } - InstanceManager::instance().switchToInstance(m_instances[i]->name()); + InstanceManager::instance().setCurrentInstance(m_instances[i]->name()); + + if (m_restartOnSelect) { + ExitModOrganizer(Exit::Restart); + } + + accept(); } QString getInstanceName( @@ -697,6 +704,11 @@ void InstanceManagerDialog::deleteInstance() } +void InstanceManagerDialog::setRestartOnSelect(bool b) +{ + m_restartOnSelect = b; +} + bool InstanceManagerDialog::doDelete(const QStringList& files, bool recycle) { if (MOBase::shellDelete(files, recycle, this)) { diff --git a/src/instancemanagerdialog.h b/src/instancemanagerdialog.h index 477a7d01..5b08ffc2 100644 --- a/src/instancemanagerdialog.h +++ b/src/instancemanagerdialog.h @@ -34,6 +34,8 @@ public: void openINI(); void deleteInstance(); + void setRestartOnSelect(bool b); + private: static const std::size_t NoSelection = -1; @@ -42,6 +44,7 @@ private: std::vector> m_instances; MOBase::FilterWidget m_filter; QStandardItemModel* m_model; + bool m_restartOnSelect; void updateInstances(); diff --git a/src/main.cpp b/src/main.cpp index fd5a47c9..2a5a3e81 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -27,6 +27,7 @@ along with Mod Organizer. If not, see . #include "instancemanager.h" #include "instancemanagerdialog.h" #include "createinstancedialog.h" +#include "createinstancedialogpages.h" #include "organizercore.h" #include "env.h" #include "envmodule.h" @@ -273,9 +274,166 @@ std::optional handleCommandLine( void openInstanceManager(PluginContainer& pc, QWidget* parent); +std::optional selectInstance() +{ + NexusInterface ni(nullptr); + + PluginContainer pc(nullptr); + pc.loadPlugins(); + + InstanceManagerDialog dlg(pc); + dlg.setRestartOnSelect(false); + + dlg.show(); + dlg.activateWindow(); + dlg.raise(); + + if (dlg.exec() != QDialog::Accepted) { + return {}; + } + + return InstanceManager::instance().currentInstance(); +} + +enum class SetupInstanceResults +{ + Ok, + TryAgain, + SelectAnother, + Exit +}; + + +void criticalOnTop(const QString& message) +{ + QMessageBox mb(QMessageBox::Critical, QObject::tr("Mod Organizer"), message); + + mb.show(); + mb.activateWindow(); + mb.raise(); + mb.exec(); +} + + +SetupInstanceResults setupInstance(Instance& instance, PluginContainer& pc) +{ + const auto setupResult = instance.setup(pc); + + switch (setupResult) + { + case Instance::SetupResults::Ok: + { + return SetupInstanceResults::Ok; + } + + case Instance::SetupResults::BadIni: + { + criticalOnTop( + QObject::tr("Cannot open instance '%1', failed to read INI file %2.") + .arg(instance.name()).arg(instance.iniPath())); + + return SetupInstanceResults::SelectAnother; + } + + case Instance::SetupResults::IniMissingGame: + { + criticalOnTop( + QObject::tr( + "Cannot open instance '%1', the managed game was not found in the INI " + "file %2. Select the game managed by this instance.") + .arg(instance.name()).arg(instance.iniPath())); + + CreateInstanceDialog dlg(pc, nullptr); + dlg.setSinglePage(); + + dlg.show(); + dlg.activateWindow(); + dlg.raise(); + + if (dlg.exec() != QDialog::Accepted) { + return SetupInstanceResults::Exit; + } + + instance.setGame( + dlg.creationInfo().game->gameName(), + dlg.creationInfo().game->gameDirectory().absolutePath()); + + return SetupInstanceResults::TryAgain; + } + + case Instance::SetupResults::PluginGone: + { + criticalOnTop( + QObject::tr( + "Cannot open instance '%1', the game plugin '%2' doesn't exist. It " + "may have been deleted by an antivirus. Select another instance.") + .arg(instance.name()).arg(instance.gameName())); + + return SetupInstanceResults::SelectAnother; + } + + case Instance::SetupResults::GameGone: + { + criticalOnTop( + QObject::tr( + "Cannot open instance '%1', the game directory '%2' doesn't exist or " + "the game plugin '%3' doesn't recognize it. Select the game managed " + "by this instance.") + .arg(instance.name()) + .arg(instance.gameDirectory()) + .arg(instance.gameName())); + + CreateInstanceDialog dlg(pc, nullptr); + dlg.setSinglePage(); + + dlg.show(); + dlg.activateWindow(); + dlg.raise(); + + if (dlg.exec() != QDialog::Accepted) { + return SetupInstanceResults::Exit; + } + + instance.setGame( + dlg.creationInfo().game->gameName(), + dlg.creationInfo().game->gameDirectory().absolutePath()); + + return SetupInstanceResults::TryAgain; + } + + case Instance::SetupResults::MissingVariant: + { + CreateInstanceDialog dlg(pc, nullptr); + + dlg.getPage()->select( + instance.gamePlugin(), instance.gameDirectory()); + + dlg.setSinglePage(); + + dlg.show(); + dlg.activateWindow(); + dlg.raise(); + + if (dlg.exec() != QDialog::Accepted) { + return SetupInstanceResults::Exit; + } + + instance.setVariant(dlg.creationInfo().gameVariant); + + return SetupInstanceResults::TryAgain; + } + + default: + { + return SetupInstanceResults::Exit; + } + } +} + int runApplication( MOApplication &application, const cl::CommandLine& cl, - SingleInstance &instance, const QString &dataPath) + SingleInstance &instance, const QString &dataPath, + Instance& currentInstance) { TimeThis tt("runApplication() to exec()"); @@ -345,57 +503,48 @@ int runApplication( pluginContainer = std::make_unique(&organizer); pluginContainer->loadPlugins(); - MOBase::IPluginGame* game = InstanceManager::instance() - .determineCurrentGame( - application.applicationDirPath(), settings, *pluginContainer); - - if (game == nullptr) { - InstanceManager &instance = InstanceManager::instance(); - QString instanceName = instance.currentInstance(); - - if (instanceName.compare("Portable", Qt::CaseInsensitive) != 0) { - instance.clearCurrentInstance(); + for (;;) + { + const auto setupResult = setupInstance(currentInstance, *pluginContainer); + + if (setupResult == SetupInstanceResults::Ok) { + break; + } else if (setupResult == SetupInstanceResults::TryAgain) { + continue; + } else if (setupResult == SetupInstanceResults::SelectAnother) { + InstanceManager::instance().clearCurrentInstance(); return RestartExitCode; + } else { + return 1; } - - return 1; } - checkPathsForSanity(*game, settings); - + checkPathsForSanity(*currentInstance.gamePlugin(), settings); - organizer.setManagedGame(game); + organizer.setManagedGame(currentInstance.gamePlugin()); organizer.createDefaultProfile(); - if (!InstanceManager::instance().determineGameEdition(settings, game)) { - return 1; - } - log::info( "using game plugin '{}' ('{}', variant {}, steam id '{}') at {}", - game->gameName(), game->gameShortName(), + currentInstance.gamePlugin()->gameName(), + currentInstance.gamePlugin()->gameShortName(), (settings.game().edition().value_or("").isEmpty() ? "(none)" : *settings.game().edition()), - game->steamAPPId(), game->gameDirectory().absolutePath()); + currentInstance.gamePlugin()->steamAPPId(), + currentInstance.gamePlugin()->gameDirectory().absolutePath()); + CategoryFactory::instance().loadCategories(); organizer.updateExecutablesList(); organizer.updateModInfoFromDisc(); - if (cl.profile()) { - InstanceManager::instance().overrideProfile(*cl.profile()); - } - - QString selectedProfileName = InstanceManager::instance() - .determineProfile(settings); - - organizer.setCurrentProfile(selectedProfileName); + organizer.setCurrentProfile(currentInstance.profileName()); if (auto r=handleCommandLine(cl, organizer)) { return *r; } - auto splash = createSplash(settings, dataPath, game); + auto splash = createSplash(settings, dataPath, currentInstance.gamePlugin()); QString apiKey; if (GlobalSettings::nexusApiKey(apiKey)) { @@ -438,11 +587,6 @@ int runApplication( tt.stop(); - QTimer::singleShot(std::chrono::milliseconds(1), [&] - { - openInstanceManager(*pluginContainer, &mainWindow); - }); - res = application.exec(); mainWindow.close(); @@ -488,28 +632,6 @@ void resetForRestart(cl::CommandLine& cl) cl.clear(); } -QString determineDataPath(const cl::CommandLine& cl) -{ - try - { - InstanceManager& instanceManager = InstanceManager::instance(); - - if (cl.instance()) - instanceManager.overrideInstance(*cl.instance()); - - return instanceManager.determineDataPath(); - } - catch (const std::exception &e) - { - if (strcmp(e.what(),"Canceled")) { - QMessageBox::critical(nullptr, QObject::tr("Failed to set up instance"), e.what()); - } - - return {}; - } -} - - int doOneRun( cl::CommandLine& cl, MOApplication& application, SingleInstance& instance) { @@ -518,24 +640,25 @@ int doOneRun( // resets things when MO is "restarted" resetForRestart(cl); + if (cl.instance()) + InstanceManager::instance().overrideInstance(*cl.instance()); - //{ - // NexusInterface ni(nullptr); - // - // PluginContainer pc(nullptr); - // pc.loadPlugins(); - // - // CreateInstanceDialog dlg(pc, nullptr); - // dlg.exec(); - //} + if (cl.profile()) { + InstanceManager::instance().overrideProfile(*cl.profile()); + } + auto currentInstance = InstanceManager::instance().currentInstance(); - const QString dataPath = determineDataPath(cl); - if (dataPath.isEmpty()) { - return 1; + if (!currentInstance) + { + currentInstance = selectInstance(); + if (!currentInstance) + return 1; } + const QString dataPath = currentInstance->directory().path(); application.setProperty("dataPath", dataPath); + setExceptionHandlers(); if (!setLogDirectory(dataPath)) { @@ -548,7 +671,7 @@ int doOneRun( tt.stop(); - return runApplication(application, cl, instance, dataPath); + return runApplication(application, cl, instance, dataPath, *currentInstance); } int main(int argc, char *argv[]) diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 0d561581..550f61d4 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -513,9 +513,11 @@ bool OrganizerCore::bootstrap() void OrganizerCore::createDefaultProfile() { QString profilesPath = settings().paths().profiles(); - if (QDir(profilesPath).entryList(QDir::AllDirs | QDir::NoDotAndDotDot).size() - == 0) { - Profile newProf("Default", managedGame(), false); + if (QDir(profilesPath).entryList(QDir::AllDirs | QDir::NoDotAndDotDot).size() == 0) { + Profile newProf( + QString::fromStdWString(AppConfig::defaultProfileName()), + managedGame(), false); + m_ProfileCreated(&newProf); } } diff --git a/src/processrunner.cpp b/src/processrunner.cpp index a0e74f47..8ee0914b 100644 --- a/src/processrunner.cpp +++ b/src/processrunner.cpp @@ -588,11 +588,14 @@ ProcessRunner& ProcessRunner::setFromShortcut(const MOShortcut& shortcut) { const auto currentInstance = InstanceManager::instance().currentInstance(); - if (shortcut.hasInstance() && shortcut.instance() != currentInstance) { - throw std::runtime_error( - QString("Refusing to run executable from different instance %1:%2") - .arg(shortcut.instance(),shortcut.executable()) - .toLocal8Bit().constData()); + if (currentInstance) + { + if (shortcut.hasInstance() && shortcut.instance() != currentInstance->name()) { + throw std::runtime_error( + QString("Refusing to run executable from different instance %1:%2") + .arg(shortcut.instance(),shortcut.executable()) + .toLocal8Bit().constData()); + } } const Executable& exe = m_core.executablesList()->get(shortcut.executable()); diff --git a/src/shared/appconfig.inc b/src/shared/appconfig.inc index 709c845d..807f1d69 100644 --- a/src/shared/appconfig.inc +++ b/src/shared/appconfig.inc @@ -9,6 +9,7 @@ APPPARAM(std::wstring, cachePath, L"webcache") APPPARAM(std::wstring, tutorialsPath, L"tutorials") APPPARAM(std::wstring, logPath, L"logs") APPPARAM(std::wstring, dumpsDir, L"crashDumps") +APPPARAM(std::wstring, defaultProfileName, L"Default") APPPARAM(std::wstring, profileTweakIni, L"profile_tweaks.ini") APPPARAM(std::wstring, logFileName, L"ModOrganizer.log") APPPARAM(std::wstring, iniFileName, L"ModOrganizer.ini") diff --git a/src/statusbar.cpp b/src/statusbar.cpp index 5897b6bb..aefabc73 100644 --- a/src/statusbar.cpp +++ b/src/statusbar.cpp @@ -153,10 +153,9 @@ void StatusBar::updateNormalMessage(OrganizerCore& core) game = tr("Unknown game"); } - QString instance = InstanceManager::instance().currentInstance(); - if (instance.isEmpty()) { - instance = tr("Portable"); - } + QString instance = "?"; + if (auto i=InstanceManager::instance().currentInstance()) + instance = i->name(); QString profile = core.profileName(); -- cgit v1.3.1 From 38d2f87b31ba4af8f6ecb73e0432460778e26f82 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 3 Nov 2020 13:35:02 -0500 Subject: replaced #pragma once by ifdefs changed pointer to ref to NexusInterface on_actionChange_Game_triggered() now creates the dialog itself instead of calling test code fixed broken command line options, they'd be reset before they were used removed useless explicit --- src/commandline.h | 6 ++++-- src/createinstancedialog.h | 2 +- src/instancemanager.h | 26 ++++---------------------- src/instancemanagerdialog.cpp | 9 --------- src/main.cpp | 14 +++++++------- src/mainwindow.cpp | 31 +++++++++---------------------- src/moshortcut.h | 31 ++++++------------------------- src/pluginlistview.cpp | 2 -- src/shared/error_report.h | 8 ++++++-- src/uilocker.h | 5 ++++- 10 files changed, 41 insertions(+), 93 deletions(-) (limited to 'src/instancemanager.h') diff --git a/src/commandline.h b/src/commandline.h index 0e300327..72018ba3 100644 --- a/src/commandline.h +++ b/src/commandline.h @@ -1,5 +1,5 @@ -#pragma once - +#ifndef MODORGANIZER_COMMANDLINE_INCLUDED +#define MODORGANIZER_COMMANDLINE_INCLUDED #include "moshortcut.h" #include #include @@ -149,3 +149,5 @@ private: }; } // namespace + +#endif // MODORGANIZER_COMMANDLINE_INCLUDED diff --git a/src/createinstancedialog.h b/src/createinstancedialog.h index f05495c6..25e383eb 100644 --- a/src/createinstancedialog.h +++ b/src/createinstancedialog.h @@ -47,7 +47,7 @@ public: }; - explicit CreateInstanceDialog( + CreateInstanceDialog( const PluginContainer& pc, Settings* s, QWidget *parent = nullptr); ~CreateInstanceDialog(); diff --git a/src/instancemanager.h b/src/instancemanager.h index ddab4a2e..69536650 100644 --- a/src/instancemanager.h +++ b/src/instancemanager.h @@ -1,25 +1,5 @@ -/* -Copyright (C) 2016 Sebastian Herbord. All rights reserved. - -This file is part of Mod Organizer. - -Mod Organizer is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -Mod Organizer is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Mod Organizer. If not, see . -*/ - - -#pragma once - +#ifndef MODORGANIZER_INSTANCEMANAGER_INCLUDED +#define MODORGANIZER_INSTANCEMANAGER_INCLUDED #include #include @@ -112,3 +92,5 @@ private: bool m_overrideProfile{false}; QString m_overrideProfileName; }; + +#endif // MODORGANIZER_INSTANCEMANAGER_INCLUDED diff --git a/src/instancemanagerdialog.cpp b/src/instancemanagerdialog.cpp index f2e9a928..231835ba 100644 --- a/src/instancemanagerdialog.cpp +++ b/src/instancemanagerdialog.cpp @@ -13,15 +13,6 @@ using namespace MOBase; -void openInstanceManager(PluginContainer& pc, QWidget* parent) -{ - //CreateInstanceDialog dlg(pc, parent); - //dlg.exec(); - InstanceManagerDialog dlg(pc, parent); - dlg.exec(); -} - - class InstanceInfo { public: diff --git a/src/main.cpp b/src/main.cpp index bd7f8303..8f7af77d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -650,13 +650,6 @@ int doOneRun( // resets things when MO is "restarted" resetForRestart(cl); - if (cl.instance()) - InstanceManager::instance().overrideInstance(*cl.instance()); - - if (cl.profile()) { - InstanceManager::instance().overrideProfile(*cl.profile()); - } - auto currentInstance = InstanceManager::instance().currentInstance(); if (!currentInstance) @@ -707,6 +700,13 @@ int main(int argc, char *argv[]) tt.stop(); + if (cl.instance()) + InstanceManager::instance().overrideInstance(*cl.instance()); + + if (cl.profile()) { + InstanceManager::instance().overrideProfile(*cl.profile()); + } + for (;;) { const auto r = doOneRun(cl, application, instance); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 6a648512..02900571 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -76,6 +76,7 @@ along with Mod Organizer. If not, see . #include "statusbar.h" #include "filterlist.h" #include "datatab.h" +#include "instancemanagerdialog.h" #include #include #include @@ -291,7 +292,7 @@ MainWindow::MainWindow(Settings &settings ui->statusBar->setup(ui, settings); { - auto* ni = &NexusInterface::instance(); + auto& ni = NexusInterface::instance(); // there are two ways to get here: // 1) the user just started MO, and @@ -311,8 +312,8 @@ MainWindow::MainWindow(Settings &settings // // in the rare case where the user restarts MO through the settings, this // will correctly pick up the previous values - updateWindowTitle(ni->getAPIUserAccount()); - ui->statusBar->setAPI(ni->getAPIStats(), ni->getAPIUserAccount()); + updateWindowTitle(ni.getAPIUserAccount()); + ui->statusBar->setAPI(ni.getAPIStats(), ni.getAPIUserAccount()); } m_Filters.reset(new FilterList(ui, &m_OrganizerCore, m_CategoryFactory)); @@ -1961,8 +1962,8 @@ void MainWindow::refreshSaveList() it.next(); files.append(it.fileInfo()); } - std::sort(files.begin(), files.end(), [](auto const& lhs, auto const& rhs) { - return lhs.fileTime(QFileDevice::FileModificationTime) < rhs.fileTime(QFileDevice::FileModificationTime); + std::sort(files.begin(), files.end(), [](auto const& lhs, auto const& rhs) { + return lhs.fileTime(QFileDevice::FileModificationTime) < rhs.fileTime(QFileDevice::FileModificationTime); }); for (const QFileInfo &file : files) { @@ -5569,7 +5570,7 @@ void MainWindow::nxmUpdateInfoAvailable(QString gameName, QVariant userData, QVa void MainWindow::finishUpdateInfo() { QFutureWatcher>>> *watcher = static_cast>>> *>(sender()); - + QString game = watcher->result().first; auto finalMods = watcher->result().second; @@ -5974,24 +5975,10 @@ void MainWindow::on_actionNotifications_triggered() scheduleCheckForProblems(); } -void openInstanceManager(PluginContainer& pc, QWidget* parent); - void MainWindow::on_actionChange_Game_triggered() { - openInstanceManager(m_PluginContainer, this); - - //if (m_OrganizerCore.settings().interface().showChangeGameConfirmation()) { - // const auto r = QMessageBox::question( - // this, tr("Are you sure?"), tr("This will restart MO, continue?"), - // QMessageBox::Yes | QMessageBox::Cancel); - // - // if (r != QMessageBox::Yes) { - // return; - // } - //} - // - //InstanceManager::instance().clearCurrentInstance(); - //ExitModOrganizer(Exit::Restart); + InstanceManagerDialog dlg(m_PluginContainer, this); + dlg.exec(); } void MainWindow::setCategoryListVisible(bool visible) diff --git a/src/moshortcut.h b/src/moshortcut.h index 0067b3bc..33346bb9 100644 --- a/src/moshortcut.h +++ b/src/moshortcut.h @@ -1,31 +1,10 @@ -/* -Copyright (C) 2016 Sebastian Herbord. All rights reserved. - -This file is part of Mod Organizer. - -Mod Organizer is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -Mod Organizer is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Mod Organizer. If not, see . -*/ - - -#pragma once - +#ifndef MODORGANIZER_MOSHORTCUT_INCLUDED +#define MODORGANIZER_MOSHORTCUT_INCLUDED #include - -class MOShortcut { - +class MOShortcut +{ public: MOShortcut(const QString& link={}); @@ -49,3 +28,5 @@ private: bool m_hasInstance; bool m_hasExecutable; }; + +#endif // MODORGANIZER_MOSHORTCUT_INCLUDED diff --git a/src/pluginlistview.cpp b/src/pluginlistview.cpp index 4217971d..a265d5d4 100644 --- a/src/pluginlistview.cpp +++ b/src/pluginlistview.cpp @@ -56,5 +56,3 @@ void PluginListView::setModel(QAbstractItemModel *model) QTreeView::setModel(model); setVerticalScrollBar(new ViewMarkingScrollBar(model, this)); } - -#pragma once diff --git a/src/shared/error_report.h b/src/shared/error_report.h index 17b25645..da07c728 100644 --- a/src/shared/error_report.h +++ b/src/shared/error_report.h @@ -17,16 +17,20 @@ You should have received a copy of the GNU General Public License along with Mod Organizer. If not, see . */ -#pragma once +#ifndef MODORGANIZER_SHARED_ERROR_REPORT_INCLUDED +#define MODORGANIZER_SHARED_ERROR_REPORT_INCLUDED #include #define WIN32_LEAN_AND_MEAN #include #include -namespace MOShared { +namespace MOShared +{ void reportError(LPCSTR format, ...); void reportError(LPCWSTR format, ...); } // namespace MOShared + +#endif // MODORGANIZER_SHARED_ERROR_REPORT_INCLUDED diff --git a/src/uilocker.h b/src/uilocker.h index cc467184..44d9d8a2 100644 --- a/src/uilocker.h +++ b/src/uilocker.h @@ -1,4 +1,5 @@ -#pragma once +#ifndef MODORGANIZER_UILOCKER_INCLUDED +#define MODORGANIZER_UILOCKER_INCLUDED #include #include @@ -94,3 +95,5 @@ private: void enableAll(); void disable(QWidget* w); }; + +#endif // MODORGANIZER_UILOCKER_INCLUDED -- cgit v1.3.1 From ad8e9d99b30578676c15d10a74f010439e132406 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Fri, 6 Nov 2020 09:27:15 -0500 Subject: renamed InstanceManager::instance() to singleton() to avoid confusion --- src/createinstancedialog.cpp | 6 +++--- src/createinstancedialogpages.cpp | 18 +++++++++--------- src/envshortcut.cpp | 2 +- src/instancemanager.cpp | 2 +- src/instancemanager.h | 2 +- src/instancemanagerdialog.cpp | 20 ++++++++++---------- src/main.cpp | 14 +++++++------- src/mainwindow.cpp | 2 +- src/processrunner.cpp | 2 +- src/statusbar.cpp | 2 +- 10 files changed, 35 insertions(+), 35 deletions(-) (limited to 'src/instancemanager.h') diff --git a/src/createinstancedialog.cpp b/src/createinstancedialog.cpp index c976ee13..2ff8adf3 100644 --- a/src/createinstancedialog.cpp +++ b/src/createinstancedialog.cpp @@ -244,7 +244,7 @@ void CreateInstanceDialog::finish() ui->creationLog->clear(); logCreation(tr("Creating instance...")); - const auto& m = InstanceManager::instance(); + const auto& m = InstanceManager::singleton(); const auto ci = creationInfo(); auto logger = [&](QString s) { @@ -332,7 +332,7 @@ void CreateInstanceDialog::finish() } if (ui->launch->isChecked()) { - InstanceManager::instance().setCurrentInstance(ci.instanceName); + InstanceManager::singleton().setCurrentInstance(ci.instanceName); if (m_settings) { // don't restart without settings, it happens on startup when there are @@ -445,7 +445,7 @@ QString CreateInstanceDialog::dataPath() const if (instanceType() == Portable) { s = QDir(InstanceManager::portablePath()).absolutePath(); } else { - s = InstanceManager::instance().instancePath(instanceName()); + s = InstanceManager::singleton().instancePath(instanceName()); } return QDir::toNativeSeparators(s); diff --git a/src/createinstancedialogpages.cpp b/src/createinstancedialogpages.cpp index 00d49b99..26f2f61d 100644 --- a/src/createinstancedialogpages.cpp +++ b/src/createinstancedialogpages.cpp @@ -136,13 +136,13 @@ TypePage::TypePage(CreateInstanceDialog& dlg) { ui->createGlobal->setDescription( ui->createGlobal->description() - .arg(InstanceManager::instance().instancesPath())); + .arg(InstanceManager::singleton().instancesPath())); ui->createPortable->setDescription( ui->createPortable->description() .arg(InstanceManager::portablePath())); - if (InstanceManager::instance().portableInstanceExists()) { + if (InstanceManager::singleton().portableInstanceExists()) { ui->createPortable->setEnabled(false); ui->portableExistsLabel->setVisible(true); } else { @@ -722,7 +722,7 @@ void NamePage::activated() m_label.setText(g->gameName()); if (!m_modified || ui->instanceName->text().isEmpty()) { - const auto n = InstanceManager::instance().makeUniqueName(g->gameName()); + const auto n = InstanceManager::singleton().makeUniqueName(g->gameName()); ui->instanceName->setText(n); m_modified = false; } @@ -737,7 +737,7 @@ QString NamePage::selectedInstanceName() const } const auto text = ui->instanceName->text().trimmed(); - return InstanceManager::instance().sanitizeInstanceName(text); + return InstanceManager::singleton().sanitizeInstanceName(text); } void NamePage::onChanged() @@ -748,7 +748,7 @@ void NamePage::onChanged() void NamePage::updateWarnings() { - const auto root = InstanceManager::instance().instancesPath(); + const auto root = InstanceManager::singleton().instancesPath(); m_okay = checkName(root, ui->instanceName->text()); updateNavigation(); @@ -765,7 +765,7 @@ bool NamePage::checkName(QString parentDir, QString name) if (name.isEmpty()) { empty = true; } else { - if (InstanceManager::instance().validInstanceName(name)) { + if (InstanceManager::singleton().validInstanceName(name)) { exists = QDir(parentDir).exists(name); } else { invalid = true; @@ -901,7 +901,7 @@ void PathsPage::setPaths(const QString& name, bool force) if (m_dlg.instanceType() == CreateInstanceDialog::Portable) { path = InstanceManager::portablePath(); } else { - const auto root = InstanceManager::instance().instancesPath(); + const auto root = InstanceManager::singleton().instancesPath(); path = root + "/" + name; } @@ -938,11 +938,11 @@ bool PathsPage::checkPath( } else { const QDir d(path); - if (InstanceManager::instance().validInstanceName(d.dirName())) { + if (InstanceManager::singleton().validInstanceName(d.dirName())) { if (m_dlg.instanceType() == CreateInstanceDialog::Portable) { // the default data path for a portable instance is the application // directory, so it's not an error if it exists - if (QDir(path) != InstanceManager::instance().portablePath()) { + if (QDir(path) != InstanceManager::singleton().portablePath()) { exists = QDir(path).exists(); } } else { diff --git a/src/envshortcut.cpp b/src/envshortcut.cpp index 5222665b..b13a7d9f 100644 --- a/src/envshortcut.cpp +++ b/src/envshortcut.cpp @@ -149,7 +149,7 @@ Shortcut::Shortcut(const Executable& exe) m_target = QFileInfo(qApp->applicationFilePath()).absoluteFilePath(); m_arguments = QString("\"moshortcut://%1:%2\"") - .arg(InstanceManager::instance().currentInstance()->name()) + .arg(InstanceManager::singleton().currentInstance()->name()) .arg(exe.title()); m_description = QString("Run %1 with ModOrganizer").arg(exe.title()); diff --git a/src/instancemanager.cpp b/src/instancemanager.cpp index 61c442be..8163149b 100644 --- a/src/instancemanager.cpp +++ b/src/instancemanager.cpp @@ -290,7 +290,7 @@ InstanceManager::InstanceManager() GlobalSettings::updateRegistryKey(); } -InstanceManager &InstanceManager::instance() +InstanceManager &InstanceManager::singleton() { static InstanceManager s_Instance; return s_Instance; diff --git a/src/instancemanager.h b/src/instancemanager.h index 69536650..79f1f30b 100644 --- a/src/instancemanager.h +++ b/src/instancemanager.h @@ -54,7 +54,7 @@ private: class InstanceManager { public: - static InstanceManager &instance(); + static InstanceManager& singleton(); void overrideInstance(const QString& instanceName); void overrideProfile(const QString& profileName); diff --git a/src/instancemanagerdialog.cpp b/src/instancemanagerdialog.cpp index 282329a5..3b6daeb8 100644 --- a/src/instancemanagerdialog.cpp +++ b/src/instancemanagerdialog.cpp @@ -101,7 +101,7 @@ public: QIcon icon(const PluginContainer& plugins) const { - const auto* game = InstanceManager::instance().gamePluginForDirectory( + const auto* game = InstanceManager::singleton().gamePluginForDirectory( m_dir, plugins); if (game) @@ -119,7 +119,7 @@ public: bool isActive() const { - auto& m = InstanceManager::instance(); + auto& m = InstanceManager::singleton(); if (auto i=m.currentInstance()) { @@ -347,7 +347,7 @@ InstanceManagerDialog::InstanceManagerDialog( void InstanceManagerDialog::updateInstances() { - auto& m = InstanceManager::instance(); + auto& m = InstanceManager::singleton(); m_instances.clear(); @@ -437,7 +437,7 @@ void InstanceManagerDialog::select(const QString& name) void InstanceManagerDialog::selectActiveInstance() { - const auto active = InstanceManager::instance().currentInstance(); + const auto active = InstanceManager::singleton().currentInstance(); if (active) { for (std::size_t i=0; iisPortable()) { - InstanceManager::instance().setCurrentInstance(""); + InstanceManager::singleton().setCurrentInstance(""); } else { - InstanceManager::instance().setCurrentInstance(m_instances[i]->name()); + InstanceManager::singleton().setCurrentInstance(m_instances[i]->name()); } if (m_restartOnSelect) { @@ -479,7 +479,7 @@ QString getInstanceName( QWidget* parent, const QString& title, const QString& moreText, const QString& label, const QString& oldName={}) { - auto& m = InstanceManager::instance(); + auto& m = InstanceManager::singleton(); QDialog dlg(parent); dlg.setWindowTitle(title); @@ -554,7 +554,7 @@ void InstanceManagerDialog::rename() const auto selIndex = singleSelectionIndex(); - auto& m = InstanceManager::instance(); + auto& m = InstanceManager::singleton(); if (i->isActive()) { QMessageBox::information(this, tr("Rename instance"), tr("The active instance cannot be renamed.")); @@ -622,7 +622,7 @@ void InstanceManagerDialog::deleteInstance() return; } - auto& m = InstanceManager::instance(); + auto& m = InstanceManager::singleton(); if (i->isActive()) { QMessageBox::information(this, tr("Deleting instance"), tr("The active instance cannot be deleted.")); @@ -800,7 +800,7 @@ void InstanceManagerDialog::fillData(const InstanceInfo& ii) ui->gameDir->setText(ii.gamePath()); setButtonsEnabled(true); - const auto& m = InstanceManager::instance(); + const auto& m = InstanceManager::singleton(); ui->rename->setEnabled(!ii.isPortable()); diff --git a/src/main.cpp b/src/main.cpp index 7005d374..e56197c9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -274,7 +274,7 @@ std::optional handleCommandLine( std::optional selectInstance() { - auto& m = InstanceManager::instance(); + auto& m = InstanceManager::singleton(); NexusInterface ni(nullptr); PluginContainer pc(nullptr); @@ -505,7 +505,7 @@ int runApplication( OrganizerCore organizer(settings); if (!organizer.bootstrap()) { reportError("failed to set up data paths"); - InstanceManager::instance().clearCurrentInstance(); + InstanceManager::singleton().clearCurrentInstance(); return 1; } @@ -522,7 +522,7 @@ int runApplication( } else if (setupResult == SetupInstanceResults::TryAgain) { continue; } else if (setupResult == SetupInstanceResults::SelectAnother) { - InstanceManager::instance().clearCurrentInstance(); + InstanceManager::singleton().clearCurrentInstance(); return RestartExitCode; } else { return 1; @@ -650,7 +650,7 @@ int doOneRun( // resets things when MO is "restarted" resetForRestart(cl); - auto& m = InstanceManager::instance(); + auto& m = InstanceManager::singleton(); auto currentInstance = m.currentInstance(); if (!currentInstance) @@ -689,7 +689,7 @@ int doOneRun( if (!setLogDirectory(dataPath)) { reportError("Failed to create log folder"); - InstanceManager::instance().clearCurrentInstance(); + InstanceManager::singleton().clearCurrentInstance(); return 1; } @@ -724,10 +724,10 @@ int main(int argc, char *argv[]) tt.stop(); if (cl.instance()) - InstanceManager::instance().overrideInstance(*cl.instance()); + InstanceManager::singleton().overrideInstance(*cl.instance()); if (cl.profile()) { - InstanceManager::instance().overrideProfile(*cl.profile()); + InstanceManager::singleton().overrideProfile(*cl.profile()); } // makes plugin data path available to plugins, see diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 02900571..ea8a0efe 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -837,7 +837,7 @@ void MainWindow::setupToolbar() log::warn("no separator found on the toolbar, icons won't be right-aligned"); } - if (!InstanceManager::instance().allowedToChangeInstance()) { + if (!InstanceManager::singleton().allowedToChangeInstance()) { ui->actionChange_Game->setVisible(false); } } diff --git a/src/processrunner.cpp b/src/processrunner.cpp index 8ee0914b..bc4e6227 100644 --- a/src/processrunner.cpp +++ b/src/processrunner.cpp @@ -586,7 +586,7 @@ ProcessRunner& ProcessRunner::setFromExecutable(const Executable& exe) ProcessRunner& ProcessRunner::setFromShortcut(const MOShortcut& shortcut) { - const auto currentInstance = InstanceManager::instance().currentInstance(); + const auto currentInstance = InstanceManager::singleton().currentInstance(); if (currentInstance) { diff --git a/src/statusbar.cpp b/src/statusbar.cpp index aefabc73..8fa43d5b 100644 --- a/src/statusbar.cpp +++ b/src/statusbar.cpp @@ -154,7 +154,7 @@ void StatusBar::updateNormalMessage(OrganizerCore& core) } QString instance = "?"; - if (auto i=InstanceManager::instance().currentInstance()) + if (auto i=InstanceManager::singleton().currentInstance()) instance = i->name(); QString profile = core.profileName(); -- cgit v1.3.1