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/createinstancedialog.ui | 655 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 655 insertions(+) create mode 100644 src/createinstancedialog.ui (limited to 'src/createinstancedialog.ui') 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 + + + + + + + + + + + -- cgit v1.3.1 From 717be0c0483839ef4da22b366ed5bf8f07e379e2 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Thu, 23 Jul 2020 09:17:43 -0400 Subject: game page --- src/createinstancedialog.cpp | 280 +++++++++++++++++++++++++++++++++++++++--- src/createinstancedialog.h | 7 +- src/createinstancedialog.ui | 55 ++++++++- src/instancemanagerdialog.cpp | 2 +- 4 files changed, 324 insertions(+), 20 deletions(-) (limited to 'src/createinstancedialog.ui') diff --git a/src/createinstancedialog.cpp b/src/createinstancedialog.cpp index d43fba1c..21dcd8ec 100644 --- a/src/createinstancedialog.cpp +++ b/src/createinstancedialog.cpp @@ -1,6 +1,9 @@ #include "createinstancedialog.h" #include "ui_createinstancedialog.h" #include "instancemanager.h" +#include "plugincontainer.h" +#include +#include namespace cid { @@ -9,7 +12,7 @@ class Page { public: Page(CreateInstanceDialog& dlg, std::size_t i) - : ui(dlg.getUI()), m_dlg(dlg), m_index(i) + : ui(dlg.getUI()), m_dlg(dlg), m_pc(dlg.pluginContainer()), m_index(i) { } @@ -25,12 +28,14 @@ public: protected: Ui::CreateInstanceDialog* ui; + CreateInstanceDialog& m_dlg; + const PluginContainer& m_pc; private: - CreateInstanceDialog& m_dlg; std::size_t m_index; }; + class TypePage : public Page { public: @@ -88,6 +93,254 @@ public: GamePage(CreateInstanceDialog& dlg, std::size_t i) : Page(dlg, i) { + createGames(); + fillList(); + + QObject::connect(ui->showAllGames, &QCheckBox::clicked, [&]{ fillList(); }); + } + + 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 (path.isEmpty()) { + checked = nullptr; + } else { + checked = checkInstallation(path, checked); + } + } + + selectButton(checked); + } + +private: + struct Game + { + MOBase::IPluginGame* game = nullptr; + QCommandLinkButton* button = nullptr; + QString dir; + bool installed = false; + + Game(MOBase::IPluginGame* g) + : game(g), installed(g->isInstalled()) + { + if (installed) { + dir = game->gameDirectory().path(); + } + } + + Game(const Game&) = delete; + Game& operator=(const Game&) = delete; + }; + + std::vector> m_games; + + + Game* findGame(MOBase::IPluginGame* game) + { + for (auto& g : m_games) { + if (g->game == game) { + return g.get(); + } + } + + return nullptr; + } + + void createGames() + { + m_games.clear(); + + for (auto* game : m_pc.plugins()) { + m_games.push_back(std::make_unique(game)); + } + } + + void createButton(Game* g) + { + g->button = new QCommandLinkButton; + g->button->setCheckable(true); + + updateButton(g); + + QObject::connect(g->button, &QAbstractButton::clicked, [g, this] { + select(g->game); + }); + } + + void updateButton(Game* g) + { + if (!g->button) { + return; + } + + g->button->setText(g->game->gameName()); + + if (g->installed) { + g->button->setDescription(g->dir); + } else { + g->button->setDescription(QObject::tr("No installation found")); + } + } + + void selectButton(Game* g) + { + for (const auto& gg : m_games) { + if (!gg->button) { + continue; + } + + if (g) { + gg->button->setChecked(gg->game == g->game); + } else { + gg->button->setChecked(false); + } + } + } + + void fillList() + { + const bool showAll = ui->showAllGames->isChecked(); + + ui->games->clear(); + + for (auto& g : m_games) { + g->button = nullptr; + + if (!showAll && !g->installed) { + // not installed + continue; + } + + createButton(g.get()); + ui->games->addButton(g->button, QDialogButtonBox::AcceptRole); + } + } + + Game* checkInstallation(const QString& path, Game* g) + { + if (g->game->looksValid(path)) { + // okay + return g; + } + + // the selected game can't use that folder, find another one + auto* otherGame = findAnotherGame(path); + if (otherGame == g->game) { + // shouldn't happen, but okay + return g; + } + + if (otherGame) { + auto* confirmedGame = confirmOtherGame(path, g->game, otherGame); + + if (!confirmedGame) { + // cancelled + return nullptr; + } + + // make it look like the user clicked that button instead + g = findGame(confirmedGame); + if (!g) { + return nullptr; + } + } else { + // nothing can manage this, but the user can override + if (!confirmUnknown(path, g->game)) { + // cancelled + return nullptr; + } + } + + // remember this path + g->dir = path; + g->installed = true; + + updateButton(g); + + return g; + } + + MOBase::IPluginGame* findAnotherGame(const QString& path) + { + for (auto* otherGame : m_pc.plugins()) { + if (otherGame->looksValid(path)) { + return otherGame; + } + } + + return nullptr; + } + + bool confirmUnknown(const QString& path, MOBase::IPluginGame* game) + { + const auto r = MOBase::TaskDialog(&m_dlg) + .title(QObject::tr("Unrecognized game")) + .main(QObject::tr("Unrecognized game")) + .content(QObject::tr( + "The folder %1 does not seem to contain installation for " + "%2 or " + "any other game Mod Organizer can manage.") + .arg(path) + .arg(game->gameName())) + .button({ + QObject::tr("Use this folder for %1").arg(game->gameName()), + QObject::tr("I know what I'm doing"), + QMessageBox::Ignore}) + .button({ + QObject::tr("Cancel"), + QMessageBox::Cancel}) + .exec(); + + return (r == QMessageBox::Ignore); + } + + MOBase::IPluginGame* confirmOtherGame( + const QString& path, + MOBase::IPluginGame* selectedGame, MOBase::IPluginGame* guessedGame) + { + const auto r = MOBase::TaskDialog(&m_dlg) + .title(QObject::tr("Incorrect game")) + .main(QObject::tr("Incorrect game")) + .content(QObject::tr( + "The folder %1 seems to contain an installation for " + "%2, " + "not " + "%3.") + .arg(path) + .arg(guessedGame->gameName()) + .arg(selectedGame->gameName())) + .button({ + QObject::tr("Manage %1 instead").arg(guessedGame->gameName()), + QMessageBox::Ok}) + .button({ + QObject::tr("Use this folder for %1").arg(selectedGame->gameName()), + QObject::tr("I know what I'm doing"), + QMessageBox::Ignore}) + .button({ + QObject::tr("Cancel"), + QMessageBox::Cancel}) + .exec(); + + switch (r) + { + case QMessageBox::Ok: + return guessedGame; + + case QMessageBox::Ignore: + return selectedGame; + + case QMessageBox::Cancel: + default: + return nullptr; + } } }; @@ -114,8 +367,9 @@ public: } // namespace -CreateInstanceDialog::CreateInstanceDialog(QWidget *parent) - : QDialog(parent), ui(new Ui::CreateInstanceDialog) +CreateInstanceDialog::CreateInstanceDialog( + const PluginContainer& pc, QWidget *parent) + : QDialog(parent), ui(new Ui::CreateInstanceDialog), m_pc(pc) { using namespace cid; @@ -132,19 +386,6 @@ CreateInstanceDialog::CreateInstanceDialog(QWidget *parent) 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; @@ -154,6 +395,11 @@ Ui::CreateInstanceDialog* CreateInstanceDialog::getUI() return ui.get(); } +const PluginContainer& CreateInstanceDialog::pluginContainer() +{ + return m_pc; +} + void CreateInstanceDialog::next() { ui->pages->setCurrentIndex(ui->pages->currentIndex() + 1); diff --git a/src/createinstancedialog.h b/src/createinstancedialog.h index 03e9de01..df056f85 100644 --- a/src/createinstancedialog.h +++ b/src/createinstancedialog.h @@ -6,22 +6,27 @@ namespace Ui { class CreateInstanceDialog; }; namespace cid { class Page; } +class PluginContainer; + class CreateInstanceDialog : public QDialog { Q_OBJECT public: - explicit CreateInstanceDialog(QWidget *parent = nullptr); + explicit CreateInstanceDialog( + const PluginContainer& pc, QWidget *parent = nullptr); ~CreateInstanceDialog(); Ui::CreateInstanceDialog* getUI(); + const PluginContainer& pluginContainer(); void next(); void back(); private: std::unique_ptr ui; + const PluginContainer& m_pc; std::vector> m_pages; void updateNavigationButtons(); diff --git a/src/createinstancedialog.ui b/src/createinstancedialog.ui index bcfddb20..8cad959d 100644 --- a/src/createinstancedialog.ui +++ b/src/createinstancedialog.ui @@ -227,7 +227,60 @@ 0 - + + + Qt::ScrollBarAlwaysOff + + + true + + + + + 0 + 0 + 455 + 287 + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Qt::Vertical + + + QDialogButtonBox::NoButton + + + + + + diff --git a/src/instancemanagerdialog.cpp b/src/instancemanagerdialog.cpp index c7042aa8..566f1aad 100644 --- a/src/instancemanagerdialog.cpp +++ b/src/instancemanagerdialog.cpp @@ -112,7 +112,7 @@ void InstanceManagerDialog::onSelection() void InstanceManagerDialog::createNew() { - CreateInstanceDialog dlg(this); + CreateInstanceDialog dlg(m_pc, this); dlg.exec(); } -- 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/createinstancedialog.ui') 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 46d81056d1eda2fd74dac200cd7b8c36d9cbc4eb Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Thu, 23 Jul 2020 10:50:31 -0400 Subject: editions page --- src/createinstancedialog.cpp | 174 +++++++++++++++++++++++++++++++++++++------ src/createinstancedialog.h | 1 + src/createinstancedialog.ui | 106 +++++++++++++++++++++++++- 3 files changed, 258 insertions(+), 23 deletions(-) (limited to 'src/createinstancedialog.ui') diff --git a/src/createinstancedialog.cpp b/src/createinstancedialog.cpp index 908c20d6..262deb20 100644 --- a/src/createinstancedialog.cpp +++ b/src/createinstancedialog.cpp @@ -11,8 +11,8 @@ namespace cid class Page { public: - Page(CreateInstanceDialog& dlg, std::size_t i) - : ui(dlg.getUI()), m_dlg(dlg), m_pc(dlg.pluginContainer()), m_index(i) + Page(CreateInstanceDialog& dlg) + : ui(dlg.getUI()), m_dlg(dlg), m_pc(dlg.pluginContainer()) { } @@ -55,21 +55,24 @@ public: return nullptr; } + virtual QString instanceName() const + { + // no-op + return {}; + } + protected: Ui::CreateInstanceDialog* ui; CreateInstanceDialog& m_dlg; const PluginContainer& m_pc; - -private: - std::size_t m_index; }; class TypePage : public Page { public: - TypePage(CreateInstanceDialog& dlg, std::size_t i) - : Page(dlg, i), m_type(CreateInstanceDialog::NoType) + TypePage(CreateInstanceDialog& dlg) + : Page(dlg), m_type(CreateInstanceDialog::NoType) { ui->createGlobal->setDescription( ui->createGlobal->description() @@ -124,8 +127,8 @@ private: class GamePage : public Page { public: - GamePage(CreateInstanceDialog& dlg, std::size_t i) - : Page(dlg, i), m_selection(nullptr) + GamePage(CreateInstanceDialog& dlg) + : Page(dlg), m_selection(nullptr) { createGames(); fillList(); @@ -474,11 +477,93 @@ private: }; +class EditionsPage : public Page +{ +public: + EditionsPage(CreateInstanceDialog& dlg) + : Page(dlg), m_previousGame(nullptr) + { + } + + bool ready() const override + { + return !m_selection.isEmpty(); + } + + bool skip() const override + { + auto* g = m_dlg.selectedGame(); + if (!g) { + // shouldn't happen + return true; + } + + const auto variants = g->gameVariants(); + return (variants.size() < 2); + } + + void activated() override + { + auto* g = m_dlg.selectedGame(); + + if (m_previousGame != g) { + m_previousGame = g; + m_selection = ""; + fillList(); + } + } + + void select(const QString& variant) + { + for (auto* b : m_buttons) { + if (b->text() == variant) { + m_selection = variant; + b->setChecked(true); + } else { + b->setChecked(false); + } + } + + updateNavigation(); + } + +private: + MOBase::IPluginGame* m_previousGame; + std::vector m_buttons; + QString m_selection; + + void fillList() + { + ui->editions->clear(); + m_buttons.clear(); + + auto* g = m_dlg.selectedGame(); + if (!g) { + // shouldn't happen + return; + } + + const auto variants = g->gameVariants(); + for (auto& v : variants) { + auto* b = new QCommandLinkButton(v); + b->setCheckable(true); + + QObject::connect(b, &QAbstractButton::clicked, [v, this] { + select(v); + }); + + ui->editions->addButton(b, QDialogButtonBox::AcceptRole); + m_buttons.push_back(b); + } + } +}; + + class NamePage : public Page { public: - NamePage(CreateInstanceDialog& dlg, std::size_t i) - : Page(dlg, i), m_modified(false), m_okay(false) + NamePage(CreateInstanceDialog& dlg) + : Page(dlg), m_modified(false), m_okay(false) { m_originalLabel = ui->instanceNameLabel->text(); @@ -515,6 +600,16 @@ public: updateWarnings(); } + QString instanceName() const override + { + if (!m_okay) { + return {}; + } + + const auto text = ui->instanceName->text().trimmed(); + return InstanceManager::instance().sanitizeInstanceName(text); + } + private: QString m_originalLabel; bool m_modified; @@ -534,16 +629,16 @@ private: auto& m = InstanceManager::instance(); - const auto name = ui->instanceName->text().trimmed(); + const auto text = ui->instanceName->text().trimmed(); - if (name.isEmpty()) { + if (text.isEmpty()) { empty = true; } else { - const auto sanitized = m.sanitizeInstanceName(name); - if (name != sanitized) { + const auto sanitized = m.sanitizeInstanceName(text); + if (text != sanitized) { invalid = true; } else { - exists = m.instanceExists(name); + exists = m.instanceExists(text); } } @@ -569,9 +664,31 @@ private: class PathsPage : public Page { public: - PathsPage(CreateInstanceDialog& dlg, std::size_t i) - : Page(dlg, i) + PathsPage(CreateInstanceDialog& dlg) + : Page(dlg) { + QObject::connect( + ui->advancedPathOptions, &QCheckBox::clicked, [&]{ onAdvanced(); }); + + ui->pathPages->setCurrentIndex(0); + } + + void activated() override + { + const auto root = InstanceManager::instance().instancesPath(); + const auto path = QDir::toNativeSeparators(root + "/" + m_dlg.instanceName()); + + ui->location->setText(path); + } + +private: + void onAdvanced() + { + if (ui->advancedPathOptions->isChecked()) { + ui->pathPages->setCurrentIndex(1); + } else { + ui->pathPages->setCurrentIndex(0); + } } }; @@ -586,10 +703,11 @@ CreateInstanceDialog::CreateInstanceDialog( 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)); + 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)); ui->pages->setCurrentIndex(0); @@ -672,3 +790,15 @@ MOBase::IPluginGame* CreateInstanceDialog::selectedGame() const return nullptr; } + +QString CreateInstanceDialog::instanceName() const +{ + for (auto&& p : m_pages) { + const auto s = p->instanceName(); + if (!s.isEmpty()) { + return s; + } + } + + return {}; +} diff --git a/src/createinstancedialog.h b/src/createinstancedialog.h index 3fd19d55..14ed9f8e 100644 --- a/src/createinstancedialog.h +++ b/src/createinstancedialog.h @@ -37,6 +37,7 @@ public: Types selectedType() const; MOBase::IPluginGame* selectedGame() const; + QString instanceName() const; private: std::unique_ptr ui; diff --git a/src/createinstancedialog.ui b/src/createinstancedialog.ui index 6f215539..751c5427 100644 --- a/src/createinstancedialog.ui +++ b/src/createinstancedialog.ui @@ -287,6 +287,110 @@ + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + <h3>Select the game edition.</h3> + + + + + + + This game has multiple variants. The correct one must be selected or Mod Organizer will not be able to launch the game properly. + + + true + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + true + + + + + 0 + 0 + 455 + 257 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Qt::Vertical + + + QDialogButtonBox::NoButton + + + + + + + + + + + + @@ -405,7 +509,7 @@ - 1 + 0 -- cgit v1.3.1 From ebbc8ed60c606b1b76fcea36eb2bd5db6373f7f0 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Thu, 23 Jul 2020 12:20:55 -0400 Subject: paths page --- src/createinstancedialog.cpp | 337 +++++++++++++++++++++++++++++++++++++------ src/createinstancedialog.h | 3 + src/createinstancedialog.ui | 148 +++++++++++++------ 3 files changed, 395 insertions(+), 93 deletions(-) (limited to 'src/createinstancedialog.ui') diff --git a/src/createinstancedialog.cpp b/src/createinstancedialog.cpp index 262deb20..4308969e 100644 --- a/src/createinstancedialog.cpp +++ b/src/createinstancedialog.cpp @@ -2,12 +2,158 @@ #include "ui_createinstancedialog.h" #include "instancemanager.h" #include "plugincontainer.h" +#include "shared/appconfig.h" #include #include namespace cid { +class PathChecker +{ +public: + PathChecker(QLabel* existsLabel, QLabel* invalidLabel) + : m_exists(existsLabel), m_invalid(invalidLabel) + { + m_existsOriginal = m_exists->text(); + m_invalidOriginal = m_invalid->text(); + } + + QString sanitizeFileName(const QString& name) const + { + 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) const + { + 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; + } + + bool checkName(QString parentDir, QString name) const + { + 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; + } + + bool checkPath(QString path) const + { + 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); + } + + return okay; + } + +private: + QLabel* m_exists; + QString m_existsOriginal; + + QLabel* m_invalid; + QString m_invalidOriginal; + + void setPossiblePlaceholder( + QLabel* label, const QString& s, const QString& arg) const + { + if (label->text().contains("%1")) { + label->setText(s.arg(arg)); + } + } +}; + + class Page { public: @@ -562,8 +708,9 @@ private: class NamePage : public Page { public: - NamePage(CreateInstanceDialog& dlg) - : Page(dlg), m_modified(false), m_okay(false) + NamePage(CreateInstanceDialog& dlg) : + Page(dlg), m_modified(false), m_okay(false), + m_checker(ui->instanceNameExists, ui->instanceNameInvalid) { m_originalLabel = ui->instanceNameLabel->text(); @@ -607,10 +754,11 @@ public: } const auto text = ui->instanceName->text().trimmed(); - return InstanceManager::instance().sanitizeInstanceName(text); + return m_checker.sanitizeFileName(text); } private: + PathChecker m_checker; QString m_originalLabel; bool m_modified; bool m_okay; @@ -623,39 +771,9 @@ private: void updateWarnings() { - bool exists = false; - bool invalid = false; - bool empty = false; - - auto& m = InstanceManager::instance(); - - const auto text = ui->instanceName->text().trimmed(); - - if (text.isEmpty()) { - empty = true; - } else { - const auto sanitized = m.sanitizeInstanceName(text); - if (text != sanitized) { - invalid = true; - } else { - exists = m.instanceExists(text); - } - } - - 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); - } + const auto root = InstanceManager::instance().instancesPath(); + m_okay = m_checker.checkName(root, ui->instanceName->text()); updateNavigation(); } }; @@ -664,31 +782,109 @@ private: class PathsPage : public Page { public: - PathsPage(CreateInstanceDialog& dlg) - : Page(dlg) + PathsPage(CreateInstanceDialog& dlg) : + Page(dlg), + m_checker(ui->locationExists, ui->locationInvalid), + m_advancedChecker(ui->advancedDirExists, ui->advancedDirInvalid) { + QObject::connect(ui->location, &QLineEdit::textEdited, [&]{ onChanged(); }); + QObject::connect(ui->base, &QLineEdit::textEdited, [&]{ onChanged(); }); + QObject::connect(ui->downloads, &QLineEdit::textEdited, [&]{ onChanged(); }); + QObject::connect(ui->mods, &QLineEdit::textEdited, [&]{ onChanged(); }); + QObject::connect(ui->profiles, &QLineEdit::textEdited, [&]{ onChanged(); }); + QObject::connect(ui->overwrite, &QLineEdit::textEdited, [&]{ onChanged(); }); + QObject::connect( ui->advancedPathOptions, &QCheckBox::clicked, [&]{ onAdvanced(); }); ui->pathPages->setCurrentIndex(0); } + bool ready() const override + { + return checkPaths(); + } + void activated() override { - const auto root = InstanceManager::instance().instancesPath(); - const auto path = QDir::toNativeSeparators(root + "/" + m_dlg.instanceName()); + const auto name = m_dlg.instanceName(); + + setPaths(name, (m_lastInstanceName != name)); + checkPaths(); + updateNavigation(); - ui->location->setText(path); + m_lastInstanceName = name; } private: + PathChecker m_checker, m_advancedChecker; + QString m_lastInstanceName; + + void onChanged() + { + checkPaths(); + updateNavigation(); + } + + bool checkPaths() const + { + if (ui->advancedPathOptions->isChecked()) { + return + checkAdvancedPath(ui->base->text()) && + checkVarPath(ui->downloads->text()); + } else { + return m_checker.checkPath(ui->location->text()); + } + } + + bool checkAdvancedPath(const QString& path) const + { + return m_advancedChecker.checkPath(path); + } + + bool checkVarPath(QString path) const + { + path.replace("%BASE_DIR%", ui->base->text()); + return checkAdvancedPath(path); + } + void onAdvanced() { if (ui->advancedPathOptions->isChecked()) { + ui->base->setText(ui->location->text()); ui->pathPages->setCurrentIndex(1); } else { + ui->location->setText(ui->base->text()); ui->pathPages->setCurrentIndex(0); } + + checkPaths(); + } + + void setPaths(const QString& name, bool force) + { + const auto root = InstanceManager::instance().instancesPath(); + const auto path = QDir::toNativeSeparators(root + "/" + name); + + 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); + setIfEmpty(ui->overwrite, makeDefaultPath(AppConfig::overwritePath()), force); + } + + void setIfEmpty(QLineEdit* e, const QString& path, bool force) + { + if (e->text().isEmpty() || force) { + e->setText(path); + } + } + + QString makeDefaultPath(const std::wstring& dir) + { + return "%BASE_DIR%\\" + QString::fromStdWString(dir); } }; @@ -702,6 +898,7 @@ CreateInstanceDialog::CreateInstanceDialog( using namespace cid; ui->setupUi(this); + m_originalNext = ui->next->text(); m_pages.push_back(std::make_unique(*this)); m_pages.push_back(std::make_unique(*this)); @@ -731,24 +928,62 @@ const PluginContainer& CreateInstanceDialog::pluginContainer() void CreateInstanceDialog::next() { - selectPage(ui->pages->currentIndex() + 1); + const auto i = ui->pages->currentIndex(); + const auto last = (i == (ui->pages->count() - 1)); + + if (last) { + finish(); + } else { + changePage(+1); + } } void CreateInstanceDialog::back() { - selectPage(ui->pages->currentIndex() - 1); + changePage(-1); } -void CreateInstanceDialog::selectPage(std::size_t i) +void CreateInstanceDialog::changePage(int d) { - while (i < m_pages.size()) { - if (!m_pages[i]->skip()) { - break; + std::size_t i = static_cast(ui->pages->currentIndex()); + + if (d > 0) { + for (;;) { + ++i; + + if (i >= m_pages.size()) { + break; + } + + if (!m_pages[i]->skip()) { + break; + } } + } else { + for (;;) { + if (i == 0) { + break; + } + + --i; - ++i; + if (!m_pages[i]->skip()) { + break; + } + } } + if (i < m_pages.size()) { + selectPage(i); + } +} + +void CreateInstanceDialog::finish() +{ +} + +void CreateInstanceDialog::selectPage(std::size_t i) +{ if (i >= m_pages.size()) { return; } @@ -764,8 +999,14 @@ void CreateInstanceDialog::updateNavigation() const auto i = ui->pages->currentIndex(); const auto last = (i == (ui->pages->count() - 1)); - ui->next->setEnabled(m_pages[i]->ready() && !last); + ui->next->setEnabled(m_pages[i]->ready()); ui->back->setEnabled(i > 0); + + if (last) { + ui->next->setText(tr("Finish")); + } else { + ui->next->setText(m_originalNext); + } } CreateInstanceDialog::Types CreateInstanceDialog::selectedType() const diff --git a/src/createinstancedialog.h b/src/createinstancedialog.h index 14ed9f8e..ac1c30b6 100644 --- a/src/createinstancedialog.h +++ b/src/createinstancedialog.h @@ -32,6 +32,8 @@ public: void next(); void back(); void selectPage(std::size_t i); + void changePage(int d); + void finish(); void updateNavigation(); @@ -43,6 +45,7 @@ private: std::unique_ptr ui; const PluginContainer& m_pc; std::vector> m_pages; + QString m_originalNext; }; #endif // MODORGANIZER_CREATEINSTANCEDIALOG_INCLUDED diff --git a/src/createinstancedialog.ui b/src/createinstancedialog.ui index 751c5427..98cbe96e 100644 --- a/src/createinstancedialog.ui +++ b/src/createinstancedialog.ui @@ -554,7 +554,7 @@ - + Qt::Vertical @@ -567,8 +567,26 @@ + + + + This folder already exists. + + + - + + + Folder + + + + + + + The folder contains invalid characters. + + @@ -604,10 +622,17 @@ 0 - - + + - Overwrite + ... + + + + + + + ... @@ -618,6 +643,27 @@ + + + + Folder + + + + + + + Folder + + + + + + + Overwrite + + + @@ -625,29 +671,30 @@ - - - - Qt::Vertical + + + + ... - - - 20 - 40 - + + + + + + Folder - + - - + + - Profiles + The folder %1 already exists. + + + true - - - @@ -656,40 +703,35 @@ - - - - - - - + + + Folder + + - - - - - - Use <code>%BASE_DIR%</code> to refer to the Base Directory. + + + Folder - - + + - ... + Profiles - - + + - ... + Use <code>%BASE_DIR%</code> to refer to the Base Directory. - - + + ... @@ -702,10 +744,26 @@ - - + + + + Qt::Vertical + + + + 20 + 40 + + + + + + - ... + The folder %1 contains invalid characters. + + + true -- cgit v1.3.1 From 2cb5c21b8e15b02a09144ff6c1ab11e87879a420 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Fri, 24 Jul 2020 09:25:02 -0400 Subject: fixed settings doing weird stuff with multiple instances sort games by name added intro and confirmation pages --- src/createinstancedialog.cpp | 55 ++++++++++++++++++++++++++---- src/createinstancedialog.h | 1 + src/createinstancedialog.ui | 79 +++++++++++++++++++++++++++++++++++++++++++ src/instancemanagerdialog.cpp | 6 ++++ src/main.cpp | 10 +++--- src/mainwindow.cpp | 26 ++++++++------ src/settings.cpp | 40 +++++++++++----------- src/settings.h | 5 ++- 8 files changed, 178 insertions(+), 44 deletions(-) (limited to 'src/createinstancedialog.ui') diff --git a/src/createinstancedialog.cpp b/src/createinstancedialog.cpp index 4308969e..5db6d685 100644 --- a/src/createinstancedialog.cpp +++ b/src/createinstancedialog.cpp @@ -214,6 +214,16 @@ protected: }; +class InfoPage : public Page +{ +public: + InfoPage(CreateInstanceDialog& dlg) + : Page(dlg) + { + } +}; + + class TypePage : public Page { public: @@ -345,7 +355,7 @@ public: void warnUnrecognized(const QString& path) { QString supportedGames; - for (auto* game : m_pc.plugins()) { + for (auto* game : sortedGamePlugins()) { supportedGames += "
  • " + game->gameName() + "
  • "; } @@ -381,6 +391,21 @@ private: Game* m_selection; + std::vector sortedGamePlugins() const + { + std::vector v; + + for (auto* game : m_pc.plugins()) { + v.push_back(game); + } + + std::sort(v.begin(), v.end(), [](auto* a, auto* b) { + return (a->gameName() < b->gameName()); + }); + + return v; + } + Game* findGame(MOBase::IPluginGame* game) { for (auto& g : m_games) { @@ -396,7 +421,7 @@ private: { m_games.clear(); - for (auto* game : m_pc.plugins()) { + for (auto* game : sortedGamePlugins()) { m_games.push_back(std::make_unique(game)); } } @@ -563,9 +588,9 @@ private: .title(QObject::tr("Unrecognized game")) .main(QObject::tr("Unrecognized game")) .content(QObject::tr( - "The folder %1 does not seem to contain installation for " + "The folder %1 does not seem to contain an installation for " "%2 or " - "any other game Mod Organizer can manage.") + "for any other game Mod Organizer can manage.") .arg(path) .arg(game->gameName())) .button({ @@ -800,6 +825,11 @@ public: ui->pathPages->setCurrentIndex(0); } + bool skip() const override + { + return (m_dlg.selectedType() == CreateInstanceDialog::Portable); + } + bool ready() const override { return checkPaths(); @@ -900,11 +930,13 @@ CreateInstanceDialog::CreateInstanceDialog( ui->setupUi(this); m_originalNext = ui->next->text(); + 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)); ui->pages->setCurrentIndex(0); @@ -926,10 +958,21 @@ const PluginContainer& CreateInstanceDialog::pluginContainer() return m_pc; } +bool CreateInstanceDialog::isOnLastPage() const +{ + for (int i=ui->pages->currentIndex() + 1; i < ui->pages->count(); ++i) { + if (!m_pages[i]->skip()) { + return false; + } + } + + return true; +} + void CreateInstanceDialog::next() { const auto i = ui->pages->currentIndex(); - const auto last = (i == (ui->pages->count() - 1)); + const auto last = isOnLastPage(); if (last) { finish(); @@ -997,7 +1040,7 @@ void CreateInstanceDialog::selectPage(std::size_t i) void CreateInstanceDialog::updateNavigation() { const auto i = ui->pages->currentIndex(); - const auto last = (i == (ui->pages->count() - 1)); + const auto last = isOnLastPage(); ui->next->setEnabled(m_pages[i]->ready()); ui->back->setEnabled(i > 0); diff --git a/src/createinstancedialog.h b/src/createinstancedialog.h index ac1c30b6..02608c8d 100644 --- a/src/createinstancedialog.h +++ b/src/createinstancedialog.h @@ -36,6 +36,7 @@ public: void finish(); void updateNavigation(); + bool isOnLastPage() const; Types selectedType() const; MOBase::IPluginGame* selectedGame() const; diff --git a/src/createinstancedialog.ui b/src/createinstancedialog.ui index 98cbe96e..72d73020 100644 --- a/src/createinstancedialog.ui +++ b/src/createinstancedialog.ui @@ -60,6 +60,24 @@ 0 + + + + + + <h3>What is an instance?</h3> +<p>An instance is a full set of mods, downloads, profiles and configuration for a game. Each game must be managed in its own instance. Mod Organizer can freely switch between instances.</p> + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + @@ -823,6 +841,67 @@ + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + <h3>Confirmation</h3> + + + + + + + The instance is about to be created. Review the information below and press 'Finish'. + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + +
    diff --git a/src/instancemanagerdialog.cpp b/src/instancemanagerdialog.cpp index 566f1aad..26e8eae1 100644 --- a/src/instancemanagerdialog.cpp +++ b/src/instancemanagerdialog.cpp @@ -8,6 +8,12 @@ #include "shared/appconfig.h" #include +void openInstanceManager(PluginContainer& pc, QWidget* parent) +{ + InstanceManagerDialog dlg(pc, parent); + dlg.exec(); +} + class InstanceInfo { public: diff --git a/src/main.cpp b/src/main.cpp index 71fb0bbd..58e22466 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -270,6 +270,8 @@ std::optional handleCommandLine( return {}; } +void openInstanceManager(PluginContainer& pc, QWidget* parent); + int runApplication( MOApplication &application, const cl::CommandLine& cl, SingleInstance &instance, const QString &dataPath) @@ -298,8 +300,9 @@ int runApplication( try { - Settings settings(dataPath + "/" + QString::fromStdWString(AppConfig::iniFileName())); - settings.setGlobalInstance(); + Settings settings( + dataPath + "/" + QString::fromStdWString(AppConfig::iniFileName()), + true); log::getDefault().setLevel(settings.diagnostics().logLevel()); @@ -434,8 +437,7 @@ int runApplication( QTimer::singleShot(std::chrono::milliseconds(1), [&] { - InstanceManagerDialog dlg(*pluginContainer, &mainWindow); - dlg.exec(); + openInstanceManager(*pluginContainer, &mainWindow); }); res = application.exec(); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 65c1d65c..c2d83e36 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -5952,20 +5952,24 @@ void MainWindow::on_actionNotifications_triggered() scheduleCheckForProblems(); } +void openInstanceManager(PluginContainer& pc, QWidget* parent); + void MainWindow::on_actionChange_Game_triggered() { - 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); + openInstanceManager(m_PluginContainer, this); - if (r != QMessageBox::Yes) { - return; - } - } - - InstanceManager::instance().clearCurrentInstance(); - ExitModOrganizer(Exit::Restart); + //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); } void MainWindow::setCategoryListVisible(bool visible) diff --git a/src/settings.cpp b/src/settings.cpp index 43e7a4fa..8db9c623 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -61,33 +61,31 @@ QString toString(EndorsementState s) Settings *Settings::s_Instance = nullptr; -Settings::Settings(const QString& path) : +Settings::Settings(const QString& path, bool globalInstance) : m_Settings(path, QSettings::IniFormat), - m_Game(m_Settings), m_Geometry(m_Settings), m_Widgets(m_Settings), - m_Colors(m_Settings), m_Plugins(m_Settings), m_Paths(m_Settings), - m_Network(m_Settings), m_Nexus(*this, m_Settings), m_Steam(*this, m_Settings), + m_Game(m_Settings), m_Geometry(m_Settings), + m_Widgets(m_Settings, globalInstance), m_Colors(m_Settings), + m_Plugins(m_Settings), m_Paths(m_Settings), m_Network(m_Settings), + m_Nexus(*this, m_Settings), m_Steam(*this, m_Settings), m_Interface(m_Settings), m_Diagnostics(m_Settings) { + if (globalInstance) { + if (s_Instance != nullptr) { + throw std::runtime_error("second instance of \"Settings\" created"); + } else { + s_Instance = this; + } + } } Settings::~Settings() { - MOBase::QuestionBoxMemory::setCallbacks({}, {}, {}); - if (s_Instance == this) { + MOBase::QuestionBoxMemory::setCallbacks({}, {}, {}); 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() { if (s_Instance == nullptr) { @@ -1011,13 +1009,15 @@ void GeometrySettings::restoreDocks(QMainWindow* mw) const } -WidgetSettings::WidgetSettings(QSettings& s) +WidgetSettings::WidgetSettings(QSettings& s, bool globalInstance) : m_Settings(s) { - MOBase::QuestionBoxMemory::setCallbacks( - [this](auto&& w, auto&& f){ return questionButton(w, f); }, - [this](auto&& w, auto&& b){ setQuestionWindowButton(w, b); }, - [this](auto&& w, auto&& f, auto&& b){ setQuestionFileButton(w, f, b); }); + if (globalInstance) { + MOBase::QuestionBoxMemory::setCallbacks( + [this](auto&& w, auto&& f){ return questionButton(w, f); }, + [this](auto&& w, auto&& b){ setQuestionWindowButton(w, b); }, + [this](auto&& w, auto&& f, auto&& b){ setQuestionFileButton(w, f, b); }); + } } std::optional WidgetSettings::index(const QComboBox* cb) const diff --git a/src/settings.h b/src/settings.h index 84102c72..a8c527d6 100644 --- a/src/settings.h +++ b/src/settings.h @@ -195,7 +195,7 @@ private: class WidgetSettings { public: - WidgetSettings(QSettings& s); + WidgetSettings(QSettings& s, bool globalInstance); // selected index for a combobox // @@ -677,11 +677,10 @@ class Settings : public QObject Q_OBJECT; public: - Settings(const QString& path); + Settings(const QString& path, bool globalInstance=false); ~Settings(); static Settings &instance(); - void setGlobalInstance(); // name of the ini file // -- cgit v1.3.1 From 7b02c77a3961d859aae270922db26bbc999677cb Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Fri, 24 Jul 2020 10:10:19 -0400 Subject: review page --- src/createinstancedialog.cpp | 197 +++++++++++++++++++++++++++++++++++-------- src/createinstancedialog.h | 31 ++++++- src/createinstancedialog.ui | 6 +- 3 files changed, 194 insertions(+), 40 deletions(-) (limited to 'src/createinstancedialog.ui') diff --git a/src/createinstancedialog.cpp b/src/createinstancedialog.cpp index 5db6d685..8c6605ed 100644 --- a/src/createinstancedialog.cpp +++ b/src/createinstancedialog.cpp @@ -189,7 +189,7 @@ public: } - virtual CreateInstanceDialog::Types selectedType() const + virtual CreateInstanceDialog::Types selectedInstanceType() const { // no-op return CreateInstanceDialog::NoType; @@ -201,7 +201,25 @@ public: return nullptr; } - virtual QString instanceName() const + virtual QString selectedGameLocation() const + { + // no-op + return {}; + } + + virtual QString selectedGameEdition() const + { + // no-op + return {}; + } + + virtual QString selectedInstanceName() const + { + // no-op + return {}; + } + + virtual CreateInstanceDialog::Paths selectedPaths() const { // no-op return {}; @@ -250,7 +268,7 @@ public: return (m_type != CreateInstanceDialog::NoType); } - CreateInstanceDialog::Types selectedType() const + CreateInstanceDialog::Types selectedInstanceType() const override { return m_type; } @@ -306,6 +324,15 @@ public: return m_selection->game; } + QString selectedGameLocation() const override + { + if (!m_selection) { + return {}; + } + + return m_selection->dir; + } + void select(MOBase::IPluginGame* game) { Game* checked = findGame(game); @@ -663,7 +690,7 @@ public: bool skip() const override { - auto* g = m_dlg.selectedGame(); + auto* g = m_dlg.game(); if (!g) { // shouldn't happen return true; @@ -675,7 +702,7 @@ public: void activated() override { - auto* g = m_dlg.selectedGame(); + auto* g = m_dlg.game(); if (m_previousGame != g) { m_previousGame = g; @@ -698,6 +725,22 @@ public: updateNavigation(); } + QString selectedGameEdition() const override + { + auto* g = m_dlg.game(); + if (!g) { + // shouldn't happen + return {}; + } + + const auto variants = g->gameVariants(); + if (variants.size() < 2) { + return {}; + } else { + return m_selection; + } + } + private: MOBase::IPluginGame* m_previousGame; std::vector m_buttons; @@ -708,7 +751,7 @@ private: ui->editions->clear(); m_buttons.clear(); - auto* g = m_dlg.selectedGame(); + auto* g = m_dlg.game(); if (!g) { // shouldn't happen return; @@ -750,12 +793,12 @@ public: bool skip() const override { - return (m_dlg.selectedType() == CreateInstanceDialog::Portable); + return (m_dlg.instanceType() == CreateInstanceDialog::Portable); } void activated() override { - auto* g = m_dlg.selectedGame(); + auto* g = m_dlg.game(); if (!g) { // shouldn't happen, next should be disabled return; @@ -772,7 +815,7 @@ public: updateWarnings(); } - QString instanceName() const override + QString selectedInstanceName() const override { if (!m_okay) { return {}; @@ -827,7 +870,7 @@ public: bool skip() const override { - return (m_dlg.selectedType() == CreateInstanceDialog::Portable); + return (m_dlg.instanceType() == CreateInstanceDialog::Portable); } bool ready() const override @@ -846,6 +889,23 @@ public: m_lastInstanceName = name; } + CreateInstanceDialog::Paths selectedPaths() const override + { + CreateInstanceDialog::Paths p; + + if (ui->advancedPathOptions->isChecked()) { + p.base = ui->base->text(); + p.downloads = ui->downloads->text(); + p.mods = ui->mods->text(); + p.profiles = ui->profiles->text(); + p.overwrite = ui->overwrite->text(); + } else { + p.base = ui->location->text(); + } + + return p; + } + private: PathChecker m_checker, m_advancedChecker; QString m_lastInstanceName; @@ -918,6 +978,82 @@ private: } }; + +class ConfirmationPage : public Page +{ +public: + ConfirmationPage(CreateInstanceDialog& dlg) + : Page(dlg) + { + } + + void activated() override + { + ui->review->setPlainText(makeReview()); + } + + QString makeReview() const + { + QStringList lines; + + const auto paths = m_dlg.paths(); + + // type + switch (m_dlg.instanceType()) + { + 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; + } + + 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; + } + + default: + { + lines.push_back(QObject::tr("Instance type: %1").arg(QObject::tr("?"))); + } + } + + // game + MOBase::IPluginGame* game = m_dlg.game(); + + QString name = game->gameName(); + if (!m_dlg.gameEdition().isEmpty()) { + name += " (" + m_dlg.gameEdition() + ")"; + } + + lines.push_back(QObject::tr("Game: %1").arg(name)); + lines.push_back(QObject::tr("Game location: %1").arg(m_dlg.gameLocation())); + + return lines.join("\n"); + } + + QString dirLine(const QString& caption, const QString& path) const + { + return QString(" - %1: %2").arg(caption).arg(path); + } +}; + } // namespace @@ -936,7 +1072,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)); ui->pages->setCurrentIndex(0); @@ -1052,37 +1188,32 @@ void CreateInstanceDialog::updateNavigation() } } -CreateInstanceDialog::Types CreateInstanceDialog::selectedType() const +CreateInstanceDialog::Types CreateInstanceDialog::instanceType() const { - for (auto&& p : m_pages) { - const auto t = p->selectedType(); - if (t != NoType) { - return t; - } - } + return getSelected(&cid::Page::selectedInstanceType); +} - return NoType; +MOBase::IPluginGame* CreateInstanceDialog::game() const +{ + return getSelected(&cid::Page::selectedGame); } -MOBase::IPluginGame* CreateInstanceDialog::selectedGame() const +QString CreateInstanceDialog::gameLocation() const { - for (auto&& p : m_pages) { - if (auto* g=p->selectedGame()) { - return g; - } - } + return getSelected(&cid::Page::selectedGameLocation); +} - return nullptr; +QString CreateInstanceDialog::gameEdition() const +{ + return getSelected(&cid::Page::selectedGameEdition); } QString CreateInstanceDialog::instanceName() const { - for (auto&& p : m_pages) { - const auto s = p->instanceName(); - if (!s.isEmpty()) { - return s; - } - } + return getSelected(&cid::Page::selectedInstanceName); +} - return {}; +CreateInstanceDialog::Paths CreateInstanceDialog::paths() const +{ + return getSelected(&cid::Page::selectedPaths); } diff --git a/src/createinstancedialog.h b/src/createinstancedialog.h index 02608c8d..d9c392ca 100644 --- a/src/createinstancedialog.h +++ b/src/createinstancedialog.h @@ -21,6 +21,17 @@ public: Portable }; + struct Paths + { + QString base; + QString downloads; + QString mods; + QString profiles; + QString overwrite; + + auto operator<=>(const Paths&) const = default; + }; + explicit CreateInstanceDialog( const PluginContainer& pc, QWidget *parent = nullptr); @@ -38,15 +49,31 @@ public: void updateNavigation(); bool isOnLastPage() const; - Types selectedType() const; - MOBase::IPluginGame* selectedGame() const; + Types instanceType() const; + MOBase::IPluginGame* game() const; + QString gameLocation() const; + QString gameEdition() const; QString instanceName() const; + Paths paths() const; private: std::unique_ptr ui; const PluginContainer& m_pc; std::vector> m_pages; QString m_originalNext; + + template + T getSelected(T (cid::Page::*mf)() const) const + { + for (auto&& p : m_pages) { + const auto t = (p.get()->*mf)(); + if (t != T()) { + return t; + } + } + + return T(); + } }; #endif // MODORGANIZER_CREATEINSTANCEDIALOG_INCLUDED diff --git a/src/createinstancedialog.ui b/src/createinstancedialog.ui index 72d73020..c899a5b7 100644 --- a/src/createinstancedialog.ui +++ b/src/createinstancedialog.ui @@ -891,11 +891,7 @@ 0 - - - Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - +
    -- 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/createinstancedialog.ui') 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/createinstancedialog.ui') 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 0389688f3b30b9888bb94a08991bc1de00b89c59 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 25 Jul 2020 21:52:30 -0400 Subject: move to next page when clicking a game add game name in the label in the paths page can't use canonicalPath(), path might not exist --- src/createinstancedialog.ui | 13 ++++++++----- src/createinstancedialogpages.cpp | 13 ++++++++++--- src/createinstancedialogpages.h | 1 + 3 files changed, 19 insertions(+), 8 deletions(-) (limited to 'src/createinstancedialog.ui') diff --git a/src/createinstancedialog.ui b/src/createinstancedialog.ui index 4d9d8b06..41703f5e 100644 --- a/src/createinstancedialog.ui +++ b/src/createinstancedialog.ui @@ -7,7 +7,7 @@ 0 0 493 - 423 + 444 @@ -58,7 +58,7 @@ - 0 + 5 @@ -506,12 +506,15 @@ <h3>Select a folder where the data should be stored.</h3> + + true + - + - This includes downloads, mods, profiles and overwrite. If there is enough space on this drive, you should use the default folder. + This includes downloads, mods, profiles and overwrite for your <b>%1</b> instance. If there is enough space on this drive, you should use the default folder. true @@ -744,7 +747,7 @@ - Use <code>%BASE_DIR%</code> to refer to the Base Directory. + Use %BASE_DIR% to refer to the Base Directory. diff --git a/src/createinstancedialogpages.cpp b/src/createinstancedialogpages.cpp index 4d2c5b8b..e5213d33 100644 --- a/src/createinstancedialogpages.cpp +++ b/src/createinstancedialogpages.cpp @@ -269,6 +269,10 @@ void GamePage::select(IPluginGame* game) m_selection = checked; selectButton(checked); updateNavigation(); + + if (checked) { + next(); + } } void GamePage::selectCustom() @@ -277,6 +281,7 @@ void GamePage::selectCustom() &m_dlg, QObject::tr("Find game installation")); if (path.isEmpty()) { + // reselect the previous button selectButton(m_selection); return; } @@ -408,7 +413,7 @@ QCommandLinkButton* GamePage::createCustomButton() QObject::connect(b, &QAbstractButton::clicked, [&] { selectCustom(); - }); + }); return b; } @@ -422,7 +427,7 @@ void GamePage::createGameButton(Game* g) QObject::connect(g->button, &QAbstractButton::clicked, [g, this] { select(g->game); - }); + }); } void GamePage::fillList() @@ -759,6 +764,7 @@ bool NamePage::checkName(QString parentDir, QString name) PathsPage::PathsPage(CreateInstanceDialog& dlg) : Page(dlg), m_lastType(CreateInstanceDialog::NoType), + m_label(ui->pathsLabel), m_simpleExists(ui->locationExists), m_simpleInvalid(ui->locationInvalid), m_advancedExists(ui->advancedDirExists), m_advancedInvalid(ui->advancedDirInvalid) @@ -793,6 +799,7 @@ void PathsPage::activated() checkPaths(); updateNavigation(); + m_label.setText(m_dlg.game()->gameName()); m_lastInstanceName = name; m_lastType = type; } @@ -868,7 +875,7 @@ void PathsPage::setPaths(const QString& name, bool force) path = root + "/" + name; } - path = QDir::toNativeSeparators(QDir(path).canonicalPath()); + path = QDir::toNativeSeparators(QDir::cleanPath(path)); setIfEmpty(ui->location, path, force); setIfEmpty(ui->base, path, force); diff --git a/src/createinstancedialogpages.h b/src/createinstancedialogpages.h index 0d210493..15e63a8f 100644 --- a/src/createinstancedialogpages.h +++ b/src/createinstancedialogpages.h @@ -179,6 +179,7 @@ public: private: QString m_lastInstanceName; CreateInstanceDialog::Types m_lastType; + PlaceholderLabel m_label; mutable PlaceholderLabel m_simpleExists, m_simpleInvalid; mutable PlaceholderLabel m_advancedExists, m_advancedInvalid; -- 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/createinstancedialog.ui') 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/createinstancedialog.ui') 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 8987693dcf7dc116361869f128fd443716511f0c Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 25 Jul 2020 23:00:16 -0400 Subject: added hide option for intro page in create instance dialog --- src/createinstancedialog.cpp | 11 ++++++++++- src/createinstancedialog.ui | 26 +++++++++++++++++++++++--- src/createinstancedialogpages.cpp | 7 ++++++- src/createinstancedialogpages.h | 6 ++++-- src/settings.cpp | 15 +++++++++++++++ src/settings.h | 6 ++++++ src/settingsdialoggeneral.cpp | 1 + 7 files changed, 65 insertions(+), 7 deletions(-) (limited to 'src/createinstancedialog.ui') diff --git a/src/createinstancedialog.cpp b/src/createinstancedialog.cpp index 5a38aa50..2edfee88 100644 --- a/src/createinstancedialog.cpp +++ b/src/createinstancedialog.cpp @@ -19,7 +19,7 @@ CreateInstanceDialog::CreateInstanceDialog( ui->setupUi(this); m_originalNext = ui->next->text(); - 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)); @@ -30,10 +30,15 @@ CreateInstanceDialog::CreateInstanceDialog( ui->pages->setCurrentIndex(0); ui->launch->setChecked(true); + if (m_pages[0]->skip()) { + next(); + } + updateNavigation(); connect(ui->next, &QPushButton::clicked, [&]{ next(); }); connect(ui->back, &QPushButton::clicked, [&]{ back(); }); + connect(ui->cancel, &QPushButton::clicked, [&]{ reject(); }); } CreateInstanceDialog::~CreateInstanceDialog() = default; @@ -290,6 +295,10 @@ void CreateInstanceDialog::finish() logCreation(tr("Done.")); + if (ui->hideIntro->isChecked()) { + GlobalSettings::setHideCreateInstanceIntro(true); + } + if (ui->launch->isChecked()) { InstanceManager::instance().setCurrentInstance(ci.instanceName); ExitModOrganizer(Exit::Restart); diff --git a/src/createinstancedialog.ui b/src/createinstancedialog.ui index 1f9a8ce1..cafccb36 100644 --- a/src/createinstancedialog.ui +++ b/src/createinstancedialog.ui @@ -58,7 +58,7 @@ - 5 + 0 @@ -76,6 +76,26 @@ + + + + Never show this again + + + + + + + Qt::Vertical + + + + 20 + 40 + + + +
    @@ -258,7 +278,7 @@ 0 0 455 - 287 + 308 @@ -368,7 +388,7 @@ 0 0 455 - 257 + 278 diff --git a/src/createinstancedialogpages.cpp b/src/createinstancedialogpages.cpp index e5213d33..3f35b4fe 100644 --- a/src/createinstancedialogpages.cpp +++ b/src/createinstancedialogpages.cpp @@ -153,11 +153,16 @@ CreateInstanceDialog::Paths Page::selectedPaths() const } -InfoPage::InfoPage(CreateInstanceDialog& dlg) +IntroPage::IntroPage(CreateInstanceDialog& dlg) : Page(dlg) { } +bool IntroPage::skip() const +{ + return GlobalSettings::hideCreateInstanceIntro(); +} + TypePage::TypePage(CreateInstanceDialog& dlg) : Page(dlg), m_type(CreateInstanceDialog::NoType) diff --git a/src/createinstancedialogpages.h b/src/createinstancedialogpages.h index 15e63a8f..b8366234 100644 --- a/src/createinstancedialogpages.h +++ b/src/createinstancedialogpages.h @@ -53,10 +53,12 @@ protected: }; -class InfoPage : public Page +class IntroPage : public Page { public: - InfoPage(CreateInstanceDialog& dlg); + IntroPage(CreateInstanceDialog& dlg); + + bool skip() const override; }; diff --git a/src/settings.cpp b/src/settings.cpp index 76378af9..4ee4cd81 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -2201,3 +2201,18 @@ QSettings GlobalSettings::settings() return QSettings(Organization, Application); } + +bool GlobalSettings::hideCreateInstanceIntro() +{ + return settings().value("HideCreateInstanceIntro", false).toBool(); +} + +void GlobalSettings::setHideCreateInstanceIntro(bool b) +{ + settings().setValue("HideCreateInstanceIntro", b); +} + +void GlobalSettings::resetDialogs() +{ + setHideCreateInstanceIntro(false); +} diff --git a/src/settings.h b/src/settings.h index 54f1bb5b..dd8abf3f 100644 --- a/src/settings.h +++ b/src/settings.h @@ -838,6 +838,12 @@ public: static QString currentInstance(); static void setCurrentInstance(const QString& s); + static bool hideCreateInstanceIntro(); + static void setHideCreateInstanceIntro(bool b); + + // resets anything that the user can disable + static void resetDialogs(); + private: static QSettings settings(); }; diff --git a/src/settingsdialoggeneral.cpp b/src/settingsdialoggeneral.cpp index 3d14d521..f29e1d24 100644 --- a/src/settingsdialoggeneral.cpp +++ b/src/settingsdialoggeneral.cpp @@ -177,6 +177,7 @@ void GeneralSettingsTab::selectStyle() void GeneralSettingsTab::resetDialogs() { settings().widgets().resetQuestionButtons(); + GlobalSettings::resetDialogs(); } void GeneralSettingsTab::onExploreStyles() -- cgit v1.3.1 From 58479e4b00d399e3025558104e50c75ef609dda0 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Wed, 29 Jul 2020 22:14:43 -0400 Subject: filter for games replaced the button box with a regular widget with vertical layout it was doing weird things to buttons with focus when removing and adding buttons on the fly because of filtering --- src/createinstancedialog.ui | 204 +++++++++++++++++++++----------------- src/createinstancedialogpages.cpp | 46 ++++++++- src/createinstancedialogpages.h | 9 +- 3 files changed, 161 insertions(+), 98 deletions(-) (limited to 'src/createinstancedialog.ui') diff --git a/src/createinstancedialog.ui b/src/createinstancedialog.ui index cafccb36..a8f21c51 100644 --- a/src/createinstancedialog.ui +++ b/src/createinstancedialog.ui @@ -127,7 +127,7 @@
    - + 0 @@ -141,11 +141,8 @@ 0 - - - - 0 - + + 0 @@ -184,22 +181,22 @@ + + + + Qt::Vertical + + + + 20 + 40 + + + + - - - - Qt::Vertical - - - - 20 - 40 - - - -
    @@ -226,32 +223,12 @@
    - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Show all supported games - - - - + 0 @@ -265,59 +242,104 @@ 0 - - - Qt::ScrollBarAlwaysOff - - - true - - - - - 0 - 0 - 455 - 308 - + + + + 0 - - - 0 - 0 - + + 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - Qt::Vertical - - - QDialogButtonBox::NoButton + + 0 + + + 0 + + + + + true + + + + + 0 + 0 + 98 + 28 + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + PushButton + + + + - - - + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Show all supported games + + + + + + + Qt::Horizontal + + + + 175 + 20 + + + + + + + + Filter + + + + @@ -387,8 +409,8 @@ 0 0 - 455 - 278 + 98 + 28 diff --git a/src/createinstancedialogpages.cpp b/src/createinstancedialogpages.cpp index 3f35b4fe..2aac93a0 100644 --- a/src/createinstancedialogpages.cpp +++ b/src/createinstancedialogpages.cpp @@ -12,6 +12,7 @@ namespace cid using MOBase::IPluginGame; using MOBase::TaskDialog; +using MOBase::FilterWidget; QString makeDefaultPath(const std::wstring& dir) { @@ -228,6 +229,9 @@ GamePage::GamePage(CreateInstanceDialog& dlg) createGames(); fillList(); + m_filter.setEdit(ui->gamesFilter); + + QObject::connect(&m_filter, &FilterWidget::changed, [&]{ fillList(); }); QObject::connect(ui->showAllGames, &QCheckBox::clicked, [&]{ fillList(); }); } @@ -362,6 +366,7 @@ void GamePage::updateButton(Game* g) } g->button->setText(g->game->gameName()); + g->button->setIcon(g->game->gameIcon()); if (g->installed) { g->button->setDescription(g->dir); @@ -394,7 +399,7 @@ void GamePage::selectButton(Game* g) // was not installed; create it and show it // and it has a button, just check it createGameButton(gg.get()); - ui->games->addButton(gg->button, QDialogButtonBox::AcceptRole); + addButton(gg->button); } gg->button->setChecked(true); @@ -439,9 +444,8 @@ void GamePage::fillList() { const bool showAll = ui->showAllGames->isChecked(); - ui->games->clear(); - - ui->games->addButton(createCustomButton(), QDialogButtonBox::AcceptRole); + clearButtons(); + addButton(createCustomButton()); for (auto& g : m_games) { g->button = nullptr; @@ -451,11 +455,43 @@ void GamePage::fillList() continue; } + if (!m_filter.matches(g->game->gameName())) { + continue; + } + createGameButton(g.get()); - ui->games->addButton(g->button, QDialogButtonBox::AcceptRole); + addButton(g->button); } } +void GamePage::clearButtons() +{ + auto* ly = static_cast(ui->games->layout()); + + ui->games->setUpdatesEnabled(false); + + // delete all children + qDeleteAll(ui->games->findChildren("", Qt::FindDirectChildrenOnly)); + + // stretch widgets added with addStretch() are not in the parent widget, + // they have to be deleted from the layout itself + while (auto* child=ly->takeAt(0)) + delete child; + + // add a stretch, buttons will be added before + ly->addStretch(); + + ui->games->setUpdatesEnabled(true); +} + +void GamePage::addButton(QAbstractButton* b) +{ + auto* ly = static_cast(ui->games->layout()); + + // insert before the stretch + ly->insertWidget(ly->count() - 1, b); +} + GamePage::Game* GamePage::checkInstallation(const QString& path, Game* g) { if (g->game->looksValid(path)) { diff --git a/src/createinstancedialogpages.h b/src/createinstancedialogpages.h index b8366234..f2f4216d 100644 --- a/src/createinstancedialogpages.h +++ b/src/createinstancedialogpages.h @@ -1,10 +1,12 @@ #ifndef MODORGANIZER_CREATEINSTANCEDIALOGPAGES_INCLUDED #define MODORGANIZER_CREATEINSTANCEDIALOGPAGES_INCLUDED +#include "createinstancedialog.h" +#include + #include #include #include -#include "createinstancedialog.h" namespace MOBase { class IPluginGame; } @@ -107,16 +109,19 @@ private: std::vector> m_games; Game* m_selection; - + MOBase::FilterWidget m_filter; std::vector sortedGamePlugins() const; Game* findGame(MOBase::IPluginGame* game); void createGames(); void updateButton(Game* g); void selectButton(Game* g); + void clearButtons(); + void addButton(QAbstractButton* b); QCommandLinkButton* createCustomButton(); void createGameButton(Game* g); void fillList(); + void onFilter(); Game* checkInstallation(const QString& path, Game* g); MOBase::IPluginGame* findAnotherGame(const QString& path); bool confirmUnknown(const QString& path, MOBase::IPluginGame* game); -- cgit v1.3.1 From c4df6b8ec98feb34cc9b9785e6d4d0eac6b3f571 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Thu, 30 Jul 2020 00:48:46 -0400 Subject: split nexus connection stuff into NexusConnectionUI so it can be reused in the instance creation dialog removed the PluginContainer* parameter from NexusInterface::instance() - it's a singleton, so it only needs to be given once, not every time - it doesn't even matter because the first time instance() is called, it creates the singleton, but the plugin container is always null - and setPluginContainer() is called much later from OrganizerCore with the correct value added non functional nexus page to instance creation dialog --- src/createinstancedialog.cpp | 1 + src/createinstancedialog.ui | 161 ++++++++++++++-- src/createinstancedialogpages.cpp | 20 ++ src/createinstancedialogpages.h | 14 ++ src/main.cpp | 10 +- src/mainwindow.cpp | 62 +++---- src/modinfo.cpp | 18 +- src/modinfo.h | 5 +- src/modinfodialognexus.cpp | 8 +- src/nexusinterface.cpp | 15 +- src/nexusinterface.h | 5 +- src/organizercore.cpp | 22 +-- src/settingsdialognexus.cpp | 382 +++++++++++++++++++++----------------- src/settingsdialognexus.h | 55 ++++-- 14 files changed, 510 insertions(+), 268 deletions(-) (limited to 'src/createinstancedialog.ui') diff --git a/src/createinstancedialog.cpp b/src/createinstancedialog.cpp index 75ccc774..935b9ee9 100644 --- a/src/createinstancedialog.cpp +++ b/src/createinstancedialog.cpp @@ -25,6 +25,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)); ui->pages->setCurrentIndex(0); diff --git a/src/createinstancedialog.ui b/src/createinstancedialog.ui index a8f21c51..ebaef84d 100644 --- a/src/createinstancedialog.ui +++ b/src/createinstancedialog.ui @@ -266,8 +266,8 @@ 0 0 - 98 - 28 + 63 + 16 @@ -283,13 +283,6 @@ 0 - - - - PushButton - - - @@ -409,8 +402,8 @@ 0 0 - 98 - 28 + 63 + 16 @@ -886,6 +879,148 @@ + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + <h3>Link Mod Organizer with your Nexus account</h3> + + + + + + + Linking with Nexus allows you to download mods directly from Mod Organizer and automatically check for updates. This is optional. + + + true + + + + + + + + + + + 0 + 100 + + + + + 0 + + + 9 + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Connect to Nexus + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Enter API Key Manually + + + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + @@ -1057,6 +1192,8 @@ - + + + diff --git a/src/createinstancedialogpages.cpp b/src/createinstancedialogpages.cpp index f37853f0..37d50ee1 100644 --- a/src/createinstancedialogpages.cpp +++ b/src/createinstancedialogpages.cpp @@ -997,6 +997,26 @@ bool PathsPage::checkPath( } +NexusPage::NexusPage(CreateInstanceDialog& dlg) + : Page(dlg) +{ +} + +bool NexusPage::ready() const +{ + return true; +} + +bool NexusPage::skip() const +{ + return Settings::instance().nexus().hasApiKey(); +} + +void NexusPage::activated() +{ +} + + ConfirmationPage::ConfirmationPage(CreateInstanceDialog& dlg) : Page(dlg) { diff --git a/src/createinstancedialogpages.h b/src/createinstancedialogpages.h index f2f4216d..88e49628 100644 --- a/src/createinstancedialogpages.h +++ b/src/createinstancedialogpages.h @@ -203,6 +203,20 @@ private: }; +class NexusPage : public Page +{ +public: + NexusPage(CreateInstanceDialog& dlg); + + bool ready() const override; + bool skip() const override; + void activated() override; + +private: + +}; + + class ConfirmationPage : public Page { public: diff --git a/src/main.cpp b/src/main.cpp index 58e22466..93580931 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -395,7 +395,7 @@ int runApplication( QString apiKey; if (settings.nexus().apiKey(apiKey)) { - NexusInterface::instance(pluginContainer.get())->getAccessManager()->apiCheck(apiKey); + NexusInterface::instance().getAccessManager()->apiCheck(apiKey); } log::debug("initializing tutorials"); @@ -415,8 +415,8 @@ int runApplication( // set up main window and its data structures MainWindow mainWindow(settings, organizer, *pluginContainer); - NexusInterface::instance(pluginContainer.get()) - ->getAccessManager()->setTopLevelWidget(&mainWindow); + NexusInterface::instance() + .getAccessManager()->setTopLevelWidget(&mainWindow); QObject::connect(&mainWindow, SIGNAL(styleChanged(QString)), &application, SLOT(setStyleFile(QString))); @@ -443,8 +443,8 @@ int runApplication( res = application.exec(); mainWindow.close(); - NexusInterface::instance(pluginContainer.get()) - ->getAccessManager()->setTopLevelWidget(nullptr); + NexusInterface::instance() + .getAccessManager()->setTopLevelWidget(nullptr); } settings.geometry().resetIfNeeded(); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 60ad77a0..9144210c 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -291,7 +291,7 @@ MainWindow::MainWindow(Settings &settings ui->statusBar->setup(ui, settings); { - auto* ni = NexusInterface::instance(&m_PluginContainer); + auto* ni = &NexusInterface::instance(); // there are two ways to get here: // 1) the user just started MO, and @@ -439,24 +439,24 @@ MainWindow::MainWindow(Settings &settings connect(m_OrganizerCore.updater(), SIGNAL(updateAvailable()), this, SLOT(updateAvailable())); connect(m_OrganizerCore.updater(), SIGNAL(motdAvailable(QString)), this, SLOT(motdReceived(QString))); - connect(NexusInterface::instance(&pluginContainer), SIGNAL(requestNXMDownload(QString)), &m_OrganizerCore, SLOT(downloadRequestedNXM(QString))); - connect(NexusInterface::instance(&pluginContainer), SIGNAL(nxmDownloadURLsAvailable(QString,int,int,QVariant,QVariant,int)), this, SLOT(nxmDownloadURLs(QString,int,int,QVariant,QVariant,int))); - connect(NexusInterface::instance(&pluginContainer), SIGNAL(needLogin()), &m_OrganizerCore, SLOT(nexusApi())); + connect(&NexusInterface::instance(), SIGNAL(requestNXMDownload(QString)), &m_OrganizerCore, SLOT(downloadRequestedNXM(QString))); + connect(&NexusInterface::instance(), SIGNAL(nxmDownloadURLsAvailable(QString,int,int,QVariant,QVariant,int)), this, SLOT(nxmDownloadURLs(QString,int,int,QVariant,QVariant,int))); + connect(&NexusInterface::instance(), SIGNAL(needLogin()), &m_OrganizerCore, SLOT(nexusApi())); connect( - NexusInterface::instance(&pluginContainer)->getAccessManager(), + NexusInterface::instance().getAccessManager(), SIGNAL(credentialsReceived(const APIUserAccount&)), this, SLOT(updateWindowTitle(const APIUserAccount&))); connect( - NexusInterface::instance(&pluginContainer)->getAccessManager(), + NexusInterface::instance().getAccessManager(), SIGNAL(credentialsReceived(const APIUserAccount&)), - NexusInterface::instance(&m_PluginContainer), + &NexusInterface::instance(), SLOT(setUserAccount(const APIUserAccount&))); connect( - NexusInterface::instance(&pluginContainer), + &NexusInterface::instance(), SIGNAL(requestsChanged(const APIStats&, const APIUserAccount&)), this, SLOT(onRequestsChanged(const APIStats&, const APIUserAccount&))); @@ -1911,9 +1911,9 @@ QDir MainWindow::currentSavesDir() const wchar_t path[MAX_PATH]; if (::GetPrivateProfileStringW( - L"General", L"SLocalSavePath", L"", - path, MAX_PATH, - iniPath.toStdWString().c_str() + L"General", L"SLocalSavePath", L"", + path, MAX_PATH, + iniPath.toStdWString().c_str() )) { savesDir.setPath(m_OrganizerCore.managedGame()->documentsDirectory().absoluteFilePath(QString::fromWCharArray(path))); } @@ -3288,7 +3288,7 @@ void MainWindow::visitOnNexus_clicked() int modID = info->nexusId(); gameName = info->gameName(); if (modID > 0) { - linkClicked(NexusInterface::instance(&m_PluginContainer)->getModURL(modID, gameName)); + linkClicked(NexusInterface::instance().getModURL(modID, gameName)); } else { log::error("mod '{}' has no nexus id", info->name()); } @@ -3298,7 +3298,7 @@ void MainWindow::visitOnNexus_clicked() int modID = m_OrganizerCore.modList()->data(m_OrganizerCore.modList()->index(m_ContextRow, 0), Qt::UserRole).toInt(); QString gameName = m_OrganizerCore.modList()->data(m_OrganizerCore.modList()->index(m_ContextRow, 0), Qt::UserRole + 4).toString(); if (modID > 0) { - linkClicked(NexusInterface::instance(&m_PluginContainer)->getModURL(modID, gameName)); + linkClicked(NexusInterface::instance().getModURL(modID, gameName)); } else { MessageDialog::showMessage(tr("Nexus ID for this mod is unknown"), this); } @@ -3355,7 +3355,7 @@ void MainWindow::visitNexusOrWebPage(const QModelIndex& idx) const auto url = info->parseCustomURL(); if (modID > 0) { - linkClicked(NexusInterface::instance(&m_PluginContainer)->getModURL(modID, gameName)); + linkClicked(NexusInterface::instance().getModURL(modID, gameName)); } else if (url.isValid()) { linkClicked(url.toString()); } else { @@ -4220,15 +4220,15 @@ void MainWindow::saveArchiveList() void MainWindow::checkModsForUpdates() { bool checkingModsForUpdate = false; - if (NexusInterface::instance(&m_PluginContainer)->getAccessManager()->validated()) { - checkingModsForUpdate = ModInfo::checkAllForUpdate(&m_PluginContainer, this); - NexusInterface::instance(&m_PluginContainer)->requestEndorsementInfo(this, QVariant(), QString()); - NexusInterface::instance(&m_PluginContainer)->requestTrackingInfo(this, QVariant(), QString()); + if (NexusInterface::instance().getAccessManager()->validated()) { + checkingModsForUpdate = ModInfo::checkAllForUpdate(this); + NexusInterface::instance().requestEndorsementInfo(this, QVariant(), QString()); + NexusInterface::instance().requestTrackingInfo(this, QVariant(), QString()); } else { QString apiKey; if (m_OrganizerCore.settings().nexus().apiKey(apiKey)) { m_OrganizerCore.doAfterLogin([this] () { this->checkModsForUpdates(); }); - NexusInterface::instance(&m_PluginContainer)->getAccessManager()->apiCheck(apiKey); + NexusInterface::instance().getAccessManager()->apiCheck(apiKey); } else { log::warn("{}", tr("You are not currently authenticated with Nexus. Please do so under Settings -> Nexus.")); } @@ -4603,7 +4603,7 @@ void MainWindow::exportModListCSV() if (nexus_ID->isChecked()) builder.setRowField("#Nexus_ID", info->nexusId()); if (mod_Nexus_URL->isChecked()) - builder.setRowField("#Mod_Nexus_URL",(info->nexusId()>0)? NexusInterface::instance(&m_PluginContainer)->getModURL(info->nexusId(), info->gameName()) : ""); + builder.setRowField("#Mod_Nexus_URL",(info->nexusId()>0)? NexusInterface::instance().getModURL(info->nexusId(), info->gameName()) : ""); if (mod_Version->isChecked()) builder.setRowField("#Mod_Version", info->version().canonicalString()); if (install_Date->isChecked()) @@ -5157,7 +5157,7 @@ void MainWindow::on_actionSettings_triggered() } if (settings.paths().cache() != oldCacheDirectory) { - NexusInterface::instance(&m_PluginContainer)->setCacheDirectory( + NexusInterface::instance().setCacheDirectory( settings.paths().cache()); } @@ -5191,7 +5191,7 @@ void MainWindow::on_actionNexus_triggered() QString gameName = game->gameShortName(); if (game->gameNexusName().isEmpty() && game->primarySources().count()) gameName = game->primarySources()[0]; - QDesktopServices::openUrl(QUrl(NexusInterface::instance(&m_PluginContainer)->getGameURL(gameName))); + QDesktopServices::openUrl(QUrl(NexusInterface::instance().getGameURL(gameName))); } @@ -5350,9 +5350,9 @@ void MainWindow::actionEndorseMO() if (QMessageBox::question(this, tr("Endorse Mod Organizer"), tr("Do you want to endorse Mod Organizer on %1 now?").arg( - NexusInterface::instance(&m_PluginContainer)->getGameURL(game->gameShortName())), + NexusInterface::instance().getGameURL(game->gameShortName())), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { - NexusInterface::instance(&m_PluginContainer)->requestToggleEndorsement( + NexusInterface::instance().requestToggleEndorsement( game->gameShortName(), game->nexusModOrganizerID(), m_OrganizerCore.getVersion().canonicalString(), true, this, QVariant(), QString()); } } @@ -5366,9 +5366,9 @@ void MainWindow::actionWontEndorseMO() if (QMessageBox::question(this, tr("Abstain from Endorsing Mod Organizer"), tr("Are you sure you want to abstain from endorsing Mod Organizer 2?\n" "You will have to visit the mod page on the %1 Nexus site to change your mind.").arg( - NexusInterface::instance(&m_PluginContainer)->getGameURL(game->gameShortName())), + NexusInterface::instance().getGameURL(game->gameShortName())), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { - NexusInterface::instance(&m_PluginContainer)->requestToggleEndorsement( + NexusInterface::instance().requestToggleEndorsement( game->gameShortName(), game->nexusModOrganizerID(), m_OrganizerCore.getVersion().canonicalString(), false, this, QVariant(), QString()); } } @@ -5430,13 +5430,13 @@ void MainWindow::updateDownloadView() void MainWindow::modUpdateCheck(std::multimap IDs) { - if (NexusInterface::instance(&m_PluginContainer)->getAccessManager()->validated()) { - ModInfo::manualUpdateCheck(&m_PluginContainer, this, IDs); + if (NexusInterface::instance().getAccessManager()->validated()) { + ModInfo::manualUpdateCheck(this, IDs); } else { QString apiKey; if (m_OrganizerCore.settings().nexus().apiKey(apiKey)) { m_OrganizerCore.doAfterLogin([=]() { this->modUpdateCheck(IDs); }); - NexusInterface::instance(&m_PluginContainer)->getAccessManager()->apiCheck(apiKey); + NexusInterface::instance().getAccessManager()->apiCheck(apiKey); } else log::warn("{}", tr("You are not currently authenticated with Nexus. Please do so under Settings -> Nexus.")); } @@ -5588,7 +5588,7 @@ void MainWindow::finishUpdateInfo() log::warn("{}", tr("All of your mods have been checked recently. We restrict update checks to help preserve your available API requests.")); for (auto game : organizedGames) - NexusInterface::instance(&m_PluginContainer)->requestUpdates(game.second, this, QVariant(), game.first, QString()); + NexusInterface::instance().requestUpdates(game.second, this, QVariant(), game.first, QString()); disconnect(sender()); delete sender(); @@ -5675,7 +5675,7 @@ void MainWindow::nxmUpdatesAvailable(QString gameName, int modID, QVariant userD } if (requiresInfo) - NexusInterface::instance(&m_PluginContainer)->requestModInfo(gameNameReal, modID, this, QVariant(), QString()); + NexusInterface::instance().requestModInfo(gameNameReal, modID, this, QVariant(), QString()); } void MainWindow::nxmModInfoAvailable(QString gameName, int modID, QVariant userData, QVariant resultData, int requestID) diff --git a/src/modinfo.cpp b/src/modinfo.cpp index bbeefb12..04cb18ce 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -266,7 +266,7 @@ void ModInfo::updateFromDisc(const QString &modDirectory, createFromOverwrite(pluginContainer, game, directoryStructure); std::sort(s_Collection.begin(), s_Collection.end(), ModInfo::ByName); - + parallelMap(std::begin(s_Collection), std::end(s_Collection), &ModInfo::prefetch, refreshThreadCount); updateIndices(); @@ -295,7 +295,7 @@ ModInfo::ModInfo(PluginContainer *pluginContainer) } -bool ModInfo::checkAllForUpdate(PluginContainer *pluginContainer, QObject *receiver) +bool ModInfo::checkAllForUpdate(QObject *receiver) { bool updatesAvailable = true; @@ -348,19 +348,19 @@ bool ModInfo::checkAllForUpdate(PluginContainer *pluginContainer, QObject *recei } for (auto game : organizedGames) - NexusInterface::instance(pluginContainer)->requestUpdates(game.second, receiver, QVariant(), game.first, QString()); + NexusInterface::instance().requestUpdates(game.second, receiver, QVariant(), game.first, QString()); } else if (earliest < QDateTime::currentDateTimeUtc().addMonths(-1)) { for (auto gameName : games) - NexusInterface::instance(pluginContainer)->requestUpdateInfo(gameName, NexusInterface::UpdatePeriod::MONTH, receiver, QVariant(true), QString()); + NexusInterface::instance().requestUpdateInfo(gameName, NexusInterface::UpdatePeriod::MONTH, receiver, QVariant(true), QString()); } else if (earliest < QDateTime::currentDateTimeUtc().addDays(-7)) { for (auto gameName : games) - NexusInterface::instance(pluginContainer)->requestUpdateInfo(gameName, NexusInterface::UpdatePeriod::MONTH, receiver, QVariant(false), QString()); + NexusInterface::instance().requestUpdateInfo(gameName, NexusInterface::UpdatePeriod::MONTH, receiver, QVariant(false), QString()); } else if (earliest < QDateTime::currentDateTimeUtc().addDays(-1)) { for (auto gameName : games) - NexusInterface::instance(pluginContainer)->requestUpdateInfo(gameName, NexusInterface::UpdatePeriod::WEEK, receiver, QVariant(false), QString()); + NexusInterface::instance().requestUpdateInfo(gameName, NexusInterface::UpdatePeriod::WEEK, receiver, QVariant(false), QString()); } else { for (auto gameName : games) - NexusInterface::instance(pluginContainer)->requestUpdateInfo(gameName, NexusInterface::UpdatePeriod::DAY, receiver, QVariant(false), QString()); + NexusInterface::instance().requestUpdateInfo(gameName, NexusInterface::UpdatePeriod::DAY, receiver, QVariant(false), QString()); } return updatesAvailable; @@ -400,7 +400,7 @@ std::set> ModInfo::filteredMods(QString gameName, QVaria return finalMods; } -void ModInfo::manualUpdateCheck(PluginContainer *pluginContainer, QObject *receiver, std::multimap IDs) +void ModInfo::manualUpdateCheck(QObject *receiver, std::multimap IDs) { std::vector> mods; std::set> organizedGames; @@ -438,7 +438,7 @@ void ModInfo::manualUpdateCheck(PluginContainer *pluginContainer, QObject *recei } for (auto game : organizedGames) { - NexusInterface::instance(pluginContainer)->requestUpdates(game.second, receiver, QVariant(), game.first, QString()); + NexusInterface::instance().requestUpdates(game.second, receiver, QVariant(), game.first, QString()); } } else { log::info("None of the selected mods can be updated."); diff --git a/src/modinfo.h b/src/modinfo.h index 7223cece..480fe013 100644 --- a/src/modinfo.h +++ b/src/modinfo.h @@ -190,15 +190,14 @@ public: // Static functions: * @brief Run a limited batch of mod update checks for "newest version" information. * */ - static void manualUpdateCheck( - PluginContainer *pluginContainer, QObject *receiver, std::multimap IDs); + static void manualUpdateCheck(QObject *receiver, std::multimap IDs); /** * @brief Query nexus information for every mod and update the "newest version" information. * * @return true if any mods are checked for update. */ - static bool checkAllForUpdate(PluginContainer *pluginContainer, QObject *receiver); + static bool checkAllForUpdate(QObject *receiver); /** * diff --git a/src/modinfodialognexus.cpp b/src/modinfodialognexus.cpp index dd95cdaa..b4099a8d 100644 --- a/src/modinfodialognexus.cpp +++ b/src/modinfodialognexus.cpp @@ -141,8 +141,8 @@ void NexusTab::updateWebpage() const int modID = mod().nexusId(); if (isValidModID(modID)) { - const QString nexusLink = NexusInterface::instance(&plugin()) - ->getModURL(modID, mod().gameName()); + const QString nexusLink = NexusInterface::instance() + .getModURL(modID, mod().gameName()); ui->visitNexus->setToolTip(nexusLink); refreshData(modID); @@ -360,8 +360,8 @@ void NexusTab::onVisitNexus() const int modID = mod().nexusId(); if (isValidModID(modID)) { - const QString nexusLink = NexusInterface::instance(&plugin()) - ->getModURL(modID, mod().gameName()); + const QString nexusLink = NexusInterface::instance() + .getModURL(modID, mod().gameName()); shell::Open(QUrl(nexusLink)); } diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp index 9eb1540a..396cec11 100644 --- a/src/nexusinterface.cpp +++ b/src/nexusinterface.cpp @@ -26,6 +26,7 @@ along with Mod Organizer. If not, see . #include #include "shared/util.h" #include +#include #include #include @@ -50,7 +51,7 @@ void throttledWarning(const APIUserAccount& user) NexusBridge::NexusBridge(PluginContainer *pluginContainer, const QString &subModule) - : m_Interface(NexusInterface::instance(pluginContainer)) + : m_Interface(&NexusInterface::instance()) , m_SubModule(subModule) { } @@ -234,8 +235,8 @@ APILimits NexusInterface::parseLimits( } -NexusInterface::NexusInterface(PluginContainer *pluginContainer) - : m_PluginContainer(pluginContainer) +NexusInterface::NexusInterface() + : m_PluginContainer(nullptr) { m_User.limits(defaultAPILimits()); m_MOVersion = createVersionInfo(); @@ -255,10 +256,10 @@ NexusInterface::~NexusInterface() cleanup(); } -NexusInterface *NexusInterface::instance(PluginContainer *pluginContainer) +NexusInterface& NexusInterface::instance() { - static NexusInterface s_Instance(pluginContainer); - return &s_Instance; + static NexusInterface ni; + return ni; } void NexusInterface::setCacheDirectory(const QString &directory) @@ -284,7 +285,7 @@ void NexusInterface::interpretNexusFileName(const QString &fileName, QString &mo static const QRegularExpression complex(R"(^([a-zA-Z0-9_'"\-.() ]*?)([-_ ][VvRr]+[0-9]+(?:(?:[\.][0-9]+){0,2}|(?:[_][0-9]+){0,2}|(?:[-.][0-9]+){0,2})?[ab]?)??-([1-9][0-9]+)?-.*?\.(zip|rar|7z))"); //complex regex explanation: //group 1: modname. - //group 2: optional version, + //group 2: optional version, // assumed to start with v (empty most of the time). //group 3: NexusId, // assumed wrapped in "-", will miss single digit IDs for better accuracy. diff --git a/src/nexusinterface.h b/src/nexusinterface.h index 35362bdf..72f30a38 100644 --- a/src/nexusinterface.h +++ b/src/nexusinterface.h @@ -154,8 +154,7 @@ public: static APILimits parseLimits(const QList& headers); ~NexusInterface(); - - static NexusInterface *instance(PluginContainer *pluginContainer); + static NexusInterface& instance(); /** * @return the access manager object used to connect to nexus @@ -535,7 +534,7 @@ private: private: - NexusInterface(PluginContainer *pluginContainer); + NexusInterface(); void nextRequest(); void requestFinished(std::list::iterator iter); MOBase::IPluginGame *getGame(QString gameName) const; diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 0849d756..7fd8c33b 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -95,12 +95,12 @@ OrganizerCore::OrganizerCore(Settings &settings) , m_PluginContainer(nullptr) , m_CurrentProfile(nullptr) , m_Settings(settings) - , m_Updater(NexusInterface::instance(m_PluginContainer)) + , m_Updater(&NexusInterface::instance()) , m_ModList(m_PluginContainer, this) , m_PluginList(this) , m_DirectoryRefresher(new DirectoryRefresher(settings.refreshThreadCount())) , m_DirectoryStructure(new DirectoryEntry(L"data", nullptr, 0)) - , m_DownloadManager(NexusInterface::instance(m_PluginContainer), this) + , m_DownloadManager(&NexusInterface::instance(), this) , m_DirectoryUpdate(false) , m_ArchivesInit(false) , m_PluginListsWriter(std::bind(&OrganizerCore::savePluginList, this)) @@ -108,8 +108,7 @@ OrganizerCore::OrganizerCore(Settings &settings) env::setHandleCloserThreadCount(settings.refreshThreadCount()); m_DownloadManager.setOutputDirectory(m_Settings.paths().downloads(), false); - NexusInterface::instance(m_PluginContainer)->setCacheDirectory( - m_Settings.paths().cache()); + NexusInterface::instance().setCacheDirectory(m_Settings.paths().cache()); m_InstallationManager.setModsDirectory(m_Settings.paths().mods()); m_InstallationManager.setDownloadDirectory(m_Settings.paths().downloads()); @@ -122,9 +121,9 @@ OrganizerCore::OrganizerCore(Settings &settings) connect(&m_ModList, SIGNAL(removeOrigin(QString)), this, SLOT(removeOrigin(QString))); - connect(NexusInterface::instance(m_PluginContainer)->getAccessManager(), + connect(NexusInterface::instance().getAccessManager(), SIGNAL(validateSuccessful(bool)), this, SLOT(loginSuccessful(bool))); - connect(NexusInterface::instance(m_PluginContainer)->getAccessManager(), + connect(NexusInterface::instance().getAccessManager(), SIGNAL(validateFailed(QString)), this, SLOT(loginFailed(QString))); // This seems awfully imperative @@ -332,8 +331,7 @@ Settings &OrganizerCore::settings() bool OrganizerCore::nexusApi(bool retry) { - NXMAccessManager *accessManager - = NexusInterface::instance(m_PluginContainer)->getAccessManager(); + auto* accessManager = NexusInterface::instance().getAccessManager(); if ((accessManager->validateAttempted() || accessManager->validated()) && !retry) { @@ -1424,13 +1422,13 @@ void OrganizerCore::updateModsInDirectoryStructure(QMap f) { - if (NexusInterface::instance(m_PluginContainer)->getAccessManager()->validated()) { + if (NexusInterface::instance().getAccessManager()->validated()) { f(); } else { QString apiKey; if (settings().nexus().apiKey(apiKey)) { doAfterLogin([f]{ f(); }); - NexusInterface::instance(m_PluginContainer)->getAccessManager()->apiCheck(apiKey); + NexusInterface::instance().getAccessManager()->apiCheck(apiKey); } else { MessageDialog::showMessage(tr("You need to be logged in with Nexus"), parent); } @@ -1704,7 +1702,7 @@ void OrganizerCore::loginSuccessful(bool necessary) } m_PostLoginTasks.clear(); - NexusInterface::instance(m_PluginContainer)->loginCompleted(); + NexusInterface::instance().loginCompleted(); } void OrganizerCore::loginSuccessfulUpdate(bool necessary) @@ -1742,7 +1740,7 @@ void OrganizerCore::loginFailed(const QString &message) qApp->activeWindow()); m_PostLoginTasks.clear(); } - NexusInterface::instance(m_PluginContainer)->loginCompleted(); + NexusInterface::instance().loginCompleted(); } void OrganizerCore::loginFailedUpdate(const QString &message) diff --git a/src/settingsdialognexus.cpp b/src/settingsdialognexus.cpp index d49e0a33..a76b9ad7 100644 --- a/src/settingsdialognexus.cpp +++ b/src/settingsdialognexus.cpp @@ -71,112 +71,45 @@ private: }; -NexusSettingsTab::NexusSettingsTab(Settings& s, SettingsDialog& d) - : SettingsTab(s, d) +NexusConnectionUI::NexusConnectionUI(Settings& s, QWidget* parent) : + m_parent(parent), m_settings(s), + m_connect(nullptr), m_disconnect(nullptr), m_manual(nullptr), m_log(nullptr) { - ui->offlineBox->setChecked(settings().network().offlineMode()); - ui->proxyBox->setChecked(settings().network().useProxy()); - ui->endorsementBox->setChecked(settings().nexus().endorsementIntegration()); - ui->hideAPICounterBox->setChecked(settings().interface().hideAPICounter()); - - // display server preferences - for (const auto& server : s.network().servers()) { - QString descriptor = server.name(); - - if (!descriptor.compare("CDN", Qt::CaseInsensitive)) { - descriptor += QStringLiteral(" (automatic)"); - } - - const auto averageSpeed = server.averageSpeed(); - if (averageSpeed > 0) { - descriptor += QString(" (%1)").arg(MOBase::localizedByteSpeed(averageSpeed)); - } - - QListWidgetItem *newItem = new ServerItem(descriptor, Qt::UserRole + 1); - - newItem->setData(Qt::UserRole, server.name()); - newItem->setData(Qt::UserRole + 1, server.preferred()); - - if (server.preferred() > 0) { - ui->preferredServersList->addItem(newItem); - } else { - ui->knownServersList->addItem(newItem); - } - - ui->preferredServersList->sortItems(Qt::DescendingOrder); - } - - QObject::connect(ui->nexusConnect, &QPushButton::clicked, [&]{ on_nexusConnect_clicked(); }); - QObject::connect(ui->nexusManualKey, &QPushButton::clicked, [&]{ on_nexusManualKey_clicked(); }); - QObject::connect(ui->nexusDisconnect, &QPushButton::clicked, [&]{ on_nexusDisconnect_clicked(); }); - QObject::connect(ui->clearCacheButton, &QPushButton::clicked, [&]{ on_clearCacheButton_clicked(); }); - QObject::connect(ui->associateButton, &QPushButton::clicked, [&]{ on_associateButton_clicked(); }); - - if (settings().nexus().hasApiKey()) { - addNexusLog(QObject::tr("Connected.")); - } else { - addNexusLog(QObject::tr("Not connected.")); - } - - updateNexusState(); } -void NexusSettingsTab::update() +void NexusConnectionUI::set( + QAbstractButton* connectButton, + QAbstractButton* disconnectButton, + QAbstractButton* manualButton, + QListWidget* logList) { - settings().network().setOfflineMode(ui->offlineBox->isChecked()); - settings().network().setUseProxy(ui->proxyBox->isChecked()); - settings().nexus().setEndorsementIntegration(ui->endorsementBox->isChecked()); - settings().interface().setHideAPICounter(ui->hideAPICounterBox->isChecked()); - - auto servers = settings().network().servers(); - - // store server preference - for (int i = 0; i < ui->knownServersList->count(); ++i) { - const QString key = ui->knownServersList->item(i)->data(Qt::UserRole).toString(); - - bool found = false; - - for (auto& server : servers) { - if (server.name() == key) { - server.setPreferred(0); - found = true; - break; - } - } - - if (!found) { - log::error("while setting preferred to 0, server '{}' not found", key); - } + m_connect = connectButton; + if (m_connect) { + QObject::connect(m_connect, &QPushButton::clicked, [&]{ connect(); }); } - const int count = ui->preferredServersList->count(); - - for (int i = 0; i < count; ++i) { - const QString key = ui->preferredServersList->item(i)->data(Qt::UserRole).toString(); - const int newPreferred = count - i; + m_disconnect = disconnectButton; + if (m_disconnect) { + QObject::connect(m_disconnect, &QPushButton::clicked, [&]{ disconnect(); }); + } - bool found = false; + m_manual = manualButton; + if (m_manual) { + QObject::connect(manualButton, &QPushButton::clicked, [&]{ manual(); }); + } - for (auto& server : servers) { + m_log = logList; - if (server.name() == key) { - server.setPreferred(newPreferred); - found = true; - break; - } - } - - if (!found) { - log::error( - "while setting preference to {}, server '{}' not found", - newPreferred, key); - } + if (m_settings.nexus().hasApiKey()) { + addLog(tr("Connected.")); + } else { + addLog(tr("Not connected.")); } - settings().network().updateServers(servers); + updateState(); } -void NexusSettingsTab::on_nexusConnect_clicked() +void NexusConnectionUI::connect() { if (m_nexusLogin && m_nexusLogin->isActive()) { m_nexusLogin->cancel(); @@ -195,19 +128,19 @@ void NexusSettingsTab::on_nexusConnect_clicked() }; } - ui->nexusLog->clear(); + m_log->clear(); m_nexusLogin->start(); - updateNexusState(); + updateState(); } -void NexusSettingsTab::on_nexusManualKey_clicked() +void NexusConnectionUI::manual() { if (m_nexusValidator && m_nexusValidator->isActive()) { m_nexusValidator->cancel(); return; } - NexusManualKeyDialog d(&dialog()); + NexusManualKeyDialog d(m_parent); if (d.exec() != QDialog::Accepted) { return; } @@ -218,162 +151,273 @@ void NexusSettingsTab::on_nexusManualKey_clicked() return; } - ui->nexusLog->clear(); + m_log->clear(); validateKey(key); } -void NexusSettingsTab::on_nexusDisconnect_clicked() +void NexusConnectionUI::disconnect() { clearKey(); - ui->nexusLog->clear(); - addNexusLog(QObject::tr("Disconnected.")); -} - -void NexusSettingsTab::on_clearCacheButton_clicked() -{ - QDir(Settings::instance().paths().cache()).removeRecursively(); - NexusInterface::instance(dialog().pluginContainer())->clearCache(); -} - -void NexusSettingsTab::on_associateButton_clicked() -{ - Settings::instance().nexus().registerAsNXMHandler(true); + m_log->clear(); + addLog(tr("Disconnected.")); } -void NexusSettingsTab::validateKey(const QString& key) +void NexusConnectionUI::validateKey(const QString& key) { if (!m_nexusValidator) { m_nexusValidator.reset(new NexusKeyValidator( - *NexusInterface::instance(dialog().pluginContainer())->getAccessManager())); + *NexusInterface::instance().getAccessManager())); m_nexusValidator->finished = [&](auto&& r, auto&& m, auto&& u) { onValidatorFinished(r, m, u); }; } - addNexusLog(QObject::tr("Checking API key...")); + addLog(tr("Checking API key...")); m_nexusValidator->start(key, NexusKeyValidator::OneShot); } -void NexusSettingsTab::onSSOKeyChanged(const QString& key) +void NexusConnectionUI::onSSOKeyChanged(const QString& key) { if (key.isEmpty()) { clearKey(); } else { - addNexusLog(QObject::tr("Received API key.")); + addLog(tr("Received API key.")); validateKey(key); } } -void NexusSettingsTab::onSSOStateChanged(NexusSSOLogin::States s, const QString& e) +void NexusConnectionUI::onSSOStateChanged( + NexusSSOLogin::States s, const QString& e) { if (s != NexusSSOLogin::Finished) { // finished state is handled in onSSOKeyChanged() const auto log = NexusSSOLogin::stateToString(s, e); for (auto&& line : log.split("\n")) { - addNexusLog(line); + addLog(line); } } - updateNexusState(); + updateState(); } -void NexusSettingsTab::onValidatorFinished( +void NexusConnectionUI::onValidatorFinished( ValidationAttempt::Result r, const QString& message, std::optional user) { if (user) { - NexusInterface::instance(dialog().pluginContainer())->setUserAccount(*user); - addNexusLog(QObject::tr("Received user acount information")); + NexusInterface::instance().setUserAccount(*user); + addLog(tr("Received user account information")); if (setKey(user->apiKey())) { - addNexusLog(QObject::tr("Linked with Nexus successfully.")); + addLog(tr("Linked with Nexus successfully.")); } else { - addNexusLog(QObject::tr("Failed to set API key")); + addLog(tr("Failed to set API key")); } } else { if (message.isEmpty()) { // shouldn't happen - addNexusLog("Unknown error"); + addLog("Unknown error"); } else { - addNexusLog(message); + addLog(message); } } - updateNexusState(); + updateState(); } -void NexusSettingsTab::addNexusLog(const QString& s) +void NexusConnectionUI::addLog(const QString& s) { - ui->nexusLog->addItem(s); - ui->nexusLog->scrollToBottom(); + m_log->addItem(s); + m_log->scrollToBottom(); } -bool NexusSettingsTab::setKey(const QString& key) +bool NexusConnectionUI::setKey(const QString& key) { - dialog().setExitNeeded(Exit::Restart); - const bool ret = settings().nexus().setApiKey(key); - updateNexusState(); + const bool ret = m_settings.nexus().setApiKey(key); + updateState(); + + emit keyChanged(); + return ret; } -bool NexusSettingsTab::clearKey() +bool NexusConnectionUI::clearKey() { - dialog().setExitNeeded(Exit::Restart); - const auto ret = settings().nexus().clearApiKey(); + const auto ret = m_settings.nexus().clearApiKey(); + + NexusInterface::instance().getAccessManager()->clearApiKey(); + updateState(); - NexusInterface::instance(dialog().pluginContainer())->getAccessManager()->clearApiKey(); - updateNexusState(); + emit keyChanged(); return ret; } -void NexusSettingsTab::updateNexusState() +void NexusConnectionUI::updateState() { - updateNexusButtons(); - updateNexusData(); -} + auto setButton = [&](QAbstractButton* b, bool enabled, QString caption={}) { + if (b) { + b->setEnabled(enabled); + if (!caption.isEmpty()) { + b->setText(caption); + } + } + }; -void NexusSettingsTab::updateNexusButtons() -{ if (m_nexusLogin && m_nexusLogin->isActive()) { // api key is in the process of being retrieved - ui->nexusConnect->setText(QObject::tr("Cancel")); - ui->nexusConnect->setEnabled(true); - ui->nexusDisconnect->setEnabled(false); - ui->nexusManualKey->setText(QObject::tr("Enter API Key Manually")); - ui->nexusManualKey->setEnabled(false); + setButton(m_connect, true, QObject::tr("Cancel")); + setButton(m_disconnect, false); + setButton(m_manual, false,QObject::tr("Enter API Key Manually")); } else if (m_nexusValidator && m_nexusValidator->isActive()) { // api key is in the process of being tested - ui->nexusConnect->setText(QObject::tr("Connect to Nexus")); - ui->nexusConnect->setEnabled(false); - ui->nexusDisconnect->setEnabled(false); - ui->nexusManualKey->setText(QObject::tr("Cancel")); - ui->nexusManualKey->setEnabled(true); + setButton(m_connect, false, QObject::tr("Connect to Nexus")); + setButton(m_disconnect, false); + setButton(m_manual, true, QObject::tr("Cancel")); } - else if (settings().nexus().hasApiKey()) { + else if (m_settings.nexus().hasApiKey()) { // api key is present - ui->nexusConnect->setText(QObject::tr("Connect to Nexus")); - ui->nexusConnect->setEnabled(false); - ui->nexusDisconnect->setEnabled(true); - ui->nexusManualKey->setText(QObject::tr("Enter API Key Manually")); - ui->nexusManualKey->setEnabled(false); + setButton(m_connect, false, QObject::tr("Connect to Nexus")); + setButton(m_disconnect, true); + setButton(m_manual, false, QObject::tr("Enter API Key Manually")); } else { // api key not present - ui->nexusConnect->setText(QObject::tr("Connect to Nexus")); - ui->nexusConnect->setEnabled(true); - ui->nexusDisconnect->setEnabled(false); - ui->nexusManualKey->setText(QObject::tr("Enter API Key Manually")); - ui->nexusManualKey->setEnabled(true); + setButton(m_connect, true, QObject::tr("Connect to Nexus")); + setButton(m_disconnect, false); + setButton(m_manual, true, QObject::tr("Enter API Key Manually")); + } + + emit stateChanged(); +} + + + +NexusSettingsTab::NexusSettingsTab(Settings& s, SettingsDialog& d) + : SettingsTab(s, d), m_connectionUI(s, &d) +{ + ui->offlineBox->setChecked(settings().network().offlineMode()); + ui->proxyBox->setChecked(settings().network().useProxy()); + ui->endorsementBox->setChecked(settings().nexus().endorsementIntegration()); + ui->hideAPICounterBox->setChecked(settings().interface().hideAPICounter()); + + // display server preferences + for (const auto& server : s.network().servers()) { + QString descriptor = server.name(); + + if (!descriptor.compare("CDN", Qt::CaseInsensitive)) { + descriptor += QStringLiteral(" (automatic)"); + } + + const auto averageSpeed = server.averageSpeed(); + if (averageSpeed > 0) { + descriptor += QString(" (%1)").arg(MOBase::localizedByteSpeed(averageSpeed)); + } + + QListWidgetItem *newItem = new ServerItem(descriptor, Qt::UserRole + 1); + + newItem->setData(Qt::UserRole, server.name()); + newItem->setData(Qt::UserRole + 1, server.preferred()); + + if (server.preferred() > 0) { + ui->preferredServersList->addItem(newItem); + } else { + ui->knownServersList->addItem(newItem); + } + + ui->preferredServersList->sortItems(Qt::DescendingOrder); } + + m_connectionUI.set( + ui->nexusConnect, + ui->nexusDisconnect, + ui->nexusManualKey, + ui->nexusLog); + + QObject::connect( + &m_connectionUI, &NexusConnectionUI::stateChanged, &d, + [&]{ updateNexusData(); }, Qt::QueuedConnection); + + QObject::connect( + &m_connectionUI, &NexusConnectionUI::keyChanged, &d, + [&]{ dialog().setExitNeeded(Exit::Restart); }); + + + QObject::connect(ui->clearCacheButton, &QPushButton::clicked, [&]{ on_clearCacheButton_clicked(); }); + QObject::connect(ui->associateButton, &QPushButton::clicked, [&]{ on_associateButton_clicked(); }); +} + +void NexusSettingsTab::update() +{ + settings().network().setOfflineMode(ui->offlineBox->isChecked()); + settings().network().setUseProxy(ui->proxyBox->isChecked()); + settings().nexus().setEndorsementIntegration(ui->endorsementBox->isChecked()); + settings().interface().setHideAPICounter(ui->hideAPICounterBox->isChecked()); + + auto servers = settings().network().servers(); + + // store server preference + for (int i = 0; i < ui->knownServersList->count(); ++i) { + const QString key = ui->knownServersList->item(i)->data(Qt::UserRole).toString(); + + bool found = false; + + for (auto& server : servers) { + if (server.name() == key) { + server.setPreferred(0); + found = true; + break; + } + } + + if (!found) { + log::error("while setting preferred to 0, server '{}' not found", key); + } + } + + const int count = ui->preferredServersList->count(); + + for (int i = 0; i < count; ++i) { + const QString key = ui->preferredServersList->item(i)->data(Qt::UserRole).toString(); + const int newPreferred = count - i; + + bool found = false; + + for (auto& server : servers) { + + if (server.name() == key) { + server.setPreferred(newPreferred); + found = true; + break; + } + } + + if (!found) { + log::error( + "while setting preference to {}, server '{}' not found", + newPreferred, key); + } + } + + settings().network().updateServers(servers); +} + +void NexusSettingsTab::on_clearCacheButton_clicked() +{ + QDir(Settings::instance().paths().cache()).removeRecursively(); + NexusInterface::instance().clearCache(); +} + +void NexusSettingsTab::on_associateButton_clicked() +{ + Settings::instance().nexus().registerAsNXMHandler(true); } void NexusSettingsTab::updateNexusData() { - const auto user = NexusInterface::instance(dialog().pluginContainer()) - ->getAPIUserAccount(); + const auto user = NexusInterface::instance().getAPIUserAccount(); if (user.isValid()) { ui->nexusUserID->setText(user.id()); diff --git a/src/settingsdialognexus.h b/src/settingsdialognexus.h index 2cb1cc1e..0adf67c7 100644 --- a/src/settingsdialognexus.h +++ b/src/settingsdialognexus.h @@ -5,38 +5,67 @@ #include "settingsdialog.h" #include "nxmaccessmanager.h" -class NexusSettingsTab : public SettingsTab +class NexusConnectionUI : public QObject { + Q_OBJECT; + public: - NexusSettingsTab(Settings& settings, SettingsDialog& dialog); - void update(); + NexusConnectionUI(Settings& s, QWidget* parent=nullptr); + + void set( + QAbstractButton* connectButton, + QAbstractButton* disconnectButton, + QAbstractButton* manualButton, + QListWidget* logList); + + void connect(); + void manual(); + void disconnect(); + +signals: + void stateChanged(); + void keyChanged(); private: + QWidget* m_parent; + Settings& m_settings; + QAbstractButton* m_connect; + QAbstractButton* m_disconnect; + QAbstractButton* m_manual; + QListWidget* m_log; + std::unique_ptr m_nexusLogin; std::unique_ptr m_nexusValidator; - void on_nexusConnect_clicked(); - void on_nexusManualKey_clicked(); - void on_nexusDisconnect_clicked(); - void on_clearCacheButton_clicked(); - void on_associateButton_clicked(); + void addLog(const QString& s); + + void updateState(); void validateKey(const QString& key); bool setKey(const QString& key); bool clearKey(); - void updateNexusState(); - void updateNexusButtons(); - void updateNexusData(); - void onSSOKeyChanged(const QString& key); void onSSOStateChanged(NexusSSOLogin::States s, const QString& e); void onValidatorFinished( ValidationAttempt::Result r, const QString& message, std::optional useR); +}; + - void addNexusLog(const QString& s); +class NexusSettingsTab : public SettingsTab +{ +public: + NexusSettingsTab(Settings& settings, SettingsDialog& dialog); + void update(); + +private: + NexusConnectionUI m_connectionUI; + + void on_clearCacheButton_clicked(); + void on_associateButton_clicked(); + void updateNexusData(); }; #endif // SETTINGSDIALOGNEXUS_H -- 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/createinstancedialog.ui') 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/createinstancedialog.ui') 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 fb2cddd92a1f09fef22dc66c737aa608408aaa18 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 3 Nov 2020 11:13:28 -0500 Subject: replace "create instance" title by "setting up instance" fixed bad game location for custom paths --- src/createinstancedialog.cpp | 7 ++++++- src/createinstancedialog.h | 6 +++--- src/createinstancedialog.ui | 11 +++++++++-- src/main.cpp | 10 +++++----- 4 files changed, 23 insertions(+), 11 deletions(-) (limited to 'src/createinstancedialog.ui') diff --git a/src/createinstancedialog.cpp b/src/createinstancedialog.cpp index d67e3451..d2846367 100644 --- a/src/createinstancedialog.cpp +++ b/src/createinstancedialog.cpp @@ -99,13 +99,18 @@ void CreateInstanceDialog::back() changePage(-1); } -void CreateInstanceDialog::setSinglePageImpl() +void CreateInstanceDialog::setSinglePageImpl(const QString& instanceName) { m_singlePage = true; if (m_pages[ui->pages->currentIndex()]->skip()) { next(); } + + // don't show the "create a new instance" title for single pages, this is + // when the instance already exists but some info is missing + ui->title->setText(tr("Setting up instance %1").arg(instanceName)); + setWindowTitle(tr("Setting up an instance %1").arg(instanceName)); } void CreateInstanceDialog::changePage(int d) diff --git a/src/createinstancedialog.h b/src/createinstancedialog.h index 6947f2e2..f05495c6 100644 --- a/src/createinstancedialog.h +++ b/src/createinstancedialog.h @@ -58,7 +58,7 @@ public: Settings* settings(); template - void setSinglePage() + void setSinglePage(const QString& instanceName) { for (auto&& p : m_pages) { if (auto* tp=dynamic_cast(p.get())) { @@ -68,7 +68,7 @@ public: } } - setSinglePageImpl(); + setSinglePageImpl(instanceName); } template @@ -113,7 +113,7 @@ private: bool m_singlePage; - void setSinglePageImpl(); + void setSinglePageImpl(const QString& instanceName); template T getSelected(T (cid::Page::*mf)() const) const diff --git a/src/createinstancedialog.ui b/src/createinstancedialog.ui index b7f4f502..b10d74a7 100644 --- a/src/createinstancedialog.ui +++ b/src/createinstancedialog.ui @@ -30,9 +30,16 @@ 0 - + + + + 14 + 75 + true + + - <h2>Creating a new instance</h2> + Creating a new instance diff --git a/src/main.cpp b/src/main.cpp index 2a5a3e81..bfc074ec 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -344,7 +344,7 @@ SetupInstanceResults setupInstance(Instance& instance, PluginContainer& pc) .arg(instance.name()).arg(instance.iniPath())); CreateInstanceDialog dlg(pc, nullptr); - dlg.setSinglePage(); + dlg.setSinglePage(instance.name()); dlg.show(); dlg.activateWindow(); @@ -356,7 +356,7 @@ SetupInstanceResults setupInstance(Instance& instance, PluginContainer& pc) instance.setGame( dlg.creationInfo().game->gameName(), - dlg.creationInfo().game->gameDirectory().absolutePath()); + dlg.creationInfo().gameLocation); return SetupInstanceResults::TryAgain; } @@ -384,7 +384,7 @@ SetupInstanceResults setupInstance(Instance& instance, PluginContainer& pc) .arg(instance.gameName())); CreateInstanceDialog dlg(pc, nullptr); - dlg.setSinglePage(); + dlg.setSinglePage(instance.name()); dlg.show(); dlg.activateWindow(); @@ -396,7 +396,7 @@ SetupInstanceResults setupInstance(Instance& instance, PluginContainer& pc) instance.setGame( dlg.creationInfo().game->gameName(), - dlg.creationInfo().game->gameDirectory().absolutePath()); + dlg.creationInfo().gameLocation); return SetupInstanceResults::TryAgain; } @@ -408,7 +408,7 @@ SetupInstanceResults setupInstance(Instance& instance, PluginContainer& pc) dlg.getPage()->select( instance.gamePlugin(), instance.gameDirectory()); - dlg.setSinglePage(); + dlg.setSinglePage(instance.name()); dlg.show(); dlg.activateWindow(); -- cgit v1.3.1