/*
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 .
*/
#include "instancemanager.h"
#include "selectiondialog.h"
#include
#include
#include
#include
#include
#include
#include
#include
static const char COMPANY_NAME[] = "Tannin";
static const char APPLICATION_NAME[] = "Mod Organizer";
static const char INSTANCE_KEY[] = "CurrentInstance";
InstanceManager::InstanceManager()
: m_AppSettings(COMPANY_NAME, APPLICATION_NAME)
{
}
InstanceManager &InstanceManager::instance()
{
static InstanceManager s_Instance;
return s_Instance;
}
QString InstanceManager::currentInstance() const
{
return m_AppSettings.value(INSTANCE_KEY, "").toString();
}
void InstanceManager::clearCurrentInstance()
{
setCurrentInstance("");
m_Reset = true;
}
void InstanceManager::setCurrentInstance(const QString &name)
{
m_AppSettings.setValue(INSTANCE_KEY, name);
}
QString InstanceManager::queryInstanceName() const
{
QString instanceId;
while (instanceId.isEmpty()) {
QInputDialog dialog;
// 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({ "Oblivion", "Skyrim", "Fallout 3",
"Fallout NV", "Fallout 4" });
dialog.setComboBoxEditable(true);
dialog.setWindowTitle(QObject::tr("Enter Instance Name"));
dialog.setLabelText(QObject::tr("Name"));
if (dialog.exec() == QDialog::Rejected) {
throw MOBase::MyException(QObject::tr("Canceled"));
}
instanceId = dialog.textValue().replace(QRegExp("[^0-9a-zA-Z ]"), "");
}
return instanceId;
}
QString InstanceManager::chooseInstance(const QStringList &instanceList) const
{
enum class Special : uint8_t {
NewInstance,
Portable
};
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, ...). Use multiple "
"instances for different games. If your MO folder is "
"writable, you can also store a single instance locally (called "
"a portable install).")),
nullptr);
selection.disableCancel();
for (const QString &instance : instanceList) {
selection.addChoice(instance, "", instance);
}
selection.addChoice(QIcon(":/MO/gui/add"), QObject::tr("New"),
QObject::tr("Create a new instance."),
static_cast(Special::NewInstance));
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));
}
if (selection.exec() == QDialog::Rejected) {
qDebug("rejected");
throw MOBase::MyException(QObject::tr("Canceled"));
}
QVariant choice = selection.getChoiceData();
if (choice.type() == QVariant::String) {
return choice.toString();
} else {
switch (choice.value()) {
case Special::NewInstance: return queryInstanceName();
case Special::Portable: return QString();
default: throw std::runtime_error("invalid selection");
}
}
}
QString InstanceManager::instancePath() const
{
return QDir::fromNativeSeparators(
QStandardPaths::writableLocation(QStandardPaths::DataLocation));
}
QStringList InstanceManager::instances() const
{
return QDir(instancePath()).entryList(QDir::Dirs | QDir::NoDotAndDotDot);
}
bool InstanceManager::portableInstall() const
{
return QFile::exists(qApp->applicationDirPath() + "/" +
QString::fromStdWString(AppConfig::iniFileName()));
}
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 (instanceId.isEmpty() && portableInstall() && !m_Reset) {
// startup, apparently using portable mode before
return qApp->applicationDirPath();
}
QString dataPath = QDir::fromNativeSeparators(
QStandardPaths::writableLocation(QStandardPaths::DataLocation)
+ "/" + instanceId);
if (instanceId.isEmpty() || !QFileInfo::exists(dataPath)) {
instanceId = chooseInstance(instances());
if (!instanceId.isEmpty()) {
dataPath = QDir::fromNativeSeparators(
QStandardPaths::writableLocation(QStandardPaths::DataLocation)
+ "/" + instanceId);
}
}
if (instanceId.isEmpty() || !QFileInfo::exists(dataPath)) {
return qApp->applicationDirPath();
} else {
setCurrentInstance(instanceId);
createDataPath(dataPath);
return dataPath;
}
}