diff options
| -rw-r--r-- | src/instancemanager.cpp | 278 | ||||
| -rw-r--r-- | src/instancemanager.h | 109 | ||||
| -rw-r--r-- | src/instancemanagerdialog.cpp | 347 | ||||
| -rw-r--r-- | src/instancemanagerdialog.h | 80 | ||||
| -rw-r--r-- | src/instancemanagerdialog.ui | 2 | ||||
| -rw-r--r-- | src/main.cpp | 8 |
6 files changed, 456 insertions, 368 deletions
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 <http://www.gnu.org/licenses/>. 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::Object> 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<Object> 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<Object> 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<Object> 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<Object> 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<Object> 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<Object> 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<Instance> 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<QDir> InstanceManager::globalInstancePaths() const +std::vector<QString> InstanceManager::globalInstancePaths() const { const std::set<QString> ignore = { "cache", "qtwebengine", @@ -379,7 +597,7 @@ std::vector<QDir> InstanceManager::globalInstancePaths() const const QDir root(globalInstancesRootPath()); const auto dirs = root.entryList(QDir::Dirs | QDir::NoDotAndDotDot); - std::vector<QDir> list; + std::vector<QString> 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 + // + 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 // - QDir directory() const; + 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<Object> 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<QDir> globalInstancePaths() const; + std::vector<QString> 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<Object> 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<Object> 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<Object> 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<Object> 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<Object> 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<Object> 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<Object> 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<Settings> 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<InstanceInfo>(d, false)); + m_instances.push_back(std::make_unique<Instance>(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<InstanceInfo>(m.portablePath(), true)); + std::make_unique<Instance>(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<Instance>(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<std::size_t>(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::InstanceManagerDialog> ui; const PluginContainer& m_pc; - std::vector<std::unique_ptr<InstanceInfo>> m_instances; + std::vector<std::unique_ptr<Instance>> 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 @@ <item> <widget class="QPushButton" name="deleteInstance"> <property name="text"> - <string>Delete instance</string> + <string>Delete instance...</string> </property> <property name="icon"> <iconset resource="resources.qrc"> 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(); |
