From dc2ada2a4249917f538938298deb193ed1dd6bb9 Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Fri, 6 Nov 2020 13:56:04 -0500
Subject: added ... to the "Delete Instance" button to make it less scary
merged InstanceInfo into Instance, most of it was redundant added logging
before deleting instance added Instance::readFromIni(), contains stuff that
used to be in setup(), used by instance dialog replaced QDir with QString in
a few places, I hate QDir
---
src/instancemanager.cpp | 278 +++++++++++++++++++++++++++++----
src/instancemanager.h | 109 ++++++++++---
src/instancemanagerdialog.cpp | 347 +++++-------------------------------------
src/instancemanagerdialog.h | 80 +++++++++-
src/instancemanagerdialog.ui | 2 +-
src/main.cpp | 8 +-
6 files changed, 456 insertions(+), 368 deletions(-)
(limited to 'src')
diff --git a/src/instancemanager.cpp b/src/instancemanager.cpp
index e7c2a611..fbafc6e8 100644
--- a/src/instancemanager.cpp
+++ b/src/instancemanager.cpp
@@ -38,7 +38,7 @@ along with Mod Organizer. If not, see .
using namespace MOBase;
-Instance::Instance(QDir dir, bool portable, QString profileName) :
+Instance::Instance(QString dir, bool portable, QString profileName) :
m_dir(std::move(dir)), m_portable(portable), m_plugin(nullptr),
m_profile(std::move(profileName))
{
@@ -49,7 +49,7 @@ QString Instance::name() const
if (isPortable())
return QObject::tr("Portable");
else
- return m_dir.dirName();
+ return QDir(m_dir).dirName();
}
QString Instance::gameName() const
@@ -62,11 +62,16 @@ QString Instance::gameDirectory() const
return m_gameDir;
}
-QDir Instance::directory() const
+QString Instance::directory() const
{
return m_dir;
}
+QString Instance::baseDirectory() const
+{
+ return m_baseDir;
+}
+
MOBase::IPluginGame* Instance::gamePlugin() const
{
return m_plugin;
@@ -87,13 +92,29 @@ bool Instance::isPortable() const
return m_portable;
}
-Instance::SetupResults Instance::setup(PluginContainer& plugins)
+bool Instance::isActive() const
+{
+ auto& m = InstanceManager::singleton();
+
+ if (auto i=m.currentInstance())
+ {
+ if (m_portable) {
+ return i->isPortable();
+ } else {
+ return (i->name() == name());
+ }
+ }
+
+ return false;
+}
+
+bool Instance::readFromIni()
{
Settings s(iniPath());
if (s.iniStatus() != QSettings::NoError) {
log::error("can't read ini {}", iniPath());
- return SetupResults::BadIni;
+ return false;
}
// game name and directory are from ini unless overridden by setGame()
@@ -107,27 +128,61 @@ Instance::SetupResults Instance::setup(PluginContainer& plugins)
m_gameDir = *v;
}
- // getting game plugin
- const auto r = getGamePlugin(plugins);
- if (r != SetupResults::Ok) {
- return r;
- }
-
- // getting game variant, error if it's missing and required by the plugin
if (m_gameVariant.isEmpty()) {
if (auto v=s.game().edition()) {
m_gameVariant = *v;
}
}
+ if (m_baseDir.isEmpty()) {
+ m_baseDir = s.paths().base();
+ }
+
+ // figuring out profile from ini if it's missing
+ getProfile(s);
+
+ return true;
+}
+
+Instance::SetupResults Instance::setup(PluginContainer& plugins)
+{
+ // read initial values from the ini
+ if (!readFromIni()) {
+ return SetupResults::BadIni;
+ }
+
+ // getting game plugin
+ const auto r = getGamePlugin(plugins);
+ if (r != SetupResults::Ok) {
+ return r;
+ }
+
+ // error if the variant missing and required by the plugin
if (m_gameVariant.isEmpty() && m_plugin->gameVariants().size() > 1) {
return SetupResults::MissingVariant;
} else {
m_plugin->setGameVariant(m_gameVariant);
}
- // figuring out profile from ini if it's missing
- getProfile(s);
+ // update the ini in case anything was missing
+ updateIni();
+
+ // the game directory may be different than what the plugin detected, the user
+ // can change it in the settings and might have multiple versions of the game
+ // installed
+ m_plugin->setGamePath(m_gameDir);
+
+ return SetupResults::Ok;
+}
+
+void Instance::updateIni()
+{
+ Settings s(iniPath());
+
+ if (s.iniStatus() != QSettings::NoError) {
+ log::error("can't open ini {}", iniPath());
+ return;
+ }
// updating the settings since some of these values might have been missing
s.game().setName(m_gameName);
@@ -138,13 +193,6 @@ Instance::SetupResults Instance::setup(PluginContainer& plugins)
// don't write a variant to the ini if the plugin doesn't require one
s.game().setEdition(m_gameVariant);
}
-
- // the game directory may be different than what the plugin detected, the user
- // can change it in the settings and might have multiple versions of the game
- // installed
- m_plugin->setGamePath(m_gameDir);
-
- return SetupResults::Ok;
}
void Instance::setGame(const QString& name, const QString& dir)
@@ -272,7 +320,6 @@ Instance::SetupResults Instance::getGamePlugin(PluginContainer& plugins)
}
}
-
void Instance::getProfile(const Settings& s)
{
if (!m_profile.isEmpty()) {
@@ -294,6 +341,176 @@ void Instance::getProfile(const Settings& s)
iniPath(), m_profile);
}
+// returns a list of files and folders that must be deleted when deleting
+// this instance
+//
+std::vector Instance::objectsForDeletion() const
+{
+ // native separators and ending slash
+ auto prettyDir = [](auto s) {
+ if (!s.endsWith("/") || !s.endsWith("\\")) {
+ s += "/";
+ }
+
+ return QDir::toNativeSeparators(s);
+ };
+
+ // native separators
+ auto prettyFile = [](auto s) {
+ return QDir::toNativeSeparators(s);
+ };
+
+
+ // lowercase, native separators and ending slash
+ auto canonicalDir = [](QString s) {
+ s = s.toLower();
+
+ if (!s.endsWith("/") || !s.endsWith("\\")) {
+ s += "/";
+ }
+
+ return QDir::toNativeSeparators(s);
+ };
+
+ // lower and native separators
+ auto canonicalFile = [](auto s) {
+ return QDir::toNativeSeparators(s.toLower());
+ };
+
+
+
+ // whether the given directory is contained in the root
+ auto dirInRoot = [&](const QString& root, const QString& dir) {
+ return canonicalDir(dir).startsWith(canonicalDir(root));
+ };
+
+ // whether the given file is contained in the root
+ auto fileInRoot = [&](const QString& root, const QString& file) {
+ return canonicalFile(file).startsWith(canonicalDir(root));
+ };
+
+
+ Settings settings(iniPath());
+
+ if (settings.iniStatus() != QSettings::NoError) {
+ log::error("can't read ini {}", iniPath());
+ return {};
+ }
+
+
+
+ const QString loc = directory();
+ const QString base = settings.paths().base();
+
+
+ // directories that might contain the individual files and directories
+ // set in the path settings
+ std::vector roots;
+
+ // a portable instance has its location in the installation directory,
+ // don't delete that
+ if (!isPortable()) {
+ if (QDir(loc).exists()) {
+ roots.push_back({loc, true});
+ }
+ }
+
+ // the base directory is the location directory by default, don't add it
+ // if it's the same
+ if (canonicalDir(base) != canonicalDir(loc)) {
+ if (QDir(base).exists()) {
+ roots.push_back({base, false});
+ }
+ }
+
+
+ // all the directories that are part of an instance; none of them are
+ // mandatory for deletion
+ const std::vector dirs = {
+ settings.paths().downloads(),
+ settings.paths().mods(),
+ settings.paths().cache(),
+ settings.paths().profiles(),
+ settings.paths().overwrite(),
+ QDir(m_dir).filePath(QString::fromStdWString(AppConfig::dumpsDir())),
+ QDir(m_dir).filePath(QString::fromStdWString(AppConfig::logPath())),
+ };
+
+ // all the files that are part of an instance
+ const std::vector files = {
+ {iniPath(), true}, // the ini file must be deleted
+ };
+
+
+ // this will contain the root directories, plus all the individual
+ // directories that are not inside these roots
+ std::vector cleanDirs;
+
+ for (const auto& f : dirs) {
+ bool inRoots = false;
+
+ for (const auto& root : roots) {
+ if (dirInRoot(root.path, f.path)) {
+ inRoots = true;
+ break;
+ }
+ }
+
+ if (!inRoots) {
+ // not in roots, this is a path that was changed by the user; make
+ // sure it exists
+ if (QDir(f.path).exists()) {
+ cleanDirs.push_back({prettyDir(f.path), f.mandatoryDelete});
+ }
+ }
+ }
+
+ // prepending the roots
+ for (auto itor=roots.rbegin(); itor!=roots.rend(); ++itor) {
+ cleanDirs.insert(
+ cleanDirs.begin(),
+ {prettyDir(itor->path), itor->mandatoryDelete});
+ }
+
+
+
+ // this will contain the individual files that are not inside the roots;
+ // not that this only contains the INI file for now, so most of this is
+ // useless
+ std::vector cleanFiles;
+
+ for (const auto& f : files) {
+ bool inRoots = false;
+
+ for (const auto& root : roots) {
+ if (fileInRoot(root.path, f.path)) {
+ inRoots = true;
+ break;
+ }
+ }
+
+ if (!inRoots) {
+ // not in roots, this is a path that was changed by the user; make
+ // sure it exists
+ if (QFileInfo(f.path).exists()) {
+ cleanFiles.push_back({prettyFile(f.path), f.mandatoryDelete});
+ }
+ }
+ }
+
+
+ // contains all the directories and files to be deleted
+ std::vector all;
+ all.insert(all.end(), cleanDirs.begin(), cleanDirs.end());
+ all.insert(all.end(), cleanFiles.begin(), cleanFiles.end());
+
+ // mandatory on top
+ std::stable_sort(all.begin(), all.end());
+
+ return all;
+}
+
+
InstanceManager::InstanceManager()
{
@@ -327,20 +544,20 @@ std::optional InstanceManager::currentInstance() const
if (!allowedToChangeInstance()) {
// force portable instance
- return Instance(QDir(portablePath()), true, profile);
+ return Instance(portablePath(), true, profile);
}
if (name.isEmpty()) {
if (portableInstanceExists()) {
// use portable
- return Instance(QDir(portablePath()), true, profile);
+ return Instance(portablePath(), true, profile);
} else {
// no instance set
return {};
}
}
- return Instance(QDir(instancePath(name)), false, profile);
+ return Instance(instancePath(name), false, profile);
}
void InstanceManager::clearCurrentInstance()
@@ -365,12 +582,13 @@ QString InstanceManager::globalInstancesRootPath() const
QStandardPaths::writableLocation(QStandardPaths::DataLocation));
}
-QString InstanceManager::iniPath(const QDir& instanceDir) const
+QString InstanceManager::iniPath(const QString& instanceDir) const
{
- return instanceDir.filePath(QString::fromStdWString(AppConfig::iniFileName()));
+ return QDir(instanceDir).filePath(
+ QString::fromStdWString(AppConfig::iniFileName()));
}
-std::vector InstanceManager::globalInstancePaths() const
+std::vector InstanceManager::globalInstancePaths() const
{
const std::set ignore = {
"cache", "qtwebengine",
@@ -379,7 +597,7 @@ std::vector InstanceManager::globalInstancePaths() const
const QDir root(globalInstancesRootPath());
const auto dirs = root.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
- std::vector list;
+ std::vector list;
for (auto&& d : dirs) {
if (!ignore.contains(QFileInfo(d).fileName().toLower())) {
@@ -416,7 +634,7 @@ bool InstanceManager::allowedToChangeInstance() const
}
const MOBase::IPluginGame* InstanceManager::gamePluginForDirectory(
- const QDir& instanceDir, const PluginContainer& plugins) const
+ const QString& instanceDir, const PluginContainer& plugins) const
{
const QString ini = iniPath(instanceDir);
diff --git a/src/instancemanager.h b/src/instancemanager.h
index e2ceb743..161df739 100644
--- a/src/instancemanager.h
+++ b/src/instancemanager.h
@@ -55,15 +55,64 @@ public:
};
+ // a file or directory owned by this instance, used by objectsForDeletion()
+ //
+ struct Object
+ {
+ // path to the file or directory
+ QString path;
+
+ // whether this object must be deleted to properly delete the instance;
+ // typically true only for the instance directory itself, but not for the
+ // base directory, etc.
+ bool mandatoryDelete;
+
+
+ Object(QString p, bool d=false)
+ : path(std::move(p)), mandatoryDelete(d)
+ {
+ }
+
+ // puts mandatory delete on top
+ //
+ bool operator<(const Object& o) const
+ {
+ if (mandatoryDelete && !o.mandatoryDelete) {
+ return true;
+ } else if (!mandatoryDelete && o.mandatoryDelete) {
+ return false;
+ }
+
+ return false;
+ }
+ };
+
+
+
// an instance that lives in the given directory; `portable` must be `true`
// if this is a portable instance
//
// `profileName` can be given to override what's in the INI; this typically
// happens when the profile is overriden on the command line
//
- Instance(QDir dir, bool portable, QString profileName={});
+ Instance(QString dir, bool portable, QString profileName={});
+
+
+ // reads in values from the INI if they were not given yet:
+ // - game name
+ // - game directory
+ // - game variant
+ // - profile name
+ //
+ // note that setup() already calls this
+ //
+ // returns false if the ini couldn't be read from
+ //
+ bool readFromIni();
+
- // finds the appropriate game plugin and sets it up so MO can use it
+ // finds the appropriate game plugin and sets it up so MO can use it; this
+ // calls readFromIni() first
//
// setup() tries to recover from some errors, but can fail for a variety of
// reasons, see SetupResults
@@ -88,22 +137,32 @@ public:
QString name() const;
// returns either:
- // 1) the game name from the INI,
- // 2) gameName() from the game plugin if it was missing, or
- // 3) whatever was given in setGame()
+ // 1) the game name from the INI, if readFromIni() was called;
+ // 2) gameName() from the game plugin if it was missing and setup() was
+ // called; or
+ // 3) whatever was given in setGame()
//
QString gameName() const;
// returns either:
- // 1) the game directory from the INI,
- // 2) gameDirectory() from the game plugin if it was missing, or
- // 3) whatever was given in setGame()
+ // 1) the game directory from the INI, if readFromIni() was called;
+ // 2) gameDirectory() from the game plugin if it was missing and setup()
+ // was called; or
+ // 3) whatever was given in setGame()
//
QString gameDirectory() const;
- // returns the instance directory; can be called without setup()
+ // returns the instance directory as given in the constructor
//
- QDir directory() const;
+ QString directory() const;
+
+ // returns the base directory; empty if readFromIni() hasn't been called
+ //
+ QString baseDirectory() const;
+
+ // returns whether this is a portable instance, as given in the constructor
+ //
+ bool isPortable() const;
// returns the selected game plugin; will return null if setup() hasn't been
// called, or if it failed
@@ -111,8 +170,8 @@ public:
MOBase::IPluginGame* gamePlugin() const;
// returns either:
- // 1) the profile name given in the constructor,
- // 2) the profile name from the INI, or
+ // 1) the profile name given in the constructor if not empty;
+ // 2) the profile name from the INI if readFromIni() was called, or
// 3) the default profile name if it's missing (see
// AppConfig::defaultProfileName())
//
@@ -123,15 +182,21 @@ public:
//
QString iniPath() const;
- // returns whether this is a portable instance; this is the flag given in the
- // constructor
+ // whether this is the currently active instance in MO
//
- bool isPortable() const;
+ bool isActive() const;
+
+ // returns a list of files and directories that must be deleted when deleting
+ // this instance; this will read the INI and fail if it's not accessible
+ //
+ // returns an empty list on failure
+ //
+ std::vector objectsForDeletion() const;
private:
- QDir m_dir;
+ QString m_dir;
bool m_portable;
- QString m_gameName, m_gameDir, m_gameVariant;
+ QString m_gameName, m_gameDir, m_gameVariant, m_baseDir;
MOBase::IPluginGame* m_plugin;
QString m_profile;
@@ -142,6 +207,10 @@ private:
// figures out the profile name for this instance
//
void getProfile(const Settings& s);
+
+ // updates the ini with the given values and the ones found by setup()
+ //
+ void updateIni();
};
@@ -175,7 +244,7 @@ public:
// returns null if all of this fails
//
const MOBase::IPluginGame* gamePluginForDirectory(
- const QDir& dir, const PluginContainer& plugins) const;
+ const QString& dir, const PluginContainer& plugins) const;
// clears the instance name from the registry; on restart, this will make MO
// either select the portable instance if it exists, or display the instance
@@ -221,7 +290,7 @@ public:
// returns the list of absolute path to all existing global instances; this
// does not include the portable instance
//
- std::vector globalInstancePaths() const;
+ std::vector globalInstancePaths() const;
// returns `name` modified so that it is a valid instance name
//
@@ -253,7 +322,7 @@ public:
// returns the absolute path to the INI file for the given instance directory;
// the file may not exist
//
- QString iniPath(const QDir& instanceDir) const;
+ QString iniPath(const QString& instanceDir) const;
private:
InstanceManager();
diff --git a/src/instancemanagerdialog.cpp b/src/instancemanagerdialog.cpp
index c2108f9d..c199fa7b 100644
--- a/src/instancemanagerdialog.cpp
+++ b/src/instancemanagerdialog.cpp
@@ -13,292 +13,19 @@
using namespace MOBase;
-class InstanceInfo
-{
-public:
- struct Object
- {
- QString path;
- bool mandatoryDelete;
-
- Object(QString p, bool d=false)
- : path(std::move(p)), mandatoryDelete(d)
- {
- }
-
- bool operator<(const Object& o) const
- {
- if (mandatoryDelete && !o.mandatoryDelete) {
- return true;
- } else if (!mandatoryDelete && o.mandatoryDelete) {
- return false;
- }
-
- return false;
- }
- };
-
-
- InstanceInfo(QDir dir, bool isPortable)
- : m_portable(isPortable)
- {
- setDir(dir);
- }
-
- void setDir(const QDir& dir)
- {
- m_dir = dir;
- m_settings.reset(new Settings(InstanceManager::singleton().iniPath(dir)));
- }
-
- QString name() const
- {
- if (m_portable) {
- return QObject::tr("Portable");
- } else {
- 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 QDir::toNativeSeparators(*n);
- } else {
- return {};
- }
- }
-
- QString location() const
- {
- return QDir::toNativeSeparators(m_dir.path());
- }
-
- QString baseDirectory() const
- {
- return QDir::toNativeSeparators(m_settings->paths().base());
- }
-
- QString iniFile() const
- {
- return InstanceManager::singleton().iniPath(m_dir);
- }
-
- QIcon icon(const PluginContainer& plugins) const
- {
- const auto* game = InstanceManager::singleton().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;
- }
-
- bool isActive() const
- {
- auto& m = InstanceManager::singleton();
-
- if (auto i=m.currentInstance())
- {
- if (m_portable) {
- return i->isPortable();
- } else {
- return (i->name() == name());
- }
- }
-
- return false;
- }
-
- // returns a list of files and folders that must be deleted when deleting
- // this instance
- //
- std::vector objectsForDeletion() const
- {
- // native separators and ending slash
- auto prettyDir = [](auto s) {
- if (!s.endsWith("/") || !s.endsWith("\\")) {
- s += "/";
- }
-
- return QDir::toNativeSeparators(s);
- };
-
- // native separators
- auto prettyFile = [](auto s) {
- return QDir::toNativeSeparators(s);
- };
-
-
- // lowercase, native separators and ending slash
- auto canonicalDir = [](auto s) {
- s = s.toLower();
- if (!s.endsWith("/") || !s.endsWith("\\")) {
- s += "/";
- }
-
- return QDir::toNativeSeparators(s);
- };
-
- // lower and native separators
- auto canonicalFile = [](auto s) {
- return QDir::toNativeSeparators(s.toLower());
- };
-
-
-
- // whether the given directory is contained in the root
- auto dirInRoot = [&](auto root, auto dir) {
- return canonicalDir(dir).startsWith(canonicalDir(root));
- };
-
- // whether the given file is contained in the root
- auto fileInRoot = [&](auto root, auto file) {
- return canonicalFile(file).startsWith(canonicalDir(root));
- };
-
+QIcon instanceIcon(const PluginContainer& pc, const Instance& i)
+{
+ const auto* game = InstanceManager::singleton()
+ .gamePluginForDirectory(i.directory(), pc);
+ if (game)
+ return game->gameIcon();
- const auto loc = location();
- const auto base = m_settings->paths().base();
-
-
- // directories that might contain the individual files and directories
- // set in the path settings
- std::vector roots;
-
- // a portable instance has its location in the installation directory,
- // don't delete that
- if (!isPortable()) {
- if (QDir(loc).exists()) {
- roots.push_back({loc, true});
- }
- }
-
- // the base directory is the location directory by default, don't add it
- // if it's the same
- if (canonicalDir(base) != canonicalDir(loc)) {
- if (QDir(base).exists()) {
- roots.push_back({base, false});
- }
- }
-
-
- // all the directories that are part of an instance; none of them are
- // mandatory for deletion
- const std::vector dirs = {
- m_settings->paths().downloads(),
- m_settings->paths().mods(),
- m_settings->paths().cache(),
- m_settings->paths().profiles(),
- m_settings->paths().overwrite(),
- m_dir.filePath(QString::fromStdWString(AppConfig::dumpsDir())),
- m_dir.filePath(QString::fromStdWString(AppConfig::logPath())),
- };
-
- // all the files that are part of an instance
- const std::vector files = {
- {iniFile(), true}, // the ini file must be deleted
- };
-
-
- // this will contain the root directories, plus all the individual
- // directories that are not inside these roots
- std::vector cleanDirs;
-
- for (const auto& f : dirs) {
- bool inRoots = false;
-
- for (const auto& root : roots) {
- if (dirInRoot(root.path, f.path)) {
- inRoots = true;
- break;
- }
- }
-
- if (!inRoots) {
- // not in roots, this is a path that was changed by the user; make
- // sure it exists
- if (QDir(f.path).exists()) {
- cleanDirs.push_back({prettyDir(f.path), f.mandatoryDelete});
- }
- }
- }
-
- // prepending the roots
- for (auto itor=roots.rbegin(); itor!=roots.rend(); ++itor) {
- cleanDirs.insert(
- cleanDirs.begin(),
- {prettyDir(itor->path), itor->mandatoryDelete});
- }
-
-
-
- // this will contain the individual files that are not inside the roots;
- // not that this only contains the INI file for now, so most of this is
- // useless
- std::vector cleanFiles;
-
- for (const auto& f : files) {
- bool inRoots = false;
-
- for (const auto& root : roots) {
- if (fileInRoot(root.path, f.path)) {
- inRoots = true;
- break;
- }
- }
-
- if (!inRoots) {
- // not in roots, this is a path that was changed by the user; make
- // sure it exists
- if (QFileInfo(f.path).exists()) {
- cleanFiles.push_back({prettyFile(f.path), f.mandatoryDelete});
- }
- }
- }
-
-
- // contains all the directories and files to be deleted
- std::vector all;
- all.insert(all.end(), cleanDirs.begin(), cleanDirs.end());
- all.insert(all.end(), cleanFiles.begin(), cleanFiles.end());
-
- // mandatory on top
- std::stable_sort(all.begin(), all.end());
-
- return all;
- }
-
-private:
- const bool m_portable;
- QDir m_dir;
- std::unique_ptr m_settings;
-};
+ QPixmap empty(32, 32);
+ empty.fill(QColor(0, 0, 0, 0));
+ return QIcon(empty);
+}
InstanceManagerDialog::~InstanceManagerDialog() = default;
@@ -352,7 +79,7 @@ void InstanceManagerDialog::updateInstances()
m_instances.clear();
for (auto&& d : m.globalInstancePaths()) {
- m_instances.push_back(std::make_unique(d, false));
+ m_instances.push_back(std::make_unique(d, false));
}
// sort first, prepend portable after so it's always on top
@@ -363,7 +90,12 @@ void InstanceManagerDialog::updateInstances()
if (m.portableInstanceExists()) {
m_instances.insert(
m_instances.begin(),
- std::make_unique(m.portablePath(), true));
+ std::make_unique(m.portablePath(), true));
+ }
+
+ // read all inis, ignore errors
+ for (auto&& i : m_instances) {
+ i->readFromIni();
}
}
@@ -381,7 +113,7 @@ void InstanceManagerDialog::updateList()
const auto& ii = *m_instances[i];
auto* item = new QStandardItem(ii.name());
- item->setIcon(ii.icon(m_pc));
+ item->setIcon(instanceIcon(m_pc, ii));
m_model->appendRow(item);
@@ -568,9 +300,9 @@ void InstanceManagerDialog::rename()
return;
}
- const QString src = i->location();
+ const QString src = i->directory();
const QString dest = QDir::toNativeSeparators(
- QFileInfo(i->location()).dir().path() + "/" + newName);
+ QFileInfo(src).dir().path() + "/" + newName);
const auto r = shell::Rename(src, dest, false);
if (!r) {
@@ -582,15 +314,19 @@ void InstanceManagerDialog::rename()
return;
}
+ auto newInstance = std::make_unique(dest, false);
+ i = newInstance.get();
+
m_model->item(selIndex)->setText(newName);
- i->setDir(dest);
+ m_instances[selIndex] = std::move(newInstance);
+
fillData(*i);
}
void InstanceManagerDialog::exploreLocation()
{
if (const auto* i=singleSelection()) {
- shell::Explore(i->location());
+ shell::Explore(i->directory());
}
}
@@ -604,14 +340,14 @@ void InstanceManagerDialog::exploreBaseDirectory()
void InstanceManagerDialog::exploreGame()
{
if (const auto* i=singleSelection()) {
- shell::Explore(i->gamePath());
+ shell::Explore(i->gameDirectory());
}
}
void InstanceManagerDialog::openINI()
{
if (const auto* i=singleSelection()) {
- shell::Open(i->iniFile());
+ shell::Open(i->iniPath());
}
}
@@ -707,6 +443,15 @@ void InstanceManagerDialog::setRestartOnSelect(bool b)
bool InstanceManagerDialog::doDelete(const QStringList& files, bool recycle)
{
+ // logging
+ for (auto&& f : files) {
+ if (recycle) {
+ log::info("will recycle {}", f);
+ } else {
+ log::info("will delete {}", f);
+ }
+ }
+
if (MOBase::shellDelete(files, recycle, this)) {
return true;
}
@@ -771,17 +516,7 @@ std::size_t InstanceManagerDialog::singleSelectionIndex() const
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 Instance* InstanceManagerDialog::singleSelection() const
{
const auto i = singleSelectionIndex();
if (i == NoSelection) {
@@ -791,13 +526,13 @@ const InstanceInfo* InstanceManagerDialog::singleSelection() const
return m_instances[i].get();
}
-void InstanceManagerDialog::fillData(const InstanceInfo& ii)
+void InstanceManagerDialog::fillData(const Instance& ii)
{
ui->name->setText(ii.name());
- ui->location->setText(ii.location());
+ ui->location->setText(ii.directory());
ui->baseDirectory->setText(ii.baseDirectory());
ui->gameName->setText(ii.gameName());
- ui->gameDir->setText(ii.gamePath());
+ ui->gameDir->setText(ii.gameDirectory());
setButtonsEnabled(true);
const auto& m = InstanceManager::singleton();
diff --git a/src/instancemanagerdialog.h b/src/instancemanagerdialog.h
index 5b08ffc2..94f379ca 100644
--- a/src/instancemanagerdialog.h
+++ b/src/instancemanagerdialog.h
@@ -6,9 +6,11 @@
namespace Ui { class InstanceManagerDialog; };
-class InstanceInfo;
+class Instance;
class PluginContainer;
+// a dialog to manage existing instances
+//
class InstanceManagerDialog : public QDialog
{
Q_OBJECT
@@ -19,21 +21,61 @@ public:
~InstanceManagerDialog();
+ // selects the instance having the given index in the list
+ //
void select(std::size_t i);
+
+ // selects the instance by name
+ //
void select(const QString& name);
+
+ // select the instance that is currently in use in MO
+ //
void selectActiveInstance();
+
+ // switches to the selected instance; restarts MO, unless
+ // was called setRestartOnSelect(false)
+ //
void openSelectedInstance();
+
+ // renames the currently selected instance
+ //
void rename();
+
+ // explores the directory of the selected instance
+ //
void exploreLocation();
+
+ // explores the base directory of the selected instance
+ //
void exploreBaseDirectory();
+
+ // explores the game directory of the selected instance
+ //
void exploreGame();
+
+ // converts the selected, portable instance to a global one; not implemented
+ //
void convertToGlobal();
+
+ // converts the selected, global instance to a portable one; not implemented
+ //
void convertToPortable();
+
+ // opens the ini of the selected instance in the shell
+ //
void openINI();
+
+ // deletes the selected instance
+ //
void deleteInstance();
+
+ // sets whether the dialog should restart MO when selecting an instance; this
+ // is false on startup when no instances exist
+ //
void setRestartOnSelect(bool b);
private:
@@ -41,27 +83,51 @@ private:
std::unique_ptr ui;
const PluginContainer& m_pc;
- std::vector> m_instances;
+ std::vector> m_instances;
MOBase::FilterWidget m_filter;
QStandardItemModel* m_model;
bool m_restartOnSelect;
+ // refreshes the list instances from disk
+ //
void updateInstances();
+ // updates the ui for the selected instance
+ //
void onSelection();
+
+ // opens the create instance dialog
+ //
void createNew();
+
+ // returns the index of selected instance, NoSelection if none
+ //
std::size_t singleSelectionIndex() const;
- InstanceInfo* singleSelection();
- const InstanceInfo* singleSelection() const;
+ // returns the InstanceInfo associated with the selected instance, null if
+ // none
+ //
+ const Instance* singleSelection() const;
+
+ // fills the instance list on the ui
+ //
void updateList();
- void fillData(const InstanceInfo& ii);
+
+ // fills the ui for the selected instance
+ //
+ void fillData(const Instance& ii);
+
+ // clears the ui when there's no selection
+ //
void clearData();
+
+ // enables/disables buttons like rename, explore...
+ //
void setButtonsEnabled(bool b);
- bool deletePortable(const InstanceInfo& ii);
- bool deleteGlobal(const InstanceInfo& ii);
+ // deletes the given files, returns false on error
+ //
bool doDelete(const QStringList& files, bool recycle);
};
diff --git a/src/instancemanagerdialog.ui b/src/instancemanagerdialog.ui
index 410b58c1..45b99e21 100644
--- a/src/instancemanagerdialog.ui
+++ b/src/instancemanagerdialog.ui
@@ -330,7 +330,7 @@
-
- Delete instance
+ Delete instance...
diff --git a/src/main.cpp b/src/main.cpp
index 7f0886f6..a2653685 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -662,17 +662,17 @@ int doOneRun(
}
else
{
- if (!currentInstance->directory().exists()) {
+ if (!QDir(currentInstance->directory()).exists()) {
// the previously used instance doesn't exist anymore
if (m.hasAnyInstances()) {
criticalOnTop(QObject::tr(
"Instance at '%1' not found. Select another instance.")
- .arg(currentInstance->directory().absolutePath()));
+ .arg(currentInstance->directory()));
} else {
criticalOnTop(QObject::tr(
"Instance at '%1' not found. You must create a new instance")
- .arg(currentInstance->directory().absolutePath()));
+ .arg(currentInstance->directory()));
}
currentInstance = selectInstance();
@@ -682,7 +682,7 @@ int doOneRun(
}
}
- const QString dataPath = currentInstance->directory().path();
+ const QString dataPath = currentInstance->directory();
application.setProperty("dataPath", dataPath);
setExceptionHandlers();
--
cgit v1.3.1