summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/createinstancedialog.cpp65
-rw-r--r--src/createinstancedialog.h99
-rw-r--r--src/createinstancedialogpages.cpp76
-rw-r--r--src/createinstancedialogpages.h4
-rw-r--r--src/instancemanagerdialog.cpp172
5 files changed, 243 insertions, 173 deletions
diff --git a/src/createinstancedialog.cpp b/src/createinstancedialog.cpp
index 13dd200c..c2cdde49 100644
--- a/src/createinstancedialog.cpp
+++ b/src/createinstancedialog.cpp
@@ -413,49 +413,6 @@ bool CreateInstanceDialog::canBack() const
return false;
}
-CreateInstanceDialog::Types CreateInstanceDialog::instanceType() const
-{
- return getSelected(&cid::Page::selectedInstanceType);
-}
-
-MOBase::IPluginGame* CreateInstanceDialog::game() const
-{
- return getSelected(&cid::Page::selectedGame);
-}
-
-QString CreateInstanceDialog::gameLocation() const
-{
- return getSelected(&cid::Page::selectedGameLocation);
-}
-
-QString CreateInstanceDialog::gameVariant() const
-{
- return getSelected(&cid::Page::selectedGameVariant);
-}
-
-QString CreateInstanceDialog::instanceName() const
-{
- return getSelected(&cid::Page::selectedInstanceName);
-}
-
-QString CreateInstanceDialog::dataPath() const
-{
- QString s;
-
- if (instanceType() == Portable) {
- s = QDir(InstanceManager::singleton().portablePath()).absolutePath();
- } else {
- s = InstanceManager::singleton().instancePath(instanceName());
- }
-
- return QDir::toNativeSeparators(s);
-}
-
-CreateInstanceDialog::Paths CreateInstanceDialog::paths() const
-{
- return getSelected(&cid::Page::selectedPaths);
-}
-
bool CreateInstanceDialog::switching() const
{
return m_switching;
@@ -482,7 +439,8 @@ void fixFilePath(QString& path)
path = QDir::toNativeSeparators(QFileInfo(path).absolutePath());
}
-CreateInstanceDialog::CreationInfo CreateInstanceDialog::creationInfo() const
+CreateInstanceDialog::CreationInfo
+CreateInstanceDialog::rawCreationInfo() const
{
const auto iniFilename = QString::fromStdWString(AppConfig::iniFileName());
@@ -491,12 +449,27 @@ CreateInstanceDialog::CreationInfo CreateInstanceDialog::creationInfo() const
ci.type = getSelected(&cid::Page::selectedInstanceType);
ci.game = getSelected(&cid::Page::selectedGame);
ci.gameLocation = getSelected(&cid::Page::selectedGameLocation);
- ci.gameVariant = getSelected(&cid::Page::selectedGameVariant);
+ ci.gameVariant = getSelected(&cid::Page::selectedGameVariant, ci.game);
ci.instanceName = getSelected(&cid::Page::selectedInstanceName);
ci.paths = getSelected(&cid::Page::selectedPaths);
- ci.dataPath = dataPath();
+
+ if (ci.type == Portable) {
+ ci.dataPath = QDir(InstanceManager::singleton().portablePath()).absolutePath();
+ } else {
+ ci.dataPath = InstanceManager::singleton().instancePath(ci.instanceName);
+ }
+
+ ci.dataPath = QDir::toNativeSeparators(ci.dataPath);
ci.iniPath = ci.dataPath + "/" + iniFilename;
+ return ci;
+}
+
+CreateInstanceDialog::CreationInfo
+CreateInstanceDialog::creationInfo() const
+{
+ auto ci = rawCreationInfo();
+
fixDirPath(ci.paths.base);
fixFilePath(ci.paths.ini);
diff --git a/src/createinstancedialog.h b/src/createinstancedialog.h
index 25e383eb..d541f45e 100644
--- a/src/createinstancedialog.h
+++ b/src/createinstancedialog.h
@@ -10,11 +10,25 @@ namespace cid { class Page; }
class PluginContainer;
class Settings;
+// this is a wizard for creating a new instance, it is made out of Page objects,
+// see createinstancedialogpages.h
+//
+// each page can give back one or more pieces of information that is collected
+// in creationInfo() and used by finish() to do the actual creation
+//
+// pages can be disabled if they return true in skip(), which happens globally
+// for some (IntroPage has a setting in the registry), depending on context
+// (NexusPage is skipped if the API key already exists) or explicitly (when
+// only some info about the instance is missing on startup, such as a game
+// variant)
+//
class CreateInstanceDialog : public QDialog
{
Q_OBJECT
public:
+ // instance type
+ //
enum Types
{
NoType = 0,
@@ -22,6 +36,10 @@ public:
Portable
};
+ // all the paths required by the instance, some may be empty, such as
+ // basically all of them except for `base` when the user doesn't use the
+ // "Advanced" part of the paths page
+ //
struct Paths
{
QString base;
@@ -34,6 +52,8 @@ public:
auto operator<=>(const Paths&) const = default;
};
+ // all the info filled in the various pages
+ //
struct CreationInfo
{
Types type;
@@ -53,10 +73,11 @@ public:
~CreateInstanceDialog();
Ui::CreateInstanceDialog* getUI();
-
const PluginContainer& pluginContainer();
Settings* settings();
+ // disables all the pages except for the given one, used on startup when some
+ // specific info is missing
template <class Page>
void setSinglePage(const QString& instanceName)
{
@@ -71,6 +92,8 @@ public:
setSinglePageImpl(instanceName);
}
+ // returns the page having the give path, or null
+ //
template <class Page>
Page* getPage()
{
@@ -83,24 +106,59 @@ public:
return nullptr;
}
+
+ // moves to the next page calls finish() if on the last one
+ //
void next();
+
+ // moves to the previous page, if any
+ //
void back();
+
+ // whether the current page reports that it is ready; if this is the last
+ // page, next() would call finish()
+ //
+ bool canNext() const;
+
+ // whether the current page is not the first one and there is an enabled page
+ // prior
+ //
+ bool canBack() const;
+
+ // selects the given page by index; this doesn't check if the page should be
+ // skipped
+ //
void selectPage(std::size_t i);
+
+ // moves by `d` pages, can be negative to move back
+ //
void changePage(int d);
+
+ // creates the instance and closes the dialog
+ //
void finish();
+
+ // updates the navigation buttons based on the current page
+ //
void updateNavigation();
+
+ // whether this is the last enabled page
+ //
bool isOnLastPage() const;
- Types instanceType() const;
- MOBase::IPluginGame* game() const;
- QString gameLocation() const;
- QString gameVariant() const;
- QString instanceName() const;
- QString dataPath() const;
- Paths paths() const;
+ // returns whether the user has requested to switch to the new instance
+ //
bool switching() const;
+ // gathers the info from all the pages as it appears, paths are not fixed;
+ // see creationInfo()
+ //
+ CreationInfo rawCreationInfo() const;
+
+ // gathers the info from all the pages: paths are converted to absolute and
+ // the base dir variable is expanded everywhere; see rawCreationInfo()
+ //
CreationInfo creationInfo() const;
private:
@@ -113,13 +171,26 @@ private:
bool m_singlePage;
+ // called from setSinglePage(), does whatever doesn't need the T
+ //
void setSinglePageImpl(const QString& instanceName);
- template <class T>
- T getSelected(T (cid::Page::*mf)() const) const
+ // adds a line to the creation log
+ //
+ void logCreation(const QString& s);
+ void logCreation(const std::wstring& s);
+
+ // calls the given member function on all pages until one returns an object
+ // that's not empty; used by gatherInfo()
+ //
+ template <class MF, class... Args>
+ auto getSelected(MF mf, Args&&... args) const
{
+ // return type
+ using T = decltype((std::declval<cid::Page>().*mf)(std::forward<Args>(args)...));
+
for (auto&& p : m_pages) {
- const auto t = (p.get()->*mf)();
+ const auto t = (p.get()->*mf)(std::forward<Args>(args)...);
if (t != T()) {
return t;
}
@@ -127,12 +198,6 @@ private:
return T();
}
-
- 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 f2a7ab36..a08bf5a1 100644
--- a/src/createinstancedialogpages.cpp
+++ b/src/createinstancedialogpages.cpp
@@ -101,7 +101,7 @@ QString Page::selectedGameLocation() const
return {};
}
-QString Page::selectedGameVariant() const
+QString Page::selectedGameVariant(MOBase::IPluginGame*) const
{
// no-op
return {};
@@ -246,6 +246,12 @@ void GamePage::select(IPluginGame* game, const QString& dir)
checked = nullptr;
} else {
checked = checkInstallation(path, checked);
+
+ if (checked) {
+ // remember this path
+ checked->dir = path;
+ checked->installed = true;
+ }
}
} else {
checked->dir = dir;
@@ -255,7 +261,9 @@ void GamePage::select(IPluginGame* game, const QString& dir)
}
m_selection = checked;
+
selectButton(checked);
+ updateButton(checked);
updateNavigation();
if (checked) {
@@ -611,7 +619,7 @@ bool VariantsPage::ready() const
bool VariantsPage::doSkip() const
{
- auto* g = m_dlg.game();
+ auto* g = m_dlg.rawCreationInfo().game;
if (!g) {
// shouldn't happen
return true;
@@ -623,7 +631,7 @@ bool VariantsPage::doSkip() const
void VariantsPage::activated()
{
- auto* g = m_dlg.game();
+ auto* g = m_dlg.rawCreationInfo().game;
if (m_previousGame != g) {
m_previousGame = g;
@@ -650,15 +658,13 @@ void VariantsPage::select(const QString& variant)
}
}
-QString VariantsPage::selectedGameVariant() const
+QString VariantsPage::selectedGameVariant(MOBase::IPluginGame* game) const
{
- auto* g = m_dlg.game();
- if (!g) {
- // shouldn't happen
+ if (!game) {
return {};
}
- const auto variants = g->gameVariants();
+ const auto variants = game->gameVariants();
if (variants.size() < 2) {
return {};
} else {
@@ -671,7 +677,7 @@ void VariantsPage::fillList()
ui->editions->clear();
m_buttons.clear();
- auto* g = m_dlg.game();
+ auto* g = m_dlg.rawCreationInfo().game;
if (!g) {
// shouldn't happen
return;
@@ -708,12 +714,12 @@ bool NamePage::ready() const
bool NamePage::doSkip() const
{
- return (m_dlg.instanceType() == CreateInstanceDialog::Portable);
+ return (m_dlg.rawCreationInfo().type == CreateInstanceDialog::Portable);
}
void NamePage::activated()
{
- auto* g = m_dlg.game();
+ auto* g = m_dlg.rawCreationInfo().game;
if (!g) {
// shouldn't happen, next should be disabled
return;
@@ -820,8 +826,8 @@ bool PathsPage::ready() const
void PathsPage::activated()
{
- const auto name = m_dlg.instanceName();
- const auto type = m_dlg.instanceType();
+ const auto name = m_dlg.rawCreationInfo().instanceName;
+ const auto type = m_dlg.rawCreationInfo().type;
const bool changed = (m_lastInstanceName != name) || (m_lastType != type);
@@ -829,7 +835,7 @@ void PathsPage::activated()
checkPaths();
updateNavigation();
- m_label.setText(m_dlg.game()->gameName());
+ m_label.setText(m_dlg.rawCreationInfo().game->gameName());
m_lastInstanceName = name;
m_lastType = type;
}
@@ -898,7 +904,7 @@ void PathsPage::setPaths(const QString& name, bool force)
{
QString path;
- if (m_dlg.instanceType() == CreateInstanceDialog::Portable) {
+ if (m_dlg.rawCreationInfo().type == CreateInstanceDialog::Portable) {
path = InstanceManager::singleton().portablePath();
} else {
const auto root = InstanceManager::singleton().globalInstancesRootPath();
@@ -939,7 +945,7 @@ bool PathsPage::checkPath(
const QDir d(path);
if (InstanceManager::singleton().validInstanceName(d.dirName())) {
- if (m_dlg.instanceType() == CreateInstanceDialog::Portable) {
+ if (m_dlg.rawCreationInfo().type == 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::singleton().portablePath()) {
@@ -1041,37 +1047,41 @@ QString ConfirmationPage::toLocalizedString(CreateInstanceDialog::Types t) const
QString ConfirmationPage::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()));
+ const auto ci = m_dlg.rawCreationInfo();
+
+ lines.push_back(QObject::tr("Instance type: %1")
+ .arg(toLocalizedString(ci.type)));
+
+ lines.push_back(QObject::tr("Instance location: %1")
+ .arg(ci.dataPath));
- if (m_dlg.instanceType() != CreateInstanceDialog::Portable) {
- lines.push_back(QObject::tr("Instance name: %1").arg(m_dlg.instanceName()));
+ if (ci.type != CreateInstanceDialog::Portable) {
+ lines.push_back(QObject::tr("Instance name: %1").arg(ci.instanceName));
}
- if (paths.downloads.isEmpty()) {
+ if (ci.paths.downloads.isEmpty()) {
// simple settings
- if (paths.base != m_dlg.dataPath()) {
- lines.push_back(QObject::tr("Base directory: %1").arg(paths.base));
+ if (ci.paths.base != ci.dataPath) {
+ lines.push_back(QObject::tr("Base directory: %1").arg(ci.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));
+ lines.push_back(QObject::tr("Base directory: %1").arg(ci.paths.base));
+ lines.push_back(dirLine(QObject::tr("Downloads"), ci.paths.downloads));
+ lines.push_back(dirLine(QObject::tr("Mods"), ci.paths.mods));
+ lines.push_back(dirLine(QObject::tr("Profiles"), ci.paths.profiles));
+ lines.push_back(dirLine(QObject::tr("Overwrite"), ci.paths.overwrite));
}
// game
- QString name = m_dlg.game()->gameName();
- if (!m_dlg.gameVariant().isEmpty()) {
- name += " (" + m_dlg.gameVariant() + ")";
+ QString name = ci.game->gameName();
+ if (!ci.gameVariant.isEmpty()) {
+ name += " (" + ci.gameVariant + ")";
}
lines.push_back(QObject::tr("Game: %1").arg(name));
- lines.push_back(QObject::tr("Game location: %1").arg(m_dlg.gameLocation()));
+ lines.push_back(QObject::tr("Game location: %1").arg(ci.gameLocation));
return lines.join("\n");
}
diff --git a/src/createinstancedialogpages.h b/src/createinstancedialogpages.h
index e239c196..099071a2 100644
--- a/src/createinstancedialogpages.h
+++ b/src/createinstancedialogpages.h
@@ -48,7 +48,7 @@ public:
virtual CreateInstanceDialog::Types selectedInstanceType() const;
virtual MOBase::IPluginGame* selectedGame() const;
virtual QString selectedGameLocation() const;
- virtual QString selectedGameVariant() const;
+ virtual QString selectedGameVariant(MOBase::IPluginGame* game) const;
virtual QString selectedInstanceName() const;
virtual CreateInstanceDialog::Paths selectedPaths() const;
@@ -146,7 +146,7 @@ public:
bool ready() const override;
void activated() override;
- QString selectedGameVariant() const override;
+ QString selectedGameVariant(MOBase::IPluginGame* game) const override;
void select(const QString& variant);
diff --git a/src/instancemanagerdialog.cpp b/src/instancemanagerdialog.cpp
index c199fa7b..71fed78d 100644
--- a/src/instancemanagerdialog.cpp
+++ b/src/instancemanagerdialog.cpp
@@ -13,7 +13,9 @@
using namespace MOBase;
-
+// returns the icon for the given instance or an empty 32x32 icon if the game
+// plugin couldn't be found
+//
QIcon instanceIcon(const PluginContainer& pc, const Instance& i)
{
const auto* game = InstanceManager::singleton()
@@ -27,6 +29,78 @@ QIcon instanceIcon(const PluginContainer& pc, const Instance& i)
return QIcon(empty);
}
+// pops up a dialog to ask for an instance name when renaming
+//
+QString getInstanceName(
+ QWidget* parent, const QString& title, const QString& moreText,
+ const QString& label, const QString& oldName={})
+{
+ auto& m = InstanceManager::singleton();
+
+ QDialog dlg(parent);
+ dlg.setWindowTitle(title);
+
+ auto* ly = new QVBoxLayout(&dlg);
+
+ auto* bb = new QDialogButtonBox(
+ QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
+
+ auto* text = new QLineEdit(oldName);
+ text->selectAll();
+
+ auto* error = new QLabel;
+
+ if (!moreText.isEmpty()) {
+ auto* lb = new QLabel(moreText);
+ lb->setWordWrap(true);
+ ly->addWidget(lb);
+ ly->addSpacing(10);
+ }
+
+ auto* lb = new QLabel(label);
+ lb->setWordWrap(true);
+ ly->addWidget(lb);
+
+ ly->addWidget(text);
+ ly->addWidget(error);
+ ly->addStretch();
+ ly->addWidget(bb);
+
+ auto check = [&] {
+ bool okay = false;
+
+ if (text->text().isEmpty()) {
+ error->setText("");
+ } else if (!m.validInstanceName(text->text())) {
+ error->setText(QObject::tr("The instance name must be a valid folder name."));
+ } else {
+ const auto name = m.sanitizeInstanceName(text->text());
+
+ if ((name != oldName) && m.instanceExists(text->text())) {
+ error->setText(QObject::tr("An instance with this name already exists."));
+ } else {
+ okay = true;
+ }
+ }
+
+ error->setVisible(!okay);
+ bb->button(QDialogButtonBox::Ok)->setEnabled(okay);
+ };
+
+ QObject::connect(text, &QLineEdit::textChanged, [&] { check(); });
+ QObject::connect(bb, &QDialogButtonBox::accepted, [&]{ dlg.accept(); });
+ QObject::connect(bb, &QDialogButtonBox::rejected, [&]{ dlg.reject(); });
+
+ check();
+
+ dlg.resize({400, 120});
+ if (dlg.exec() != QDialog::Accepted) {
+ return {};
+ }
+
+ return m.sanitizeInstanceName(text->text());
+}
+
InstanceManagerDialog::~InstanceManagerDialog() = default;
@@ -106,9 +180,9 @@ void InstanceManagerDialog::updateList()
m_model->clear();
- const std::size_t NoSel = -1;
- std::size_t sel = NoSel;
+ std::size_t sel = NoSelection;
+ // creating items for instances
for (std::size_t i=0; i<m_instances.size(); ++i) {
const auto& ii = *m_instances[i];
@@ -128,7 +202,7 @@ void InstanceManagerDialog::updateList()
if (m_instances.empty()) {
select(-1);
} else {
- if (sel == NoSel) {
+ if (sel == NoSelection) {
if (prevSelIndex >= m_instances.size()) {
sel = m_instances.size() - 1;
} else {
@@ -207,76 +281,6 @@ void InstanceManagerDialog::openSelectedInstance()
accept();
}
-QString getInstanceName(
- QWidget* parent, const QString& title, const QString& moreText,
- const QString& label, const QString& oldName={})
-{
- auto& m = InstanceManager::singleton();
-
- QDialog dlg(parent);
- dlg.setWindowTitle(title);
-
- auto* ly = new QVBoxLayout(&dlg);
-
- auto* bb = new QDialogButtonBox(
- QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
-
- auto* text = new QLineEdit(oldName);
- text->selectAll();
-
- auto* error = new QLabel;
-
- if (!moreText.isEmpty()) {
- auto* lb = new QLabel(moreText);
- lb->setWordWrap(true);
- ly->addWidget(lb);
- ly->addSpacing(10);
- }
-
- auto* lb = new QLabel(label);
- lb->setWordWrap(true);
- ly->addWidget(lb);
-
- ly->addWidget(text);
- ly->addWidget(error);
- ly->addStretch();
- ly->addWidget(bb);
-
- auto check = [&] {
- bool okay = false;
-
- if (text->text().isEmpty()) {
- error->setText("");
- } else if (!m.validInstanceName(text->text())) {
- error->setText(QObject::tr("The instance name must be a valid folder name."));
- } else {
- const auto name = m.sanitizeInstanceName(text->text());
-
- if ((name != oldName) && m.instanceExists(text->text())) {
- error->setText(QObject::tr("An instance with this name already exists."));
- } else {
- okay = true;
- }
- }
-
- error->setVisible(!okay);
- bb->button(QDialogButtonBox::Ok)->setEnabled(okay);
- };
-
- QObject::connect(text, &QLineEdit::textChanged, [&] { check(); });
- QObject::connect(bb, &QDialogButtonBox::accepted, [&]{ dlg.accept(); });
- QObject::connect(bb, &QDialogButtonBox::rejected, [&]{ dlg.reject(); });
-
- check();
-
- dlg.resize({400, 120});
- if (dlg.exec() != QDialog::Accepted) {
- return {};
- }
-
- return m.sanitizeInstanceName(text->text());
-}
-
void InstanceManagerDialog::rename()
{
auto* i = singleSelection();
@@ -293,6 +297,8 @@ void InstanceManagerDialog::rename()
return;
}
+
+ // getting new name
const auto newName = getInstanceName(
this, tr("Rename instance"), "", tr("Instance name"), i->name());
@@ -300,11 +306,16 @@ void InstanceManagerDialog::rename()
return;
}
+
+ // renaming
const QString src = i->directory();
const QString dest = QDir::toNativeSeparators(
QFileInfo(src).dir().path() + "/" + newName);
+ log::info("renaming {} to {}", src, dest);
+
const auto r = shell::Rename(src, dest, false);
+
if (!r) {
QMessageBox::critical(
this, tr("Error"),
@@ -314,6 +325,8 @@ void InstanceManagerDialog::rename()
return;
}
+
+ // updating ui
auto newInstance = std::make_unique<Instance>(dest, false);
i = newInstance.get();
@@ -365,6 +378,8 @@ void InstanceManagerDialog::deleteInstance()
return;
}
+ // creating dialog
+
const auto Recycle = QMessageBox::Save;
const auto Delete = QMessageBox::Yes;
const auto Cancel = QMessageBox::Cancel;
@@ -388,6 +403,8 @@ void InstanceManagerDialog::deleteInstance()
list->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
list->setMaximumHeight(160);
+
+ // filling the list
for (const auto& f : files) {
auto* item = new QListWidgetItem(f.path);
@@ -412,6 +429,7 @@ void InstanceManagerDialog::deleteInstance()
}
+ // gathering all the selected items
QStringList selected;
for (int i=0; i<list->count(); ++i) {
@@ -427,10 +445,14 @@ void InstanceManagerDialog::deleteInstance()
return;
}
+
+ // deleting
if (!doDelete(selected, (r == Recycle))) {
return;
}
+
+ // updating ui
updateInstances();
updateList();
}
@@ -501,7 +523,7 @@ void InstanceManagerDialog::createNew()
updateInstances();
updateList();
- select(dlg.instanceName());
+ select(dlg.creationInfo().instanceName);
}
std::size_t InstanceManagerDialog::singleSelectionIndex() const