diff options
| author | isanae <14251494+isanae@users.noreply.github.com> | 2020-07-24 15:43:13 -0400 |
|---|---|---|
| committer | isanae <14251494+isanae@users.noreply.github.com> | 2020-11-03 11:39:05 -0500 |
| commit | 048e3ac0d9b4e4258c9c2ac4d39d332f340e73a0 (patch) | |
| tree | 52e14f6e9d6dd6044ecf7935e6e8a560360143aa /src/createinstancedialog.cpp | |
| parent | 4ac8ab98563e223075c1c93852d4112ab6e4579c (diff) | |
split create instance dialog pages
Diffstat (limited to 'src/createinstancedialog.cpp')
| -rw-r--r-- | src/createinstancedialog.cpp | 1053 |
1 files changed, 3 insertions, 1050 deletions
diff --git a/src/createinstancedialog.cpp b/src/createinstancedialog.cpp index 5c85aa68..63be7346 100644 --- a/src/createinstancedialog.cpp +++ b/src/createinstancedialog.cpp @@ -1,1063 +1,16 @@ #include "createinstancedialog.h" #include "ui_createinstancedialog.h" +#include "createinstancedialogpages.h" #include "instancemanager.h" -#include "plugincontainer.h" +//#include "plugincontainer.h" #include "settings.h" #include "shared/appconfig.h" -#include <report.h> +//#include <report.h> #include <iplugingame.h> #include <utility.h> using namespace MOBase; -namespace cid -{ - -using MOBase::TaskDialog; - -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)); - } - } -}; - -QString makeDefaultPath(const std::wstring& dir) -{ - return QDir::toNativeSeparators( - PathSettings::makeDefaultPath(QString::fromStdWString(dir))); -} - - -class Page -{ -public: - Page(CreateInstanceDialog& dlg) - : ui(dlg.getUI()), m_dlg(dlg), m_pc(dlg.pluginContainer()) - { - } - - virtual bool ready() const - { - 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 selectedInstanceType() const - { - // no-op - return CreateInstanceDialog::NoType; - } - - virtual IPluginGame* selectedGame() const - { - // no-op - return nullptr; - } - - 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 {}; - } - -protected: - Ui::CreateInstanceDialog* ui; - CreateInstanceDialog& m_dlg; - const PluginContainer& m_pc; -}; - - -class InfoPage : public Page -{ -public: - InfoPage(CreateInstanceDialog& dlg) - : Page(dlg) - { - } -}; - - -class TypePage : public Page -{ -public: - TypePage(CreateInstanceDialog& dlg) - : Page(dlg), m_type(CreateInstanceDialog::NoType) - { - ui->createGlobal->setDescription( - ui->createGlobal->description() - .arg(InstanceManager::instance().instancesPath())); - - ui->createPortable->setDescription( - ui->createPortable->description() - .arg(qApp->applicationDirPath())); - - QObject::connect( - ui->createGlobal, &QAbstractButton::clicked, [&]{ global(); }); - - QObject::connect( - ui->createPortable, &QAbstractButton::clicked, [&]{ portable(); }); - } - - bool ready() const override - { - return (m_type != CreateInstanceDialog::NoType); - } - - CreateInstanceDialog::Types selectedInstanceType() const override - { - return m_type; - } - - void global() - { - m_type = CreateInstanceDialog::Global; - - ui->createGlobal->setChecked(true); - ui->createPortable->setChecked(false); - - next(); - } - - void portable() - { - m_type = CreateInstanceDialog::Portable; - - ui->createGlobal->setChecked(false); - ui->createPortable->setChecked(true); - - next(); - } - -private: - CreateInstanceDialog::Types m_type; -}; - - -class GamePage : public Page -{ -public: - GamePage(CreateInstanceDialog& dlg) - : Page(dlg), m_selection(nullptr) - { - createGames(); - fillList(); - - QObject::connect(ui->showAllGames, &QCheckBox::clicked, [&]{ fillList(); }); - } - - bool ready() const override - { - return (m_selection != nullptr); - } - - IPluginGame* selectedGame() const override - { - if (!m_selection) { - return nullptr; - } - - return m_selection->game; - } - - QString selectedGameLocation() const override - { - if (!m_selection) { - return {}; - } - - return QDir::toNativeSeparators(m_selection->dir); - } - - void select(IPluginGame* game) - { - Game* checked = findGame(game); - - 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); - } - } - } - - m_selection = checked; - selectButton(checked); - updateNavigation(); - } - - void selectCustom() - { - const auto path = QFileDialog::getExistingDirectory( - &m_dlg, QObject::tr("Find game installation")); - - if (path.isEmpty()) { - selectButton(m_selection); - return; - } - - for (auto& g : m_games) { - if (g->game->looksValid(path)) { - g->dir = path; - g->installed = true; - select(g->game); - updateButton(g.get()); - return; - } - } - - warnUnrecognized(path); - selectButton(m_selection); - } - - void warnUnrecognized(const QString& path) - { - QString supportedGames; - for (auto* game : sortedGamePlugins()) { - supportedGames += "<li>" + game->gameName() + "</li>"; - } - - QMessageBox::warning(&m_dlg, - QObject::tr("Unrecognized game"), - QObject::tr( - "The folder %1 does not seem to contain a game Mod Organizer can " - "manage.<br><br><b>These are the games that can be managed:</b>" - "<ul>%2</ul>").arg(path).arg(supportedGames)); - } - -private: - struct Game - { - IPluginGame* game = nullptr; - QCommandLinkButton* button = nullptr; - QString dir; - bool installed = false; - - Game(IPluginGame* g) - : game(g), installed(g->isInstalled()) - { - if (installed) { - dir = game->gameDirectory().path(); - } - } - - Game(const Game&) = delete; - Game& operator=(const Game&) = delete; - }; - - std::vector<std::unique_ptr<Game>> m_games; - Game* m_selection; - - - std::vector<IPluginGame*> sortedGamePlugins() const - { - std::vector<IPluginGame*> v; - - for (auto* game : m_pc.plugins<IPluginGame>()) { - v.push_back(game); - } - - std::sort(v.begin(), v.end(), [](auto* a, auto* b) { - return (a->gameName() < b->gameName()); - }); - - return v; - } - - Game* findGame(IPluginGame* game) - { - for (auto& g : m_games) { - if (g->game == game) { - return g.get(); - } - } - - return nullptr; - } - - void createGames() - { - m_games.clear(); - - for (auto* game : sortedGamePlugins()) { - m_games.push_back(std::make_unique<Game>(game)); - } - } - - void updateButton(Game* g) - { - if (!g || !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) - { - // go through each game, set the button that is for game `g` as active; - // some button might not exist, which happens when selecting a custom - // folder for a game that was considered uninstalled - - for (const auto& gg : m_games) { - if (!g) { - // nothing should be selected - if (gg->button) { - gg->button->setChecked(false); - } - - continue; - } - - if (gg->game == g->game) { - // this is the button that should be selected - - if (!gg->button) { - // this happens when the button wasn't visible because the game - // 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); - } - - gg->button->setChecked(true); - gg->button->setFocus(); - } else { - // this is not the button you're looking for - if (gg->button) { - gg->button->setChecked(false); - } - } - } - } - - QCommandLinkButton* createCustomButton() - { - auto* b = new QCommandLinkButton; - - b->setText(QObject::tr("Browse...")); - b->setDescription( - QObject::tr("The folder must contain a valid game installation")); - - QObject::connect(b, &QAbstractButton::clicked, [&] { - selectCustom(); - }); - - return b; - } - - void createGameButton(Game* g) - { - g->button = new QCommandLinkButton; - g->button->setCheckable(true); - - updateButton(g); - - QObject::connect(g->button, &QAbstractButton::clicked, [g, this] { - select(g->game); - }); - } - - void fillList() - { - const bool showAll = ui->showAllGames->isChecked(); - - ui->games->clear(); - - ui->games->addButton(createCustomButton(), QDialogButtonBox::AcceptRole); - - for (auto& g : m_games) { - g->button = nullptr; - - if (!showAll && !g->installed) { - // not installed - continue; - } - - createGameButton(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; - } - - IPluginGame* findAnotherGame(const QString& path) - { - for (auto* otherGame : m_pc.plugins<IPluginGame>()) { - if (otherGame->looksValid(path)) { - return otherGame; - } - } - - return nullptr; - } - - bool confirmUnknown(const QString& path, IPluginGame* game) - { - const auto r = TaskDialog(&m_dlg) - .title(QObject::tr("Unrecognized game")) - .main(QObject::tr("Unrecognized game")) - .content(QObject::tr( - "The folder %1 does not seem to contain an installation for " - "<span style=\"white-space: nowrap; font-weight: bold;\">%2</span> or " - "for 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); - } - - IPluginGame* confirmOtherGame( - const QString& path, - IPluginGame* selectedGame, IPluginGame* guessedGame) - { - const auto r = 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 " - "<span style=\"white-space: nowrap; font-weight: bold;\">%2</span>, " - "not " - "<span style=\"white-space: nowrap; font-weight: bold;\">%3</span>.") - .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; - } - } -}; - - -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.game(); - if (!g) { - // shouldn't happen - return true; - } - - const auto variants = g->gameVariants(); - return (variants.size() < 2); - } - - void activated() override - { - auto* g = m_dlg.game(); - - 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(); - } - - 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: - IPluginGame* m_previousGame; - std::vector<QCommandLinkButton*> m_buttons; - QString m_selection; - - void fillList() - { - ui->editions->clear(); - m_buttons.clear(); - - auto* g = m_dlg.game(); - 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) : - Page(dlg), m_modified(false), m_okay(false), - m_checker(ui->instanceNameExists, ui->instanceNameInvalid) - { - 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.instanceType() == CreateInstanceDialog::Portable); - } - - void activated() override - { - auto* g = m_dlg.game(); - 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(); - } - - QString selectedInstanceName() const override - { - if (!m_okay) { - return {}; - } - - const auto text = ui->instanceName->text().trimmed(); - return m_checker.sanitizeFileName(text); - } - -private: - PathChecker m_checker; - QString m_originalLabel; - bool m_modified; - bool m_okay; - - void onChanged() - { - m_modified = true; - updateWarnings(); - } - - void updateWarnings() - { - const auto root = InstanceManager::instance().instancesPath(); - - m_okay = m_checker.checkName(root, ui->instanceName->text()); - updateNavigation(); - } -}; - - -class PathsPage : public Page -{ -public: - 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 name = m_dlg.instanceName(); - - setPaths(name, (m_lastInstanceName != name)); - checkPaths(); - updateNavigation(); - - 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; - - 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 = PathSettings::resolve(path, 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); - } - } -}; - - -class ConfirmationPage : public Page -{ -public: - ConfirmationPage(CreateInstanceDialog& dlg) - : Page(dlg) - { - } - - void activated() override - { - ui->review->setPlainText(makeReview()); - ui->creationLog->clear(); - } - - QString toLocalizedString(CreateInstanceDialog::Types t) const - { - switch (t) - { - case CreateInstanceDialog::Global: - return QObject::tr("Global"); - - case CreateInstanceDialog::Portable: - return QObject::tr("Portable"); - - default: - 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 - QString name = m_dlg.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 - - CreateInstanceDialog::CreateInstanceDialog( const PluginContainer& pc, QWidget *parent) : QDialog(parent), ui(new Ui::CreateInstanceDialog), m_pc(pc) |
