summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/createinstancedialog.ui21
-rw-r--r--src/createinstancedialogpages.cpp62
-rw-r--r--src/instancemanager.cpp9
-rw-r--r--src/instancemanager.h1
-rw-r--r--src/instancemanagerdialog.cpp154
-rw-r--r--src/instancemanagerdialog.h12
-rw-r--r--src/instancemanagerdialog.ui27
7 files changed, 221 insertions, 65 deletions
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 @@
</widget>
</item>
<item>
- <widget class="QCheckBox" name="hideIntro">
+ <widget class="LinkLabel" name="label_19">
<property name="text">
- <string>Never show this again</string>
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/ModOrganizer2/modorganizer/wiki/Instance&quot;&gt;More information&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="openExternalLinks">
+ <bool>true</bool>
</property>
</widget>
</item>
@@ -96,6 +99,13 @@
</property>
</spacer>
</item>
+ <item>
+ <widget class="QCheckBox" name="hideIntro">
+ <property name="text">
+ <string>Never show this page again</string>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
<widget class="QWidget" name="page">
@@ -1202,6 +1212,13 @@
</item>
</layout>
</widget>
+ <customwidgets>
+ <customwidget>
+ <class>LinkLabel</class>
+ <extends>QLabel</extends>
+ <header location="global">linklabel.h</header>
+ </customwidget>
+ </customwidgets>
<resources>
<include location="resources.qrc"/>
</resources>
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 <iplugingame.h>
+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<InstanceInfo>(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<std::size_t>(sel[0].row());
+ return static_cast<std::size_t>(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 <filterwidget.h>
#include <QDialog>
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::InstanceManagerDialog> ui;
const PluginContainer& m_pc;
std::vector<std::unique_ptr<InstanceInfo>> 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 @@
<number>0</number>
</property>
<item>
- <widget class="QListWidget" name="list"/>
+ <widget class="QListView" name="list"/>
+ </item>
+ <item>
+ <widget class="QWidget" name="widget_5" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLineEdit" name="filter">
+ <property name="placeholderText">
+ <string>Filter</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
</item>
</layout>
</widget>