summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorisanae <14251494+isanae@users.noreply.github.com>2020-07-23 12:20:55 -0400
committerisanae <14251494+isanae@users.noreply.github.com>2020-11-03 11:39:04 -0500
commitebbc8ed60c606b1b76fcea36eb2bd5db6373f7f0 (patch)
treecdbb34f1aa9a6adf2024dba27abaeab71a1059ef
parent46d81056d1eda2fd74dac200cd7b8c36d9cbc4eb (diff)
paths page
-rw-r--r--src/createinstancedialog.cpp337
-rw-r--r--src/createinstancedialog.h3
-rw-r--r--src/createinstancedialog.ui148
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 &lt;code&gt;%BASE_DIR%&lt;/code&gt; 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 &lt;code&gt;%BASE_DIR%&lt;/code&gt; 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>