diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/createinstancedialog.cpp | 337 | ||||
| -rw-r--r-- | src/createinstancedialog.h | 3 | ||||
| -rw-r--r-- | src/createinstancedialog.ui | 148 |
3 files changed, 395 insertions, 93 deletions
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 <report.h> #include <iplugingame.h> 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<TypePage>(*this)); m_pages.push_back(std::make_unique<GamePage>(*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<std::size_t>(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::CreateInstanceDialog> ui; const PluginContainer& m_pc; std::vector<std::unique_ptr<cid::Page>> 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 @@ </property> </widget> </item> - <item row="1" column="0"> + <item row="3" column="0"> <spacer name="verticalSpacer_3"> <property name="orientation"> <enum>Qt::Vertical</enum> @@ -567,8 +567,26 @@ </property> </spacer> </item> + <item row="2" column="0" colspan="3"> + <widget class="QLabel" name="locationExists"> + <property name="text"> + <string>This folder already exists.</string> + </property> + </widget> + </item> <item row="0" column="1"> - <widget class="QLineEdit" name="location"/> + <widget class="QLineEdit" name="location"> + <property name="placeholderText"> + <string>Folder</string> + </property> + </widget> + </item> + <item row="1" column="0" colspan="3"> + <widget class="QLabel" name="locationInvalid"> + <property name="text"> + <string>The folder contains invalid characters.</string> + </property> + </widget> </item> </layout> </widget> @@ -604,10 +622,17 @@ <property name="bottomMargin"> <number>0</number> </property> - <item row="4" column="0"> - <widget class="QLabel" name="label_13"> + <item row="4" column="3"> + <widget class="QToolButton" name="browseOverwrite"> <property name="text"> - <string>Overwrite</string> + <string>...</string> + </property> + </widget> + </item> + <item row="2" column="3"> + <widget class="QToolButton" name="browseMods"> + <property name="text"> + <string>...</string> </property> </widget> </item> @@ -618,6 +643,27 @@ </property> </widget> </item> + <item row="4" column="2"> + <widget class="QLineEdit" name="overwrite"> + <property name="placeholderText"> + <string>Folder</string> + </property> + </widget> + </item> + <item row="2" column="2"> + <widget class="QLineEdit" name="mods"> + <property name="placeholderText"> + <string>Folder</string> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="label_13"> + <property name="text"> + <string>Overwrite</string> + </property> + </widget> + </item> <item row="0" column="0"> <widget class="QLabel" name="label_8"> <property name="text"> @@ -625,29 +671,30 @@ </property> </widget> </item> - <item row="6" column="0"> - <spacer name="verticalSpacer_4"> - <property name="orientation"> - <enum>Qt::Vertical</enum> + <item row="1" column="3"> + <widget class="QToolButton" name="browseDownloads"> + <property name="text"> + <string>...</string> </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>40</height> - </size> + </widget> + </item> + <item row="3" column="2"> + <widget class="QLineEdit" name="profiles"> + <property name="placeholderText"> + <string>Folder</string> </property> - </spacer> + </widget> </item> - <item row="3" column="0"> - <widget class="QLabel" name="label_12"> + <item row="5" column="0" colspan="4"> + <widget class="QLabel" name="advancedDirExists"> <property name="text"> - <string>Profiles</string> + <string>The folder %1 already exists.</string> + </property> + <property name="wordWrap"> + <bool>true</bool> </property> </widget> </item> - <item row="2" column="2"> - <widget class="QLineEdit" name="mods"/> - </item> <item row="1" column="0"> <widget class="QLabel" name="label_9"> <property name="text"> @@ -656,40 +703,35 @@ </widget> </item> <item row="1" column="2"> - <widget class="QLineEdit" name="downloads"/> - </item> - <item row="3" column="2"> - <widget class="QLineEdit" name="profiles"/> - </item> - <item row="4" column="2"> - <widget class="QLineEdit" name="overwrite"/> + <widget class="QLineEdit" name="downloads"> + <property name="placeholderText"> + <string>Folder</string> + </property> + </widget> </item> <item row="0" column="2"> - <widget class="QLineEdit" name="base"/> - </item> - <item row="5" column="0" colspan="4"> - <widget class="QLabel" name="label_11"> - <property name="text"> - <string>Use <code>%BASE_DIR%</code> to refer to the Base Directory.</string> + <widget class="QLineEdit" name="base"> + <property name="placeholderText"> + <string>Folder</string> </property> </widget> </item> - <item row="0" column="3"> - <widget class="QToolButton" name="browseBase"> + <item row="3" column="0"> + <widget class="QLabel" name="label_12"> <property name="text"> - <string>...</string> + <string>Profiles</string> </property> </widget> </item> - <item row="1" column="3"> - <widget class="QToolButton" name="browseDownloads"> + <item row="8" column="0" colspan="4"> + <widget class="QLabel" name="label_11"> <property name="text"> - <string>...</string> + <string>Use <code>%BASE_DIR%</code> to refer to the Base Directory.</string> </property> </widget> </item> - <item row="4" column="3"> - <widget class="QToolButton" name="browseOverwrite"> + <item row="0" column="3"> + <widget class="QToolButton" name="browseBase"> <property name="text"> <string>...</string> </property> @@ -702,10 +744,26 @@ </property> </widget> </item> - <item row="2" column="3"> - <widget class="QToolButton" name="browseMods"> + <item row="7" column="0"> + <spacer name="verticalSpacer_4"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item row="6" column="0" colspan="4"> + <widget class="QLabel" name="advancedDirInvalid"> <property name="text"> - <string>...</string> + <string>The folder %1 contains invalid characters.</string> + </property> + <property name="wordWrap"> + <bool>true</bool> </property> </widget> </item> |
