diff options
| author | isanae <14251494+isanae@users.noreply.github.com> | 2020-11-09 04:27:12 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-11-09 04:27:12 -0500 |
| commit | 84e53f3ab3cd2b89eed3e37be734da3e997f4032 (patch) | |
| tree | 2b14f10377b1ca2b85317c62267a6cf74c64d102 /src/instancemanagerdialog.cpp | |
| parent | d23b38b4dfbc61ffe509dd2627ecbf3589409409 (diff) | |
| parent | efdda8e7385446096a1032c2b7c2aeaf57132c78 (diff) | |
Merge pull request #1284 from isanae/command-line-cleanup
Command line cleanup
Diffstat (limited to 'src/instancemanagerdialog.cpp')
| -rw-r--r-- | src/instancemanagerdialog.cpp | 527 |
1 files changed, 174 insertions, 353 deletions
diff --git a/src/instancemanagerdialog.cpp b/src/instancemanagerdialog.cpp index 3b6daeb8..b461d39d 100644 --- a/src/instancemanagerdialog.cpp +++ b/src/instancemanagerdialog.cpp @@ -13,292 +13,93 @@ using namespace MOBase; -class InstanceInfo +// returns the icon for the given instance or an empty 32x32 icon if the game +// plugin couldn't be found +// +QIcon instanceIcon(const PluginContainer& pc, const Instance& i) { -public: - struct Object - { - QString path; - bool mandatoryDelete; + const auto* game = InstanceManager::singleton() + .gamePluginForDirectory(i.directory(), pc); - Object(QString p, bool d=false) - : path(std::move(p)), mandatoryDelete(d) - { - } - - bool operator<(const Object& o) const - { - if (mandatoryDelete && !o.mandatoryDelete) { - return true; - } else if (!mandatoryDelete && o.mandatoryDelete) { - return false; - } - - return false; - } - }; + if (game) + return game->gameIcon(); + QPixmap empty(32, 32); + empty.fill(QColor(0, 0, 0, 0)); + return QIcon(empty); +} - InstanceInfo(QDir dir, bool isPortable) - : m_portable(isPortable) - { - setDir(dir); - } - - void setDir(const QDir& dir) - { - m_dir = dir; - m_settings.reset(new Settings(InstanceManager::iniPath(dir))); - } - - QString name() const - { - if (m_portable) { - return QObject::tr("Portable"); - } else { - return m_dir.dirName(); - } - } +// pops up a dialog to ask for an instance name when renaming +// +QString getInstanceName( + QWidget* parent, const QString& title, const QString& moreText, + const QString& label, const QString& oldName={}) +{ + auto& m = InstanceManager::singleton(); - QString gameName() const - { - if (auto n=m_settings->game().name()) { - if (auto e=m_settings->game().edition()) { - if (!e->isEmpty()) { - return *n + " (" + *e + ")"; - } - } + QDialog dlg(parent); + dlg.setWindowTitle(title); - return *n; - } else { - return {}; - } - } + auto* ly = new QVBoxLayout(&dlg); - QString gamePath() const - { - if (auto n=m_settings->game().directory()) { - return QDir::toNativeSeparators(*n); - } else { - return {}; - } - } + auto* bb = new QDialogButtonBox( + QDialogButtonBox::Cancel | QDialogButtonBox::Ok); - QString location() const - { - return QDir::toNativeSeparators(m_dir.path()); - } + auto* text = new QLineEdit(oldName); + text->selectAll(); - QString baseDirectory() const - { - return QDir::toNativeSeparators(m_settings->paths().base()); - } + auto* error = new QLabel; - QString iniFile() const - { - return InstanceManager::iniPath(m_dir); + if (!moreText.isEmpty()) { + auto* lb = new QLabel(moreText); + lb->setWordWrap(true); + ly->addWidget(lb); + ly->addSpacing(10); } - QIcon icon(const PluginContainer& plugins) const - { - const auto* game = InstanceManager::singleton().gamePluginForDirectory( - m_dir, plugins); - - if (game) - return game->gameIcon(); + auto* lb = new QLabel(label); + lb->setWordWrap(true); + ly->addWidget(lb); - QPixmap empty(32, 32); - empty.fill(QColor(0, 0, 0, 0)); - return QIcon(empty); - } + ly->addWidget(text); + ly->addWidget(error); + ly->addStretch(); + ly->addWidget(bb); - bool isPortable() const - { - return m_portable; - } + auto check = [&] { + bool okay = false; - bool isActive() const - { - auto& m = InstanceManager::singleton(); + if (text->text().isEmpty()) { + error->setText(""); + } else if (!m.validInstanceName(text->text())) { + error->setText(QObject::tr("The instance name must be a valid folder name.")); + } else { + const auto name = m.sanitizeInstanceName(text->text()); - if (auto i=m.currentInstance()) - { - if (m_portable) { - return i->isPortable(); + if ((name != oldName) && m.instanceExists(text->text())) { + error->setText(QObject::tr("An instance with this name already exists.")); } else { - return (i->name() == name()); - } - } - - return false; - } - - // returns a list of files and folders that must be deleted when deleting - // this instance - // - std::vector<Object> objectsForDeletion() const - { - // native separators and ending slash - auto prettyDir = [](auto s) { - if (!s.endsWith("/") || !s.endsWith("\\")) { - s += "/"; - } - - return QDir::toNativeSeparators(s); - }; - - // native separators - auto prettyFile = [](auto s) { - return QDir::toNativeSeparators(s); - }; - - - // lowercase, native separators and ending slash - auto canonicalDir = [](auto s) { - s = s.toLower(); - if (!s.endsWith("/") || !s.endsWith("\\")) { - s += "/"; - } - - return QDir::toNativeSeparators(s); - }; - - // lower and native separators - auto canonicalFile = [](auto s) { - return QDir::toNativeSeparators(s.toLower()); - }; - - - - // whether the given directory is contained in the root - auto dirInRoot = [&](auto root, auto dir) { - return canonicalDir(dir).startsWith(canonicalDir(root)); - }; - - // whether the given file is contained in the root - auto fileInRoot = [&](auto root, auto file) { - return canonicalFile(file).startsWith(canonicalDir(root)); - }; - - - - - const auto loc = location(); - const auto base = m_settings->paths().base(); - - - // directories that might contain the individual files and directories - // set in the path settings - std::vector<Object> roots; - - // a portable instance has its location in the installation directory, - // don't delete that - if (!isPortable()) { - if (QDir(loc).exists()) { - roots.push_back({loc, true}); - } - } - - // the base directory is the location directory by default, don't add it - // if it's the same - if (canonicalDir(base) != canonicalDir(loc)) { - if (QDir(base).exists()) { - roots.push_back({base, false}); - } - } - - - // all the directories that are part of an instance; none of them are - // mandatory for deletion - const std::vector<Object> dirs = { - m_settings->paths().downloads(), - m_settings->paths().mods(), - m_settings->paths().cache(), - m_settings->paths().profiles(), - m_settings->paths().overwrite(), - m_dir.filePath(QString::fromStdWString(AppConfig::dumpsDir())), - m_dir.filePath(QString::fromStdWString(AppConfig::logPath())), - }; - - // all the files that are part of an instance - const std::vector<Object> files = { - {iniFile(), true}, // the ini file must be deleted - }; - - - // this will contain the root directories, plus all the individual - // directories that are not inside these roots - std::vector<Object> cleanDirs; - - for (const auto& f : dirs) { - bool inRoots = false; - - for (const auto& root : roots) { - if (dirInRoot(root.path, f.path)) { - inRoots = true; - break; - } - } - - if (!inRoots) { - // not in roots, this is a path that was changed by the user; make - // sure it exists - if (QDir(f.path).exists()) { - cleanDirs.push_back({prettyDir(f.path), f.mandatoryDelete}); - } - } - } - - // prepending the roots - for (auto itor=roots.rbegin(); itor!=roots.rend(); ++itor) { - cleanDirs.insert( - cleanDirs.begin(), - {prettyDir(itor->path), itor->mandatoryDelete}); - } - - - - // this will contain the individual files that are not inside the roots; - // not that this only contains the INI file for now, so most of this is - // useless - std::vector<Object> cleanFiles; - - for (const auto& f : files) { - bool inRoots = false; - - for (const auto& root : roots) { - if (fileInRoot(root.path, f.path)) { - inRoots = true; - break; - } - } - - if (!inRoots) { - // not in roots, this is a path that was changed by the user; make - // sure it exists - if (QFileInfo(f.path).exists()) { - cleanFiles.push_back({prettyFile(f.path), f.mandatoryDelete}); - } + okay = true; } } + error->setVisible(!okay); + bb->button(QDialogButtonBox::Ok)->setEnabled(okay); + }; - // contains all the directories and files to be deleted - std::vector<Object> all; - all.insert(all.end(), cleanDirs.begin(), cleanDirs.end()); - all.insert(all.end(), cleanFiles.begin(), cleanFiles.end()); + QObject::connect(text, &QLineEdit::textChanged, [&] { check(); }); + QObject::connect(bb, &QDialogButtonBox::accepted, [&]{ dlg.accept(); }); + QObject::connect(bb, &QDialogButtonBox::rejected, [&]{ dlg.reject(); }); - // mandatory on top - std::stable_sort(all.begin(), all.end()); + check(); - return all; + dlg.resize({400, 120}); + if (dlg.exec() != QDialog::Accepted) { + return {}; } -private: - const bool m_portable; - QDir m_dir; - std::unique_ptr<Settings> m_settings; -}; + return m.sanitizeInstanceName(text->text()); +} InstanceManagerDialog::~InstanceManagerDialog() = default; @@ -310,7 +111,7 @@ InstanceManagerDialog::InstanceManagerDialog( { ui->setupUi(this); - ui->splitter->setSizes({200, 1}); + ui->splitter->setSizes({250, 1}); ui->splitter->setStretchFactor(0, 0); ui->splitter->setStretchFactor(1, 1); @@ -345,14 +146,40 @@ InstanceManagerDialog::InstanceManagerDialog( connect(ui->close, &QPushButton::clicked, [&]{ close(); }); } +void InstanceManagerDialog::showEvent(QShowEvent* e) +{ + // there might not be a global Settings object if this is called on startup + // when there's no current instance + const auto* s = Settings::maybeInstance(); + + if (s) { + s->geometry().restoreGeometry(this); + } + + QDialog::showEvent(e); +} + +void InstanceManagerDialog::done(int r) +{ + // there might not be a global Settings object if this is called on startup + // when there's no current instance + auto* s = Settings::maybeInstance(); + + if (s) { + s->geometry().saveGeometry(this); + } + + QDialog::done(r); +} + void InstanceManagerDialog::updateInstances() { auto& m = InstanceManager::singleton(); m_instances.clear(); - for (auto&& d : m.instancePaths()) { - m_instances.push_back(std::make_unique<InstanceInfo>(d, false)); + for (auto&& d : m.globalInstancePaths()) { + m_instances.push_back(std::make_unique<Instance>(d, false)); } // sort first, prepend portable after so it's always on top @@ -363,7 +190,12 @@ void InstanceManagerDialog::updateInstances() if (m.portableInstanceExists()) { m_instances.insert( m_instances.begin(), - std::make_unique<InstanceInfo>(m.portablePath(), true)); + std::make_unique<Instance>(m.portablePath(), true)); + } + + // read all inis, ignore errors + for (auto&& i : m_instances) { + i->readFromIni(); } } @@ -374,14 +206,14 @@ void InstanceManagerDialog::updateList() m_model->clear(); - const std::size_t NoSel = -1; - std::size_t sel = NoSel; + std::size_t sel = NoSelection; + // creating items for instances for (std::size_t i=0; i<m_instances.size(); ++i) { const auto& ii = *m_instances[i]; auto* item = new QStandardItem(ii.name()); - item->setIcon(ii.icon(m_pc)); + item->setIcon(instanceIcon(m_pc, ii)); m_model->appendRow(item); @@ -396,7 +228,7 @@ void InstanceManagerDialog::updateList() if (m_instances.empty()) { select(-1); } else { - if (sel == NoSel) { + if (sel == NoSelection) { if (prevSelIndex >= m_instances.size()) { sel = m_instances.size() - 1; } else { @@ -462,10 +294,16 @@ void InstanceManagerDialog::openSelectedInstance() return; } - if (m_instances[i]->isPortable()) { + const auto& to = *m_instances[i]; + + if (!confirmSwitch(to)) { + return; + } + + if (to.isPortable()) { InstanceManager::singleton().setCurrentInstance(""); } else { - InstanceManager::singleton().setCurrentInstance(m_instances[i]->name()); + InstanceManager::singleton().setCurrentInstance(to.name()); } if (m_restartOnSelect) { @@ -475,74 +313,36 @@ void InstanceManagerDialog::openSelectedInstance() accept(); } -QString getInstanceName( - QWidget* parent, const QString& title, const QString& moreText, - const QString& label, const QString& oldName={}) +bool InstanceManagerDialog::confirmSwitch(const Instance& to) { - auto& m = InstanceManager::singleton(); - - QDialog dlg(parent); - dlg.setWindowTitle(title); - - auto* ly = new QVBoxLayout(&dlg); - - auto* bb = new QDialogButtonBox( - QDialogButtonBox::Cancel | QDialogButtonBox::Ok); - - auto* text = new QLineEdit(oldName); - text->selectAll(); - - auto* error = new QLabel; + // there might not be a global Settings object if this is called on startup + // when there's no current instance + const auto* s = Settings::maybeInstance(); - if (!moreText.isEmpty()) { - auto* lb = new QLabel(moreText); - lb->setWordWrap(true); - ly->addWidget(lb); - ly->addSpacing(10); + // if there is are no settings, no instances are loaded and the confirmation + // wouldn't make sense + if (!s) { + return true; } - auto* lb = new QLabel(label); - lb->setWordWrap(true); - ly->addWidget(lb); - - ly->addWidget(text); - ly->addWidget(error); - ly->addStretch(); - ly->addWidget(bb); - - auto check = [&] { - bool okay = false; - - if (text->text().isEmpty()) { - error->setText(""); - } else if (!m.validInstanceName(text->text())) { - error->setText(QObject::tr("The instance name must be a valid folder name.")); - } else { - const auto name = m.sanitizeInstanceName(text->text()); - - if ((name != oldName) && m.instanceExists(text->text())) { - error->setText(QObject::tr("An instance with this name already exists.")); - } else { - okay = true; - } - } - - error->setVisible(!okay); - bb->button(QDialogButtonBox::Ok)->setEnabled(okay); - }; - - QObject::connect(text, &QLineEdit::textChanged, [&] { check(); }); - QObject::connect(bb, &QDialogButtonBox::accepted, [&]{ dlg.accept(); }); - QObject::connect(bb, &QDialogButtonBox::rejected, [&]{ dlg.reject(); }); + if (!s->interface().showChangeGameConfirmation()) { + // user disabled confirmation + return true; + } - check(); + MOBase::TaskDialog dlg(this); - dlg.resize({400, 120}); - if (dlg.exec() != QDialog::Accepted) { - return {}; - } + const auto r = dlg + .title(tr("Switching instances")) + .main(tr("Mod Organizer must restart to manage the instance '%1'.") + .arg(to.name())) + .content(tr("This confirmation can be disabled in the settings.")) + .icon(QMessageBox::Question) + .button({tr("Restart Mod Organizer"), QMessageBox::Ok}) + .button({tr("Cancel"), QMessageBox::Cancel}) + .exec(); - return m.sanitizeInstanceName(text->text()); + return (r == QMessageBox::Ok); } void InstanceManagerDialog::rename() @@ -561,6 +361,8 @@ void InstanceManagerDialog::rename() return; } + + // getting new name const auto newName = getInstanceName( this, tr("Rename instance"), "", tr("Instance name"), i->name()); @@ -568,11 +370,16 @@ void InstanceManagerDialog::rename() return; } - const QString src = i->location(); + + // renaming + const QString src = i->directory(); const QString dest = QDir::toNativeSeparators( - QFileInfo(i->location()).dir().path() + "/" + newName); + QFileInfo(src).dir().path() + "/" + newName); + + log::info("renaming {} to {}", src, dest); const auto r = shell::Rename(src, dest, false); + if (!r) { QMessageBox::critical( this, tr("Error"), @@ -582,15 +389,21 @@ void InstanceManagerDialog::rename() return; } + + // updating ui + auto newInstance = std::make_unique<Instance>(dest, false); + i = newInstance.get(); + m_model->item(selIndex)->setText(newName); - i->setDir(dest); + m_instances[selIndex] = std::move(newInstance); + fillData(*i); } void InstanceManagerDialog::exploreLocation() { if (const auto* i=singleSelection()) { - shell::Explore(i->location()); + shell::Explore(i->directory()); } } @@ -604,14 +417,14 @@ void InstanceManagerDialog::exploreBaseDirectory() void InstanceManagerDialog::exploreGame() { if (const auto* i=singleSelection()) { - shell::Explore(i->gamePath()); + shell::Explore(i->gameDirectory()); } } void InstanceManagerDialog::openINI() { if (const auto* i=singleSelection()) { - shell::Open(i->iniFile()); + shell::Open(i->iniPath()); } } @@ -629,6 +442,8 @@ void InstanceManagerDialog::deleteInstance() return; } + // creating dialog + const auto Recycle = QMessageBox::Save; const auto Delete = QMessageBox::Yes; const auto Cancel = QMessageBox::Cancel; @@ -652,6 +467,8 @@ void InstanceManagerDialog::deleteInstance() list->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); list->setMaximumHeight(160); + + // filling the list for (const auto& f : files) { auto* item = new QListWidgetItem(f.path); @@ -676,6 +493,7 @@ void InstanceManagerDialog::deleteInstance() } + // gathering all the selected items QStringList selected; for (int i=0; i<list->count(); ++i) { @@ -691,10 +509,14 @@ void InstanceManagerDialog::deleteInstance() return; } + + // deleting if (!doDelete(selected, (r == Recycle))) { return; } + + // updating ui updateInstances(); updateList(); } @@ -707,6 +529,15 @@ void InstanceManagerDialog::setRestartOnSelect(bool b) bool InstanceManagerDialog::doDelete(const QStringList& files, bool recycle) { + // logging + for (auto&& f : files) { + if (recycle) { + log::info("will recycle {}", f); + } else { + log::info("will delete {}", f); + } + } + if (MOBase::shellDelete(files, recycle, this)) { return true; } @@ -756,7 +587,7 @@ void InstanceManagerDialog::createNew() updateInstances(); updateList(); - select(dlg.instanceName()); + select(dlg.creationInfo().instanceName); } std::size_t InstanceManagerDialog::singleSelectionIndex() const @@ -771,17 +602,7 @@ std::size_t InstanceManagerDialog::singleSelectionIndex() const 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 Instance* InstanceManagerDialog::singleSelection() const { const auto i = singleSelectionIndex(); if (i == NoSelection) { @@ -791,13 +612,13 @@ const InstanceInfo* InstanceManagerDialog::singleSelection() const return m_instances[i].get(); } -void InstanceManagerDialog::fillData(const InstanceInfo& ii) +void InstanceManagerDialog::fillData(const Instance& ii) { ui->name->setText(ii.name()); - ui->location->setText(ii.location()); + ui->location->setText(ii.directory()); ui->baseDirectory->setText(ii.baseDirectory()); ui->gameName->setText(ii.gameName()); - ui->gameDir->setText(ii.gamePath()); + ui->gameDir->setText(ii.gameDirectory()); setButtonsEnabled(true); const auto& m = InstanceManager::singleton(); |
