From af95b3b8637d28517f69a70f13b901cc7f43d121 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 20 Jul 2019 09:31:38 -0400 Subject: renamed tab classes, clashing with mod info dialog split nexus tab --- src/settingsdialognexus.cpp | 374 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 374 insertions(+) create mode 100644 src/settingsdialognexus.cpp (limited to 'src/settingsdialognexus.cpp') diff --git a/src/settingsdialognexus.cpp b/src/settingsdialognexus.cpp new file mode 100644 index 00000000..7d4414fd --- /dev/null +++ b/src/settingsdialognexus.cpp @@ -0,0 +1,374 @@ +#include "settingsdialognexus.h" +#include "ui_settingsdialog.h" +#include "ui_nexusmanualkey.h" +#include "nexusinterface.h" +#include + +namespace shell = MOBase::shell; + +template +class ServerItem : public QListWidgetItem { +public: + ServerItem(const QString &text, int sortRole = Qt::DisplayRole, QListWidget *parent = 0, int type = Type) + : QListWidgetItem(text, parent, type), m_SortRole(sortRole) {} + + virtual bool operator< ( const QListWidgetItem & other ) const { + return this->data(m_SortRole).value() < other.data(m_SortRole).value(); + } +private: + int m_SortRole; +}; + + +class NexusManualKeyDialog : public QDialog +{ +public: + NexusManualKeyDialog(QWidget* parent) + : QDialog(parent), ui(new Ui::NexusManualKeyDialog) + { + ui->setupUi(this); + ui->key->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); + + connect(ui->openBrowser, &QPushButton::clicked, [&]{ openBrowser(); }); + connect(ui->paste, &QPushButton::clicked, [&]{ paste(); }); + connect(ui->clear, &QPushButton::clicked, [&]{ clear(); }); + } + + void accept() override + { + m_key = ui->key->toPlainText(); + QDialog::accept(); + } + + const QString& key() const + { + return m_key; + } + + void openBrowser() + { + shell::OpenLink(QUrl("https://www.nexusmods.com/users/myaccount?tab=api")); + } + + void paste() + { + const auto text = QApplication::clipboard()->text(); + if (!text.isEmpty()) { + ui->key->setPlainText(text); + } + } + + void clear() + { + ui->key->clear(); + } + +private: + std::unique_ptr ui; + QString m_key; +}; + + +NexusSettingsTab::NexusSettingsTab(Settings *parent, SettingsDialog &dialog) + : SettingsTab(parent, dialog) +{ + ui->offlineBox->setChecked(parent->offlineMode()); + ui->proxyBox->setChecked(parent->useProxy()); + ui->endorsementBox->setChecked(parent->endorsementIntegration()); + ui->hideAPICounterBox->setChecked(parent->hideAPICounter()); + + // display server preferences + m_Settings.beginGroup("Servers"); + for (const QString &key : m_Settings.childKeys()) { + QVariantMap val = m_Settings.value(key).toMap(); + QString descriptor = key; + if (!descriptor.compare("CDN", Qt::CaseInsensitive)) { + descriptor += QStringLiteral(" (automatic)"); + } + if (val.contains("downloadSpeed") && val.contains("downloadCount") && (val["downloadCount"].toInt() > 0)) { + int bps = static_cast(val["downloadSpeed"].toDouble() / val["downloadCount"].toInt()); + descriptor += QString(" (%1 kbps)").arg(bps / 1024); + } + + QListWidgetItem *newItem = new ServerItem(descriptor, Qt::UserRole + 1); + + newItem->setData(Qt::UserRole, key); + newItem->setData(Qt::UserRole + 1, val["preferred"].toInt()); + if (val["preferred"].toInt() > 0) { + ui->preferredServersList->addItem(newItem); + } else { + ui->knownServersList->addItem(newItem); + } + ui->preferredServersList->sortItems(Qt::DescendingOrder); + } + m_Settings.endGroup(); + + QObject::connect(ui->nexusConnect, &QPushButton::clicked, [&]{ on_nexusConnect_clicked(); }); + QObject::connect(ui->nexusManualKey, &QPushButton::clicked, [&]{ on_nexusManualKey_clicked(); }); + QObject::connect(ui->nexusDisconnect, &QPushButton::clicked, [&]{ on_nexusDisconnect_clicked(); }); + QObject::connect(ui->clearCacheButton, &QPushButton::clicked, [&]{ on_clearCacheButton_clicked(); }); + QObject::connect(ui->associateButton, &QPushButton::clicked, [&]{ on_associateButton_clicked(); }); + + updateNexusState(); +} + +void NexusSettingsTab::update() +{ + /* + if (m_loginCheckBox->isChecked()) { + m_Settings.setValue("Settings/nexus_login", true); + m_Settings.setValue("Settings/nexus_username", m_usernameEdit->text()); + m_Settings.setValue("Settings/nexus_password", obfuscate(m_passwordEdit->text())); + } else { + m_Settings.setValue("Settings/nexus_login", false); + m_Settings.remove("Settings/nexus_username"); + m_Settings.remove("Settings/nexus_password"); + } + */ + m_Settings.setValue("Settings/offline_mode", ui->offlineBox->isChecked()); + m_Settings.setValue("Settings/use_proxy", ui->proxyBox->isChecked()); + m_Settings.setValue("Settings/endorsement_integration", ui->endorsementBox->isChecked()); + m_Settings.setValue("Settings/hide_api_counter", ui->hideAPICounterBox->isChecked()); + + // store server preference + m_Settings.beginGroup("Servers"); + for (int i = 0; i < ui->knownServersList->count(); ++i) { + QString key = ui->knownServersList->item(i)->data(Qt::UserRole).toString(); + QVariantMap val = m_Settings.value(key).toMap(); + val["preferred"] = 0; + m_Settings.setValue(key, val); + } + int count = ui->preferredServersList->count(); + for (int i = 0; i < count; ++i) { + QString key = ui->preferredServersList->item(i)->data(Qt::UserRole).toString(); + QVariantMap val = m_Settings.value(key).toMap(); + val["preferred"] = count - i; + m_Settings.setValue(key, val); + } + m_Settings.endGroup(); +} + +void NexusSettingsTab::on_nexusConnect_clicked() +{ + if (m_nexusLogin && m_nexusLogin->isActive()) { + m_nexusLogin->cancel(); + return; + } + + if (!m_nexusLogin) { + m_nexusLogin.reset(new NexusSSOLogin); + + m_nexusLogin->keyChanged = [&](auto&& s){ + onSSOKeyChanged(s); + }; + + m_nexusLogin->stateChanged = [&](auto&& s, auto&& e){ + onSSOStateChanged(s, e); + }; + } + + ui->nexusLog->clear(); + m_nexusLogin->start(); + updateNexusState(); +} + +void NexusSettingsTab::on_nexusManualKey_clicked() +{ + if (m_nexusValidator && m_nexusValidator->isActive()) { + m_nexusValidator->cancel(); + return; + } + + NexusManualKeyDialog dialog(parentWidget()); + if (dialog.exec() != QDialog::Accepted) { + return; + } + + const auto key = dialog.key(); + if (key.isEmpty()) { + clearKey(); + return; + } + + ui->nexusLog->clear(); + validateKey(key); +} + +void NexusSettingsTab::on_nexusDisconnect_clicked() +{ + clearKey(); + ui->nexusLog->clear(); + addNexusLog(QObject::tr("Disconnected.")); +} + +void NexusSettingsTab::on_clearCacheButton_clicked() +{ + QDir(Settings::instance().getCacheDirectory()).removeRecursively(); + NexusInterface::instance(m_dialog.m_PluginContainer)->clearCache(); +} + +void NexusSettingsTab::on_associateButton_clicked() +{ + Settings::instance().registerAsNXMHandler(true); +} + +void NexusSettingsTab::validateKey(const QString& key) +{ + if (!m_nexusValidator) { + m_nexusValidator.reset(new NexusKeyValidator( + *NexusInterface::instance(m_dialog.m_PluginContainer)->getAccessManager())); + + m_nexusValidator->stateChanged = [&](auto&& s, auto&& e){ + onValidatorStateChanged(s, e); + }; + + m_nexusValidator->finished = [&](auto&& user) { + onValidatorFinished(user); + }; + } + + addNexusLog(QObject::tr("Checking API key...")); + m_nexusValidator->start(key); +} + +void NexusSettingsTab::onSSOKeyChanged(const QString& key) +{ + if (key.isEmpty()) { + clearKey(); + } else { + addNexusLog(QObject::tr("Received API key.")); + validateKey(key); + } +} + +void NexusSettingsTab::onSSOStateChanged(NexusSSOLogin::States s, const QString& e) +{ + if (s != NexusSSOLogin::Finished) { + // finished state is handled in onSSOKeyChanged() + const auto log = NexusSSOLogin::stateToString(s, e); + + for (auto&& line : log.split("\n")) { + addNexusLog(line); + } + } + + updateNexusState(); +} + +void NexusSettingsTab::onValidatorStateChanged( + NexusKeyValidator::States s, const QString& e) +{ + if (s != NexusKeyValidator::Finished) { + // finished state is handled in onValidatorFinished() + const auto log = NexusKeyValidator::stateToString(s, e); + + for (auto&& line : log.split("\n")) { + addNexusLog(line); + } + } + + updateNexusState(); +} + +void NexusSettingsTab::onValidatorFinished(const APIUserAccount& user) +{ + NexusInterface::instance(m_dialog.m_PluginContainer)->setUserAccount(user); + + if (!user.apiKey().isEmpty()) { + if (setKey(user.apiKey())) { + addNexusLog(QObject::tr("Linked with Nexus successfully.")); + } + } +} + +void NexusSettingsTab::addNexusLog(const QString& s) +{ + ui->nexusLog->addItem(s); + ui->nexusLog->scrollToBottom(); +} + +bool NexusSettingsTab::setKey(const QString& key) +{ + m_dialog.m_keyChanged = true; + const bool ret = m_parent->setNexusApiKey(key); + updateNexusState(); + return ret; +} + +bool NexusSettingsTab::clearKey() +{ + m_dialog.m_keyChanged = true; + const auto ret = m_parent->clearNexusApiKey(); + + NexusInterface::instance(m_dialog.m_PluginContainer)->getAccessManager()->clearApiKey(); + updateNexusState(); + + return ret; +} + +void NexusSettingsTab::updateNexusState() +{ + updateNexusButtons(); + updateNexusData(); +} + +void NexusSettingsTab::updateNexusButtons() +{ + if (m_nexusLogin && m_nexusLogin->isActive()) { + // api key is in the process of being retrieved + ui->nexusConnect->setText(QObject::tr("Cancel")); + ui->nexusConnect->setEnabled(true); + ui->nexusDisconnect->setEnabled(false); + ui->nexusManualKey->setText(QObject::tr("Enter API Key Manually")); + ui->nexusManualKey->setEnabled(false); + } + else if (m_nexusValidator && m_nexusValidator->isActive()) { + // api key is in the process of being tested + ui->nexusConnect->setText(QObject::tr("Connect to Nexus")); + ui->nexusConnect->setEnabled(false); + ui->nexusDisconnect->setEnabled(false); + ui->nexusManualKey->setText(QObject::tr("Cancel")); + ui->nexusManualKey->setEnabled(true); + } + else if (m_parent->hasNexusApiKey()) { + // api key is present + ui->nexusConnect->setText(QObject::tr("Connect to Nexus")); + ui->nexusConnect->setEnabled(false); + ui->nexusDisconnect->setEnabled(true); + ui->nexusManualKey->setText(QObject::tr("Enter API Key Manually")); + ui->nexusManualKey->setEnabled(false); + } else { + // api key not present + ui->nexusConnect->setText(QObject::tr("Connect to Nexus")); + ui->nexusConnect->setEnabled(true); + ui->nexusDisconnect->setEnabled(false); + ui->nexusManualKey->setText(QObject::tr("Enter API Key Manually")); + ui->nexusManualKey->setEnabled(true); + } +} + +void NexusSettingsTab::updateNexusData() +{ + const auto user = NexusInterface::instance(m_dialog.m_PluginContainer) + ->getAPIUserAccount(); + + if (user.isValid()) { + ui->nexusUserID->setText(user.id()); + ui->nexusName->setText(user.name()); + ui->nexusAccount->setText(localizedUserAccountType(user.type())); + + ui->nexusDailyRequests->setText(QString("%1/%2") + .arg(user.limits().remainingDailyRequests) + .arg(user.limits().maxDailyRequests)); + + ui->nexusHourlyRequests->setText(QString("%1/%2") + .arg(user.limits().remainingHourlyRequests) + .arg(user.limits().maxHourlyRequests)); + } else { + ui->nexusUserID->setText(QObject::tr("N/A")); + ui->nexusName->setText(QObject::tr("N/A")); + ui->nexusAccount->setText(QObject::tr("N/A")); + ui->nexusDailyRequests->setText(QObject::tr("N/A")); + ui->nexusHourlyRequests->setText(QObject::tr("N/A")); + } +} -- cgit v1.3.1 From a05862aaa13b028e2f250347daa7a2e0f64c2380 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 20 Jul 2019 11:20:27 -0400 Subject: cleaned up includes removed commented out code reordered member functions in Settings --- src/settings.cpp | 44 ------------------------------------------- src/settings.h | 36 +++++++---------------------------- src/settingsdialog.cpp | 26 ------------------------- src/settingsdialog.h | 4 +--- src/settingsdialoggeneral.cpp | 24 ----------------------- src/settingsdialognexus.cpp | 11 ----------- src/settingsdialognexus.h | 1 + 7 files changed, 9 insertions(+), 137 deletions(-) (limited to 'src/settingsdialognexus.cpp') diff --git a/src/settings.cpp b/src/settings.cpp index dc07e107..e7a853a2 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -18,58 +18,16 @@ along with Mod Organizer. If not, see . */ #include "settings.h" - -#include "pluginsetting.h" #include "serverinfo.h" -#include "versioninfo.h" #include "appconfig.h" -#include "organizercore.h" #include -#include #include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include // for Qt::UserRole, etc - -#include // For ShellExecuteW, HINSTANCE, etc -#include // For storage - -#include // for sort -#include -#include // for runtime_error -#include -#include // for pair, make_pair - - using namespace MOBase; - Settings *Settings::s_Instance = nullptr; - Settings::Settings(const QSettings &settingsSource) : m_Settings(settingsSource.fileName(), settingsSource.format()) { @@ -80,13 +38,11 @@ Settings::Settings(const QSettings &settingsSource) } } - Settings::~Settings() { s_Instance = nullptr; } - Settings &Settings::instance() { if (s_Instance == nullptr) { diff --git a/src/settings.h b/src/settings.h index 899baaa3..b20e78d0 100644 --- a/src/settings.h +++ b/src/settings.h @@ -23,17 +23,6 @@ along with Mod Organizer. If not, see . #include "loadmechanism.h" #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - namespace MOBase { class IPlugin; class IPluginGame; @@ -376,6 +365,8 @@ public: */ bool colorSeparatorScrollbar() const; + static QColor getIdealTextColor(const QColor& rBackgroundColor); + // temp QSettings& settingsRef() { return m_Settings; } MOBase::IPluginGame const *gamePlugin() { return m_GamePlugin; } @@ -386,37 +377,24 @@ public: const LoadMechanism& loadMechanism() const { return m_LoadMechanism; } public slots: - void managedGameChanged(MOBase::IPluginGame const *gamePlugin); -public: - static QColor getIdealTextColor(const QColor& rBackgroundColor); -private: - - static bool obfuscate(const QString key, const QString data); - static QString deObfuscate(const QString key); - - void readPluginBlacklist(); - QString getConfigurablePath(const QString &key, const QString &def, bool resolve) const; - -private slots: signals: - void languageChanged(const QString &newLanguage); void styleChanged(const QString &newStyle); private: - static Settings *s_Instance; - MOBase::IPluginGame const *m_GamePlugin; - QSettings m_Settings; - LoadMechanism m_LoadMechanism; - std::vector m_Plugins; + static bool obfuscate(const QString key, const QString data); + static QString deObfuscate(const QString key); + + void readPluginBlacklist(); + QString getConfigurablePath(const QString &key, const QString &def, bool resolve) const; }; #endif // SETTINGS_H diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index 8c5b2678..e008086a 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -18,17 +18,7 @@ along with Mod Organizer. If not, see . */ #include "settingsdialog.h" - #include "ui_settingsdialog.h" -#include "categoriesdialog.h" -#include "helper.h" -#include "noeditdelegate.h" -#include "iplugingame.h" -#include "settings.h" -#include "instancemanager.h" -#include "nexusinterface.h" -#include "plugincontainer.h" - #include "settingsdialogdiagnostics.h" #include "settingsdialoggeneral.h" #include "settingsdialognexus.h" @@ -37,22 +27,6 @@ along with Mod Organizer. If not, see . #include "settingsdialogsteam.h" #include "settingsdialogworkarounds.h" -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#define WIN32_LEAN_AND_MEAN -#include - - using namespace MOBase; SettingsDialog::SettingsDialog(PluginContainer *pluginContainer, Settings* settings, QWidget *parent) diff --git a/src/settingsdialog.h b/src/settingsdialog.h index f2367315..03bba7cf 100644 --- a/src/settingsdialog.h +++ b/src/settingsdialog.h @@ -21,15 +21,13 @@ along with Mod Organizer. If not, see . #define SETTINGSDIALOG_H #include "tutorabledialog.h" -#include "nxmaccessmanager.h" -#include -#include class PluginContainer; class Settings; class SettingsDialog; namespace Ui { class SettingsDialog; } + class SettingsTab { public: diff --git a/src/settingsdialoggeneral.cpp b/src/settingsdialoggeneral.cpp index cd98dfdc..324dc4f4 100644 --- a/src/settingsdialoggeneral.cpp +++ b/src/settingsdialoggeneral.cpp @@ -33,28 +33,6 @@ GeneralSettingsTab::GeneralSettingsTab(Settings *m_parent, SettingsDialog &m_dia ui->styleBox->setCurrentIndex(currentID); } } - /* verision using palette only works with fusion theme for some stupid reason... - m_overwritingBtn->setAutoFillBackground(true); - m_overwrittenBtn->setAutoFillBackground(true); - m_containsBtn->setAutoFillBackground(true); - m_containedBtn->setAutoFillBackground(true); - m_overwritingBtn->setPalette(QPalette(m_parent->modlistOverwritingLooseColor())); - m_overwrittenBtn->setPalette(QPalette(m_parent->modlistOverwrittenLooseColor())); - m_containsBtn->setPalette(QPalette(m_parent->modlistContainsPluginColor())); - m_containedBtn->setPalette(QPalette(m_parent->pluginListContainedColor())); - QPalette palette1 = m_overwritingBtn->palette(); - QPalette palette2 = m_overwrittenBtn->palette(); - QPalette palette3 = m_containsBtn->palette(); - QPalette palette4 = m_containedBtn->palette(); - palette1.setColor(QPalette::Background, m_parent->modlistOverwritingLooseColor()); - palette2.setColor(QPalette::Background, m_parent->modlistOverwrittenLooseColor()); - palette3.setColor(QPalette::Background, m_parent->modlistContainsPluginColor()); - palette4.setColor(QPalette::Background, m_parent->pluginListContainedColor()); - m_overwritingBtn->setPalette(palette1); - m_overwrittenBtn->setPalette(palette2); - m_containsBtn->setPalette(palette3); - m_containedBtn->setPalette(palette4); - */ //version with stylesheet setButtonColor(ui->overwritingBtn, m_parent->modlistOverwritingLooseColor()); @@ -137,12 +115,10 @@ void GeneralSettingsTab::addLanguages() } } languages.push_back(std::make_pair(QString("%1").arg(languageString), exp.cap(1))); - //languageBox->addItem(QString("%1").arg(languageString), exp.cap(1)); } } if (!ui->languageBox->findText("English")) { languages.push_back(std::make_pair(QString("English"), QString("en_US"))); - //languageBox->addItem("English", "en_US"); } std::sort(languages.begin(), languages.end()); for (const auto &lang : languages) { diff --git a/src/settingsdialognexus.cpp b/src/settingsdialognexus.cpp index 7d4414fd..575f54d0 100644 --- a/src/settingsdialognexus.cpp +++ b/src/settingsdialognexus.cpp @@ -114,17 +114,6 @@ NexusSettingsTab::NexusSettingsTab(Settings *parent, SettingsDialog &dialog) void NexusSettingsTab::update() { - /* - if (m_loginCheckBox->isChecked()) { - m_Settings.setValue("Settings/nexus_login", true); - m_Settings.setValue("Settings/nexus_username", m_usernameEdit->text()); - m_Settings.setValue("Settings/nexus_password", obfuscate(m_passwordEdit->text())); - } else { - m_Settings.setValue("Settings/nexus_login", false); - m_Settings.remove("Settings/nexus_username"); - m_Settings.remove("Settings/nexus_password"); - } - */ m_Settings.setValue("Settings/offline_mode", ui->offlineBox->isChecked()); m_Settings.setValue("Settings/use_proxy", ui->proxyBox->isChecked()); m_Settings.setValue("Settings/endorsement_integration", ui->endorsementBox->isChecked()); diff --git a/src/settingsdialognexus.h b/src/settingsdialognexus.h index 5c01f61f..cca2e1b5 100644 --- a/src/settingsdialognexus.h +++ b/src/settingsdialognexus.h @@ -3,6 +3,7 @@ #include "settings.h" #include "settingsdialog.h" +#include "nxmaccessmanager.h" class NexusSettingsTab : public SettingsTab { -- cgit v1.3.1 From 2ffad7edf2946e66585a67c4ab58c0522cd8e412 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 17 Aug 2019 13:20:53 -0400 Subject: made member variables in SettingsTab private, added accessors SettingsDialog now uses GeometrySaver --- src/loglist.h | 2 + src/mainwindow.cpp | 2 +- src/organizercore.cpp | 9 +--- src/settingsdialog.cpp | 40 ++++++++-------- src/settingsdialog.h | 18 ++++--- src/settingsdialogdiagnostics.cpp | 16 +++---- src/settingsdialogdiagnostics.h | 2 +- src/settingsdialoggeneral.cpp | 98 +++++++++++++++++++-------------------- src/settingsdialoggeneral.h | 2 +- src/settingsdialognexus.cpp | 66 +++++++++++++------------- src/settingsdialognexus.h | 2 +- src/settingsdialogpaths.cpp | 48 +++++++++---------- src/settingsdialogpaths.h | 3 +- src/settingsdialogplugins.cpp | 26 +++++------ src/settingsdialogplugins.h | 2 +- src/settingsdialogsteam.cpp | 8 ++-- src/settingsdialogsteam.h | 5 +- src/settingsdialogworkarounds.cpp | 46 +++++++++--------- src/settingsdialogworkarounds.h | 3 +- 19 files changed, 196 insertions(+), 202 deletions(-) (limited to 'src/settingsdialognexus.cpp') diff --git a/src/loglist.h b/src/loglist.h index 0b25dfd1..36671be4 100644 --- a/src/loglist.h +++ b/src/loglist.h @@ -61,6 +61,8 @@ private: class LogList : public QTreeView { + Q_OBJECT; + public: LogList(QWidget* parent=nullptr); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 2ce6f9d9..f1a2047f 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -5018,7 +5018,7 @@ void MainWindow::on_actionSettings_triggered() DownloadManager *dlManager = m_OrganizerCore.downloadManager(); - SettingsDialog dialog(&m_PluginContainer, &settings, this); + SettingsDialog dialog(&m_PluginContainer, settings, this); dialog.exec(); if (oldManagedGameDirectory != settings.getManagedGameDirectory()) { diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 233a631e..73d0abac 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -745,16 +745,11 @@ void OrganizerCore::setCurrentProfile(const QString &profileName) m_CurrentProfile->deactivateInvalidation(); } + m_Settings.setSelectedProfileName(m_CurrentProfile->name()); + connect(m_CurrentProfile, SIGNAL(modStatusChanged(uint)), this, SLOT(modStatusChanged(uint))); connect(m_CurrentProfile, SIGNAL(modStatusChanged(QList)), this, SLOT(modStatusChanged(QList))); refreshDirectoryStructure(); - - //This line is not actually needed and was only added to allow some - //outside detection of Mo2 profile change. (like BaobobMiller utility) - if (m_CurrentProfile != nullptr) { - settings().directInterface().setValue("selected_profile", - m_CurrentProfile->name().toUtf8().constData()); - } } MOBase::IModRepositoryBridge *OrganizerCore::createNexusBridge() const diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index d74507c9..097dafc8 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -29,7 +29,7 @@ along with Mod Organizer. If not, see . using namespace MOBase; -SettingsDialog::SettingsDialog(PluginContainer *pluginContainer, Settings* settings, QWidget *parent) +SettingsDialog::SettingsDialog(PluginContainer *pluginContainer, Settings& settings, QWidget *parent) : TutorableDialog("SettingsDialog", parent) , ui(new Ui::SettingsDialog) , m_settings(settings) @@ -45,18 +45,13 @@ SettingsDialog::SettingsDialog(PluginContainer *pluginContainer, Settings* setti m_tabs.push_back(std::unique_ptr(new SteamSettingsTab(settings, *this))); m_tabs.push_back(std::unique_ptr(new PluginsSettingsTab(settings, *this))); m_tabs.push_back(std::unique_ptr(new WorkaroundsSettingsTab(settings, *this))); - - auto& qsettings = settings->directInterface(); - - QString key = QString("geometry/%1").arg(objectName()); - if (qsettings.contains(key)) { - restoreGeometry(qsettings.value(key).toByteArray()); - } } int SettingsDialog::exec() { - auto& qsettings = m_settings->directInterface(); + GeometrySaver gs(m_settings, this); + + auto& qsettings = m_settings.directInterface(); auto ret = TutorableDialog::exec(); if (ret == QDialog::Accepted) { @@ -92,9 +87,6 @@ int SettingsDialog::exec() qsettings.endGroup(); } - QString key = QString("geometry/%1").arg(objectName()); - qsettings.setValue(key, saveGeometry()); - // These changes happen regardless of accepted or rejected bool restartNeeded = false; if (getApiKeyChanged()) { @@ -158,18 +150,24 @@ bool SettingsDialog::getApiKeyChanged() } -SettingsTab::SettingsTab(Settings *m_parent, SettingsDialog &m_dialog) - : m_parent(m_parent) - , m_Settings(m_parent->directInterface()) - , m_dialog(m_dialog) - , ui(m_dialog.ui) +SettingsTab::SettingsTab(Settings& s, SettingsDialog& d) + : ui(d.ui), m_settings(s), m_qsettings(s.directInterface()), m_dialog(d) { } -SettingsTab::~SettingsTab() -{} +SettingsTab::~SettingsTab() = default; + +Settings& SettingsTab::settings() +{ + return m_settings; +} + +QSettings& SettingsTab::qsettings() +{ + return m_qsettings; +} -QWidget* SettingsTab::parentWidget() +SettingsDialog& SettingsTab::dialog() { - return &m_dialog; + return m_dialog; } diff --git a/src/settingsdialog.h b/src/settingsdialog.h index efc4a095..0aad8863 100644 --- a/src/settingsdialog.h +++ b/src/settingsdialog.h @@ -31,19 +31,23 @@ namespace Ui { class SettingsDialog; } class SettingsTab { public: - SettingsTab(Settings *m_parent, SettingsDialog &m_dialog); + SettingsTab(Settings& settings, SettingsDialog& m_dialog); virtual ~SettingsTab(); virtual void update() = 0; virtual void closing() {} protected: - Settings *m_parent; - QSettings &m_Settings; - SettingsDialog &m_dialog; Ui::SettingsDialog* ui; - QWidget* parentWidget(); + Settings& settings(); + QSettings& qsettings(); + SettingsDialog& dialog(); + +private: + Settings& m_settings; + QSettings& m_qsettings; + SettingsDialog& m_dialog; }; @@ -58,7 +62,7 @@ class SettingsDialog : public MOBase::TutorableDialog public: explicit SettingsDialog( - PluginContainer *pluginContainer, Settings* settings, QWidget *parent = 0); + PluginContainer *pluginContainer, Settings& settings, QWidget *parent = 0); ~SettingsDialog(); @@ -82,7 +86,7 @@ public: bool getApiKeyChanged(); private: - Settings* m_settings; + Settings& m_settings; std::vector> m_tabs; }; diff --git a/src/settingsdialogdiagnostics.cpp b/src/settingsdialogdiagnostics.cpp index daf81d5c..227d1dfa 100644 --- a/src/settingsdialogdiagnostics.cpp +++ b/src/settingsdialogdiagnostics.cpp @@ -6,12 +6,12 @@ using namespace MOBase; -DiagnosticsSettingsTab::DiagnosticsSettingsTab(Settings *m_parent, SettingsDialog &m_dialog) - : SettingsTab(m_parent, m_dialog) +DiagnosticsSettingsTab::DiagnosticsSettingsTab(Settings& s, SettingsDialog& d) + : SettingsTab(s, d) { setLevelsBox(); - ui->dumpsTypeBox->setCurrentIndex(m_parent->crashDumpsType()); - ui->dumpsMaxEdit->setValue(m_parent->crashDumpsMax()); + ui->dumpsTypeBox->setCurrentIndex(settings().crashDumpsType()); + ui->dumpsMaxEdit->setValue(settings().crashDumpsMax()); QString logsPath = qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::logPath()); ui->diagnosticsExplainedLabel->setText( @@ -33,7 +33,7 @@ void DiagnosticsSettingsTab::setLevelsBox() ui->logLevelBox->addItem(QObject::tr("Error"), log::Error); for (int i=0; ilogLevelBox->count(); ++i) { - if (ui->logLevelBox->itemData(i) == m_parent->logLevel()) { + if (ui->logLevelBox->itemData(i) == settings().logLevel()) { ui->logLevelBox->setCurrentIndex(i); break; } @@ -42,7 +42,7 @@ void DiagnosticsSettingsTab::setLevelsBox() void DiagnosticsSettingsTab::update() { - m_Settings.setValue("Settings/log_level", ui->logLevelBox->currentData().toInt()); - m_Settings.setValue("Settings/crash_dumps_type", ui->dumpsTypeBox->currentIndex()); - m_Settings.setValue("Settings/crash_dumps_max", ui->dumpsMaxEdit->value()); + qsettings().setValue("Settings/log_level", ui->logLevelBox->currentData().toInt()); + qsettings().setValue("Settings/crash_dumps_type", ui->dumpsTypeBox->currentIndex()); + qsettings().setValue("Settings/crash_dumps_max", ui->dumpsMaxEdit->value()); } diff --git a/src/settingsdialogdiagnostics.h b/src/settingsdialogdiagnostics.h index 4c1805e2..f20413f8 100644 --- a/src/settingsdialogdiagnostics.h +++ b/src/settingsdialogdiagnostics.h @@ -7,7 +7,7 @@ class DiagnosticsSettingsTab : public SettingsTab { public: - DiagnosticsSettingsTab(Settings *parent, SettingsDialog &dialog); + DiagnosticsSettingsTab(Settings& settings, SettingsDialog& dialog); void update(); diff --git a/src/settingsdialoggeneral.cpp b/src/settingsdialoggeneral.cpp index fda50220..35012db7 100644 --- a/src/settingsdialoggeneral.cpp +++ b/src/settingsdialoggeneral.cpp @@ -6,12 +6,12 @@ using MOBase::QuestionBoxMemory; -GeneralSettingsTab::GeneralSettingsTab(Settings *m_parent, SettingsDialog &m_dialog) - : SettingsTab(m_parent, m_dialog) +GeneralSettingsTab::GeneralSettingsTab(Settings& s, SettingsDialog& d) + : SettingsTab(s, d) { addLanguages(); { - QString languageCode = m_parent->language(); + QString languageCode = settings().language(); int currentID = ui->languageBox->findData(languageCode); // I made a mess. :( Most languages are stored with only the iso country // code (2 characters like "de") but chinese @@ -28,31 +28,31 @@ GeneralSettingsTab::GeneralSettingsTab(Settings *m_parent, SettingsDialog &m_dia addStyles(); { int currentID = ui->styleBox->findData( - m_Settings.value("Settings/style", "").toString()); + qsettings().value("Settings/style", "").toString()); if (currentID != -1) { ui->styleBox->setCurrentIndex(currentID); } } //version with stylesheet - setButtonColor(ui->overwritingBtn, m_parent->modlistOverwritingLooseColor()); - setButtonColor(ui->overwrittenBtn, m_parent->modlistOverwrittenLooseColor()); - setButtonColor(ui->overwritingArchiveBtn, m_parent->modlistOverwritingArchiveColor()); - setButtonColor(ui->overwrittenArchiveBtn, m_parent->modlistOverwrittenArchiveColor()); - setButtonColor(ui->containsBtn, m_parent->modlistContainsPluginColor()); - setButtonColor(ui->containedBtn, m_parent->pluginListContainedColor()); - - setOverwritingColor(m_parent->modlistOverwritingLooseColor()); - setOverwrittenColor(m_parent->modlistOverwrittenLooseColor()); - setOverwritingArchiveColor(m_parent->modlistOverwritingArchiveColor()); - setOverwrittenArchiveColor(m_parent->modlistOverwrittenArchiveColor()); - setContainsColor(m_parent->modlistContainsPluginColor()); - setContainedColor(m_parent->pluginListContainedColor()); - - ui->compactBox->setChecked(m_parent->compactDownloads()); - ui->showMetaBox->setChecked(m_parent->metaDownloads()); - ui->usePrereleaseBox->setChecked(m_parent->usePrereleases()); - ui->colorSeparatorsBox->setChecked(m_parent->colorSeparatorScrollbar()); + setButtonColor(ui->overwritingBtn, settings().modlistOverwritingLooseColor()); + setButtonColor(ui->overwrittenBtn, settings().modlistOverwrittenLooseColor()); + setButtonColor(ui->overwritingArchiveBtn, settings().modlistOverwritingArchiveColor()); + setButtonColor(ui->overwrittenArchiveBtn, settings().modlistOverwrittenArchiveColor()); + setButtonColor(ui->containsBtn, settings().modlistContainsPluginColor()); + setButtonColor(ui->containedBtn, settings().pluginListContainedColor()); + + setOverwritingColor(settings().modlistOverwritingLooseColor()); + setOverwrittenColor(settings().modlistOverwrittenLooseColor()); + setOverwritingArchiveColor(settings().modlistOverwritingArchiveColor()); + setOverwrittenArchiveColor(settings().modlistOverwrittenArchiveColor()); + setContainsColor(settings().modlistContainsPluginColor()); + setContainedColor(settings().pluginListContainedColor()); + + ui->compactBox->setChecked(settings().compactDownloads()); + ui->showMetaBox->setChecked(settings().metaDownloads()); + ui->usePrereleaseBox->setChecked(settings().usePrereleases()); + ui->colorSeparatorsBox->setChecked(settings().colorSeparatorScrollbar()); QObject::connect(ui->overwritingArchiveBtn, &QPushButton::clicked, [&]{ on_overwritingArchiveBtn_clicked(); }); QObject::connect(ui->overwritingBtn, &QPushButton::clicked, [&]{ on_overwritingBtn_clicked(); }); @@ -67,30 +67,30 @@ GeneralSettingsTab::GeneralSettingsTab(Settings *m_parent, SettingsDialog &m_dia void GeneralSettingsTab::update() { - QString oldLanguage = m_parent->language(); + QString oldLanguage = settings().language(); QString newLanguage = ui->languageBox->itemData(ui->languageBox->currentIndex()).toString(); if (newLanguage != oldLanguage) { - m_Settings.setValue("Settings/language", newLanguage); - emit m_parent->languageChanged(newLanguage); + qsettings().setValue("Settings/language", newLanguage); + emit settings().languageChanged(newLanguage); } - QString oldStyle = m_Settings.value("Settings/style", "").toString(); + QString oldStyle = qsettings().value("Settings/style", "").toString(); QString newStyle = ui->styleBox->itemData(ui->styleBox->currentIndex()).toString(); if (oldStyle != newStyle) { - m_Settings.setValue("Settings/style", newStyle); - emit m_parent->styleChanged(newStyle); - } - - m_Settings.setValue("Settings/overwritingLooseFilesColor", getOverwritingColor()); - m_Settings.setValue("Settings/overwrittenLooseFilesColor", getOverwrittenColor()); - m_Settings.setValue("Settings/overwritingArchiveFilesColor", getOverwritingArchiveColor()); - m_Settings.setValue("Settings/overwrittenArchiveFilesColor", getOverwrittenArchiveColor()); - m_Settings.setValue("Settings/containsPluginColor", getContainsColor()); - m_Settings.setValue("Settings/containedColor", getContainedColor()); - m_Settings.setValue("Settings/compact_downloads", ui->compactBox->isChecked()); - m_Settings.setValue("Settings/meta_downloads", ui->showMetaBox->isChecked()); - m_Settings.setValue("Settings/use_prereleases", ui->usePrereleaseBox->isChecked()); - m_Settings.setValue("Settings/colorSeparatorScrollbars", ui->colorSeparatorsBox->isChecked()); + qsettings().setValue("Settings/style", newStyle); + emit settings().styleChanged(newStyle); + } + + qsettings().setValue("Settings/overwritingLooseFilesColor", getOverwritingColor()); + qsettings().setValue("Settings/overwrittenLooseFilesColor", getOverwrittenColor()); + qsettings().setValue("Settings/overwritingArchiveFilesColor", getOverwritingArchiveColor()); + qsettings().setValue("Settings/overwrittenArchiveFilesColor", getOverwrittenArchiveColor()); + qsettings().setValue("Settings/containsPluginColor", getContainsColor()); + qsettings().setValue("Settings/containedColor", getContainedColor()); + qsettings().setValue("Settings/compact_downloads", ui->compactBox->isChecked()); + qsettings().setValue("Settings/meta_downloads", ui->showMetaBox->isChecked()); + qsettings().setValue("Settings/use_prereleases", ui->usePrereleaseBox->isChecked()); + qsettings().setValue("Settings/colorSeparatorScrollbars", ui->colorSeparatorsBox->isChecked()); } void GeneralSettingsTab::addLanguages() @@ -141,7 +141,7 @@ void GeneralSettingsTab::addStyles() void GeneralSettingsTab::resetDialogs() { - m_parent->resetQuestionButtons(); + settings().resetQuestionButtons(); } void GeneralSettingsTab::setButtonColor(QPushButton *button, const QColor &color) @@ -163,7 +163,7 @@ void GeneralSettingsTab::setButtonColor(QPushButton *button, const QColor &color void GeneralSettingsTab::on_containsBtn_clicked() { - QColor result = QColorDialog::getColor(m_ContainsColor, parentWidget(), "Color Picker: Mod contains selected plugin", QColorDialog::ShowAlphaChannel); + QColor result = QColorDialog::getColor(m_ContainsColor, &dialog(), "Color Picker: Mod contains selected plugin", QColorDialog::ShowAlphaChannel); if (result.isValid()) { m_ContainsColor = result; setButtonColor(ui->containsBtn, result); @@ -172,7 +172,7 @@ void GeneralSettingsTab::on_containsBtn_clicked() void GeneralSettingsTab::on_containedBtn_clicked() { - QColor result = QColorDialog::getColor(m_ContainedColor, parentWidget(), "ColorPicker: Plugin is Contained in selected Mod", QColorDialog::ShowAlphaChannel); + QColor result = QColorDialog::getColor(m_ContainedColor, &dialog(), "ColorPicker: Plugin is Contained in selected Mod", QColorDialog::ShowAlphaChannel); if (result.isValid()) { m_ContainedColor = result; setButtonColor(ui->containedBtn, result); @@ -181,7 +181,7 @@ void GeneralSettingsTab::on_containedBtn_clicked() void GeneralSettingsTab::on_overwrittenBtn_clicked() { - QColor result = QColorDialog::getColor(m_OverwrittenColor, parentWidget(), "ColorPicker: Is overwritten (loose files)", QColorDialog::ShowAlphaChannel); + QColor result = QColorDialog::getColor(m_OverwrittenColor, &dialog(), "ColorPicker: Is overwritten (loose files)", QColorDialog::ShowAlphaChannel); if (result.isValid()) { m_OverwrittenColor = result; setButtonColor(ui->overwrittenBtn, result); @@ -190,7 +190,7 @@ void GeneralSettingsTab::on_overwrittenBtn_clicked() void GeneralSettingsTab::on_overwritingBtn_clicked() { - QColor result = QColorDialog::getColor(m_OverwritingColor, parentWidget(), "ColorPicker: Is overwriting (loose files)", QColorDialog::ShowAlphaChannel); + QColor result = QColorDialog::getColor(m_OverwritingColor, &dialog(), "ColorPicker: Is overwriting (loose files)", QColorDialog::ShowAlphaChannel); if (result.isValid()) { m_OverwritingColor = result; setButtonColor(ui->overwritingBtn, result); @@ -199,7 +199,7 @@ void GeneralSettingsTab::on_overwritingBtn_clicked() void GeneralSettingsTab::on_overwrittenArchiveBtn_clicked() { - QColor result = QColorDialog::getColor(m_OverwrittenArchiveColor, parentWidget(), "ColorPicker: Is overwritten (archive files)", QColorDialog::ShowAlphaChannel); + QColor result = QColorDialog::getColor(m_OverwrittenArchiveColor, &dialog(), "ColorPicker: Is overwritten (archive files)", QColorDialog::ShowAlphaChannel); if (result.isValid()) { m_OverwrittenArchiveColor = result; setButtonColor(ui->overwrittenArchiveBtn, result); @@ -208,7 +208,7 @@ void GeneralSettingsTab::on_overwrittenArchiveBtn_clicked() void GeneralSettingsTab::on_overwritingArchiveBtn_clicked() { - QColor result = QColorDialog::getColor(m_OverwritingArchiveColor, parentWidget(), "ColorPicker: Is overwriting (archive files)", QColorDialog::ShowAlphaChannel); + QColor result = QColorDialog::getColor(m_OverwritingArchiveColor, &dialog(), "ColorPicker: Is overwriting (archive files)", QColorDialog::ShowAlphaChannel); if (result.isValid()) { m_OverwritingArchiveColor = result; setButtonColor(ui->overwritingArchiveBtn, result); @@ -234,7 +234,7 @@ void GeneralSettingsTab::on_resetColorsBtn_clicked() void GeneralSettingsTab::on_resetDialogsButton_clicked() { - if (QMessageBox::question(parentWidget(), QObject::tr("Confirm?"), + if (QMessageBox::question(&dialog(), QObject::tr("Confirm?"), QObject::tr("This will make all dialogs show up again where you checked the \"Remember selection\"-box. Continue?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { resetDialogs(); @@ -243,7 +243,7 @@ void GeneralSettingsTab::on_resetDialogsButton_clicked() void GeneralSettingsTab::on_categoriesBtn_clicked() { - CategoriesDialog dialog(parentWidget()); + CategoriesDialog dialog(&dialog()); if (dialog.exec() == QDialog::Accepted) { dialog.commitChanges(); } diff --git a/src/settingsdialoggeneral.h b/src/settingsdialoggeneral.h index c7fcae36..2038ba31 100644 --- a/src/settingsdialoggeneral.h +++ b/src/settingsdialoggeneral.h @@ -7,7 +7,7 @@ class GeneralSettingsTab : public SettingsTab { public: - GeneralSettingsTab(Settings *m_parent, SettingsDialog &m_dialog); + GeneralSettingsTab(Settings& settings, SettingsDialog& dialog); void update(); diff --git a/src/settingsdialognexus.cpp b/src/settingsdialognexus.cpp index 575f54d0..b1964069 100644 --- a/src/settingsdialognexus.cpp +++ b/src/settingsdialognexus.cpp @@ -69,18 +69,18 @@ private: }; -NexusSettingsTab::NexusSettingsTab(Settings *parent, SettingsDialog &dialog) - : SettingsTab(parent, dialog) +NexusSettingsTab::NexusSettingsTab(Settings& s, SettingsDialog& d) + : SettingsTab(s, d) { - ui->offlineBox->setChecked(parent->offlineMode()); - ui->proxyBox->setChecked(parent->useProxy()); - ui->endorsementBox->setChecked(parent->endorsementIntegration()); - ui->hideAPICounterBox->setChecked(parent->hideAPICounter()); + ui->offlineBox->setChecked(settings().offlineMode()); + ui->proxyBox->setChecked(settings().useProxy()); + ui->endorsementBox->setChecked(settings().endorsementIntegration()); + ui->hideAPICounterBox->setChecked(settings().hideAPICounter()); // display server preferences - m_Settings.beginGroup("Servers"); - for (const QString &key : m_Settings.childKeys()) { - QVariantMap val = m_Settings.value(key).toMap(); + qsettings().beginGroup("Servers"); + for (const QString &key : qsettings().childKeys()) { + QVariantMap val = qsettings().value(key).toMap(); QString descriptor = key; if (!descriptor.compare("CDN", Qt::CaseInsensitive)) { descriptor += QStringLiteral(" (automatic)"); @@ -101,7 +101,7 @@ NexusSettingsTab::NexusSettingsTab(Settings *parent, SettingsDialog &dialog) } ui->preferredServersList->sortItems(Qt::DescendingOrder); } - m_Settings.endGroup(); + qsettings().endGroup(); QObject::connect(ui->nexusConnect, &QPushButton::clicked, [&]{ on_nexusConnect_clicked(); }); QObject::connect(ui->nexusManualKey, &QPushButton::clicked, [&]{ on_nexusManualKey_clicked(); }); @@ -114,27 +114,27 @@ NexusSettingsTab::NexusSettingsTab(Settings *parent, SettingsDialog &dialog) void NexusSettingsTab::update() { - m_Settings.setValue("Settings/offline_mode", ui->offlineBox->isChecked()); - m_Settings.setValue("Settings/use_proxy", ui->proxyBox->isChecked()); - m_Settings.setValue("Settings/endorsement_integration", ui->endorsementBox->isChecked()); - m_Settings.setValue("Settings/hide_api_counter", ui->hideAPICounterBox->isChecked()); + qsettings().setValue("Settings/offline_mode", ui->offlineBox->isChecked()); + qsettings().setValue("Settings/use_proxy", ui->proxyBox->isChecked()); + qsettings().setValue("Settings/endorsement_integration", ui->endorsementBox->isChecked()); + qsettings().setValue("Settings/hide_api_counter", ui->hideAPICounterBox->isChecked()); // store server preference - m_Settings.beginGroup("Servers"); + qsettings().beginGroup("Servers"); for (int i = 0; i < ui->knownServersList->count(); ++i) { QString key = ui->knownServersList->item(i)->data(Qt::UserRole).toString(); - QVariantMap val = m_Settings.value(key).toMap(); + QVariantMap val = qsettings().value(key).toMap(); val["preferred"] = 0; - m_Settings.setValue(key, val); + qsettings().setValue(key, val); } int count = ui->preferredServersList->count(); for (int i = 0; i < count; ++i) { QString key = ui->preferredServersList->item(i)->data(Qt::UserRole).toString(); - QVariantMap val = m_Settings.value(key).toMap(); + QVariantMap val = qsettings().value(key).toMap(); val["preferred"] = count - i; - m_Settings.setValue(key, val); + qsettings().setValue(key, val); } - m_Settings.endGroup(); + qsettings().endGroup(); } void NexusSettingsTab::on_nexusConnect_clicked() @@ -168,12 +168,12 @@ void NexusSettingsTab::on_nexusManualKey_clicked() return; } - NexusManualKeyDialog dialog(parentWidget()); - if (dialog.exec() != QDialog::Accepted) { + NexusManualKeyDialog d(&dialog()); + if (d.exec() != QDialog::Accepted) { return; } - const auto key = dialog.key(); + const auto key = d.key(); if (key.isEmpty()) { clearKey(); return; @@ -193,7 +193,7 @@ void NexusSettingsTab::on_nexusDisconnect_clicked() void NexusSettingsTab::on_clearCacheButton_clicked() { QDir(Settings::instance().getCacheDirectory()).removeRecursively(); - NexusInterface::instance(m_dialog.m_PluginContainer)->clearCache(); + NexusInterface::instance(dialog().m_PluginContainer)->clearCache(); } void NexusSettingsTab::on_associateButton_clicked() @@ -205,7 +205,7 @@ void NexusSettingsTab::validateKey(const QString& key) { if (!m_nexusValidator) { m_nexusValidator.reset(new NexusKeyValidator( - *NexusInterface::instance(m_dialog.m_PluginContainer)->getAccessManager())); + *NexusInterface::instance(dialog().m_PluginContainer)->getAccessManager())); m_nexusValidator->stateChanged = [&](auto&& s, auto&& e){ onValidatorStateChanged(s, e); @@ -261,7 +261,7 @@ void NexusSettingsTab::onValidatorStateChanged( void NexusSettingsTab::onValidatorFinished(const APIUserAccount& user) { - NexusInterface::instance(m_dialog.m_PluginContainer)->setUserAccount(user); + NexusInterface::instance(dialog().m_PluginContainer)->setUserAccount(user); if (!user.apiKey().isEmpty()) { if (setKey(user.apiKey())) { @@ -278,18 +278,18 @@ void NexusSettingsTab::addNexusLog(const QString& s) bool NexusSettingsTab::setKey(const QString& key) { - m_dialog.m_keyChanged = true; - const bool ret = m_parent->setNexusApiKey(key); + dialog().m_keyChanged = true; + const bool ret = settings().setNexusApiKey(key); updateNexusState(); return ret; } bool NexusSettingsTab::clearKey() { - m_dialog.m_keyChanged = true; - const auto ret = m_parent->clearNexusApiKey(); + dialog().m_keyChanged = true; + const auto ret = settings().clearNexusApiKey(); - NexusInterface::instance(m_dialog.m_PluginContainer)->getAccessManager()->clearApiKey(); + NexusInterface::instance(dialog().m_PluginContainer)->getAccessManager()->clearApiKey(); updateNexusState(); return ret; @@ -319,7 +319,7 @@ void NexusSettingsTab::updateNexusButtons() ui->nexusManualKey->setText(QObject::tr("Cancel")); ui->nexusManualKey->setEnabled(true); } - else if (m_parent->hasNexusApiKey()) { + else if (settings().hasNexusApiKey()) { // api key is present ui->nexusConnect->setText(QObject::tr("Connect to Nexus")); ui->nexusConnect->setEnabled(false); @@ -338,7 +338,7 @@ void NexusSettingsTab::updateNexusButtons() void NexusSettingsTab::updateNexusData() { - const auto user = NexusInterface::instance(m_dialog.m_PluginContainer) + const auto user = NexusInterface::instance(dialog().m_PluginContainer) ->getAPIUserAccount(); if (user.isValid()) { diff --git a/src/settingsdialognexus.h b/src/settingsdialognexus.h index cca2e1b5..89a6618f 100644 --- a/src/settingsdialognexus.h +++ b/src/settingsdialognexus.h @@ -8,7 +8,7 @@ class NexusSettingsTab : public SettingsTab { public: - NexusSettingsTab(Settings *m_parent, SettingsDialog &m_dialog); + NexusSettingsTab(Settings& settings, SettingsDialog& dialog); void update(); private: diff --git a/src/settingsdialogpaths.cpp b/src/settingsdialogpaths.cpp index 6e8fe994..290ceeb3 100644 --- a/src/settingsdialogpaths.cpp +++ b/src/settingsdialogpaths.cpp @@ -3,19 +3,19 @@ #include "appconfig.h" #include -PathsSettingsTab::PathsSettingsTab(Settings *parent, SettingsDialog &dialog) - : SettingsTab(parent, dialog) +PathsSettingsTab::PathsSettingsTab(Settings& s, SettingsDialog& d) + : SettingsTab(s, d) { - ui->baseDirEdit->setText(m_parent->getBaseDirectory()); - ui->managedGameDirEdit->setText(m_parent->gamePlugin()->gameDirectory().absoluteFilePath(m_parent->gamePlugin()->binaryName())); - QString basePath = parent->getBaseDirectory(); + ui->baseDirEdit->setText(settings().getBaseDirectory()); + ui->managedGameDirEdit->setText(settings().gamePlugin()->gameDirectory().absoluteFilePath(settings().gamePlugin()->binaryName())); + QString basePath = settings().getBaseDirectory(); QDir baseDir(basePath); for (const auto &dir : { - std::make_pair(ui->downloadDirEdit, m_parent->getDownloadDirectory(false)), - std::make_pair(ui->modDirEdit, m_parent->getModDirectory(false)), - std::make_pair(ui->cacheDirEdit, m_parent->getCacheDirectory(false)), - std::make_pair(ui->profilesDirEdit, m_parent->getProfileDirectory(false)), - std::make_pair(ui->overwriteDirEdit, m_parent->getOverwriteDirectory(false)) + std::make_pair(ui->downloadDirEdit, settings().getDownloadDirectory(false)), + std::make_pair(ui->modDirEdit, settings().getModDirectory(false)), + std::make_pair(ui->cacheDirEdit, settings().getCacheDirectory(false)), + std::make_pair(ui->profilesDirEdit, settings().getProfileDirectory(false)), + std::make_pair(ui->overwriteDirEdit, settings().getOverwriteDirectory(false)) }) { QString storePath = baseDir.relativeFilePath(dir.second); storePath = dir.second; @@ -42,7 +42,7 @@ void PathsSettingsTab::update() { typedef std::tuple Directory; - QString basePath = m_parent->getBaseDirectory(); + QString basePath = settings().getBaseDirectory(); for (const Directory &dir :{ Directory{ui->downloadDirEdit->text(), "download_directory", AppConfig::downloadPath()}, @@ -71,30 +71,30 @@ void PathsSettingsTab::update() if (QFileInfo(realPath) != QFileInfo(basePath + "/" + QString::fromStdWString(defaultName))) { - m_Settings.setValue(settingsKey, path); + qsettings().setValue(settingsKey, path); } else { - m_Settings.remove(settingsKey); + qsettings().remove(settingsKey); } } if (QFileInfo(ui->baseDirEdit->text()) != QFileInfo(qApp->property("dataPath").toString())) { - m_Settings.setValue("Settings/base_directory", ui->baseDirEdit->text()); + qsettings().setValue("Settings/base_directory", ui->baseDirEdit->text()); } else { - m_Settings.remove("Settings/base_directory"); + qsettings().remove("Settings/base_directory"); } - QFileInfo oldGameExe(m_parent->gamePlugin()->gameDirectory().absoluteFilePath(m_parent->gamePlugin()->binaryName())); + QFileInfo oldGameExe(settings().gamePlugin()->gameDirectory().absoluteFilePath(settings().gamePlugin()->binaryName())); QFileInfo newGameExe(ui->managedGameDirEdit->text()); if (oldGameExe != newGameExe) { - m_Settings.setValue("gamePath", newGameExe.absolutePath()); + qsettings().setValue("gamePath", newGameExe.absolutePath()); } } void PathsSettingsTab::on_browseBaseDirBtn_clicked() { QString temp = QFileDialog::getExistingDirectory( - parentWidget(), QObject::tr("Select base directory"), ui->baseDirEdit->text()); + &dialog(), QObject::tr("Select base directory"), ui->baseDirEdit->text()); if (!temp.isEmpty()) { ui->baseDirEdit->setText(temp); } @@ -105,7 +105,7 @@ void PathsSettingsTab::on_browseDownloadDirBtn_clicked() QString searchPath = ui->downloadDirEdit->text(); searchPath.replace("%BASE_DIR%", ui->baseDirEdit->text()); - QString temp = QFileDialog::getExistingDirectory(parentWidget(), QObject::tr("Select download directory"), searchPath); + QString temp = QFileDialog::getExistingDirectory(&dialog(), QObject::tr("Select download directory"), searchPath); if (!temp.isEmpty()) { ui->downloadDirEdit->setText(temp); } @@ -116,7 +116,7 @@ void PathsSettingsTab::on_browseModDirBtn_clicked() QString searchPath = ui->modDirEdit->text(); searchPath.replace("%BASE_DIR%", ui->baseDirEdit->text()); - QString temp = QFileDialog::getExistingDirectory(parentWidget(), QObject::tr("Select mod directory"), searchPath); + QString temp = QFileDialog::getExistingDirectory(&dialog(), QObject::tr("Select mod directory"), searchPath); if (!temp.isEmpty()) { ui->modDirEdit->setText(temp); } @@ -127,7 +127,7 @@ void PathsSettingsTab::on_browseCacheDirBtn_clicked() QString searchPath = ui->cacheDirEdit->text(); searchPath.replace("%BASE_DIR%", ui->baseDirEdit->text()); - QString temp = QFileDialog::getExistingDirectory(parentWidget(), QObject::tr("Select cache directory"), searchPath); + QString temp = QFileDialog::getExistingDirectory(&dialog(), QObject::tr("Select cache directory"), searchPath); if (!temp.isEmpty()) { ui->cacheDirEdit->setText(temp); } @@ -138,7 +138,7 @@ void PathsSettingsTab::on_browseProfilesDirBtn_clicked() QString searchPath = ui->profilesDirEdit->text(); searchPath.replace("%BASE_DIR%", ui->baseDirEdit->text()); - QString temp = QFileDialog::getExistingDirectory(parentWidget(), QObject::tr("Select profiles directory"), searchPath); + QString temp = QFileDialog::getExistingDirectory(&dialog(), QObject::tr("Select profiles directory"), searchPath); if (!temp.isEmpty()) { ui->profilesDirEdit->setText(temp); } @@ -149,7 +149,7 @@ void PathsSettingsTab::on_browseOverwriteDirBtn_clicked() QString searchPath = ui->overwriteDirEdit->text(); searchPath.replace("%BASE_DIR%", ui->baseDirEdit->text()); - QString temp = QFileDialog::getExistingDirectory(parentWidget(), QObject::tr("Select overwrite directory"), searchPath); + QString temp = QFileDialog::getExistingDirectory(&dialog(), QObject::tr("Select overwrite directory"), searchPath); if (!temp.isEmpty()) { ui->overwriteDirEdit->setText(temp); } @@ -159,7 +159,7 @@ void PathsSettingsTab::on_browseGameDirBtn_clicked() { QFileInfo oldGameExe(ui->managedGameDirEdit->text()); - QString temp = QFileDialog::getOpenFileName(parentWidget(), QObject::tr("Select game executable"), oldGameExe.absolutePath(), oldGameExe.fileName()); + QString temp = QFileDialog::getOpenFileName(&dialog(), QObject::tr("Select game executable"), oldGameExe.absolutePath(), oldGameExe.fileName()); if (!temp.isEmpty()) { ui->managedGameDirEdit->setText(temp); } diff --git a/src/settingsdialogpaths.h b/src/settingsdialogpaths.h index f661b624..a2073188 100644 --- a/src/settingsdialogpaths.h +++ b/src/settingsdialogpaths.h @@ -7,8 +7,7 @@ class PathsSettingsTab : public SettingsTab { public: - PathsSettingsTab(Settings *parent, SettingsDialog &dialog); - + PathsSettingsTab(Settings& settings, SettingsDialog& dialog); void update(); private: diff --git a/src/settingsdialogplugins.cpp b/src/settingsdialogplugins.cpp index 53b28fcc..329ba301 100644 --- a/src/settingsdialogplugins.cpp +++ b/src/settingsdialogplugins.cpp @@ -5,26 +5,26 @@ using MOBase::IPlugin; -PluginsSettingsTab::PluginsSettingsTab(Settings *m_parent, SettingsDialog &m_dialog) - : SettingsTab(m_parent, m_dialog) +PluginsSettingsTab::PluginsSettingsTab(Settings& s, SettingsDialog& d) + : SettingsTab(s, d) { ui->pluginSettingsList->setStyleSheet("QTreeWidget::item {padding-right: 10px;}"); // display plugin settings QSet handledNames; - for (IPlugin *plugin : m_parent->plugins()) { + for (IPlugin *plugin : settings().plugins()) { if (handledNames.contains(plugin->name())) continue; QListWidgetItem *listItem = new QListWidgetItem(plugin->name(), ui->pluginsList); listItem->setData(Qt::UserRole, QVariant::fromValue((void*)plugin)); - listItem->setData(Qt::UserRole + 1, m_parent->m_PluginSettings[plugin->name()]); - listItem->setData(Qt::UserRole + 2, m_parent->m_PluginDescriptions[plugin->name()]); + listItem->setData(Qt::UserRole + 1, settings().m_PluginSettings[plugin->name()]); + listItem->setData(Qt::UserRole + 2, settings().m_PluginDescriptions[plugin->name()]); ui->pluginsList->addItem(listItem); handledNames.insert(plugin->name()); } // display plugin blacklist - for (const QString &pluginName : m_parent->m_PluginBlacklist) { + for (const QString &pluginName : settings().m_PluginBlacklist) { ui->pluginBlacklist->addItem(pluginName); } @@ -34,7 +34,7 @@ PluginsSettingsTab::PluginsSettingsTab(Settings *m_parent, SettingsDialog &m_dia QShortcut *delShortcut = new QShortcut( QKeySequence(Qt::Key_Delete), ui->pluginBlacklist); - QObject::connect(delShortcut, &QShortcut::activated, parentWidget(), [&]{ deleteBlacklistItem(); }); + QObject::connect(delShortcut, &QShortcut::activated, &dialog(), [&]{ deleteBlacklistItem(); }); } void PluginsSettingsTab::update() @@ -42,21 +42,21 @@ void PluginsSettingsTab::update() // transfer plugin settings to in-memory structure for (int i = 0; i < ui->pluginsList->count(); ++i) { QListWidgetItem *item = ui->pluginsList->item(i); - m_parent->m_PluginSettings[item->text()] = item->data(Qt::UserRole + 1).toMap(); + settings().m_PluginSettings[item->text()] = item->data(Qt::UserRole + 1).toMap(); } // store plugin settings on disc - for (auto iterPlugins = m_parent->m_PluginSettings.begin(); iterPlugins != m_parent->m_PluginSettings.end(); ++iterPlugins) { + for (auto iterPlugins = settings().m_PluginSettings.begin(); iterPlugins != settings().m_PluginSettings.end(); ++iterPlugins) { for (auto iterSettings = iterPlugins->begin(); iterSettings != iterPlugins->end(); ++iterSettings) { - m_Settings.setValue("Plugins/" + iterPlugins.key() + "/" + iterSettings.key(), iterSettings.value()); + qsettings().setValue("Plugins/" + iterPlugins.key() + "/" + iterSettings.key(), iterSettings.value()); } } // store plugin blacklist - m_parent->m_PluginBlacklist.clear(); + settings().m_PluginBlacklist.clear(); for (QListWidgetItem *item : ui->pluginBlacklist->findItems("*", Qt::MatchWildcard)) { - m_parent->m_PluginBlacklist.insert(item->text()); + settings().m_PluginBlacklist.insert(item->text()); } - m_parent->writePluginBlacklist(); + settings().writePluginBlacklist(); } void PluginsSettingsTab::closing() diff --git a/src/settingsdialogplugins.h b/src/settingsdialogplugins.h index 9d21daa6..8e2dae2a 100644 --- a/src/settingsdialogplugins.h +++ b/src/settingsdialogplugins.h @@ -7,7 +7,7 @@ class PluginsSettingsTab : public SettingsTab { public: - PluginsSettingsTab(Settings *m_parent, SettingsDialog &m_dialog); + PluginsSettingsTab(Settings& settings, SettingsDialog& dialog); void update(); void closing() override; diff --git a/src/settingsdialogsteam.cpp b/src/settingsdialogsteam.cpp index 34c2d76b..9ed93e47 100644 --- a/src/settingsdialogsteam.cpp +++ b/src/settingsdialogsteam.cpp @@ -1,11 +1,11 @@ #include "settingsdialogsteam.h" #include "ui_settingsdialog.h" -SteamSettingsTab::SteamSettingsTab(Settings *m_parent, SettingsDialog &m_dialog) - : SettingsTab(m_parent, m_dialog) +SteamSettingsTab::SteamSettingsTab(Settings& s, SettingsDialog& d) + : SettingsTab(s, d) { QString username, password; - m_parent->getSteamLogin(username, password); + settings().getSteamLogin(username, password); ui->steamUserEdit->setText(username); ui->steamPassEdit->setText(password); @@ -13,5 +13,5 @@ SteamSettingsTab::SteamSettingsTab(Settings *m_parent, SettingsDialog &m_dialog) void SteamSettingsTab::update() { - m_parent->setSteamLogin(ui->steamUserEdit->text(), ui->steamPassEdit->text()); + settings().setSteamLogin(ui->steamUserEdit->text(), ui->steamPassEdit->text()); } diff --git a/src/settingsdialogsteam.h b/src/settingsdialogsteam.h index dbd85151..6a3d75f4 100644 --- a/src/settingsdialogsteam.h +++ b/src/settingsdialogsteam.h @@ -7,11 +7,8 @@ class SteamSettingsTab : public SettingsTab { public: - SteamSettingsTab(Settings *m_parent, SettingsDialog &m_dialog); - + SteamSettingsTab(Settings& settings, SettingsDialog& dialog); void update(); - -private: }; #endif // SETTINGSDIALOGSTEAM_H diff --git a/src/settingsdialogworkarounds.cpp b/src/settingsdialogworkarounds.cpp index fc859289..443ba54e 100644 --- a/src/settingsdialogworkarounds.cpp +++ b/src/settingsdialogworkarounds.cpp @@ -3,15 +3,15 @@ #include "helper.h" #include -WorkaroundsSettingsTab::WorkaroundsSettingsTab(Settings *m_parent, SettingsDialog &m_dialog) - : SettingsTab(m_parent, m_dialog) +WorkaroundsSettingsTab::WorkaroundsSettingsTab(Settings& s, SettingsDialog& d) + : SettingsTab(s, d) { - ui->appIDEdit->setText(m_parent->getSteamAppID()); + ui->appIDEdit->setText(settings().getSteamAppID()); - LoadMechanism::EMechanism mechanismID = m_parent->getLoadMechanism(); + LoadMechanism::EMechanism mechanismID = settings().getLoadMechanism(); int index = 0; - if (m_parent->loadMechanism().isDirectLoadingSupported()) { + if (settings().loadMechanism().isDirectLoadingSupported()) { ui->mechanismBox->addItem(QObject::tr("Mod Organizer"), LoadMechanism::LOAD_MODORGANIZER); if (mechanismID == LoadMechanism::LOAD_MODORGANIZER) { index = ui->mechanismBox->count() - 1; @@ -20,13 +20,13 @@ WorkaroundsSettingsTab::WorkaroundsSettingsTab(Settings *m_parent, SettingsDialo ui->mechanismBox->setCurrentIndex(index); - ui->hideUncheckedBox->setChecked(m_parent->hideUncheckedPlugins()); - ui->forceEnableBox->setChecked(m_parent->forceEnableCoreFiles()); - ui->displayForeignBox->setChecked(m_parent->displayForeign()); - ui->lockGUIBox->setChecked(m_parent->lockGUI()); - ui->enableArchiveParsingBox->setChecked(m_parent->archiveParsing()); + ui->hideUncheckedBox->setChecked(settings().hideUncheckedPlugins()); + ui->forceEnableBox->setChecked(settings().forceEnableCoreFiles()); + ui->displayForeignBox->setChecked(settings().displayForeign()); + ui->lockGUIBox->setChecked(settings().lockGUI()); + ui->enableArchiveParsingBox->setChecked(settings().archiveParsing()); - setExecutableBlacklist(m_parent->executablesBlacklist()); + setExecutableBlacklist(settings().executablesBlacklist()); QObject::connect(ui->bsaDateBtn, &QPushButton::clicked, [&]{ on_bsaDateBtn_clicked(); }); QObject::connect(ui->execBlacklistBtn, &QPushButton::clicked, [&]{ on_execBlacklistBtn_clicked(); }); @@ -35,26 +35,26 @@ WorkaroundsSettingsTab::WorkaroundsSettingsTab(Settings *m_parent, SettingsDialo void WorkaroundsSettingsTab::update() { - if (ui->appIDEdit->text() != m_parent->gamePlugin()->steamAPPId()) { - m_Settings.setValue("Settings/app_id", ui->appIDEdit->text()); + if (ui->appIDEdit->text() != settings().gamePlugin()->steamAPPId()) { + qsettings().setValue("Settings/app_id", ui->appIDEdit->text()); } else { - m_Settings.remove("Settings/app_id"); + qsettings().remove("Settings/app_id"); } - m_Settings.setValue("Settings/load_mechanism", ui->mechanismBox->itemData(ui->mechanismBox->currentIndex()).toInt()); - m_Settings.setValue("Settings/hide_unchecked_plugins", ui->hideUncheckedBox->isChecked()); - m_Settings.setValue("Settings/force_enable_core_files", ui->forceEnableBox->isChecked()); - m_Settings.setValue("Settings/display_foreign", ui->displayForeignBox->isChecked()); - m_Settings.setValue("Settings/lock_gui", ui->lockGUIBox->isChecked()); - m_Settings.setValue("Settings/archive_parsing_experimental", ui->enableArchiveParsingBox->isChecked()); + qsettings().setValue("Settings/load_mechanism", ui->mechanismBox->itemData(ui->mechanismBox->currentIndex()).toInt()); + qsettings().setValue("Settings/hide_unchecked_plugins", ui->hideUncheckedBox->isChecked()); + qsettings().setValue("Settings/force_enable_core_files", ui->forceEnableBox->isChecked()); + qsettings().setValue("Settings/display_foreign", ui->displayForeignBox->isChecked()); + qsettings().setValue("Settings/lock_gui", ui->lockGUIBox->isChecked()); + qsettings().setValue("Settings/archive_parsing_experimental", ui->enableArchiveParsingBox->isChecked()); - m_Settings.setValue("Settings/executable_blacklist", getExecutableBlacklist()); + qsettings().setValue("Settings/executable_blacklist", getExecutableBlacklist()); } void WorkaroundsSettingsTab::on_execBlacklistBtn_clicked() { bool ok = false; QString result = QInputDialog::getMultiLineText( - parentWidget(), + &dialog(), QObject::tr("Executables Blacklist"), QObject::tr("Enter one executable per line to be blacklisted from the virtual file system.\n" "Mods and other virtualized files will not be visible to these executables and\n" @@ -96,7 +96,7 @@ void WorkaroundsSettingsTab::on_resetGeometryBtn_clicked() nullptr, caption, text, QMessageBox::Yes | QMessageBox::Cancel); if (res == QMessageBox::Yes) { - m_parent->geometry().requestReset(); + settings().geometry().requestReset(); qApp->exit(INT_MAX); } } diff --git a/src/settingsdialogworkarounds.h b/src/settingsdialogworkarounds.h index 1687624b..d5d6815f 100644 --- a/src/settingsdialogworkarounds.h +++ b/src/settingsdialogworkarounds.h @@ -7,8 +7,7 @@ class WorkaroundsSettingsTab : public SettingsTab { public: - WorkaroundsSettingsTab(Settings *m_parent, SettingsDialog &m_dialog); - + WorkaroundsSettingsTab(Settings& settings, SettingsDialog& dialog); void update(); private: -- cgit v1.3.1 From b1687380c5c10342699f95361e30f18a32bad585 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 26 Aug 2019 00:01:07 -0400 Subject: moved more nexus stuff to settings settings tab remembered --- src/settings.cpp | 24 ++++++++++++++++++++++-- src/settings.h | 4 ++++ src/settingsdialog.cpp | 4 ++++ src/settingsdialognexus.cpp | 12 ++++++------ 4 files changed, 36 insertions(+), 8 deletions(-) (limited to 'src/settingsdialognexus.cpp') diff --git a/src/settings.cpp b/src/settings.cpp index 2236fc9d..9c9e4c4d 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -545,6 +545,11 @@ std::optional Settings::getUseProxy() const return getOptional(m_Settings, "Settings/use_proxy"); } +void Settings::setUseProxy(bool b) +{ + m_Settings.setValue("Settings/use_proxy", b); +} + std::optional Settings::getVersion() const { if (auto v=getOptional(m_Settings, "version")) { @@ -658,6 +663,11 @@ bool Settings::offlineMode() const return m_Settings.value("Settings/offline_mode", false).toBool(); } +void Settings::setOfflineMode(bool b) +{ + m_Settings.setValue("Settings/offline_mode", b); +} + log::Levels Settings::logLevel() const { return static_cast(m_Settings.value("Settings/log_level").toInt()); @@ -756,6 +766,11 @@ bool Settings::endorsementIntegration() const return m_Settings.value("Settings/endorsement_integration", true).toBool(); } +void Settings::setEndorsementIntegration(bool b) const +{ + m_Settings.setValue("Settings/endorsement_integration", b); +} + EndorsementState Settings::endorsementState() const { const auto v = getOptional(m_Settings, "endorse_state"); @@ -778,6 +793,11 @@ bool Settings::hideAPICounter() const return m_Settings.value("Settings/hide_api_counter", false).toBool(); } +void Settings::setHideAPICounter(bool b) +{ + m_Settings.setValue("Settings/hide_api_counter", b); +} + bool Settings::displayForeign() const { return m_Settings.value("Settings/display_foreign", true).toBool(); @@ -886,8 +906,8 @@ void Settings::updateServers(const QList &servers) data["premium"] = server.premium; m_Settings.setValue(server.name, data); + } } - } // clean up unavailable servers QDate now = QDate::currentDate(); @@ -1166,7 +1186,7 @@ void Settings::dump() const { static const QStringList ignore({ "username", "password", "nexus_api_key" - }); + }); log::debug("settings:"); diff --git a/src/settings.h b/src/settings.h index 5b1d7bfc..21776169 100644 --- a/src/settings.h +++ b/src/settings.h @@ -359,6 +359,7 @@ public: * @return true if the user disabled internet features */ bool offlineMode() const; + void setOfflineMode(bool b); /** * @return true if the user chose compact downloads @@ -405,11 +406,13 @@ public: * @return true if the user configured the use of a network proxy */ bool useProxy() const; + void setUseProxy(bool b); /** * @return true if endorsement integration is enabled */ bool endorsementIntegration() const; + void setEndorsementIntegration(bool b) const; EndorsementState endorsementState() const; void setEndorsementState(EndorsementState s); @@ -419,6 +422,7 @@ public: * @return true if the API counter should be hidden */ bool hideAPICounter() const; + void setHideAPICounter(bool b); /** * @return true if the user wants to see non-official plugins installed outside MO in his mod list diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index 9d031785..a24416e9 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -51,8 +51,12 @@ int SettingsDialog::exec() { GeometrySaver gs(m_settings, this); + m_settings.restoreIndex(ui->tabWidget); + auto ret = TutorableDialog::exec(); + m_settings.saveIndex(ui->tabWidget); + if (ret == QDialog::Accepted) { for (auto&& tab : m_tabs) { tab->closing(); diff --git a/src/settingsdialognexus.cpp b/src/settingsdialognexus.cpp index b1964069..4d9a18a2 100644 --- a/src/settingsdialognexus.cpp +++ b/src/settingsdialognexus.cpp @@ -114,10 +114,10 @@ NexusSettingsTab::NexusSettingsTab(Settings& s, SettingsDialog& d) void NexusSettingsTab::update() { - qsettings().setValue("Settings/offline_mode", ui->offlineBox->isChecked()); - qsettings().setValue("Settings/use_proxy", ui->proxyBox->isChecked()); - qsettings().setValue("Settings/endorsement_integration", ui->endorsementBox->isChecked()); - qsettings().setValue("Settings/hide_api_counter", ui->hideAPICounterBox->isChecked()); + settings().setOfflineMode(ui->offlineBox->isChecked()); + settings().setUseProxy(ui->proxyBox->isChecked()); + settings().setEndorsementIntegration(ui->endorsementBox->isChecked()); + settings().setHideAPICounter(ui->hideAPICounterBox->isChecked()); // store server preference qsettings().beginGroup("Servers"); @@ -126,14 +126,14 @@ void NexusSettingsTab::update() QVariantMap val = qsettings().value(key).toMap(); val["preferred"] = 0; qsettings().setValue(key, val); - } + } int count = ui->preferredServersList->count(); for (int i = 0; i < count; ++i) { QString key = ui->preferredServersList->item(i)->data(Qt::UserRole).toString(); QVariantMap val = qsettings().value(key).toMap(); val["preferred"] = count - i; qsettings().setValue(key, val); - } + } qsettings().endGroup(); } -- cgit v1.3.1 From 36dbb4bad74b097d44b843a2e934aa4a58ef6492 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 26 Aug 2019 00:48:25 -0400 Subject: ServerList instead of a QList of ServerInfo changed preferred to an int moved all server settings to Settings --- src/mainwindow.cpp | 24 ++++++++------ src/serverinfo.cpp | 63 ++++++++++++++++++++++++++++++++++--- src/serverinfo.h | 35 +++++++++++++++++++-- src/settings.cpp | 36 ++++++++++++++++++--- src/settings.h | 5 ++- src/settingsdialognexus.cpp | 77 ++++++++++++++++++++++++++++++++------------- 6 files changed, 194 insertions(+), 46 deletions(-) (limited to 'src/settingsdialognexus.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index b6ae59a1..9921ad82 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -5904,18 +5904,22 @@ void MainWindow::nxmTrackedModsAvailable(QVariant userData, QVariant resultData, void MainWindow::nxmDownloadURLs(QString, int, int, QVariant, QVariant resultData, int) { - QVariantList serverList = resultData.toList(); - - QList servers; - for (const QVariant &server : serverList) { - QVariantMap serverInfo = server.toMap(); - ServerInfo info( - serverInfo["short_name"].toString(), - serverInfo["name"].toString().contains("Premium", Qt::CaseInsensitive), + ServerList servers; + + for (const QVariant &var : resultData.toList()) { + const QVariantMap map = var.toMap(); + + ServerInfo server( + map["short_name"].toString(), + map["name"].toString().contains("Premium", Qt::CaseInsensitive), QDate::currentDate(), - serverInfo["short_name"].toString().contains("CDN", Qt::CaseInsensitive)); - servers.append(info); + map["short_name"].toString().contains("CDN", Qt::CaseInsensitive) ? 1 : 0, + map["downloadCount"].toInt(), + map["downloadSpeed"].toDouble()); + + servers.add(std::move(server)); } + m_OrganizerCore.settings().updateServers(servers); } diff --git a/src/serverinfo.cpp b/src/serverinfo.cpp index 5912c226..67a80b9e 100644 --- a/src/serverinfo.cpp +++ b/src/serverinfo.cpp @@ -1,13 +1,15 @@ #include "serverinfo.h" ServerInfo::ServerInfo() - : ServerInfo({}, false, {}, false) + : ServerInfo({}, false, {}, 0, 0, 0.0) { } -ServerInfo::ServerInfo(QString n, bool premium, QDate last, bool preferred) : - m_name(std::move(n)), m_premium(premium), m_lastSeen(std::move(last)), - m_preferred(preferred) +ServerInfo::ServerInfo( + QString name, bool premium, QDate last, int preferred, + int count, double speed) : + m_name(std::move(name)), m_premium(premium), m_lastSeen(std::move(last)), + m_preferred(preferred), m_downloadCount(count), m_downloadSpeed(speed) { } @@ -26,7 +28,58 @@ const QDate& ServerInfo::lastSeen() const return m_lastSeen; } -bool ServerInfo::isPreferred() const +int ServerInfo::preferred() const { return m_preferred; } + +int ServerInfo::downloadCount() const +{ + return m_downloadCount; +} + +double ServerInfo::downloadSpeed() const +{ + return m_downloadSpeed; +} + +void ServerInfo::setPreferred(int i) +{ + m_preferred = i; +} + + +void ServerList::add(ServerInfo s) +{ + m_servers.push_back(std::move(s)); +} + +ServerList::iterator ServerList::begin() +{ + return m_servers.begin(); +} + +ServerList::const_iterator ServerList::begin() const +{ + return m_servers.begin(); +} + +ServerList::iterator ServerList::end() +{ + return m_servers.end(); +} + +ServerList::const_iterator ServerList::end() const +{ + return m_servers.end(); +} + +std::size_t ServerList::size() const +{ + return m_servers.size(); +} + +bool ServerList::empty() const +{ + return m_servers.empty(); +} diff --git a/src/serverinfo.h b/src/serverinfo.h index 79b90e77..0a8a5028 100644 --- a/src/serverinfo.h +++ b/src/serverinfo.h @@ -9,20 +9,49 @@ class ServerInfo { public: ServerInfo(); - ServerInfo(QString name, bool premium, QDate lastSeen, bool preferred); + ServerInfo( + QString name, bool premium, QDate lastSeen, int preferred, + int downloadCount, double downloadSpeed); const QString& name() const; bool isPremium() const; const QDate& lastSeen() const; - bool isPreferred() const; + int preferred() const; + int downloadCount() const; + double downloadSpeed() const; + + void setPreferred(int i); private: QString m_name; bool m_premium; QDate m_lastSeen; - bool m_preferred; + int m_preferred; + int m_downloadCount; + double m_downloadSpeed; }; Q_DECLARE_METATYPE(ServerInfo) + +class ServerList +{ +public: + using container = QList; + using iterator = container::iterator; + using const_iterator = container::const_iterator; + + void add(ServerInfo s); + + iterator begin(); + const_iterator begin() const; + iterator end(); + const_iterator end() const; + std::size_t size() const; + bool empty() const; + +private: + container m_servers; +}; + #endif // SERVERINFO_H diff --git a/src/settings.cpp b/src/settings.cpp index 9975642b..5ddffd8f 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -884,17 +884,42 @@ std::map Settings::getPreferredServers() return result; } -void Settings::updateServers(const QList &servers) +ServerList Settings::getServers() const +{ + ServerList list; + + m_Settings.beginGroup("Servers"); + + for (const QString &serverKey : m_Settings.childKeys()) { + QVariantMap data = m_Settings.value(serverKey).toMap(); + + ServerInfo server( + serverKey, + data["premium"].toBool(), + data["lastSeen"].toDate(), + data["preferred"].toInt(), + data["downloadCount"].toInt(), + data["downloadSpeed"].toDouble()); + + list.add(std::move(server)); + } + + m_Settings.endGroup(); + + return list; +} + +void Settings::updateServers(const ServerList& servers) { m_Settings.beginGroup("Servers"); QStringList oldServerKeys = m_Settings.childKeys(); - for (const ServerInfo &server : servers) { + for (const auto& server : servers) { if (!oldServerKeys.contains(server.name())) { // not yet known server QVariantMap newVal; newVal["premium"] = server.isPremium(); - newVal["preferred"] = server.isPreferred() ? 1 : 0; + newVal["preferred"] = server.preferred(); newVal["lastSeen"] = server.lastSeen(); newVal["downloadCount"] = 0; newVal["downloadSpeed"] = 0.0; @@ -902,12 +927,13 @@ void Settings::updateServers(const QList &servers) m_Settings.setValue(server.name(), newVal); } else { QVariantMap data = m_Settings.value(server.name()).toMap(); - data["lastSeen"] = server.lastSeen(); data["premium"] = server.isPremium(); + data["lastSeen"] = server.lastSeen(); + data["preferred"] = server.preferred(); m_Settings.setValue(server.name(), data); - } } + } // clean up unavailable servers QDate now = QDate::currentDate(); diff --git a/src/settings.h b/src/settings.h index 4662ed19..31dbf85c 100644 --- a/src/settings.h +++ b/src/settings.h @@ -34,6 +34,7 @@ class QSplitter; class PluginContainer; class ServerInfo; +class ServerList; class Settings; class ExpanderWidget; @@ -493,11 +494,13 @@ public: */ std::map getPreferredServers(); + ServerList getServers() const; + /** * @brief updates the list of known servers * @param list of servers from a recent query */ - void updateServers(const QList &servers); + void updateServers(const ServerList& servers); /** * @brief add a plugin that is to be blacklisted diff --git a/src/settingsdialognexus.cpp b/src/settingsdialognexus.cpp index 4d9a18a2..926ea9a6 100644 --- a/src/settingsdialognexus.cpp +++ b/src/settingsdialognexus.cpp @@ -2,9 +2,11 @@ #include "ui_settingsdialog.h" #include "ui_nexusmanualkey.h" #include "nexusinterface.h" +#include "serverinfo.h" +#include "log.h" #include -namespace shell = MOBase::shell; +using namespace MOBase; template class ServerItem : public QListWidgetItem { @@ -78,30 +80,31 @@ NexusSettingsTab::NexusSettingsTab(Settings& s, SettingsDialog& d) ui->hideAPICounterBox->setChecked(settings().hideAPICounter()); // display server preferences - qsettings().beginGroup("Servers"); - for (const QString &key : qsettings().childKeys()) { - QVariantMap val = qsettings().value(key).toMap(); - QString descriptor = key; + for (const auto& server : s.getServers()) { + QString descriptor = server.name(); + if (!descriptor.compare("CDN", Qt::CaseInsensitive)) { descriptor += QStringLiteral(" (automatic)"); } - if (val.contains("downloadSpeed") && val.contains("downloadCount") && (val["downloadCount"].toInt() > 0)) { - int bps = static_cast(val["downloadSpeed"].toDouble() / val["downloadCount"].toInt()); + + if (server.downloadSpeed() > 0 && server.downloadCount() > 0) { + const int bps = static_cast(server.downloadSpeed() / server.downloadCount()); descriptor += QString(" (%1 kbps)").arg(bps / 1024); } QListWidgetItem *newItem = new ServerItem(descriptor, Qt::UserRole + 1); - newItem->setData(Qt::UserRole, key); - newItem->setData(Qt::UserRole + 1, val["preferred"].toInt()); - if (val["preferred"].toInt() > 0) { + newItem->setData(Qt::UserRole, server.name()); + newItem->setData(Qt::UserRole + 1, server.preferred()); + + if (server.preferred() > 0) { ui->preferredServersList->addItem(newItem); } else { ui->knownServersList->addItem(newItem); } + ui->preferredServersList->sortItems(Qt::DescendingOrder); } - qsettings().endGroup(); QObject::connect(ui->nexusConnect, &QPushButton::clicked, [&]{ on_nexusConnect_clicked(); }); QObject::connect(ui->nexusManualKey, &QPushButton::clicked, [&]{ on_nexusManualKey_clicked(); }); @@ -119,22 +122,52 @@ void NexusSettingsTab::update() settings().setEndorsementIntegration(ui->endorsementBox->isChecked()); settings().setHideAPICounter(ui->hideAPICounterBox->isChecked()); + auto servers = settings().getServers(); + // store server preference - qsettings().beginGroup("Servers"); for (int i = 0; i < ui->knownServersList->count(); ++i) { - QString key = ui->knownServersList->item(i)->data(Qt::UserRole).toString(); - QVariantMap val = qsettings().value(key).toMap(); - val["preferred"] = 0; - qsettings().setValue(key, val); + const QString key = ui->knownServersList->item(i)->data(Qt::UserRole).toString(); + + bool found = false; + + for (auto& server : servers) { + if (server.name() == key) { + server.setPreferred(0); + found = true; + break; + } } - int count = ui->preferredServersList->count(); + + if (!found) { + log::error("while setting preferred to 0, server '{}' not found", key); + } + } + + const int count = ui->preferredServersList->count(); + for (int i = 0; i < count; ++i) { - QString key = ui->preferredServersList->item(i)->data(Qt::UserRole).toString(); - QVariantMap val = qsettings().value(key).toMap(); - val["preferred"] = count - i; - qsettings().setValue(key, val); + const QString key = ui->preferredServersList->item(i)->data(Qt::UserRole).toString(); + const int newPreferred = count - i; + + bool found = false; + + for (auto& server : servers) { + + if (server.name() == key) { + server.setPreferred(newPreferred); + found = true; + break; } - qsettings().endGroup(); + } + + if (!found) { + log::error( + "while setting preference to {}, server '{}' not found", + newPreferred, key); + } + } + + settings().updateServers(servers); } void NexusSettingsTab::on_nexusConnect_clicked() -- cgit v1.3.1 From c42e5fb2fec9b20fe6956d8798b85874b2eff73e Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 26 Aug 2019 03:01:55 -0400 Subject: changed total speed and count to a list of the last 5 downloads existing servers now merged when retrieving the download links download manager doesn't store the servers any more, queries the settings every time --- src/downloadmanager.cpp | 17 +++++------ src/downloadmanager.h | 17 ----------- src/mainwindow.cpp | 31 ++++++++++++------- src/organizercore.cpp | 1 - src/serverinfo.cpp | 72 +++++++++++++++++++++++++++++++++++++++------ src/serverinfo.h | 21 ++++++++----- src/settings.cpp | 51 ++++++++++++++++++++++---------- src/settingsdialognexus.cpp | 6 ++-- 8 files changed, 143 insertions(+), 73 deletions(-) (limited to 'src/settingsdialognexus.cpp') diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 45cfdeed..93ca1608 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -288,12 +288,6 @@ void DownloadManager::setOutputDirectory(const QString &outputDirectory) } -void DownloadManager::setServers(const ServerList& servers) -{ - m_Servers = servers; -} - - void DownloadManager::setSupportedExtensions(const QStringList &extensions) { m_SupportedExtensions = extensions; @@ -1669,7 +1663,7 @@ void DownloadManager::nxmFileInfoAvailable(QString gameName, int modID, int file static int evaluateFileInfoMap( const QVariantMap &map, - const QList& preferredServers) + const ServerList::container& preferredServers) { int preference = 0; bool found = false; @@ -1692,8 +1686,9 @@ static int evaluateFileInfoMap( } // sort function to sort by best download server -bool DownloadManager::ServerByPreference( - const QList& preferredServers, +// +bool ServerByPreference( + const ServerList::container& preferredServers, const QVariant &LHS, const QVariant &RHS) { const auto a = evaluateFileInfoMap(LHS.toMap(), preferredServers); @@ -1747,10 +1742,12 @@ void DownloadManager::nxmDownloadURLsAvailable(QString gameName, int modID, int return; } + const auto servers = m_OrganizerCore->settings().getServers(); + std::sort( resultList.begin(), resultList.end(), - boost::bind(&DownloadManager::ServerByPreference, m_Servers.getPreferred(), _1, _2)); + boost::bind(&ServerByPreference, servers.getPreferred(), _1, _2)); info->userData["downloadMap"] = resultList; diff --git a/src/downloadmanager.h b/src/downloadmanager.h index f739f4f0..bed1b3cc 100644 --- a/src/downloadmanager.h +++ b/src/downloadmanager.h @@ -174,11 +174,6 @@ public: **/ QString getOutputDirectory() const { return m_OutputDirectory; } - /** - * @brief sets the list of servers - */ - void setServers(const ServerList& servers); - /** * @brief set the list of supported extensions * @param extensions list of supported extensions @@ -361,17 +356,6 @@ public: */ void refreshList(); - /** - * @brief Sort function for download servers - * @param LHS - * @param RHS - * @return - */ - static bool ServerByPreference( - const QList& preferredServers, - const QVariant &LHS, const QVariant &RHS); - - virtual int startDownloadURLs(const QStringList &urls); virtual int startDownloadNexusFile(int modID, int fileID); @@ -551,7 +535,6 @@ private: QVector m_ActiveDownloads; QString m_OutputDirectory; - ServerList m_Servers; QStringList m_SupportedExtensions; std::set m_RequestIDs; QVector m_AlphabeticalTranslation; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 79203d29..4c2594b8 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -5050,7 +5050,6 @@ void MainWindow::on_actionSettings_triggered() dlManager->setOutputDirectory(settings.getDownloadDirectory()); } } - dlManager->setServers(settings.getServers()); if ((settings.getModDirectory() != oldModDirectory) || (settings.displayForeign() != oldDisplayForeign)) { @@ -5904,20 +5903,32 @@ void MainWindow::nxmTrackedModsAvailable(QVariant userData, QVariant resultData, void MainWindow::nxmDownloadURLs(QString, int, int, QVariant, QVariant resultData, int) { - ServerList servers; + auto servers = m_OrganizerCore.settings().getServers(); for (const QVariant &var : resultData.toList()) { const QVariantMap map = var.toMap(); - ServerInfo server( - map["short_name"].toString(), - map["name"].toString().contains("Premium", Qt::CaseInsensitive), - QDate::currentDate(), - map["short_name"].toString().contains("CDN", Qt::CaseInsensitive) ? 1 : 0, - map["downloadCount"].toInt(), - map["downloadSpeed"].toDouble()); + const auto name = map["short_name"].toString(); + const auto isPremium = map["name"].toString().contains("Premium", Qt::CaseInsensitive); + const auto isCDN = map["short_name"].toString().contains("CDN", Qt::CaseInsensitive); - servers.add(std::move(server)); + bool found = false; + + for (auto& server : servers) { + if (server.name() == name) { + // already exists, update + server.setPremium(isPremium); + server.updateLastSeen(); + found = true; + break; + } + } + + if (!found) { + // new server + ServerInfo server(name, isPremium, QDate::currentDate(), isCDN ? 1 : 0, {}); + servers.add(std::move(server)); + } } m_OrganizerCore.settings().updateServers(servers); diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 522d28be..ec13ca9c 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -276,7 +276,6 @@ OrganizerCore::OrganizerCore(Settings &settings) , m_PluginListsWriter(std::bind(&OrganizerCore::savePluginList, this)) { m_DownloadManager.setOutputDirectory(m_Settings.getDownloadDirectory()); - m_DownloadManager.setServers(m_Settings.getServers()); NexusInterface::instance(m_PluginContainer)->setCacheDirectory(m_Settings.getCacheDirectory()); diff --git a/src/serverinfo.cpp b/src/serverinfo.cpp index 16e65f52..aece61da 100644 --- a/src/serverinfo.cpp +++ b/src/serverinfo.cpp @@ -3,17 +3,23 @@ using namespace MOBase; +const std::size_t MaxDownloadCount = 5; + + ServerInfo::ServerInfo() - : ServerInfo({}, false, {}, 0, 0, 0.0) + : ServerInfo({}, false, {}, 0, {}) { } ServerInfo::ServerInfo( QString name, bool premium, QDate last, int preferred, - int count, double speed) : + SpeedList lastDownloads) : m_name(std::move(name)), m_premium(premium), m_lastSeen(std::move(last)), - m_preferred(preferred), m_downloadCount(count), m_downloadSpeed(speed) + m_preferred(preferred), m_lastDownloads(std::move(lastDownloads)) { + if (m_lastDownloads.size() > MaxDownloadCount) { + m_lastDownloads.resize(MaxDownloadCount); + } } const QString& ServerInfo::name() const @@ -26,29 +32,77 @@ bool ServerInfo::isPremium() const return m_premium; } +void ServerInfo::setPremium(bool b) +{ + m_premium = b; +} + const QDate& ServerInfo::lastSeen() const { return m_lastSeen; } +void ServerInfo::updateLastSeen() +{ + m_lastSeen = QDate::currentDate(); +} + int ServerInfo::preferred() const { return m_preferred; } -int ServerInfo::downloadCount() const +void ServerInfo::setPreferred(int i) { - return m_downloadCount; + m_preferred = i; } -double ServerInfo::downloadSpeed() const +const ServerInfo::SpeedList& ServerInfo::lastDownloads() const { - return m_downloadSpeed; + return m_lastDownloads; } -void ServerInfo::setPreferred(int i) +int ServerInfo::averageSpeed() const { - m_preferred = i; + int count = 0; + int total = 0; + + for (const auto& s : m_lastDownloads) { + if (s > 0) { + ++count; + total += s; + } + } + + if (count > 0) { + return static_cast(total) / count; + } + + return 0; +} + +void ServerInfo::addDownload(int bytesPerSecond) +{ + if (bytesPerSecond <= 0) { + log::error( + "trying to add download with {} B/s to server '{}'; ignoring", + bytesPerSecond, m_name); + + return; + } + + if (m_lastDownloads.size() == MaxDownloadCount) { + std::rotate( + m_lastDownloads.begin(), + m_lastDownloads.begin() + 1, + m_lastDownloads.end()); + + m_lastDownloads.back() = bytesPerSecond; + } else { + m_lastDownloads.push_back(bytesPerSecond); + } + + log::debug("added download at {} B/s to server '{}'", bytesPerSecond, m_name); } diff --git a/src/serverinfo.h b/src/serverinfo.h index c6e3b640..af8f77c8 100644 --- a/src/serverinfo.h +++ b/src/serverinfo.h @@ -8,27 +8,34 @@ class ServerInfo { public: + using SpeedList = std::vector; + ServerInfo(); ServerInfo( QString name, bool premium, QDate lastSeen, int preferred, - int downloadCount, double downloadSpeed); + SpeedList lastDownloads); const QString& name() const; + bool isPremium() const; + void setPremium(bool b); + const QDate& lastSeen() const; - int preferred() const; - int downloadCount() const; - double downloadSpeed() const; + void updateLastSeen(); + int preferred() const; void setPreferred(int i); + const SpeedList& lastDownloads() const; + int averageSpeed() const; + void addDownload(int bytesPerSecond); + private: QString m_name; bool m_premium; QDate m_lastSeen; int m_preferred; - int m_downloadCount; - double m_downloadSpeed; + SpeedList m_lastDownloads; }; Q_DECLARE_METATYPE(ServerInfo) @@ -37,7 +44,7 @@ Q_DECLARE_METATYPE(ServerInfo) class ServerList { public: - using container = QList; + using container = std::vector; using iterator = container::iterator; using const_iterator = container::const_iterator; diff --git a/src/settings.cpp b/src/settings.cpp index 3a7bda75..b11bc61c 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -850,21 +850,21 @@ void Settings::setLanguage(const QString& name) m_Settings.setValue("Settings/language", name); } -void Settings::setDownloadSpeed(const QString &serverName, int bytesPerSecond) +void Settings::setDownloadSpeed(const QString& name, int bytesPerSecond) { - m_Settings.beginGroup("Servers"); + auto servers = getServers(); - for (const QString &serverKey : m_Settings.childKeys()) { - QVariantMap data = m_Settings.value(serverKey).toMap(); - if (serverKey == serverName) { - data["downloadCount"] = data["downloadCount"].toInt() + 1; - data["downloadSpeed"] = data["downloadSpeed"].toDouble() + static_cast(bytesPerSecond); - m_Settings.setValue(serverKey, data); + for (auto& server : servers) { + if (server.name() == name) { + server.addDownload(bytesPerSecond); + updateServers(servers); + return; } } - m_Settings.endGroup(); - m_Settings.sync(); + log::error( + "server '{}' not found while trying to add a download with bps {}", + name, bytesPerSecond); } ServerList Settings::getServers() const @@ -885,6 +885,7 @@ ServerList Settings::getServers() const return getServersFromOldMap(); } + // post 2.2.1 format, array of values ServerList list; @@ -893,13 +894,22 @@ ServerList Settings::getServers() const for (int i=0; i 0) { + lastDownloads.push_back(bytesPerSecond); + } + } + ServerInfo server( m_Settings.value("name").toString(), m_Settings.value("premium").toBool(), QDate::fromString(m_Settings.value("lastSeen").toString(), Qt::ISODate), m_Settings.value("preferred").toInt(), - m_Settings.value("downloadCount").toInt(), - m_Settings.value("downloadSpeed").toDouble()); + lastDownloads); list.add(std::move(server)); } @@ -925,8 +935,10 @@ ServerList Settings::getServersFromOldMap() const data["premium"].toBool(), data["lastSeen"].toDate(), data["preferred"].toInt(), - data["downloadCount"].toInt(), - data["downloadSpeed"].toDouble()); + {}); + + // ignoring download count and speed, it's now a list of values instead of + // a total list.add(std::move(server)); } @@ -955,8 +967,15 @@ void Settings::updateServers(ServerList servers) m_Settings.setValue("premium", server.isPremium()); m_Settings.setValue("lastSeen", server.lastSeen().toString(Qt::ISODate)); m_Settings.setValue("preferred", server.preferred()); - m_Settings.setValue("downloadCount", server.downloadCount()); - m_Settings.setValue("downloadSpeed", server.downloadSpeed()); + + QString lastDownloads; + for (const auto& speed : server.lastDownloads()) { + if (speed > 0) { + lastDownloads += QString("%1 ").arg(speed); + } + } + + m_Settings.setValue("lastDownloads", lastDownloads.trimmed()); ++i; } diff --git a/src/settingsdialognexus.cpp b/src/settingsdialognexus.cpp index 926ea9a6..f2bd3ab5 100644 --- a/src/settingsdialognexus.cpp +++ b/src/settingsdialognexus.cpp @@ -87,9 +87,9 @@ NexusSettingsTab::NexusSettingsTab(Settings& s, SettingsDialog& d) descriptor += QStringLiteral(" (automatic)"); } - if (server.downloadSpeed() > 0 && server.downloadCount() > 0) { - const int bps = static_cast(server.downloadSpeed() / server.downloadCount()); - descriptor += QString(" (%1 kbps)").arg(bps / 1024); + const auto averageSpeed = server.averageSpeed(); + if (averageSpeed > 0) { + descriptor += QString(" (%1 kbps)").arg(averageSpeed / 1024); } QListWidgetItem *newItem = new ServerItem(descriptor, Qt::UserRole + 1); -- cgit v1.3.1 From 2eee72da6815f9d5c643b58c95f633e69da5150a Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 26 Aug 2019 04:21:06 -0400 Subject: moved code for byte sizes and speed to uibase added scoped classes for QSettings groups and arrays servers logged on startup --- src/downloadlist.cpp | 21 +--- src/downloadlist.h | 2 - src/downloadmanager.cpp | 19 +--- src/settings.cpp | 270 +++++++++++++++++++++++++++++--------------- src/settingsdialognexus.cpp | 2 +- 5 files changed, 184 insertions(+), 130 deletions(-) (limited to 'src/settingsdialognexus.cpp') diff --git a/src/downloadlist.cpp b/src/downloadlist.cpp index 36bc2b7f..6957f270 100644 --- a/src/downloadlist.cpp +++ b/src/downloadlist.cpp @@ -19,6 +19,7 @@ along with Mod Organizer. If not, see . #include "downloadlist.h" #include "downloadmanager.h" +#include #include #include #include @@ -121,7 +122,7 @@ QVariant DownloadList::data(const QModelIndex &index, int role) const return QString("%1").arg(m_Manager->getModID(index.row())); } } - case COL_SIZE: return sizeFormat(m_Manager->getFileSize(index.row())); + case COL_SIZE: return MOBase::localizedByteSize(m_Manager->getFileSize(index.row())); case COL_FILETIME: return m_Manager->getFileTime(index.row()); case COL_STATUS: switch (m_Manager->getState(index.row())) { @@ -195,21 +196,3 @@ void DownloadList::update(int row) else log::error("invalid row {} in download list, update failed", row); } - -QString DownloadList::sizeFormat(quint64 size) const -{ - qreal calc = size; - QStringList list; - list << "MB" << "GB" << "TB"; - - QStringListIterator i(list); - QString unit("KB"); - - calc /= 1024.0; - while (calc >= 1024.0 && i.hasNext()) { - unit = i.next(); - calc /= 1024.0; - } - - return QString().setNum(calc, 'f', 2) + " " + unit; -} diff --git a/src/downloadlist.h b/src/downloadlist.h index 6f63f0c8..51ab4541 100644 --- a/src/downloadlist.h +++ b/src/downloadlist.h @@ -99,8 +99,6 @@ private: DownloadManager *m_Manager; bool m_MetaDisplay; - - QString sizeFormat(quint64 size) const; }; #endif // DOWNLOADLIST_H diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 93ca1608..a5dc164c 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -1441,22 +1441,11 @@ void DownloadManager::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) std::get<4>(info->m_SpeedDiff) = ((calc*0.5) + (std::get<4>(info->m_SpeedDiff)*1.5)) / 2; // calculate the download speed - double speed = (std::get<4>(info->m_SpeedDiff) * 1000.0) / (5 * 1000); + const double speed = (std::get<4>(info->m_SpeedDiff) * 1000.0) / (5 * 1000); - QString unit; - if (speed < 1000) { - unit = "B/s"; - } - else if (speed < 1000*1024) { - speed /= 1024; - unit = "KB/s"; - } - else { - speed /= 1024 * 1024; - unit = "MB/s"; - } - - info->m_Progress.second = QString::fromLatin1("%1% - %2 %3").arg(info->m_Progress.first).arg(QString::number(speed, 'f', 1)).arg(unit); + info->m_Progress.second = QString::fromLatin1("%1% - %2") + .arg(info->m_Progress.first) + .arg(MOBase::localizedByteSpeed(speed)); TaskProgressManager::instance().updateProgress(info->m_TaskProgressId, bytesReceived, bytesTotal); emit update(index); diff --git a/src/settings.cpp b/src/settings.cpp index b11bc61c..406544f7 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -27,6 +27,78 @@ along with Mod Organizer. If not, see . using namespace MOBase; +class ScopedGroup +{ +public: + ScopedGroup(QSettings& s, const QString& name) + : m_settings(s) + { + m_settings.beginGroup(name); + } + + ~ScopedGroup() + { + m_settings.endGroup(); + } + + ScopedGroup(const ScopedGroup&) = delete; + ScopedGroup& operator=(const ScopedGroup&) = delete; + +private: + QSettings& m_settings; +}; + + +class ScopedReadArray +{ +public: + ScopedReadArray(QSettings& s, const QString& name) + : m_settings(s), m_count(0) + { + m_count = m_settings.beginReadArray(name); + } + + ~ScopedReadArray() + { + m_settings.endArray(); + } + + ScopedReadArray(const ScopedReadArray&) = delete; + ScopedReadArray& operator=(const ScopedReadArray&) = delete; + + int count() const + { + return m_count; + } + +private: + QSettings& m_settings; + int m_count; +}; + + +class ScopedWriteArray +{ +public: + ScopedWriteArray(QSettings& s, const QString& name) + : m_settings(s) + { + m_settings.beginWriteArray(name); + } + + ~ScopedWriteArray() + { + m_settings.endArray(); + } + + ScopedWriteArray(const ScopedWriteArray&) = delete; + ScopedWriteArray& operator=(const ScopedWriteArray&) = delete; + +private: + QSettings& m_settings; +}; + + template std::optional getOptional( const QSettings& s, const QString& name, std::optional def={}) @@ -206,19 +278,21 @@ void Settings::processUpdates( } if (lastVersion < QVersionNumber(2, 2, 0)) { - m_Settings.beginGroup("Settings"); - m_Settings.remove("steam_password"); - m_Settings.remove("nexus_username"); - m_Settings.remove("nexus_password"); - m_Settings.remove("nexus_login"); - m_Settings.remove("nexus_api_key"); - m_Settings.remove("ask_for_nexuspw"); - m_Settings.remove("nmm_version"); - m_Settings.endGroup(); - - m_Settings.beginGroup("Servers"); - m_Settings.remove(""); - m_Settings.endGroup(); + { + ScopedGroup sg(m_Settings, "Settings"); + m_Settings.remove("steam_password"); + m_Settings.remove("nexus_username"); + m_Settings.remove("nexus_password"); + m_Settings.remove("nexus_login"); + m_Settings.remove("nexus_api_key"); + m_Settings.remove("ask_for_nexuspw"); + m_Settings.remove("nmm_version"); + } + + { + ScopedGroup sg(m_Settings, "Servers"); + m_Settings.remove(""); + } } if (lastVersion < QVersionNumber(2, 2, 1)) { @@ -251,12 +325,12 @@ void Settings::clearPlugins() m_PluginSettings.clear(); m_PluginBlacklist.clear(); - int count = m_Settings.beginReadArray("pluginBlacklist"); - for (int i = 0; i < count; ++i) { + + ScopedReadArray sra(m_Settings, "pluginBlacklist"); + for (int i = 0; i < sra.count(); ++i) { m_Settings.setArrayIndex(i); m_PluginBlacklist.insert(m_Settings.value("name").toString()); } - m_Settings.endArray(); } bool Settings::pluginBlacklisted(const QString &fileName) const @@ -876,46 +950,50 @@ ServerList Settings::getServers() const // in 2.2.1, one key per server is returned // getting the keys - m_Settings.beginGroup("Servers"); - const auto keys = m_Settings.childKeys(); - m_Settings.endGroup(); + QStringList keys; + + { + ScopedGroup sg(m_Settings, "Servers"); + keys = m_Settings.childKeys(); + } if (!keys.empty() && keys[0] != "size") { // old format return getServersFromOldMap(); } + // post 2.2.1 format, array of values ServerList list; - const int size = m_Settings.beginReadArray("Servers"); + { + ScopedReadArray sra(m_Settings, "Servers"); - for (int i=0; i 0) { - lastDownloads.push_back(bytesPerSecond); + const auto lastDownloadsString = m_Settings.value("lastDownloads").toString(); + for (const auto& s : lastDownloadsString.split(" ")) { + const auto bytesPerSecond = s.toInt(); + if (bytesPerSecond > 0) { + lastDownloads.push_back(bytesPerSecond); + } } - } - ServerInfo server( - m_Settings.value("name").toString(), - m_Settings.value("premium").toBool(), - QDate::fromString(m_Settings.value("lastSeen").toString(), Qt::ISODate), - m_Settings.value("preferred").toInt(), - lastDownloads); + ServerInfo server( + m_Settings.value("name").toString(), + m_Settings.value("premium").toBool(), + QDate::fromString(m_Settings.value("lastSeen").toString(), Qt::ISODate), + m_Settings.value("preferred").toInt(), + lastDownloads); - list.add(std::move(server)); + list.add(std::move(server)); + } } - m_Settings.endArray(); - return list; } @@ -924,8 +1002,7 @@ ServerList Settings::getServersFromOldMap() const // for 2.2.1 and before ServerList list; - - m_Settings.beginGroup("Servers"); + ScopedGroup sg(m_Settings, "Servers"); for (const QString &serverKey : m_Settings.childKeys()) { QVariantMap data = m_Settings.value(serverKey).toMap(); @@ -943,8 +1020,6 @@ ServerList Settings::getServersFromOldMap() const list.add(std::move(server)); } - m_Settings.endGroup(); - return list; } @@ -953,34 +1028,35 @@ void Settings::updateServers(ServerList servers) // clean up unavailable servers servers.cleanup(); - m_Settings.beginGroup("Servers"); - m_Settings.remove(""); - m_Settings.endGroup(); - - m_Settings.beginWriteArray("Servers"); - - int i=0; - for (const auto& server : servers) { - m_Settings.setArrayIndex(i); - - m_Settings.setValue("name", server.name()); - m_Settings.setValue("premium", server.isPremium()); - m_Settings.setValue("lastSeen", server.lastSeen().toString(Qt::ISODate)); - m_Settings.setValue("preferred", server.preferred()); + { + ScopedGroup sg(m_Settings, "Servers"); + m_Settings.remove(""); + } - QString lastDownloads; - for (const auto& speed : server.lastDownloads()) { - if (speed > 0) { - lastDownloads += QString("%1 ").arg(speed); + { + ScopedWriteArray swa(m_Settings, "Servers"); + + int i=0; + for (const auto& server : servers) { + m_Settings.setArrayIndex(i); + + m_Settings.setValue("name", server.name()); + m_Settings.setValue("premium", server.isPremium()); + m_Settings.setValue("lastSeen", server.lastSeen().toString(Qt::ISODate)); + m_Settings.setValue("preferred", server.preferred()); + + QString lastDownloads; + for (const auto& speed : server.lastDownloads()) { + if (speed > 0) { + lastDownloads += QString("%1 ").arg(speed); + } } - } - m_Settings.setValue("lastDownloads", lastDownloads.trimmed()); + m_Settings.setValue("lastDownloads", lastDownloads.trimmed()); - ++i; + ++i; + } } - - m_Settings.endArray(); } void Settings::addBlacklistPlugin(const QString &fileName) @@ -992,23 +1068,22 @@ void Settings::addBlacklistPlugin(const QString &fileName) void Settings::writePluginBlacklist() { m_Settings.remove("pluginBlacklist"); - m_Settings.beginWriteArray("pluginBlacklist"); + + ScopedWriteArray swa(m_Settings, "pluginBlacklist"); int idx = 0; for (const QString &plugin : m_PluginBlacklist) { m_Settings.setArrayIndex(idx++); m_Settings.setValue("name", plugin); } - - m_Settings.endArray(); } std::map Settings::getRecentDirectories() const { std::map map; - const int size = m_Settings.beginReadArray("recentDirectories"); + ScopedReadArray sra(m_Settings, "recentDirectories"); - for (int i=0; i Settings::getRecentDirectories() const } } - m_Settings.endArray(); - return map; } void Settings::setRecentDirectories(const std::map& map) { m_Settings.remove("recentDirectories"); - m_Settings.beginWriteArray("recentDirectories"); + + ScopedWriteArray swa(m_Settings, "recentDirectories"); int index = 0; for (auto&& p : map) { @@ -1037,16 +1111,14 @@ void Settings::setRecentDirectories(const std::map& map) ++index; } - - m_Settings.endArray(); } std::vector> Settings::getExecutables() const { - const int count = m_Settings.beginReadArray("customExecutables"); + ScopedReadArray sra(m_Settings, "customExecutables"); std::vector> v; - for (int i=0; i map; @@ -1059,15 +1131,14 @@ std::vector> Settings::getExecutables() const v.push_back(map); } - m_Settings.endArray(); - return v; } void Settings::setExecutables(const std::vector>& v) { m_Settings.remove("customExecutables"); - m_Settings.beginWriteArray("customExecutables"); + + ScopedWriteArray swa(m_Settings, "customExecutables"); int i = 0; @@ -1080,8 +1151,6 @@ void Settings::setExecutables(const std::vector>& v) ++i; } - - m_Settings.endArray(); } bool Settings::isTutorialCompleted(const QString& windowName) const @@ -1154,9 +1223,8 @@ void Settings::setQuestionFileButton( void Settings::resetQuestionButtons() { - m_Settings.beginGroup("DialogChoices"); + ScopedGroup sg(m_Settings, "DialogChoices"); m_Settings.remove(""); - m_Settings.endGroup(); } std::optional Settings::getIndex(const QComboBox* cb) const @@ -1248,17 +1316,34 @@ void Settings::dump() const log::debug("settings:"); - m_Settings.beginGroup("Settings"); + { + ScopedGroup sg(m_Settings, "Settings"); - for (auto k : m_Settings.allKeys()) { - if (ignore.contains(k, Qt::CaseInsensitive)) { - continue; - } + for (auto k : m_Settings.allKeys()) { + if (ignore.contains(k, Qt::CaseInsensitive)) { + continue; + } - log::debug(" . {}={}", k, m_Settings.value(k).toString()); + log::debug(" . {}={}", k, m_Settings.value(k).toString()); + } } - m_Settings.endGroup(); + log::debug("servers:"); + + for (const auto& server : getServers()) { + QString lastDownloads; + for (auto speed : server.lastDownloads()) { + lastDownloads += QString("%1 ").arg(speed); + } + + log::debug( + " . {} premium={} lastSeen={} preferred={} lastDownloads={}", + server.name(), + server.isPremium() ? "yes" : "no", + server.lastSeen().toString(Qt::ISODate), + server.preferred(), + lastDownloads.trimmed()); + } } @@ -1278,9 +1363,8 @@ void GeometrySettings::resetIfNeeded() return; } - m_Settings.beginGroup("geometry"); + ScopedGroup sg(m_Settings, "geometry"); m_Settings.remove(""); - m_Settings.endGroup(); } void GeometrySettings::saveGeometry(const QWidget* w) diff --git a/src/settingsdialognexus.cpp b/src/settingsdialognexus.cpp index f2bd3ab5..3de1a6ba 100644 --- a/src/settingsdialognexus.cpp +++ b/src/settingsdialognexus.cpp @@ -89,7 +89,7 @@ NexusSettingsTab::NexusSettingsTab(Settings& s, SettingsDialog& d) const auto averageSpeed = server.averageSpeed(); if (averageSpeed > 0) { - descriptor += QString(" (%1 kbps)").arg(averageSpeed / 1024); + descriptor += QString(" (%1)").arg(MOBase::localizedByteSpeed(averageSpeed)); } QListWidgetItem *newItem = new ServerItem(descriptor, Qt::UserRole + 1); -- cgit v1.3.1 From ec3fb7b3509fb10a8a1392740e209509ae6c092c Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 2 Sep 2019 12:56:50 -0400 Subject: removed duplicate useProxy() use dedicated functions to set, get or remove settings, allows for logging --- src/loadmechanism.cpp | 13 + src/loadmechanism.h | 2 + src/mainwindow.cpp | 12 +- src/settings.cpp | 852 +++++++++++++++++++++++++++----------------- src/settings.h | 16 +- src/settingsdialognexus.cpp | 2 +- 6 files changed, 556 insertions(+), 341 deletions(-) (limited to 'src/settingsdialognexus.cpp') diff --git a/src/loadmechanism.cpp b/src/loadmechanism.cpp index 06e9f201..0c81b7b2 100644 --- a/src/loadmechanism.cpp +++ b/src/loadmechanism.cpp @@ -49,3 +49,16 @@ void LoadMechanism::activate(EMechanism) { // no-op } + + +QString toString(LoadMechanism::EMechanism e) +{ + switch (e) + { + case LoadMechanism::LOAD_MODORGANIZER: + return "ModOrganizer"; + + default: + return QString("unknown (%1)").arg(static_cast(e)); + } +} diff --git a/src/loadmechanism.h b/src/loadmechanism.h index 49eb0c52..151e804f 100644 --- a/src/loadmechanism.h +++ b/src/loadmechanism.h @@ -56,4 +56,6 @@ private: }; +QString toString(LoadMechanism::EMechanism e); + #endif // LOADMECHANISM_H diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 4c2594b8..42b19cb7 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2157,10 +2157,8 @@ void MainWindow::readSettings(const Settings& settings) ui->displayCategoriesBtn->setChecked(v); } - if (auto v=settings.getUseProxy()) { - if (*v) { - activateProxy(true); - } + if (settings.getUseProxy()) { + activateProxy(true); } } @@ -5014,7 +5012,7 @@ void MainWindow::on_actionSettings_triggered() QString oldProfilesDirectory(settings.getProfileDirectory()); QString oldManagedGameDirectory(settings.getManagedGameDirectory().value_or("")); bool oldDisplayForeign(settings.displayForeign()); - bool proxy = settings.useProxy(); + bool proxy = settings.getUseProxy(); DownloadManager *dlManager = m_OrganizerCore.downloadManager(); @@ -5081,8 +5079,8 @@ void MainWindow::on_actionSettings_triggered() NexusInterface::instance(&m_PluginContainer)->setCacheDirectory(settings.getCacheDirectory()); } - if (proxy != settings.useProxy()) { - activateProxy(settings.useProxy()); + if (proxy != settings.getUseProxy()) { + activateProxy(settings.getUseProxy()); } ui->statusBar->checkSettings(m_OrganizerCore.settings()); diff --git a/src/settings.cpp b/src/settings.cpp index f6be8ba0..71288950 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -27,13 +27,164 @@ along with Mod Organizer. If not, see . using namespace MOBase; +template +struct ValueConverter +{ + static const T& convert(const T& t) + { + return t; + } +}; + +template +struct ValueConverter>> +{ + static QString convert(const T& t) + { + return QString("%1").arg(static_cast>(t)); + } +}; + + +template +void logChange( + const QString& displayName, std::optional oldValue, const T& newValue) +{ + using VC = ValueConverter; + + if (oldValue) { + log::debug( + "setting '{}' changed from '{}' to '{}'", + displayName, VC::convert(*oldValue), VC::convert(newValue)); + } else { + log::debug( + "setting '{}' set to '{}'", + displayName, VC::convert(newValue)); + } +} + +void logRemoval(const QString& name) +{ + log::debug("setting '{}' removed", name); +} + + +QString settingName(const QString& section, const QString& key) +{ + if (section.isEmpty()) { + return key; + } else if (key.isEmpty()) { + return section; + } else { + if (section.compare("General", Qt::CaseInsensitive) == 0) { + return key; + } else { + return section + "/" + key; + } + } +} + +template +void setImpl( + QSettings& settings, const QString& displayName, + const QString& section, const QString& key, const T& value) +{ + const auto current = getOptional(settings, section, key); + + if (current && *current == value) { + // no change + return; + } + + const auto name = settingName(section, key); + + logChange(displayName, current, value); + + if constexpr (std::is_enum_v) { + settings.setValue( + name, static_cast>(value)); + } else { + settings.setValue(name, value); + } +} + +void removeImpl( + QSettings& settings, const QString& displayName, + const QString& section, const QString& key) +{ + if (key.isEmpty()) { + if (!settings.childGroups().contains(section, Qt::CaseInsensitive)) { + // not there + return; + } + } else { + if (!settings.contains(settingName(section, key))) { + // not there + return; + } + } + + logRemoval(displayName); + settings.remove(settingName(section, key)); +} + + +template +std::optional getOptional( + const QSettings& settings, + const QString& section, const QString& key, std::optional def={}) +{ + if (settings.contains(settingName(section, key))) { + const auto v = settings.value(settingName(section, key)); + + if constexpr (std::is_enum_v) { + return static_cast(v.value>()); + } else { + return v.value(); + } + } + + return def; +} + +template +T get( + const QSettings& settings, + const QString& section, const QString& key, T def={}) +{ + if (auto v=getOptional(settings, section, key)) { + return *v; + } else { + return def; + } +} + +template +void set( + QSettings& settings, + const QString& section, const QString& key, const T& value) +{ + setImpl(settings, settingName(section, key), section, key, value); +} + +void remove(QSettings& settings, const QString& section, const QString& key) +{ + removeImpl(settings, settingName(section, key), section, key); +} + +void removeSection(QSettings& settings, const QString& section) +{ + removeImpl(settings, section, section, ""); +} + + class ScopedGroup { public: ScopedGroup(QSettings& s, const QString& name) - : m_settings(s) + : m_settings(s), m_name(name) { - m_settings.beginGroup(name); + m_settings.beginGroup(m_name); } ~ScopedGroup() @@ -44,18 +195,55 @@ public: ScopedGroup(const ScopedGroup&) = delete; ScopedGroup& operator=(const ScopedGroup&) = delete; + template + void set(const QString& key, const T& value) + { + setImpl(m_settings, settingName(m_name, key), "", key, value); + } + + void remove(const QString& key) + { + removeImpl(m_settings, settingName(m_name, key), "", key); + } + + QStringList keys() const + { + return m_settings.childKeys(); + } + + template + void for_each(F&& f) const + { + for (const QString& key : keys()) { + f(key); + } + } + + template + std::optional getOptional(const QString& key, std::optional def={}) const + { + return ::getOptional(m_settings, "", key, def); + } + + template + T get(const QString& key, T def={}) const + { + return ::get(m_settings, "", key, def); + } + private: QSettings& m_settings; + QString m_name; }; class ScopedReadArray { public: - ScopedReadArray(QSettings& s, const QString& name) + ScopedReadArray(QSettings& s, const QString& section) : m_settings(s), m_count(0) { - m_count = m_settings.beginReadArray(name); + m_count = m_settings.beginReadArray(section); } ~ScopedReadArray() @@ -66,11 +254,37 @@ public: ScopedReadArray(const ScopedReadArray&) = delete; ScopedReadArray& operator=(const ScopedReadArray&) = delete; + template + void for_each(F&& f) const + { + for (int i=0; i + std::optional getOptional(const QString& key, std::optional def={}) const + { + return ::getOptional(m_settings, "", key, def); + } + + template + T get(const QString& key, T def={}) const + { + return ::get(m_settings, "", key, def); + } + int count() const { return m_count; } + QStringList keys() const + { + return m_settings.childKeys(); + } + private: QSettings& m_settings; int m_count; @@ -80,10 +294,10 @@ private: class ScopedWriteArray { public: - ScopedWriteArray(QSettings& s, const QString& name) - : m_settings(s) + ScopedWriteArray(QSettings& s, const QString& section) + : m_settings(s), m_section(section), m_i(0) { - m_settings.beginWriteArray(name); + m_settings.beginWriteArray(section); } ~ScopedWriteArray() @@ -94,27 +308,28 @@ public: ScopedWriteArray(const ScopedWriteArray&) = delete; ScopedWriteArray& operator=(const ScopedWriteArray&) = delete; -private: - QSettings& m_settings; -}; - + void next() + { + m_settings.setArrayIndex(m_i); + ++m_i; + } -template -std::optional getOptional( - const QSettings& s, const QString& name, std::optional def={}) -{ - if (s.contains(name)) { - const auto v = s.value(name); + template + void set(const QString& key, const T& value) + { + const auto displayName = QString("%1/%2\\%3") + .arg(m_section) + .arg(m_i) + .arg(key); - if constexpr (std::is_enum_v) { - return static_cast(v.value>()); - } else { - return v.value(); - } + setImpl(m_settings, displayName, "", key, value); } - return def; -} +private: + QSettings& m_settings; + QString m_section; + int m_i; +}; EndorsementState endorsementStateFromString(const QString& s) @@ -132,15 +347,15 @@ QString toString(EndorsementState s) { switch (s) { - case EndorsementState::Accepted: - return "Endorsed"; + case EndorsementState::Accepted: + return "Endorsed"; - case EndorsementState::Refused: - return "Abstained"; + case EndorsementState::Refused: + return "Abstained"; - case EndorsementState::NoDecision: // fall-through - default: - return {}; + case EndorsementState::NoDecision: // fall-through + default: + return {}; } } @@ -198,24 +413,24 @@ QString widgetName(const QWidget* w) template QString geoSettingName(const Widget* widget) { - return "geometry/" + widgetName(widget) + "_geometry"; + return widgetName(widget) + "_geometry"; } template QString stateSettingName(const Widget* widget) { - return "geometry/" + widgetName(widget) + "_state"; + return widgetName(widget) + "_state"; } template QString visibilitySettingName(const Widget* widget) { - return "geometry/" + widgetName(widget) + "_visibility"; + return widgetName(widget) + "_visibility"; } QString dockSettingName(const QDockWidget* dock) { - return "geometry/MainWindow_docks_" + dock->objectName() + "_size"; + return "MainWindow_docks_" + dock->objectName() + "_size"; } QString indexSettingName(const QWidget* widget) @@ -278,40 +493,34 @@ void Settings::processUpdates( } if (lastVersion < QVersionNumber(2, 2, 0)) { - { - ScopedGroup sg(m_Settings, "Settings"); - m_Settings.remove("steam_password"); - m_Settings.remove("nexus_username"); - m_Settings.remove("nexus_password"); - m_Settings.remove("nexus_login"); - m_Settings.remove("nexus_api_key"); - m_Settings.remove("ask_for_nexuspw"); - m_Settings.remove("nmm_version"); - } + remove(m_Settings, "Settings", "steam_password"); + remove(m_Settings, "Settings", "nexus_username"); + remove(m_Settings, "Settings", "nexus_password"); + remove(m_Settings, "Settings", "nexus_login"); + remove(m_Settings, "Settings", "nexus_api_key"); + remove(m_Settings, "Settings", "ask_for_nexuspw"); + remove(m_Settings, "Settings", "nmm_version"); - { - ScopedGroup sg(m_Settings, "Servers"); - m_Settings.remove(""); - } + removeSection(m_Settings, "Servers"); } if (lastVersion < QVersionNumber(2, 2, 1)) { - m_Settings.remove("mod_info_tabs"); - m_Settings.remove("mod_info_conflict_expanders"); - m_Settings.remove("mod_info_conflicts"); - m_Settings.remove("mod_info_advanced_conflicts"); - m_Settings.remove("mod_info_conflicts_overwrite"); - m_Settings.remove("mod_info_conflicts_noconflict"); - m_Settings.remove("mod_info_conflicts_overwritten"); + remove(m_Settings, "General", "mod_info_tabs"); + remove(m_Settings, "General", "mod_info_conflict_expanders"); + remove(m_Settings, "General", "mod_info_conflicts"); + remove(m_Settings, "General", "mod_info_advanced_conflicts"); + remove(m_Settings, "General", "mod_info_conflicts_overwrite"); + remove(m_Settings, "General", "mod_info_conflicts_noconflict"); + remove(m_Settings, "General", "mod_info_conflicts_overwritten"); } if (lastVersion < QVersionNumber(2, 2, 2)) { // log splitter is gone, it's a dock now - m_Settings.remove("log_split"); + remove(m_Settings, "General", "log_split"); } //save version in all case - m_Settings.setValue("version", currentVersion.toString()); + set(m_Settings, "General", "version", currentVersion.toString()); } QString Settings::getFilename() const @@ -339,12 +548,12 @@ void Settings::registerAsNXMHandler(bool force) bool Settings::colorSeparatorScrollbar() const { - return m_Settings.value("Settings/colorSeparatorScrollbars", true).toBool(); + return get(m_Settings, "Settings", "colorSeparatorScrollbars", true); } void Settings::setColorSeparatorScrollbar(bool b) { - m_Settings.setValue("Settings/colorSeparatorScrollbars", b); + set(m_Settings, "Settings", "colorSeparatorScrollbars", b); } void Settings::managedGameChanged(IPluginGame const *gamePlugin) @@ -418,71 +627,69 @@ QColor Settings::getIdealTextColor(const QColor& rBackgroundColor) bool Settings::hideUncheckedPlugins() const { - return m_Settings.value("Settings/hide_unchecked_plugins", false).toBool(); + return get(m_Settings, "Settings", "hide_unchecked_plugins", false); } void Settings::setHideUncheckedPlugins(bool b) { - m_Settings.setValue("Settings/hide_unchecked_plugins", b); + set(m_Settings, "Settings", "hide_unchecked_plugins", b); } bool Settings::forceEnableCoreFiles() const { - return m_Settings.value("Settings/force_enable_core_files", true).toBool(); + return get(m_Settings, "Settings", "force_enable_core_files", true); } void Settings::setForceEnableCoreFiles(bool b) { - m_Settings.setValue("Settings/force_enable_core_files", b); + set(m_Settings, "Settings", "force_enable_core_files", b); } bool Settings::lockGUI() const { - return m_Settings.value("Settings/lock_gui", true).toBool(); + return get(m_Settings, "Settings", "lock_gui", true); } void Settings::setLockGUI(bool b) { - m_Settings.setValue("Settings/lock_gui", b); + set(m_Settings, "Settings", "lock_gui", b); } bool Settings::automaticLoginEnabled() const { - return m_Settings.value("Settings/nexus_login", false).toBool(); + return get(m_Settings, "Settings", "nexus_login", false); } QString Settings::getSteamAppID() const { - return m_Settings.value("Settings/app_id", m_GamePlugin->steamAPPId()).toString(); + return get(m_Settings, "Settings", "app_id", m_GamePlugin->steamAPPId()); } void Settings::setSteamAppID(const QString& id) { if (id.isEmpty()) { - m_Settings.remove("Settings/app_id"); + remove(m_Settings, "Settings", "app_id"); } else { - m_Settings.setValue("Settings/app_id", id); + set(m_Settings, "Settings", "app_id", id); } } bool Settings::usePrereleases() const { - return m_Settings.value("Settings/use_prereleases", false).toBool(); + return get(m_Settings, "Settings", "use_prereleases", false); } void Settings::setUsePrereleases(bool b) { - m_Settings.setValue("Settings/use_prereleases", b); + set(m_Settings, "Settings", "use_prereleases", b); } QString Settings::getConfigurablePath(const QString &key, - const QString &def, - bool resolve) const + const QString &def, + bool resolve) const { - const QString settingName = "Settings/" + key; - QString result = QDir::fromNativeSeparators( - m_Settings.value(settingName, QString("%BASE_DIR%/") + def).toString()); + get(m_Settings, "Settings", key, QString("%BASE_DIR%/") + def)); if (resolve) { result.replace("%BASE_DIR%", getBaseDirectory()); @@ -493,20 +700,17 @@ QString Settings::getConfigurablePath(const QString &key, void Settings::setConfigurablePath(const QString &key, const QString& path) { - const QString settingName = "Settings/" + key; - if (path.isEmpty()) { - m_Settings.remove(settingName); + remove(m_Settings, "Settings", key); } else { - m_Settings.setValue(settingName, path); + set(m_Settings, "Settings", key, path); } } QString Settings::getBaseDirectory() const { - return QDir::fromNativeSeparators(m_Settings.value( - "settings/base_directory", - qApp->property("dataPath").toString()).toString()); + return QDir::fromNativeSeparators(get(m_Settings, + "Settings", "base_directory", qApp->property("dataPath").toString())); } QString Settings::getDownloadDirectory(bool resolve) const @@ -552,9 +756,9 @@ QString Settings::getOverwriteDirectory(bool resolve) const void Settings::setBaseDirectory(const QString& path) { if (path.isEmpty()) { - m_Settings.remove("Settings/base_directory"); + remove(m_Settings, "Settings", "base_directory"); } else { - m_Settings.setValue("Settings/base_directory", path); + set(m_Settings, "Settings", "base_directory", path); } } @@ -585,7 +789,7 @@ void Settings::setOverwriteDirectory(const QString& path) std::optional Settings::getManagedGameDirectory() const { - if (auto v=getOptional(m_Settings, "gamePath")) { + if (auto v=getOptional(m_Settings, "General", "gamePath")) { return QString::fromUtf8(*v); } @@ -594,32 +798,32 @@ std::optional Settings::getManagedGameDirectory() const void Settings::setManagedGameDirectory(const QString& path) { - m_Settings.setValue("gamePath", QDir::toNativeSeparators(path).toUtf8()); + set(m_Settings, "General", "gamePath", QDir::toNativeSeparators(path).toUtf8()); } std::optional Settings::getManagedGameName() const { - return getOptional(m_Settings, "gameName"); + return getOptional(m_Settings, "General", "gameName"); } void Settings::setManagedGameName(const QString& name) { - m_Settings.setValue("gameName", name); + set(m_Settings, "General", "gameName", name); } std::optional Settings::getManagedGameEdition() const { - return getOptional(m_Settings, "game_edition"); + return getOptional(m_Settings, "General", "game_edition"); } void Settings::setManagedGameEdition(const QString& name) { - m_Settings.setValue("game_edition", name); + set(m_Settings, "General", "game_edition", name); } std::optional Settings::getSelectedProfileName() const { - if (auto v=getOptional(m_Settings, "selected_profile")) { + if (auto v=getOptional(m_Settings, "General", "selected_profile")) { return QString::fromUtf8(*v); } @@ -628,32 +832,32 @@ std::optional Settings::getSelectedProfileName() const void Settings::setSelectedProfileName(const QString& name) { - m_Settings.setValue("selected_profile", name.toUtf8()); + set(m_Settings, "General", "selected_profile", name.toUtf8()); } std::optional Settings::getStyleName() const { - return getOptional(m_Settings, "Settings/style"); + return getOptional(m_Settings, "Settings", "style"); } void Settings::setStyleName(const QString& name) { - m_Settings.setValue("Settings/style", name); + set(m_Settings, "Settings", "style", name); } -std::optional Settings::getUseProxy() const +bool Settings::getUseProxy() const { - return getOptional(m_Settings, "Settings/use_proxy"); + return get(m_Settings, "Settings", "use_proxy", false); } void Settings::setUseProxy(bool b) { - m_Settings.setValue("Settings/use_proxy", b); + set(m_Settings, "Settings", "use_proxy", b); } std::optional Settings::getVersion() const { - if (auto v=getOptional(m_Settings, "version")) { + if (auto v=getOptional(m_Settings, "General", "version")) { return QVersionNumber::fromString(*v).normalized(); } @@ -662,17 +866,17 @@ std::optional Settings::getVersion() const bool Settings::getFirstStart() const { - return getOptional(m_Settings, "first_start").value_or(true); + return get(m_Settings, "General", "first_start", true); } void Settings::setFirstStart(bool b) { - m_Settings.setValue("first_start", b); + set(m_Settings, "General", "first_start", b); } std::optional Settings::getPreviousSeparatorColor() const { - const auto c = getOptional(m_Settings, "previousSeparatorColor"); + const auto c = getOptional(m_Settings, "General", "previousSeparatorColor"); if (c && c->isValid()) { return c; } @@ -682,12 +886,12 @@ std::optional Settings::getPreviousSeparatorColor() const void Settings::setPreviousSeparatorColor(const QColor& c) const { - m_Settings.setValue("previousSeparatorColor", c); + set(m_Settings, "General", "previousSeparatorColor", c); } void Settings::removePreviousSeparatorColor() { - m_Settings.remove("previousSeparatorColor"); + remove(m_Settings, "General", "previousSeparatorColor"); } bool Settings::getNexusApiKey(QString &apiKey) const @@ -695,6 +899,7 @@ bool Settings::getNexusApiKey(QString &apiKey) const QString tempKey = deObfuscate("APIKEY"); if (tempKey.isEmpty()) return false; + apiKey = tempKey; return true; } @@ -722,7 +927,7 @@ bool Settings::hasNexusApiKey() const bool Settings::getSteamLogin(QString &username, QString &password) const { - username = m_Settings.value("Settings/steam_username", "").toString(); + username = get(m_Settings, "Settings", "steam_username", ""); password = deObfuscate("steam_password"); return !username.isEmpty() && !password.isEmpty(); @@ -730,95 +935,96 @@ bool Settings::getSteamLogin(QString &username, QString &password) const bool Settings::compactDownloads() const { - return m_Settings.value("Settings/compact_downloads", false).toBool(); + return get(m_Settings, "Settings", "compact_downloads", false); } void Settings::setCompactDownloads(bool b) { - m_Settings.setValue("Settings/compact_downloads", b); + set(m_Settings, "Settings", "compact_downloads", b); } bool Settings::metaDownloads() const { - return m_Settings.value("Settings/meta_downloads", false).toBool(); + return get(m_Settings, "Settings", "meta_downloads", false); } void Settings::setMetaDownloads(bool b) { - m_Settings.setValue("Settings/meta_downloads", b); + set(m_Settings, "Settings", "meta_downloads", b); } bool Settings::offlineMode() const { - return m_Settings.value("Settings/offline_mode", false).toBool(); + return get(m_Settings, "Settings/offline_mode", false); } void Settings::setOfflineMode(bool b) { - m_Settings.setValue("Settings/offline_mode", b); + set(m_Settings, "Settings", "offline_mode", b); } log::Levels Settings::logLevel() const { - return static_cast(m_Settings.value("Settings/log_level").toInt()); + return get(m_Settings, "Settings", "log_level", log::Levels::Info); } void Settings::setLogLevel(log::Levels level) { - m_Settings.setValue("Settings/log_level", static_cast(level)); + set(m_Settings, "Settings", "log_level", level); } CrashDumpsType Settings::crashDumpsType() const { - const auto v = getOptional(m_Settings, "Settings/crash_dumps_type"); - return v.value_or(CrashDumpsType::Mini); + return get(m_Settings, + "Settings", "crash_dumps_type", CrashDumpsType::Mini); } void Settings::setCrashDumpsType(CrashDumpsType type) { - m_Settings.setValue("Settings/crash_dumps_type", static_cast(type)); + set(m_Settings, "Settings", "crash_dumps_type", type); } int Settings::crashDumpsMax() const { - return m_Settings.value("Settings/crash_dumps_max", 5).toInt(); + return get(m_Settings, "Settings", "crash_dumps_max", 5); } void Settings::setCrashDumpsMax(int n) { - return m_Settings.setValue("Settings/crash_dumps_max", n); + set(m_Settings, "Settings", "crash_dumps_max", n); } QString Settings::executablesBlacklist() const { - return m_Settings.value("Settings/executable_blacklist", ( - QStringList() - << "Chrome.exe" - << "Firefox.exe" - << "TSVNCache.exe" - << "TGitCache.exe" - << "Steam.exe" - << "GameOverlayUI.exe" - << "Discord.exe" - << "GalaxyClient.exe" - << "Spotify.exe" - ).join(";") - ).toString(); + static const QString def = (QStringList() + << "Chrome.exe" + << "Firefox.exe" + << "TSVNCache.exe" + << "TGitCache.exe" + << "Steam.exe" + << "GameOverlayUI.exe" + << "Discord.exe" + << "GalaxyClient.exe" + << "Spotify.exe" + ).join(";"); + + return get(m_Settings, "Settings", "executable_blacklist", def); } void Settings::setExecutablesBlacklist(const QString& s) { - m_Settings.setValue("Settings/executable_blacklist", s); + set(m_Settings, "Settings", "executable_blacklist", s); } void Settings::setSteamLogin(QString username, QString password) { if (username == "") { - m_Settings.remove("Settings/steam_username"); + remove(m_Settings, "Settings", "steam_username"); password = ""; } else { - m_Settings.setValue("Settings/steam_username", username); + set(m_Settings, "Settings", "steam_username", username); } + if (!obfuscate("steam_password", password)) { const auto e = GetLastError(); log::error("Storing or deleting password failed: {}", formatSystemMessage(e)); @@ -827,26 +1033,37 @@ void Settings::setSteamLogin(QString username, QString password) LoadMechanism::EMechanism Settings::getLoadMechanism() const { - const auto i = m_Settings.value("Settings/load_mechanism").toInt(); + const auto def = LoadMechanism::LOAD_MODORGANIZER; + + const auto i = get(m_Settings, + "Settings", "load_mechanism", def); switch (i) { - case LoadMechanism::LOAD_MODORGANIZER: - return LoadMechanism::LOAD_MODORGANIZER; + // ok + case LoadMechanism::LOAD_MODORGANIZER: // fall-through + { + break; + } - default: - qCritical().nospace().noquote() - << "invalid load mechanism " << i << ", reverting to modorganizer"; + default: + { + log::error( + "invalid load mechanism {}, reverting to {}", + static_cast(i), toString(def)); - m_Settings.setValue("Settings/load_mechanism", LoadMechanism::LOAD_MODORGANIZER); + set(m_Settings, "Settings", "load_mechanism", def); - return LoadMechanism::LOAD_MODORGANIZER; + return def; } + } + + return i; } void Settings::setLoadMechanism(LoadMechanism::EMechanism m) { - m_Settings.setValue("Settings/load_mechanism", static_cast(m)); + set(m_Settings, "Settings", "load_mechanism", m); } void Settings::setupLoadMechanism() @@ -854,26 +1071,20 @@ void Settings::setupLoadMechanism() m_LoadMechanism.activate(getLoadMechanism()); } - -bool Settings::useProxy() const -{ - return m_Settings.value("Settings/use_proxy", false).toBool(); -} - bool Settings::endorsementIntegration() const { - return m_Settings.value("Settings/endorsement_integration", true).toBool(); + return get(m_Settings, "Settings", "endorsement_integration", true); } void Settings::setEndorsementIntegration(bool b) const { - m_Settings.setValue("Settings/endorsement_integration", b); + set(m_Settings, "Settings", "endorsement_integration", b); } EndorsementState Settings::endorsementState() const { - const auto v = getOptional(m_Settings, "endorse_state"); - return endorsementStateFromString(v.value_or("")); + return endorsementStateFromString( + get(m_Settings, "General", "endorse_state", "")); } void Settings::setEndorsementState(EndorsementState s) @@ -881,57 +1092,59 @@ void Settings::setEndorsementState(EndorsementState s) const auto v = toString(s); if (v.isEmpty()) { - m_Settings.remove("endorse_state"); + remove(m_Settings, "General", "endorse_state"); } else { - m_Settings.setValue("endorse_state", v); + set(m_Settings, "General", "endorse_state", v); } } bool Settings::hideAPICounter() const { - return m_Settings.value("Settings/hide_api_counter", false).toBool(); + return get(m_Settings, "Settings", "hide_api_counter", false); } void Settings::setHideAPICounter(bool b) { - m_Settings.setValue("Settings/hide_api_counter", b); + set(m_Settings, "Settings", "hide_api_counter", b); } bool Settings::displayForeign() const { - return m_Settings.value("Settings/display_foreign", true).toBool(); + return get(m_Settings, "Settings", "display_foreign", true); } void Settings::setDisplayForeign(bool b) { - m_Settings.setValue("Settings/display_foreign", b); + set(m_Settings, "Settings", "display_foreign", b); } void Settings::setMotDHash(uint hash) { - m_Settings.setValue("motd_hash", hash); + set(m_Settings, "General", "motd_hash", hash); } -uint Settings::getMotDHash() const +unsigned int Settings::getMotDHash() const { - return m_Settings.value("motd_hash", 0).toUInt(); + return get(m_Settings, "motd_hash", 0); } bool Settings::archiveParsing() const { - return m_Settings.value("Settings/archive_parsing_experimental", false).toBool(); + return get(m_Settings, "Settings", "archive_parsing_experimental", false); } void Settings::setArchiveParsing(bool b) { - m_Settings.setValue("Settings/archive_parsing_experimental", b); + set(m_Settings, "Settings", "archive_parsing_experimental", b); } QString Settings::language() { - QString result = m_Settings.value("Settings/language", "").toString(); + QString result = get(m_Settings, "Settings", "language", ""); + if (result.isEmpty()) { QStringList languagePreferences = QLocale::system().uiLanguages(); + if (languagePreferences.length() > 0) { // the users most favoritest language result = languagePreferences.at(0); @@ -940,12 +1153,13 @@ QString Settings::language() result = QLocale::system().name(); } } + return result; } void Settings::setLanguage(const QString& name) { - m_Settings.setValue("Settings/language", name); + set(m_Settings, "Settings", "language", name); } void Settings::setDownloadSpeed(const QString& name, int bytesPerSecond) @@ -972,18 +1186,13 @@ ServerList Settings::getServers() const // // so post 2.2.1, only one key is returned: "size", the size of the arrays; // in 2.2.1, one key per server is returned - - // getting the keys - QStringList keys; - { - ScopedGroup sg(m_Settings, "Servers"); - keys = m_Settings.childKeys(); - } + const QStringList keys = ScopedGroup(m_Settings, "Servers").keys(); - if (!keys.empty() && keys[0] != "size") { - // old format - return getServersFromOldMap(); + if (!keys.empty() && keys[0] != "size") { + // old format + return getServersFromOldMap(); + } } @@ -994,12 +1203,11 @@ ServerList Settings::getServers() const { ScopedReadArray sra(m_Settings, "Servers"); - for (int i=0; i("lastDownloads", ""); + for (const auto& s : lastDownloadsString.split(" ")) { const auto bytesPerSecond = s.toInt(); if (bytesPerSecond > 0) { @@ -1008,14 +1216,14 @@ ServerList Settings::getServers() const } ServerInfo server( - m_Settings.value("name").toString(), - m_Settings.value("premium").toBool(), - QDate::fromString(m_Settings.value("lastSeen").toString(), Qt::ISODate), - m_Settings.value("preferred").toInt(), + sra.get("name", ""), + sra.get("premium", false), + QDate::fromString(sra.get("lastSeen", ""), Qt::ISODate), + sra.get("preferred", 0), lastDownloads); list.add(std::move(server)); - } + }); } return list; @@ -1026,10 +1234,10 @@ ServerList Settings::getServersFromOldMap() const // for 2.2.1 and before ServerList list; - ScopedGroup sg(m_Settings, "Servers"); + const ScopedGroup sg(m_Settings, "Servers"); - for (const QString &serverKey : m_Settings.childKeys()) { - QVariantMap data = m_Settings.value(serverKey).toMap(); + sg.for_each([&](auto&& serverKey) { + QVariantMap data = sg.get(serverKey); ServerInfo server( serverKey, @@ -1042,7 +1250,7 @@ ServerList Settings::getServersFromOldMap() const // a total list.add(std::move(server)); - } + }); return list; } @@ -1052,22 +1260,18 @@ void Settings::updateServers(ServerList servers) // clean up unavailable servers servers.cleanup(); - { - ScopedGroup sg(m_Settings, "Servers"); - m_Settings.remove(""); - } + removeSection(m_Settings, "Servers"); { ScopedWriteArray swa(m_Settings, "Servers"); - int i=0; for (const auto& server : servers) { - m_Settings.setArrayIndex(i); + swa.next(); - m_Settings.setValue("name", server.name()); - m_Settings.setValue("premium", server.isPremium()); - m_Settings.setValue("lastSeen", server.lastSeen().toString(Qt::ISODate)); - m_Settings.setValue("preferred", server.preferred()); + swa.set("name", server.name()); + swa.set("premium", server.isPremium()); + swa.set("lastSeen", server.lastSeen().toString(Qt::ISODate)); + swa.set("preferred", server.preferred()); QString lastDownloads; for (const auto& speed : server.lastDownloads()) { @@ -1076,9 +1280,7 @@ void Settings::updateServers(ServerList servers) } } - m_Settings.setValue("lastDownloads", lastDownloads.trimmed()); - - ++i; + swa.set("lastDownloads", lastDownloads.trimmed()); } } } @@ -1087,35 +1289,31 @@ std::map Settings::getRecentDirectories() const { std::map map; - ScopedReadArray sra(m_Settings, "recentDirectories"); - - for (int i=0; i("name"); + const QVariant dir = sra.get("directory"); if (name.isValid() && dir.isValid()) { map.emplace(name.toString(), dir.toString()); } - } + }); return map; } void Settings::setRecentDirectories(const std::map& map) { - m_Settings.remove("recentDirectories"); + removeSection(m_Settings, "RecentDirectories"); ScopedWriteArray swa(m_Settings, "recentDirectories"); - int index = 0; for (auto&& p : map) { - m_Settings.setArrayIndex(index); - m_Settings.setValue("name", p.first); - m_Settings.setValue("directory", p.second); + swa.next(); - ++index; + swa.set("name", p.first); + swa.set("directory", p.second); } } @@ -1124,78 +1322,67 @@ std::vector> Settings::getExecutables() const ScopedReadArray sra(m_Settings, "customExecutables"); std::vector> v; - for (int i=0; i map; - const auto keys = m_Settings.childKeys(); - for (auto&& key : keys) { + for (auto&& key : sra.keys()) { map[key] = m_Settings.value(key); } v.push_back(map); - } + }); return v; } void Settings::setExecutables(const std::vector>& v) { - m_Settings.remove("customExecutables"); + removeSection(m_Settings, "customExecutables"); ScopedWriteArray swa(m_Settings, "customExecutables"); - int i = 0; - for (const auto& map : v) { - m_Settings.setArrayIndex(i); + swa.next(); for (auto&& p : map) { - m_Settings.setValue(p.first, p.second); + swa.set(p.first, p.second); } - - ++i; } } bool Settings::isTutorialCompleted(const QString& windowName) const { - const auto v = getOptional( - m_Settings, "CompletedWindowTutorials/" + windowName); - - return v.value_or(false); + return get(m_Settings, "CompletedWindowTutorials", windowName, false); } void Settings::setTutorialCompleted(const QString& windowName, bool b) { - m_Settings.setValue("CompletedWindowTutorials/" + windowName, true); + set(m_Settings, "CompletedWindowTutorials", windowName, b); } bool Settings::keepBackupOnInstall() const { - return getOptional(m_Settings, "backup_install").value_or(false); + return get(m_Settings, "backup_install", false); } void Settings::setKeepBackupOnInstall(bool b) { - m_Settings.setValue("backup_install", b); + set(m_Settings, "General", "backup_install", b); } QuestionBoxMemory::Button Settings::getQuestionButton( const QString& windowName, const QString& filename) const { - const QString windowSetting("DialogChoices/" + windowName); + const QString sectionName("DialogChoices"); if (!filename.isEmpty()) { - const auto fileSetting = windowSetting + "/" + filename; - - if (auto v=getOptional(m_Settings, fileSetting)) { + const auto fileSetting = windowName + "/" + filename; + if (auto v=getOptional(m_Settings, sectionName, filename)) { return static_cast(*v); } } - if (auto v=getOptional(m_Settings, windowSetting)) { + if (auto v=getOptional(m_Settings, sectionName, windowName)) { return static_cast(*v); } @@ -1205,12 +1392,12 @@ QuestionBoxMemory::Button Settings::getQuestionButton( void Settings::setQuestionWindowButton( const QString& windowName, QuestionBoxMemory::Button button) { - const QString settingName("DialogChoices/" + windowName); + const QString sectionName("DialogChoices/"); if (button == QuestionBoxMemory::NoButton) { - m_Settings.remove(settingName); + remove(m_Settings, sectionName, windowName); } else { - m_Settings.setValue(settingName, static_cast(button)); + set(m_Settings, sectionName, windowName, button); } } @@ -1218,51 +1405,51 @@ void Settings::setQuestionFileButton( const QString& windowName, const QString& filename, QuestionBoxMemory::Button button) { - const QString settingName("DialogChoices/" + windowName + "/" + filename); + const QString sectionName("DialogChoices"); + const QString settingName(windowName + "/" + filename); if (button == QuestionBoxMemory::NoButton) { - m_Settings.remove(settingName); + remove(m_Settings, sectionName, settingName); } else { - m_Settings.setValue(settingName, static_cast(button)); + set(m_Settings, sectionName, settingName, button); } } void Settings::resetQuestionButtons() { - ScopedGroup sg(m_Settings, "DialogChoices"); - m_Settings.remove(""); + removeSection(m_Settings, "DialogChoices"); } std::optional Settings::getIndex(const QComboBox* cb) const { - return getOptional(m_Settings, indexSettingName(cb)); + return getOptional(m_Settings, "Widgets", indexSettingName(cb)); } void Settings::saveIndex(const QComboBox* cb) { - m_Settings.setValue(indexSettingName(cb), cb->currentIndex()); + set(m_Settings, "Widgets", indexSettingName(cb), cb->currentIndex()); } void Settings::restoreIndex(QComboBox* cb, std::optional def) const { - if (auto v=getOptional(m_Settings, indexSettingName(cb), def)) { + if (auto v=getOptional(m_Settings, "Widgets", indexSettingName(cb), def)) { cb->setCurrentIndex(*v); } } std::optional Settings::getIndex(const QTabWidget* w) const { - return getOptional(m_Settings, indexSettingName(w)); + return getOptional(m_Settings, "Widgets", indexSettingName(w)); } void Settings::saveIndex(const QTabWidget* w) { - m_Settings.setValue(indexSettingName(w), w->currentIndex()); + set(m_Settings, "Widgets", indexSettingName(w), w->currentIndex()); } void Settings::restoreIndex(QTabWidget* w, std::optional def) const { - if (auto v=getOptional(m_Settings, indexSettingName(w), def)) { + if (auto v=getOptional(m_Settings, "Widgets", indexSettingName(w), def)) { w->setCurrentIndex(*v); } } @@ -1270,20 +1457,20 @@ void Settings::restoreIndex(QTabWidget* w, std::optional def) const std::optional Settings::getChecked(const QAbstractButton* w) const { warnIfNotCheckable(w); - return getOptional(m_Settings, checkedSettingName(w)); + return getOptional(m_Settings, "Widgets", checkedSettingName(w)); } void Settings::saveChecked(const QAbstractButton* w) { warnIfNotCheckable(w); - m_Settings.setValue(checkedSettingName(w), w->isChecked()); + set(m_Settings, "Widgets", checkedSettingName(w), w->isChecked()); } void Settings::restoreChecked(QAbstractButton* w, std::optional def) const { warnIfNotCheckable(w); - if (auto v=getOptional(m_Settings, checkedSettingName(w), def)) { + if (auto v=getOptional(m_Settings, "Widgets", checkedSettingName(w), def)) { w->setChecked(*v); } } @@ -1328,7 +1515,7 @@ void Settings::dump() const { static const QStringList ignore({ "username", "password", "nexus_api_key" - }); + }); log::debug("settings:"); @@ -1379,18 +1566,17 @@ void GeometrySettings::resetIfNeeded() return; } - ScopedGroup sg(m_Settings, "geometry"); - m_Settings.remove(""); + removeSection(m_Settings, "Geometry"); } void GeometrySettings::saveGeometry(const QWidget* w) { - m_Settings.setValue(geoSettingName(w), w->saveGeometry()); + set(m_Settings, "Geometry", geoSettingName(w), w->saveGeometry()); } bool GeometrySettings::restoreGeometry(QWidget* w) const { - if (auto v=getOptional(m_Settings, geoSettingName(w))) { + if (auto v=getOptional(m_Settings, "Geometry", geoSettingName(w))) { w->restoreGeometry(*v); return true; } @@ -1400,12 +1586,12 @@ bool GeometrySettings::restoreGeometry(QWidget* w) const void GeometrySettings::saveState(const QMainWindow* w) { - m_Settings.setValue(stateSettingName(w), w->saveState()); + set(m_Settings, "Geometry", stateSettingName(w), w->saveState()); } bool GeometrySettings::restoreState(QMainWindow* w) const { - if (auto v=getOptional(m_Settings, stateSettingName(w))) { + if (auto v=getOptional(m_Settings, "Geometry", stateSettingName(w))) { w->restoreState(*v); return true; } @@ -1415,12 +1601,12 @@ bool GeometrySettings::restoreState(QMainWindow* w) const void GeometrySettings::saveState(const QHeaderView* w) { - m_Settings.setValue(stateSettingName(w), w->saveState()); + set(m_Settings, "Geometry", stateSettingName(w), w->saveState()); } bool GeometrySettings::restoreState(QHeaderView* w) const { - if (auto v=getOptional(m_Settings, stateSettingName(w))) { + if (auto v=getOptional(m_Settings, "Geometry", stateSettingName(w))) { w->restoreState(*v); return true; } @@ -1430,12 +1616,12 @@ bool GeometrySettings::restoreState(QHeaderView* w) const void GeometrySettings::saveState(const QSplitter* w) { - m_Settings.setValue(stateSettingName(w), w->saveState()); + set(m_Settings, "Geometry", stateSettingName(w), w->saveState()); } bool GeometrySettings::restoreState(QSplitter* w) const { - if (auto v=getOptional(m_Settings, stateSettingName(w))) { + if (auto v=getOptional(m_Settings, "Geometry", stateSettingName(w))) { w->restoreState(*v); return true; } @@ -1445,12 +1631,12 @@ bool GeometrySettings::restoreState(QSplitter* w) const void GeometrySettings::saveState(const ExpanderWidget* expander) { - m_Settings.setValue(stateSettingName(expander), expander->saveState()); + set(m_Settings, "Geometry", stateSettingName(expander), expander->saveState()); } bool GeometrySettings::restoreState(ExpanderWidget* expander) const { - if (auto v=getOptional(m_Settings, stateSettingName(expander))) { + if (auto v=getOptional(m_Settings, "Geometry", stateSettingName(expander))) { expander->restoreState(*v); return true; } @@ -1460,12 +1646,12 @@ bool GeometrySettings::restoreState(ExpanderWidget* expander) const void GeometrySettings::saveVisibility(const QWidget* w) { - m_Settings.setValue(visibilitySettingName(w), w->isVisible()); + set(m_Settings, "Geometry", visibilitySettingName(w), w->isVisible()); } bool GeometrySettings::restoreVisibility(QWidget* w, std::optional def) const { - if (auto v=getOptional(m_Settings, visibilitySettingName(w), def)) { + if (auto v=getOptional(m_Settings, "Geometry", visibilitySettingName(w), def)) { w->setVisible(*v); return true; } @@ -1476,8 +1662,8 @@ bool GeometrySettings::restoreVisibility(QWidget* w, std::optional def) co void GeometrySettings::restoreToolbars(QMainWindow* w) const { // all toolbars have the same size and button style settings - const auto size = getOptional(m_Settings, "toolbar_size"); - const auto style = getOptional(m_Settings, "toolbar_button_style"); + const auto size = getOptional(m_Settings, "Geometry", "toolbar_size"); + const auto style = getOptional(m_Settings, "Geometry", "toolbar_button_style"); for (auto* tb : w->findChildren()) { if (size) { @@ -1506,8 +1692,8 @@ void GeometrySettings::saveToolbars(const QMainWindow* w) if (!tbs.isEmpty()) { const auto* tb = tbs[0]; - m_Settings.setValue("toolbar_size", tb->iconSize()); - m_Settings.setValue("toolbar_button_style", static_cast(tb->toolButtonStyle())); + set(m_Settings, "Geometry", "toolbar_size", tb->iconSize()); + set(m_Settings, "Geometry", "toolbar_button_style", static_cast(tb->toolButtonStyle())); } } @@ -1544,12 +1730,13 @@ QStringList GeometrySettings::getModInfoTabOrder() const void GeometrySettings::setModInfoTabOrder(const QString& names) { - m_Settings.setValue("mod_info_tab_order", names); + set(m_Settings, "Geometry", "mod_info_tab_order", names); } void GeometrySettings::centerOnMainWindowMonitor(QWidget* w) { - const auto monitor = getOptional(m_Settings, "geometry/MainWindow_monitor"); + const auto monitor = getOptional( + m_Settings, "Geometry", "MainWindow_monitor"); QPoint center; @@ -1567,7 +1754,7 @@ void GeometrySettings::saveMainWindowMonitor(const QMainWindow* w) if (auto* handle=w->windowHandle()) { if (auto* screen = handle->screen()) { const int screenId = QGuiApplication::screens().indexOf(screen); - m_Settings.setValue("geometry/MainWindow_monitor", screenId); + set(m_Settings, "Geometry", "MainWindow_monitor", screenId); } } } @@ -1617,7 +1804,7 @@ void GeometrySettings::saveDocks(const QMainWindow* mw) size = dock->size().height(); } - m_Settings.setValue(dockSettingName(dock), size); + set(m_Settings, "Geometry", dockSettingName(dock), size); } } @@ -1634,7 +1821,7 @@ void GeometrySettings::restoreDocks(QMainWindow* mw) const // for each dock for (auto* dock : mw->findChildren()) { - if (auto size=getOptional(m_Settings, dockSettingName(dock))) { + if (auto size=getOptional(m_Settings, "Geometry", dockSettingName(dock))) { // remember this dock, its size and orientation dockInfos.push_back({dock, *size, dockOrientation(mw, dock)}); } @@ -1649,7 +1836,7 @@ void GeometrySettings::restoreDocks(QMainWindow* mw) const for (const auto& info : dockInfos) { mw->resizeDocks({info.d}, {info.size}, info.ori); } - }); + }); } @@ -1660,68 +1847,74 @@ ColorSettings::ColorSettings(QSettings& s) QColor ColorSettings::modlistOverwrittenLoose() const { - return getOptional(m_Settings, "Settings/overwrittenLooseFilesColor") - .value_or(QColor(0, 255, 0, 64)); + return get( + m_Settings, "Settings", "overwrittenLooseFilesColor", + QColor(0, 255, 0, 64)); } void ColorSettings::setModlistOverwrittenLoose(const QColor& c) { - m_Settings.setValue("Settings/overwrittenLooseFilesColor", c); + set(m_Settings, "Settings", "overwrittenLooseFilesColor", c); } QColor ColorSettings::modlistOverwritingLoose() const { - return getOptional(m_Settings, "Settings/overwritingLooseFilesColor") - .value_or(QColor(255, 0, 0, 64)); + return get( + m_Settings, "Settings", "overwritingLooseFilesColor", + QColor(255, 0, 0, 64)); } void ColorSettings::setModlistOverwritingLoose(const QColor& c) { - m_Settings.setValue("Settings/overwritingLooseFilesColor", c); + set(m_Settings, "Settings", "overwritingLooseFilesColor", c); } QColor ColorSettings::modlistOverwrittenArchive() const { - return getOptional(m_Settings, "Settings/overwrittenArchiveFilesColor") - .value_or(QColor(0, 255, 255, 64)); + return get( + m_Settings, "Settings", "overwrittenArchiveFilesColor", + QColor(0, 255, 255, 64)); } void ColorSettings::setModlistOverwrittenArchive(const QColor& c) { - m_Settings.setValue("Settings/overwrittenArchiveFilesColor", c); + set(m_Settings, "Settings", "overwrittenArchiveFilesColor", c); } QColor ColorSettings::modlistOverwritingArchive() const { - return getOptional(m_Settings, "Settings/overwritingArchiveFilesColor") - .value_or(QColor(255, 0, 255, 64)); + return get( + m_Settings, "Settings", "overwritingArchiveFilesColor", + QColor(255, 0, 255, 64)); } void ColorSettings::setModlistOverwritingArchive(const QColor& c) { - m_Settings.setValue("Settings/overwritingArchiveFilesColor", c); + set(m_Settings, "Settings", "overwritingArchiveFilesColor", c); } QColor ColorSettings::modlistContainsPlugin() const { - return getOptional(m_Settings, "Settings/containsPluginColor") - .value_or(QColor(0, 0, 255, 64)); + return get( + m_Settings, "Settings", "containsPluginColor", + QColor(0, 0, 255, 64)); } void ColorSettings::setModlistContainsPlugin(const QColor& c) { - m_Settings.setValue("Settings/containsPluginColor", c); + set(m_Settings, "Settings", "containsPluginColor", c); } QColor ColorSettings::pluginListContained() const { - return getOptional(m_Settings, "Settings/containedColor") - .value_or(QColor(0, 0, 255, 64)); + return get( + m_Settings, "Settings", "containedColor", + QColor(0, 0, 255, 64)); } void ColorSettings::setPluginListContained(const QColor& c) { - m_Settings.setValue("Settings/containedColor", c); + set(m_Settings, "Settings", "containedColor", c); } @@ -1738,10 +1931,9 @@ void PluginSettings::clearPlugins() m_PluginBlacklist.clear(); ScopedReadArray sra(m_Settings, "pluginBlacklist"); - for (int i = 0; i < sra.count(); ++i) { - m_Settings.setArrayIndex(i); - m_PluginBlacklist.insert(m_Settings.value("name").toString()); - } + sra.for_each([&]{ + m_PluginBlacklist.insert(sra.get("name")); + }); } void PluginSettings::registerPlugin(IPlugin *plugin) @@ -1749,16 +1941,26 @@ void PluginSettings::registerPlugin(IPlugin *plugin) m_Plugins.push_back(plugin); m_PluginSettings.insert(plugin->name(), QVariantMap()); m_PluginDescriptions.insert(plugin->name(), QVariantMap()); + for (const PluginSetting &setting : plugin->settings()) { - QVariant temp = m_Settings.value("Plugins/" + plugin->name() + "/" + setting.key, setting.defaultValue); + const QString settingName = plugin->name() + "/" + setting.key; + + QVariant temp = get( + m_Settings, "Plugins", settingName, setting.defaultValue); + if (!temp.convert(setting.defaultValue.type())) { log::warn( "failed to interpret \"{}\" as correct type for \"{}\" in plugin \"{}\", using default", temp.toString(), setting.key, plugin->name()); + temp = setting.defaultValue; } + m_PluginSettings[plugin->name()][setting.key] = temp; - m_PluginDescriptions[plugin->name()][setting.key] = QString("%1 (default: %2)").arg(setting.description).arg(setting.defaultValue.toString()); + + m_PluginDescriptions[plugin->name()][setting.key] = QString("%1 (default: %2)") + .arg(setting.description) + .arg(setting.defaultValue.toString()); } } @@ -1773,6 +1975,7 @@ QVariant PluginSettings::pluginSetting(const QString &pluginName, const QString if (iterPlugin == m_PluginSettings.end()) { return QVariant(); } + auto iterSetting = iterPlugin->find(key); if (iterSetting == iterPlugin->end()) { return QVariant(); @@ -1784,13 +1987,16 @@ QVariant PluginSettings::pluginSetting(const QString &pluginName, const QString void PluginSettings::setPluginSetting(const QString &pluginName, const QString &key, const QVariant &value) { auto iterPlugin = m_PluginSettings.find(pluginName); + if (iterPlugin == m_PluginSettings.end()) { - throw MyException(QObject::tr("attempt to store setting for unknown plugin \"%1\"").arg(pluginName)); + throw MyException( + QObject::tr("attempt to store setting for unknown plugin \"%1\"") + .arg(pluginName)); } // store the new setting both in memory and in the ini m_PluginSettings[pluginName][key] = value; - m_Settings.setValue("Plugins/" + pluginName + "/" + key, value); + set(m_Settings, "Plugins", pluginName + "/" + key, value); } QVariant PluginSettings::pluginPersistent(const QString &pluginName, const QString &key, const QVariant &def) const @@ -1798,15 +2004,21 @@ QVariant PluginSettings::pluginPersistent(const QString &pluginName, const QStri if (!m_PluginSettings.contains(pluginName)) { return def; } - return m_Settings.value("PluginPersistance/" + pluginName + "/" + key, def); + + return get(m_Settings, "PluginPersistance", pluginName + "/" + key, def); } -void PluginSettings::setPluginPersistent(const QString &pluginName, const QString &key, const QVariant &value, bool sync) +void PluginSettings::setPluginPersistent( + const QString &pluginName, const QString &key, const QVariant &value, bool sync) { if (!m_PluginSettings.contains(pluginName)) { - throw MyException(QObject::tr("attempt to store setting for unknown plugin \"%1\"").arg(pluginName)); + throw MyException( + QObject::tr("attempt to store setting for unknown plugin \"%1\"") + .arg(pluginName)); } - m_Settings.setValue("PluginPersistance/" + pluginName + "/" + key, value); + + set(m_Settings, "PluginPersistance", pluginName + "/" + key, value); + if (sync) { m_Settings.sync(); } @@ -1820,13 +2032,13 @@ void PluginSettings::addBlacklistPlugin(const QString &fileName) void PluginSettings::writePluginBlacklist() { - m_Settings.remove("pluginBlacklist"); + removeSection(m_Settings, "PluginBlacklist"); + + ScopedWriteArray swa(m_Settings, "PluginBlacklist"); - ScopedWriteArray swa(m_Settings, "pluginBlacklist"); - int idx = 0; for (const QString &plugin : m_PluginBlacklist) { - m_Settings.setArrayIndex(idx++); - m_Settings.setValue("name", plugin); + swa.next(); + swa.set("name", plugin); } } @@ -1868,8 +2080,8 @@ void PluginSettings::save() { for (auto iterPlugins=m_PluginSettings.begin(); iterPlugins!=m_PluginSettings.end(); ++iterPlugins) { for (auto iterSettings=iterPlugins->begin(); iterSettings!=iterPlugins->end(); ++iterSettings) { - const auto key = "Plugins/" + iterPlugins.key() + "/" + iterSettings.key(); - m_Settings.setValue(key, iterSettings.value()); + const auto key = iterPlugins.key() + "/" + iterSettings.key(); + set(m_Settings, "Plugins", key, iterSettings.value()); } } diff --git a/src/settings.h b/src/settings.h index ae29d788..403c2d71 100644 --- a/src/settings.h +++ b/src/settings.h @@ -68,9 +68,6 @@ public: void saveState(const QHeaderView* header); bool restoreState(QHeaderView* header) const; - void saveState(const QToolBar* toolbar); - bool restoreState(QToolBar* toolbar) const; - void saveState(const QSplitter* splitter); bool restoreState(QSplitter* splitter) const; @@ -258,8 +255,6 @@ public: std::optional getStyleName() const; void setStyleName(const QString& name); - std::optional getUseProxy() const; - std::optional getVersion() const; bool getFirstStart() const; @@ -408,7 +403,7 @@ public: /** * @return true if the user configured the use of a network proxy */ - bool useProxy() const; + bool getUseProxy() const; void setUseProxy(bool b); /** @@ -419,7 +414,6 @@ public: EndorsementState endorsementState() const; void setEndorsementState(EndorsementState s); - void setEndorsementState(const QString& s); /** * @return true if the API counter should be hidden @@ -436,7 +430,8 @@ public: /** * @brief sets the new motd hash **/ - void setMotDHash(uint hash); + unsigned int getMotDHash() const; + void setMotDHash(unsigned int hash); /** * @return true if the user wants to have archives being parsed to show conflicts and contents @@ -444,11 +439,6 @@ public: bool archiveParsing() const; void setArchiveParsing(bool b); - /** - * @return hash of the last displayed message of the day - **/ - uint getMotDHash() const; - /** * @return short code of the configured language (corresponding to the translation files) */ diff --git a/src/settingsdialognexus.cpp b/src/settingsdialognexus.cpp index 3de1a6ba..8822200e 100644 --- a/src/settingsdialognexus.cpp +++ b/src/settingsdialognexus.cpp @@ -75,7 +75,7 @@ NexusSettingsTab::NexusSettingsTab(Settings& s, SettingsDialog& d) : SettingsTab(s, d) { ui->offlineBox->setChecked(settings().offlineMode()); - ui->proxyBox->setChecked(settings().useProxy()); + ui->proxyBox->setChecked(settings().getUseProxy()); ui->endorsementBox->setChecked(settings().endorsementIntegration()); ui->hideAPICounterBox->setChecked(settings().hideAPICounter()); -- cgit v1.3.1 From e9dba260cb9548dd5863ac66da18c295f6499b92 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 2 Sep 2019 14:52:02 -0400 Subject: split settings into a bunch of classes removed "get" from the getters that had it --- src/browserdialog.cpp | 2 +- src/downloadlistsortproxy.cpp | 2 +- src/downloadmanager.cpp | 2 +- src/executableslist.cpp | 2 +- src/filedialogmemory.cpp | 4 +- src/main.cpp | 26 +- src/mainwindow.cpp | 121 +-- src/modinfodialog.cpp | 2 +- src/modinfodialogconflicts.cpp | 16 +- src/modinfodialogimages.cpp | 4 +- src/modinfodialognexus.cpp | 2 +- src/modinfooverwrite.cpp | 2 +- src/modinforegular.cpp | 2 +- src/modlist.cpp | 8 +- src/nxmaccessmanager.cpp | 2 +- src/organizercore.cpp | 112 +- src/pluginlist.cpp | 2 +- src/profile.cpp | 8 +- src/profilesdialog.cpp | 4 +- src/settings.cpp | 2075 ++++++++++++++++++++----------------- src/settings.h | 569 ++++++---- src/settingsdialog.cpp | 6 +- src/settingsdialogdiagnostics.cpp | 13 +- src/settingsdialoggeneral.cpp | 28 +- src/settingsdialognexus.cpp | 32 +- src/settingsdialogpaths.cpp | 52 +- src/settingsdialogsteam.cpp | 4 +- src/settingsdialogworkarounds.cpp | 30 +- src/statusbar.cpp | 2 +- src/usvfsconnector.cpp | 4 +- 30 files changed, 1712 insertions(+), 1426 deletions(-) (limited to 'src/settingsdialognexus.cpp') diff --git a/src/browserdialog.cpp b/src/browserdialog.cpp index 70da0b9c..72cb8862 100644 --- a/src/browserdialog.cpp +++ b/src/browserdialog.cpp @@ -49,7 +49,7 @@ BrowserDialog::BrowserDialog(QWidget *parent) ui->setupUi(this); m_AccessManager->setCookieJar(new PersistentCookieJar( - QDir::fromNativeSeparators(Settings::instance().getCacheDirectory() + "/cookies.dat"))); + QDir::fromNativeSeparators(Settings::instance().paths().cache() + "/cookies.dat"))); Qt::WindowFlags flags = windowFlags() | Qt::WindowMaximizeButtonHint | Qt::WindowMinimizeButtonHint; Qt::WindowFlags helpFlag = Qt::WindowContextHelpButtonHint; diff --git a/src/downloadlistsortproxy.cpp b/src/downloadlistsortproxy.cpp index 7bda139b..a69993c0 100644 --- a/src/downloadlistsortproxy.cpp +++ b/src/downloadlistsortproxy.cpp @@ -110,7 +110,7 @@ bool DownloadListSortProxy::filterAcceptsRow(int sourceRow, const QModelIndex&) if (m_CurrentFilter.length() == 0) { return true; } else if (sourceRow < m_Manager->numTotalDownloads()) { - QString displayedName = Settings::instance().metaDownloads() + QString displayedName = Settings::instance().interface().metaDownloads() ? m_Manager->getDisplayName(sourceRow) : m_Manager->getFileName(sourceRow); return displayedName.contains(m_CurrentFilter, Qt::CaseInsensitive); diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index a5dc164c..56238ef3 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -1731,7 +1731,7 @@ void DownloadManager::nxmDownloadURLsAvailable(QString gameName, int modID, int return; } - const auto servers = m_OrganizerCore->settings().getServers(); + const auto servers = m_OrganizerCore->settings().network().servers(); std::sort( resultList.begin(), diff --git a/src/executableslist.cpp b/src/executableslist.cpp index f2df2d6d..dce9181b 100644 --- a/src/executableslist.cpp +++ b/src/executableslist.cpp @@ -75,7 +75,7 @@ void ExecutablesList::load(const MOBase::IPluginGame* game, const Settings& s) // executables from 2.2.0, see upgradeFromCustom() bool needsUpgrade = false; - for (auto& map : s.getExecutables()) { + for (auto& map : s.executables()) { Executable::Flags flags; if (map["toolbar"].toBool()) diff --git a/src/filedialogmemory.cpp b/src/filedialogmemory.cpp index 96587ac7..8cfeb6b5 100644 --- a/src/filedialogmemory.cpp +++ b/src/filedialogmemory.cpp @@ -25,12 +25,12 @@ static std::map g_Cache; void FileDialogMemory::save(Settings& s) { - s.setRecentDirectories(g_Cache); + s.paths().setRecent(g_Cache); } void FileDialogMemory::restore(const Settings& s) { - g_Cache = s.getRecentDirectories(); + g_Cache = s.paths().recent(); } QString FileDialogMemory::getOpenFileName( diff --git a/src/main.cpp b/src/main.cpp index aa781c19..b5568fec 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -246,7 +246,7 @@ static bool HaveWriteAccess(const std::wstring &path) QString determineProfile(QStringList &arguments, const Settings &settings) { - auto selectedProfileName = settings.getSelectedProfileName(); + auto selectedProfileName = settings.game().selectedProfileName(); { // see if there is a profile on the command line int profileIndex = arguments.indexOf("-p", 1); @@ -271,12 +271,12 @@ QString determineProfile(QStringList &arguments, const Settings &settings) MOBase::IPluginGame *selectGame( Settings &settings, QDir const &gamePath, MOBase::IPluginGame *game) { - settings.setManagedGameName(game->gameName()); + settings.game().setName(game->gameName()); QString gameDir = gamePath.absolutePath(); game->setGamePath(gameDir); - settings.setManagedGameDirectory(gameDir); + settings.game().setDirectory(gameDir); return game; } @@ -289,7 +289,7 @@ MOBase::IPluginGame *determineCurrentGame( //user has done something odd. //If the game name has been set up, try to use that. - const auto gameName = settings.getManagedGameName(); + const auto gameName = settings.game().name(); const bool gameConfigured = (gameName.has_value() && *gameName != ""); if (gameConfigured) { @@ -299,7 +299,7 @@ MOBase::IPluginGame *determineCurrentGame( return nullptr; } - auto gamePath = settings.getManagedGameDirectory(); + auto gamePath = settings.game().directory(); if (!gamePath || *gamePath == "") { gamePath = game->gameDirectory().absolutePath(); } @@ -320,7 +320,7 @@ MOBase::IPluginGame *determineCurrentGame( //If we've made it this far and the instance is already configured for a game, something has gone wrong. //Tell the user about it. if (gameConfigured) { - const auto gamePath = settings.getManagedGameDirectory(); + const auto gamePath = settings.game().directory(); reportError( QObject::tr("Could not use configuration settings for game \"%1\", path \"%2\".") @@ -570,11 +570,11 @@ int runApplication(MOApplication &application, SingleInstance &instance, log::info("working directory: {}", QDir::currentPath()); Settings settings(dataPath + "/" + QString::fromStdWString(AppConfig::iniFileName())); - log::getDefault().setLevel(settings.logLevel()); + log::getDefault().setLevel(settings.diagnostics().logLevel()); // global crashDumpType sits in OrganizerCore to make a bit less ugly to // update it when the settings are changed during runtime - OrganizerCore::setGlobalCrashDumpsType(settings.crashDumpsType()); + OrganizerCore::setGlobalCrashDumpsType(settings.diagnostics().crashDumpsType()); env::Environment env; @@ -621,7 +621,7 @@ int runApplication(MOApplication &application, SingleInstance &instance, QString edition; - if (auto v=settings.getManagedGameEdition()) { + if (auto v=settings.game().edition()) { edition = *v; } else { QStringList editions = game->gameVariants(); @@ -640,7 +640,7 @@ int runApplication(MOApplication &application, SingleInstance &instance, return 1; } else { edition = selection.getChoiceString(); - settings.setManagedGameEdition(edition); + settings.game().setEdition(edition); } } } @@ -702,7 +702,7 @@ int runApplication(MOApplication &application, SingleInstance &instance, splash.activateWindow(); QString apiKey; - if (settings.getNexusApiKey(apiKey)) { + if (settings.nexus().apiKey(apiKey)) { NexusInterface::instance(&pluginContainer)->getAccessManager()->apiCheck(apiKey); } @@ -712,9 +712,9 @@ int runApplication(MOApplication &application, SingleInstance &instance, + QString::fromStdWString(AppConfig::tutorialsPath()) + "/", &organizer); - if (!application.setStyleFile(settings.getStyleName().value_or(""))) { + if (!application.setStyleFile(settings.interface().styleName().value_or(""))) { // disable invalid stylesheet - settings.setStyleName(""); + settings.interface().setStyleName(""); } int res = 1; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 42b19cb7..657c1a27 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -222,8 +222,8 @@ MainWindow::MainWindow(Settings &settings { QWebEngineProfile::defaultProfile()->setPersistentCookiesPolicy(QWebEngineProfile::NoPersistentCookies); QWebEngineProfile::defaultProfile()->setHttpCacheMaximumSize(52428800); - QWebEngineProfile::defaultProfile()->setCachePath(settings.getCacheDirectory()); - QWebEngineProfile::defaultProfile()->setPersistentStoragePath(settings.getCacheDirectory()); + QWebEngineProfile::defaultProfile()->setCachePath(settings.paths().cache()); + QWebEngineProfile::defaultProfile()->setPersistentStoragePath(settings.paths().cache()); ui->setupUi(this); ui->statusBar->setup(ui); @@ -253,7 +253,7 @@ MainWindow::MainWindow(Settings &settings ui->statusBar->setAPI(ni->getAPIStats(), ni->getAPIUserAccount()); } - languageChange(settings.language()); + languageChange(settings.interface().language()); m_CategoryFactory.loadCategories(); @@ -1194,7 +1194,7 @@ void MainWindow::hookUpWindowTutorials() QString firstLine = QString::fromUtf8(file.readLine()); if (firstLine.startsWith("//WIN")) { QString windowName = firstLine.mid(6).trimmed(); - if (!m_OrganizerCore.settings().isTutorialCompleted(windowName)) { + if (!m_OrganizerCore.settings().interface().isTutorialCompleted(windowName)) { TutorialManager::instance().activateTutorial(windowName, fileName); } } @@ -1225,7 +1225,7 @@ void MainWindow::showEvent(QShowEvent *event) hookUpWindowTutorials(); - if (m_OrganizerCore.settings().getFirstStart()) { + if (m_OrganizerCore.settings().firstStart()) { QString firstStepsTutorial = ToQString(AppConfig::firstStepsTutorial()); if (TutorialManager::instance().hasTutorial(firstStepsTutorial)) { if (QMessageBox::question(this, tr("Show tutorial?"), @@ -1247,11 +1247,11 @@ void MainWindow::showEvent(QShowEvent *event) m_OrganizerCore.settings().setFirstStart(false); } - m_OrganizerCore.settings().restoreIndex(ui->groupCombo); + m_OrganizerCore.settings().widgets().restoreIndex(ui->groupCombo); allowListResize(); - m_OrganizerCore.settings().registerAsNXMHandler(false); + m_OrganizerCore.settings().nexus().registerAsNXMHandler(false); m_WasVisible = true; updateProblemsButton(); } @@ -1751,7 +1751,7 @@ bool MainWindow::refreshProfiles(bool selectProfile) profileBox->clear(); profileBox->addItem(QObject::tr("")); - QDir profilesDir(Settings::instance().getProfileDirectory()); + QDir profilesDir(Settings::instance().paths().profiles()); profilesDir.setFilter(QDir::AllDirs | QDir::NoDotAndDotDot); QDirIterator profileIter(profilesDir); @@ -1990,7 +1990,7 @@ void MainWindow::updateBSAList(const QStringList &defaultArchives, const QString newItem->setFlags(newItem->flags() & ~(Qt::ItemIsDropEnabled | Qt::ItemIsUserCheckable)); newItem->setCheckState(0, (index != -1) ? Qt::Checked : Qt::Unchecked); newItem->setData(0, Qt::UserRole, false); - if (m_OrganizerCore.settings().forceEnableCoreFiles() + if (m_OrganizerCore.settings().game().forceEnableCoreFiles() && defaultArchives.contains(fileInfo.fileName())) { newItem->setCheckState(0, Qt::Checked); newItem->setDisabled(true); @@ -2140,7 +2140,7 @@ void MainWindow::readSettings(const Settings& settings) { // special case in case someone puts 0 in the INI - auto v = settings.getIndex(ui->executablesListBox); + auto v = settings.widgets().index(ui->executablesListBox); if (!v || v == 0) { v = 1; } @@ -2148,7 +2148,7 @@ void MainWindow::readSettings(const Settings& settings) ui->executablesListBox->setCurrentIndex(*v); } - settings.restoreIndex(ui->groupCombo); + settings.widgets().restoreIndex(ui->groupCombo); { settings.geometry().restoreVisibility(ui->categoriesGroup, false); @@ -2157,7 +2157,7 @@ void MainWindow::readSettings(const Settings& settings) ui->displayCategoriesBtn->setChecked(v); } - if (settings.getUseProxy()) { + if (settings.network().useProxy()) { activateProxy(true); } } @@ -2165,12 +2165,12 @@ void MainWindow::readSettings(const Settings& settings) void MainWindow::processUpdates(Settings& settings) { const auto earliest = QVersionNumber::fromString("2.1.2").normalized(); - const auto lastVersion = settings.getVersion().value_or(earliest); + const auto lastVersion = settings.version().value_or(earliest); const auto currentVersion = m_OrganizerCore.getVersion().asQVersionNumber(); settings.processUpdates(currentVersion, lastVersion); - if (!settings.getFirstStart()) { + if (!settings.firstStart()) { if (lastVersion < QVersionNumber(2, 1, 3)) { bool lastHidden = true; for (int i = ModList::COL_GAME; i < ui->modList->model()->columnCount(); ++i) { @@ -2222,8 +2222,8 @@ void MainWindow::storeSettings(Settings& s) s.geometry().saveState(ui->downloadView->header()); s.geometry().saveState(ui->modList->header()); - s.saveIndex(ui->groupCombo); - s.saveIndex(ui->executablesListBox); + s.widgets().saveIndex(ui->groupCombo); + s.widgets().saveIndex(ui->executablesListBox); } ILockedWaitingForProcess* MainWindow::lock() @@ -2751,7 +2751,7 @@ void MainWindow::restoreBackup_clicked() ModInfo::Ptr modInfo = ModInfo::getByIndex(m_ContextRow); if (backupRegEx.indexIn(modInfo->name()) != -1) { QString regName = backupRegEx.cap(1); - QDir modDir(QDir::fromNativeSeparators(m_OrganizerCore.settings().getModDirectory())); + QDir modDir(QDir::fromNativeSeparators(m_OrganizerCore.settings().paths().mods())); if (!modDir.exists(regName) || (QMessageBox::question(this, tr("Overwrite?"), tr("This will replace the existing mod \"%1\". Continue?").arg(regName), @@ -2759,7 +2759,7 @@ void MainWindow::restoreBackup_clicked() if (modDir.exists(regName) && !shellDelete(QStringList(modDir.absoluteFilePath(regName)))) { reportError(tr("failed to remove mod \"%1\"").arg(regName)); } else { - QString destinationPath = QDir::fromNativeSeparators(m_OrganizerCore.settings().getModDirectory()) + "/" + regName; + QString destinationPath = QDir::fromNativeSeparators(m_OrganizerCore.settings().paths().mods()) + "/" + regName; if (!modDir.rename(modInfo->absolutePath(), destinationPath)) { reportError(tr("failed to rename \"%1\" to \"%2\"").arg(modInfo->absolutePath()).arg(destinationPath)); } @@ -3015,7 +3015,7 @@ void MainWindow::untrack_clicked() void MainWindow::windowTutorialFinished(const QString &windowName) { - m_OrganizerCore.settings().setTutorialCompleted(windowName); + m_OrganizerCore.settings().interface().setTutorialCompleted(windowName); } void MainWindow::overwriteClosed(int) @@ -3645,7 +3645,7 @@ void MainWindow::createSeparator_clicked() m_OrganizerCore.modList()->changeModPriority(ModInfo::getIndex(name), newPriority); } - if (auto c=m_OrganizerCore.settings().getPreviousSeparatorColor()) { + if (auto c=m_OrganizerCore.settings().colors().previousSeparatorColor()) { ModInfo::getByIndex(ModInfo::getIndex(name))->setColor(*c); } } @@ -3662,7 +3662,7 @@ void MainWindow::setColor_clicked() if (currentColor.isValid()) { dialog.setCurrentColor(currentColor); } - else if (auto c=settings.getPreviousSeparatorColor()) { + else if (auto c=settings.colors().previousSeparatorColor()) { dialog.setCurrentColor(*c); } @@ -3673,7 +3673,7 @@ void MainWindow::setColor_clicked() if (!currentColor.isValid()) return; - settings.setPreviousSeparatorColor(currentColor); + settings.colors().setPreviousSeparatorColor(currentColor); QItemSelectionModel *selection = ui->modList->selectionModel(); if (selection->hasSelection() && selection->selectedRows().count() > 1) { @@ -3710,7 +3710,7 @@ void MainWindow::resetColor_clicked() modInfo->setColor(color); } - m_OrganizerCore.settings().removePreviousSeparatorColor(); + m_OrganizerCore.settings().colors().removePreviousSeparatorColor(); } void MainWindow::createModFromOverwrite() @@ -4184,7 +4184,7 @@ void MainWindow::checkModsForUpdates() NexusInterface::instance(&m_PluginContainer)->requestTrackingInfo(this, QVariant(), QString()); } else { QString apiKey; - if (m_OrganizerCore.settings().getNexusApiKey(apiKey)) { + if (m_OrganizerCore.settings().nexus().apiKey(apiKey)) { m_OrganizerCore.doAfterLogin([this] () { this->checkModsForUpdates(); }); NexusInterface::instance(&m_PluginContainer)->getAccessManager()->apiCheck(apiKey); } else { @@ -4387,12 +4387,12 @@ void MainWindow::openIniFolder() void MainWindow::openDownloadsFolder() { - shell::ExploreFile(m_OrganizerCore.settings().getDownloadDirectory()); + shell::ExploreFile(m_OrganizerCore.settings().paths().downloads()); } void MainWindow::openModsFolder() { - shell::ExploreFile(m_OrganizerCore.settings().getModDirectory()); + shell::ExploreFile(m_OrganizerCore.settings().paths().mods()); } void MainWindow::openGameFolder() @@ -4758,7 +4758,7 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) menu.addSeparator(); - if (info->getNexusID() > 0 && Settings::instance().endorsementIntegration()) { + if (info->getNexusID() > 0 && Settings::instance().nexus().endorsementIntegration()) { switch (info->endorsedState()) { case ModInfo::ENDORSED_TRUE: { menu.addAction(tr("Un-Endorse"), this, SLOT(unendorse_clicked())); @@ -5007,19 +5007,19 @@ void MainWindow::on_actionSettings_triggered() { Settings &settings = m_OrganizerCore.settings(); - QString oldModDirectory(settings.getModDirectory()); - QString oldCacheDirectory(settings.getCacheDirectory()); - QString oldProfilesDirectory(settings.getProfileDirectory()); - QString oldManagedGameDirectory(settings.getManagedGameDirectory().value_or("")); - bool oldDisplayForeign(settings.displayForeign()); - bool proxy = settings.getUseProxy(); + QString oldModDirectory(settings.paths().mods()); + QString oldCacheDirectory(settings.paths().cache()); + QString oldProfilesDirectory(settings.paths().profiles()); + QString oldManagedGameDirectory(settings.game().directory().value_or("")); + bool oldDisplayForeign(settings.interface().displayForeign()); + bool proxy = settings.network().useProxy(); DownloadManager *dlManager = m_OrganizerCore.downloadManager(); SettingsDialog dialog(&m_PluginContainer, settings, this); dialog.exec(); - if (oldManagedGameDirectory != settings.getManagedGameDirectory()) { + if (oldManagedGameDirectory != settings.game().directory()) { QMessageBox::about(this, tr("Restarting MO"), tr("Changing the managed game directory requires restarting MO.\n" "Any pending downloads will be paused.\n\n" @@ -5029,28 +5029,28 @@ void MainWindow::on_actionSettings_triggered() } InstallationManager *instManager = m_OrganizerCore.installationManager(); - instManager->setModsDirectory(settings.getModDirectory()); - instManager->setDownloadDirectory(settings.getDownloadDirectory()); + instManager->setModsDirectory(settings.paths().mods()); + instManager->setDownloadDirectory(settings.paths().downloads()); fixCategories(); refreshFilters(); - if (settings.getProfileDirectory() != oldProfilesDirectory) { + if (settings.paths().profiles() != oldProfilesDirectory) { refreshProfiles(); } - if (dlManager->getOutputDirectory() != settings.getDownloadDirectory()) { + if (dlManager->getOutputDirectory() != settings.paths().downloads()) { if (dlManager->downloadsInProgress()) { MessageDialog::showMessage(tr("Can't change download directory while " "downloads are in progress!"), this); } else { - dlManager->setOutputDirectory(settings.getDownloadDirectory()); + dlManager->setOutputDirectory(settings.paths().downloads()); } } - if ((settings.getModDirectory() != oldModDirectory) - || (settings.displayForeign() != oldDisplayForeign)) { + if ((settings.paths().mods() != oldModDirectory) + || (settings.interface().displayForeign() != oldDisplayForeign)) { m_OrganizerCore.profileRefresh(); } @@ -5075,18 +5075,19 @@ void MainWindow::on_actionSettings_triggered() m_OrganizerCore.refreshLists(); } - if (settings.getCacheDirectory() != oldCacheDirectory) { - NexusInterface::instance(&m_PluginContainer)->setCacheDirectory(settings.getCacheDirectory()); + if (settings.paths().cache() != oldCacheDirectory) { + NexusInterface::instance(&m_PluginContainer)->setCacheDirectory( + settings.paths().cache()); } - if (proxy != settings.getUseProxy()) { - activateProxy(settings.getUseProxy()); + if (proxy != settings.network().useProxy()) { + activateProxy(settings.network().useProxy()); } ui->statusBar->checkSettings(m_OrganizerCore.settings()); updateDownloadView(); - m_OrganizerCore.setLogLevel(settings.logLevel()); + m_OrganizerCore.setLogLevel(settings.diagnostics().logLevel()); m_OrganizerCore.cycleDiagnostics(); toggleMO2EndorseState(); @@ -5402,10 +5403,10 @@ void MainWindow::motdReceived(const QString &motd) // internet connection is faster next time if (m_StartTime.secsTo(QTime::currentTime()) < 5) { uint hash = qHash(motd); - if (hash != m_OrganizerCore.settings().getMotDHash()) { + if (hash != m_OrganizerCore.settings().motdHash()) { MotDDialog dialog(motd); dialog.exec(); - m_OrganizerCore.settings().setMotDHash(hash); + m_OrganizerCore.settings().setMotdHash(hash); } } } @@ -5528,7 +5529,7 @@ void MainWindow::initDownloadView() void MainWindow::updateDownloadView() { // set the view attribute and default row sizes - if (m_OrganizerCore.settings().compactDownloads()) { + if (m_OrganizerCore.settings().interface().compactDownloads()) { ui->downloadView->setProperty("downloadView", "compact"); setStyleSheet("DownloadListWidget::item { padding: 4px 2px; }"); } else { @@ -5541,7 +5542,7 @@ void MainWindow::updateDownloadView() // reapply global stylesheet on the widget level (!) to override the defaults //ui->downloadView->setStyleSheet(styleSheet()); - ui->downloadView->setMetaDisplay(m_OrganizerCore.settings().metaDownloads()); + ui->downloadView->setMetaDisplay(m_OrganizerCore.settings().interface().metaDownloads()); ui->downloadView->style()->unpolish(ui->downloadView); ui->downloadView->style()->polish(ui->downloadView); qobject_cast(ui->downloadView->header())->customResizeSections(); @@ -5554,7 +5555,7 @@ void MainWindow::modUpdateCheck(std::multimap IDs) ModInfo::manualUpdateCheck(&m_PluginContainer, this, IDs); } else { QString apiKey; - if (m_OrganizerCore.settings().getNexusApiKey(apiKey)) { + if (m_OrganizerCore.settings().nexus().apiKey(apiKey)) { m_OrganizerCore.doAfterLogin([=]() { this->modUpdateCheck(IDs); }); NexusInterface::instance(&m_PluginContainer)->getAccessManager()->apiCheck(apiKey); } else @@ -5566,7 +5567,7 @@ void MainWindow::toggleMO2EndorseState() { const auto& s = m_OrganizerCore.settings(); - if (!s.endorsementIntegration()) { + if (!s.nexus().endorsementIntegration()) { ui->actionEndorseMO->setVisible(false); return; } @@ -5576,7 +5577,7 @@ void MainWindow::toggleMO2EndorseState() bool enabled = false; QString text; - switch (s.endorsementState()) + switch (s.nexus().endorsementState()) { case EndorsementState::Accepted: { @@ -5631,9 +5632,9 @@ void MainWindow::nxmEndorsementsAvailable(QVariant userData, QVariant resultData mod->setIsEndorsed(false); } - if (Settings::instance().endorsementIntegration()) { + if (Settings::instance().nexus().endorsementIntegration()) { if (result->first == "skyrimspecialedition" && result->second.first == gamePlugin->nexusModOrganizerID()) { - m_OrganizerCore.settings().setEndorsementState( + m_OrganizerCore.settings().nexus().setEndorsementState( endorsementStateFromString(result->second.second)); toggleMO2EndorseState(); @@ -5642,13 +5643,13 @@ void MainWindow::nxmEndorsementsAvailable(QVariant userData, QVariant resultData } } - if (!searchedMO2NexusGame && Settings::instance().endorsementIntegration()) { + if (!searchedMO2NexusGame && Settings::instance().nexus().endorsementIntegration()) { auto gamePlugin = m_OrganizerCore.getGame("SkyrimSE"); if (gamePlugin) { auto iter = sorted.equal_range(gamePlugin->gameNexusName()); for (auto result = iter.first; result != iter.second; ++result) { if (result->second.first == gamePlugin->nexusModOrganizerID()) { - m_OrganizerCore.settings().setEndorsementState( + m_OrganizerCore.settings().nexus().setEndorsementState( endorsementStateFromString(result->second.second)); toggleMO2EndorseState(); @@ -5862,7 +5863,7 @@ void MainWindow::nxmEndorsementToggled(QString, int, QVariant, QVariant resultDa } } - m_OrganizerCore.settings().setEndorsementState(s); + m_OrganizerCore.settings().nexus().setEndorsementState(s); toggleMO2EndorseState(); if (!disconnect(sender(), SIGNAL(nxmEndorsementToggled(QString, int, QVariant, QVariant, int)), @@ -5901,7 +5902,7 @@ void MainWindow::nxmTrackedModsAvailable(QVariant userData, QVariant resultData, void MainWindow::nxmDownloadURLs(QString, int, int, QVariant, QVariant resultData, int) { - auto servers = m_OrganizerCore.settings().getServers(); + auto servers = m_OrganizerCore.settings().network().servers(); for (const QVariant &var : resultData.toList()) { const QVariantMap map = var.toMap(); @@ -5929,7 +5930,7 @@ void MainWindow::nxmDownloadURLs(QString, int, int, QVariant, QVariant resultDat } } - m_OrganizerCore.settings().updateServers(servers); + m_OrganizerCore.settings().network().updateServers(servers); } diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index f3840230..2178ef34 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -381,7 +381,7 @@ void ModInfoDialog::reAddTabs( Q_ASSERT(visibility.size() == m_tabs.size()); // ordered tab names from settings - const auto orderedNames = m_core->settings().geometry().getModInfoTabOrder(); + const auto orderedNames = m_core->settings().geometry().modInfoTabOrder(); // whether the tabs can be sorted; if the object name of a tab widget is not // found in orderedNames, the list cannot be sorted safely diff --git a/src/modinfodialogconflicts.cpp b/src/modinfodialogconflicts.cpp index 7840269d..3a71b405 100644 --- a/src/modinfodialogconflicts.cpp +++ b/src/modinfodialogconflicts.cpp @@ -409,7 +409,7 @@ void ConflictsTab::clear() void ConflictsTab::saveState(Settings& s) { - s.saveIndex(ui->tabConflictsTabs); + s.widgets().saveIndex(ui->tabConflictsTabs); m_general.saveState(s); m_advanced.saveState(s); @@ -417,7 +417,7 @@ void ConflictsTab::saveState(Settings& s) void ConflictsTab::restoreState(const Settings& s) { - s.restoreIndex(ui->tabConflictsTabs, 0); + s.widgets().restoreIndex(ui->tabConflictsTabs, 0); m_general.restoreState(s); m_advanced.restoreState(s); @@ -1014,17 +1014,17 @@ void AdvancedConflictsTab::clear() void AdvancedConflictsTab::saveState(Settings& s) { s.geometry().saveState(ui->conflictsAdvancedList->header()); - s.saveChecked(ui->conflictsAdvancedShowNoConflict); - s.saveChecked(ui->conflictsAdvancedShowAll); - s.saveChecked(ui->conflictsAdvancedShowNearest); + s.widgets().saveChecked(ui->conflictsAdvancedShowNoConflict); + s.widgets().saveChecked(ui->conflictsAdvancedShowAll); + s.widgets().saveChecked(ui->conflictsAdvancedShowNearest); } void AdvancedConflictsTab::restoreState(const Settings& s) { s.geometry().restoreState(ui->conflictsAdvancedList->header()); - s.restoreChecked(ui->conflictsAdvancedShowNoConflict); - s.restoreChecked(ui->conflictsAdvancedShowAll); - s.restoreChecked(ui->conflictsAdvancedShowNearest); + s.widgets().restoreChecked(ui->conflictsAdvancedShowNoConflict); + s.widgets().restoreChecked(ui->conflictsAdvancedShowAll); + s.widgets().restoreChecked(ui->conflictsAdvancedShowNearest); } void AdvancedConflictsTab::update() diff --git a/src/modinfodialogimages.cpp b/src/modinfodialogimages.cpp index 38c12d8a..9d347f57 100644 --- a/src/modinfodialogimages.cpp +++ b/src/modinfodialogimages.cpp @@ -130,13 +130,13 @@ void ImagesTab::update() void ImagesTab::saveState(Settings& s) { - s.saveChecked(ui->imagesShowDDS); + s.widgets().saveChecked(ui->imagesShowDDS); s.geometry().saveState(ui->tabImagesSplitter); } void ImagesTab::restoreState(const Settings& s) { - s.restoreChecked(ui->imagesShowDDS); + s.widgets().restoreChecked(ui->imagesShowDDS); s.geometry().restoreState(ui->tabImagesSplitter); } diff --git a/src/modinfodialognexus.cpp b/src/modinfodialognexus.cpp index 6d28cbe3..95e62328 100644 --- a/src/modinfodialognexus.cpp +++ b/src/modinfodialognexus.cpp @@ -19,7 +19,7 @@ NexusTab::NexusTab(ModInfoDialogTabContext cx) : ModInfoDialogTab(std::move(cx)), m_requestStarted(false), m_loading(false) { ui->modID->setValidator(new QIntValidator(ui->modID)); - ui->endorse->setVisible(core().settings().endorsementIntegration()); + ui->endorse->setVisible(core().settings().nexus().endorsementIntegration()); connect(ui->modID, &QLineEdit::editingFinished, [&]{ onModIDChanged(); }); connect( diff --git a/src/modinfooverwrite.cpp b/src/modinfooverwrite.cpp index 37c8c650..fb110abb 100644 --- a/src/modinfooverwrite.cpp +++ b/src/modinfooverwrite.cpp @@ -23,7 +23,7 @@ bool ModInfoOverwrite::isEmpty() const QString ModInfoOverwrite::absolutePath() const { - return Settings::instance().getOverwriteDirectory(); + return Settings::instance().paths().overwrite(); } std::vector ModInfoOverwrite::getFlags() const diff --git a/src/modinforegular.cpp b/src/modinforegular.cpp index ce29e11e..3cff914a 100644 --- a/src/modinforegular.cpp +++ b/src/modinforegular.cpp @@ -631,7 +631,7 @@ std::vector ModInfoRegular::getFlags() const std::vector result = ModInfoWithConflictInfo::getFlags(); if ((m_NexusID > 0) && (endorsedState() == ENDORSED_FALSE) && - Settings::instance().endorsementIntegration()) { + Settings::instance().nexus().endorsementIntegration()) { result.push_back(ModInfo::FLAG_NOTENDORSED); } if ((m_NexusID > 0) && diff --git a/src/modlist.cpp b/src/modlist.cpp index 94b4a387..6018d3d4 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -390,7 +390,7 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const return QVariant(); } else if (role == Qt::ForegroundRole) { if (modInfo->hasFlag(ModInfo::FLAG_SEPARATOR) && modInfo->getColor().isValid()) { - return Settings::getIdealTextColor(modInfo->getColor()); + return ColorSettings::idealTextColor(modInfo->getColor()); } else if (column == COL_NAME) { int highlight = modInfo->getHighlight(); if (highlight & ModInfo::HIGHLIGHT_IMPORTANT) @@ -428,7 +428,7 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const } else if (modInfo->hasFlag(ModInfo::FLAG_SEPARATOR) && modInfo->getColor().isValid() && ((role != ViewMarkingScrollBar::DEFAULT_ROLE) - || Settings::instance().colorSeparatorScrollbar())) { + || Settings::instance().colors().colorSeparatorScrollbar())) { return modInfo->getColor(); } else { return QVariant(); @@ -999,8 +999,8 @@ bool ModList::dropURLs(const QMimeData *mimeData, int row, const QModelIndex &pa ModInfo::Ptr modInfo = ModInfo::getByIndex(row); QDir modDir = QDir(modInfo->absolutePath()); - QDir allModsDir(Settings::instance().getModDirectory()); - QDir overwriteDir(Settings::instance().getOverwriteDirectory()); + QDir allModsDir(Settings::instance().paths().mods()); + QDir overwriteDir(Settings::instance().paths().overwrite()); QStringList sourceList; QStringList targetList; diff --git a/src/nxmaccessmanager.cpp b/src/nxmaccessmanager.cpp index 16190ca4..c6ef7bc7 100644 --- a/src/nxmaccessmanager.cpp +++ b/src/nxmaccessmanager.cpp @@ -572,7 +572,7 @@ NXMAccessManager::NXMAccessManager(QObject *parent, const QString &moVersion) m_validator.finished = [&](auto&& user){ onValidatorFinished(user); }; setCookieJar(new PersistentCookieJar(QDir::fromNativeSeparators( - Settings::instance().getCacheDirectory() + "/nexus_cookies.dat"))); + Settings::instance().paths().cache() + "/nexus_cookies.dat"))); if (networkAccessible() == QNetworkAccessManager::UnknownAccessibility) { // why is this necessary all of a sudden? diff --git a/src/organizercore.cpp b/src/organizercore.cpp index af0cf969..1a89641d 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -146,7 +146,7 @@ static void startSteam(QWidget *widget) QStringList args; QString username; QString password; - if (Settings::instance().getSteamLogin(username, password)) { + if (Settings::instance().steam().login(username, password)) { args << "-login"; args << username; if (password != "") { @@ -275,12 +275,13 @@ OrganizerCore::OrganizerCore(Settings &settings) , m_ArchivesInit(false) , m_PluginListsWriter(std::bind(&OrganizerCore::savePluginList, this)) { - m_DownloadManager.setOutputDirectory(m_Settings.getDownloadDirectory()); + m_DownloadManager.setOutputDirectory(m_Settings.paths().downloads()); - NexusInterface::instance(m_PluginContainer)->setCacheDirectory(m_Settings.getCacheDirectory()); + NexusInterface::instance(m_PluginContainer)->setCacheDirectory( + m_Settings.paths().cache()); - m_InstallationManager.setModsDirectory(m_Settings.getModDirectory()); - m_InstallationManager.setDownloadDirectory(m_Settings.getDownloadDirectory()); + m_InstallationManager.setModsDirectory(m_Settings.paths().mods()); + m_InstallationManager.setDownloadDirectory(m_Settings.paths().downloads()); connect(&m_DownloadManager, SIGNAL(downloadSpeed(QString, int)), this, SLOT(downloadSpeed(QString, int))); @@ -333,7 +334,7 @@ OrganizerCore::~OrganizerCore() void OrganizerCore::storeSettings() { if (m_CurrentProfile != nullptr) { - m_Settings.setSelectedProfileName(m_CurrentProfile->name()); + m_Settings.game().setSelectedProfileName(m_CurrentProfile->name()); } m_ExecutablesList.store(m_Settings); @@ -356,7 +357,7 @@ void OrganizerCore::storeSettings() QMessageBox::critical( qApp->activeWindow(), tr("Failed to write settings"), tr("An error occurred trying to write back MO settings to %1: %2") - .arg(m_Settings.getFilename(), reason)); + .arg(m_Settings.filename(), reason)); } } @@ -432,8 +433,9 @@ void OrganizerCore::updateExecutablesList() // TODO this has nothing to do with executables list move to an appropriate // function! - ModInfo::updateFromDisc(m_Settings.getModDirectory(), &m_DirectoryStructure, - m_PluginContainer, m_Settings.displayForeign(), managedGame()); + ModInfo::updateFromDisc( + m_Settings.paths().mods(), &m_DirectoryStructure, + m_PluginContainer, m_Settings.interface().displayForeign(), managedGame()); } void OrganizerCore::setUserInterface(IUserInterface *userInterface, @@ -478,7 +480,7 @@ void OrganizerCore::setUserInterface(IUserInterface *userInterface, if (userInterface != nullptr) { // this currently wouldn't work reliably if the ui isn't initialized yet to // display the result - if (isOnline() && !m_Settings.offlineMode()) { + if (isOnline() && !m_Settings.network().offlineMode()) { m_Updater.testForUpdate(); } else { log::debug("user doesn't seem to be connected to the internet"); @@ -541,7 +543,7 @@ bool OrganizerCore::nexusApi(bool retry) return false; } else { QString apiKey; - if (m_Settings.getNexusApiKey(apiKey)) { + if (m_Settings.nexus().apiKey(apiKey)) { // credentials stored or user entered them manually log::debug("attempt to verify nexus api key"); accessManager->apiCheck(apiKey); @@ -608,7 +610,7 @@ void OrganizerCore::removeOrigin(const QString &name) void OrganizerCore::downloadSpeed(const QString &serverName, int bytesPerSecond) { - m_Settings.setDownloadSpeed(serverName, bytesPerSecond); + m_Settings.network().setDownloadSpeed(serverName, bytesPerSecond); } InstallationManager *OrganizerCore::installationManager() @@ -629,9 +631,9 @@ bool OrganizerCore::createDirectory(const QString &path) { } bool OrganizerCore::checkPathSymlinks() { - bool hasSymlink = (QFileInfo(m_Settings.getProfileDirectory()).isSymLink() || - QFileInfo(m_Settings.getModDirectory()).isSymLink() || - QFileInfo(m_Settings.getOverwriteDirectory()).isSymLink()); + bool hasSymlink = (QFileInfo(m_Settings.paths().profiles()).isSymLink() || + QFileInfo(m_Settings.paths().mods()).isSymLink() || + QFileInfo(m_Settings.paths().overwrite()).isSymLink()); if (hasSymlink) { QMessageBox::critical(nullptr, QObject::tr("Error"), QObject::tr("One of the configured MO2 directories (profiles, mods, or overwrite) " @@ -643,17 +645,17 @@ bool OrganizerCore::checkPathSymlinks() { } bool OrganizerCore::bootstrap() { - return createDirectory(m_Settings.getProfileDirectory()) && - createDirectory(m_Settings.getModDirectory()) && - createDirectory(m_Settings.getDownloadDirectory()) && - createDirectory(m_Settings.getOverwriteDirectory()) && + return createDirectory(m_Settings.paths().profiles()) && + createDirectory(m_Settings.paths().mods()) && + createDirectory(m_Settings.paths().downloads()) && + createDirectory(m_Settings.paths().overwrite()) && createDirectory(QString::fromStdWString(crashDumpsPath())) && checkPathSymlinks() && cycleDiagnostics(); } void OrganizerCore::createDefaultProfile() { - QString profilesPath = settings().getProfileDirectory(); + QString profilesPath = settings().paths().profiles(); if (QDir(profilesPath).entryList(QDir::AllDirs | QDir::NoDotAndDotDot).size() == 0) { Profile newProf("Default", managedGame(), false); @@ -674,18 +676,18 @@ void OrganizerCore::updateVFSParams( void OrganizerCore::setLogLevel(log::Levels level) { - m_Settings.setLogLevel(level); + m_Settings.diagnostics().setLogLevel(level); updateVFSParams( - m_Settings.logLevel(), - m_Settings.crashDumpsType(), + m_Settings.diagnostics().logLevel(), + m_Settings.diagnostics().crashDumpsType(), m_Settings.executablesBlacklist()); - log::getDefault().setLevel(m_Settings.logLevel()); + log::getDefault().setLevel(m_Settings.diagnostics().logLevel()); } bool OrganizerCore::cycleDiagnostics() { - if (int maxDumps = settings().crashDumpsMax()) + if (int maxDumps = settings().diagnostics().crashDumpsMax()) removeOldFiles(QString::fromStdWString(crashDumpsPath()), "*.dmp", maxDumps, QDir::Time|QDir::Reversed); return true; } @@ -720,7 +722,7 @@ void OrganizerCore::setCurrentProfile(const QString &profileName) return; } - QDir profileBaseDir(settings().getProfileDirectory()); + QDir profileBaseDir(settings().paths().profiles()); QString profileDir = profileBaseDir.absoluteFilePath(profileName); if (!QDir(profileDir).exists()) { @@ -744,7 +746,7 @@ void OrganizerCore::setCurrentProfile(const QString &profileName) m_CurrentProfile->deactivateInvalidation(); } - m_Settings.setSelectedProfileName(m_CurrentProfile->name()); + m_Settings.game().setSelectedProfileName(m_CurrentProfile->name()); connect(m_CurrentProfile, SIGNAL(modStatusChanged(uint)), this, SLOT(modStatusChanged(uint))); connect(m_CurrentProfile, SIGNAL(modStatusChanged(QList)), this, SLOT(modStatusChanged(QList))); @@ -776,22 +778,22 @@ QString OrganizerCore::profilePath() const QString OrganizerCore::downloadsPath() const { - return QDir::fromNativeSeparators(m_Settings.getDownloadDirectory()); + return QDir::fromNativeSeparators(m_Settings.paths().downloads()); } QString OrganizerCore::overwritePath() const { - return QDir::fromNativeSeparators(m_Settings.getOverwriteDirectory()); + return QDir::fromNativeSeparators(m_Settings.paths().overwrite()); } QString OrganizerCore::basePath() const { - return QDir::fromNativeSeparators(m_Settings.getBaseDirectory()); + return QDir::fromNativeSeparators(m_Settings.paths().base()); } QString OrganizerCore::modsPath() const { - return QDir::fromNativeSeparators(m_Settings.getModDirectory()); + return QDir::fromNativeSeparators(m_Settings.paths().mods()); } MOBase::VersionInfo OrganizerCore::appVersion() const @@ -821,10 +823,10 @@ MOBase::IModInterface *OrganizerCore::createMod(GuessedValue &name) return nullptr; } - m_InstallationManager.setModsDirectory(m_Settings.getModDirectory()); + m_InstallationManager.setModsDirectory(m_Settings.paths().mods()); QString targetDirectory - = QDir::fromNativeSeparators(m_Settings.getModDirectory()) + = QDir::fromNativeSeparators(m_Settings.paths().mods()) .append("/") .append(name); @@ -912,7 +914,7 @@ MOBase::IModInterface *OrganizerCore::installMod(const QString &fileName, modName.update(initModName, GUESS_USER); } m_CurrentProfile->writeModlistNow(); - m_InstallationManager.setModsDirectory(m_Settings.getModDirectory()); + m_InstallationManager.setModsDirectory(m_Settings.paths().mods()); if (m_InstallationManager.install(fileName, modName, hasIniTweaks)) { MessageDialog::showMessage(tr("Installation successful"), qApp->activeWindow()); @@ -977,7 +979,7 @@ void OrganizerCore::installDownload(int index) m_CurrentProfile->writeModlistNow(); bool hasIniTweaks = false; - m_InstallationManager.setModsDirectory(m_Settings.getModDirectory()); + m_InstallationManager.setModsDirectory(m_Settings.paths().mods()); if (m_InstallationManager.install(fileName, modName, hasIniTweaks)) { MessageDialog::showMessage(tr("Installation successful"), qApp->activeWindow()); @@ -1270,7 +1272,7 @@ bool OrganizerCore::previewFileWithAlternatives( else { // crude: we search for the next slash after the base mod directory to skip // everything up to the data-relative directory - int offset = settings().getModDirectory().size() + 1; + int offset = settings().paths().mods().size() + 1; offset = fileName.indexOf("/", offset); fileName = fileName.mid(offset + 1); } @@ -1412,7 +1414,7 @@ HANDLE OrganizerCore::spawnBinaryDirect(const QFileInfo &binary, LPDWORD exitCode) { HANDLE processHandle = spawnBinaryProcess(binary, arguments, profileName, currentDirectory, steamAppID, customOverwrite, forcedLibraries); - if (Settings::instance().lockGUI() && processHandle != INVALID_HANDLE_VALUE) { + if (Settings::instance().interface().lockGUI() && processHandle != INVALID_HANDLE_VALUE) { std::unique_ptr dlg; ILockedWaitingForProcess* uilock = nullptr; @@ -1461,7 +1463,7 @@ HANDLE OrganizerCore::spawnBinaryProcess(const QFileInfo &binary, ::SetEnvironmentVariableW(L"SteamAPPId", ToWString(steamAppID).c_str()); } else { ::SetEnvironmentVariableW(L"SteamAPPId", - ToWString(m_Settings.getSteamAppID()).c_str()); + ToWString(m_Settings.steam().appID()).c_str()); } QWidget *window = qApp->activeWindow(); @@ -1477,7 +1479,7 @@ HANDLE OrganizerCore::spawnBinaryProcess(const QFileInfo &binary, || QFileInfo(managedGame()->gameDirectory().absoluteFilePath( "steam_api64.dll")) .exists()) - && (m_Settings.getLoadMechanism() == LoadMechanism::LOAD_MODORGANIZER)) { + && (m_Settings.game().loadMechanismType() == LoadMechanism::LOAD_MODORGANIZER)) { bool steamFound = true; bool steamAccess = true; @@ -1592,7 +1594,7 @@ HANDLE OrganizerCore::spawnBinaryProcess(const QFileInfo &binary, } } - QString modsPath = settings().getModDirectory(); + QString modsPath = settings().paths().mods(); // Check if this a request with either an executable or a working directory under our mods folder // then will start the process in a virtualized "environment" with the appropriate paths fixed: @@ -1749,7 +1751,7 @@ HANDLE OrganizerCore::startApplication(const QString &executable, bool OrganizerCore::waitForApplication(HANDLE handle, LPDWORD exitCode) { - if (!Settings::instance().lockGUI()) + if (!Settings::instance().interface().lockGUI()) return true; ILockedWaitingForProcess* uilock = nullptr; @@ -1960,8 +1962,10 @@ void OrganizerCore::refreshModList(bool saveChanges) if (saveChanges) { m_CurrentProfile->writeModlistNow(true); } - ModInfo::updateFromDisc(m_Settings.getModDirectory(), &m_DirectoryStructure, - m_PluginContainer, m_Settings.displayForeign(), managedGame()); + + ModInfo::updateFromDisc( + m_Settings.paths().mods(), &m_DirectoryStructure, + m_PluginContainer, m_Settings.interface().displayForeign(), managedGame()); m_CurrentProfile->refreshModStatus(); @@ -2130,7 +2134,7 @@ void OrganizerCore::updateModsInDirectoryStructure(QMaparchivesWriter().writeImmediately(false); @@ -2156,7 +2160,7 @@ void OrganizerCore::loggedInAction(QWidget* parent, std::function f) f(); } else { QString apiKey; - if (settings().getNexusApiKey(apiKey)) { + if (settings().nexus().apiKey(apiKey)) { doAfterLogin([f]{ f(); }); NexusInterface::instance(m_PluginContainer)->getAccessManager()->apiCheck(apiKey); } else { @@ -2295,8 +2299,10 @@ void OrganizerCore::profileRefresh() { // have to refresh mods twice (again in refreshModList), otherwise the refresh // isn't complete. Not sure why - ModInfo::updateFromDisc(m_Settings.getModDirectory(), &m_DirectoryStructure, - m_PluginContainer, m_Settings.displayForeign(), managedGame()); + ModInfo::updateFromDisc( + m_Settings.paths().mods(), &m_DirectoryStructure, + m_PluginContainer, m_Settings.interface().displayForeign(), managedGame()); + m_CurrentProfile->refreshModStatus(); refreshModList(); @@ -2463,7 +2469,7 @@ void OrganizerCore::syncOverwrite() SyncOverwriteDialog syncDialog(modInfo->absolutePath(), m_DirectoryStructure, qApp->activeWindow()); if (syncDialog.exec() == QDialog::Accepted) { - syncDialog.apply(QDir::fromNativeSeparators(m_Settings.getModDirectory())); + syncDialog.apply(QDir::fromNativeSeparators(m_Settings.paths().mods())); modInfo->testValid(); refreshDirectoryStructure(); } @@ -2486,7 +2492,7 @@ std::vector OrganizerCore::activeProblems() const const auto& hookdll = oldMO1HookDll(); if (!hookdll.isEmpty()) { // This warning will now be shown every time the problems are checked, which is a bit - // of a "log spam". But since this is a sevre error which will most likely make the + // of a "log spam". But since this is a sever error which will most likely make the // game crash/freeze/etc. and is very hard to diagnose, this "log spam" will make it // easier for the user to notice the warning. log::warn("hook.dll found in game folder: {}", hookdll); @@ -2562,7 +2568,7 @@ void OrganizerCore::savePluginList() } m_PluginList.saveTo(m_CurrentProfile->getLockedOrderFileName(), m_CurrentProfile->getDeleterFileName(), - m_Settings.hideUncheckedPlugins()); + m_Settings.game().hideUncheckedPlugins()); m_PluginList.saveLoadOrder(*m_DirectoryStructure); } @@ -2574,7 +2580,7 @@ void OrganizerCore::prepareStart() m_CurrentProfile->writeModlist(); m_CurrentProfile->createTweakedIniFile(); saveCurrentLists(); - m_Settings.setupLoadMechanism(); + m_Settings.game().setupLoadMechanism(); storeSettings(); } @@ -2588,7 +2594,7 @@ std::vector OrganizerCore::fileMapping(const QString &profileName, } IPluginGame *game = qApp->property("managed_game").value(); - Profile profile(QDir(m_Settings.getProfileDirectory() + "/" + profileName), + Profile profile(QDir(m_Settings.paths().profiles() + "/" + profileName), game); MappingType result; @@ -2634,7 +2640,7 @@ std::vector OrganizerCore::fileMapping(const QString &profileName, } result.insert(result.end(), { - QDir::toNativeSeparators(m_Settings.getOverwriteDirectory()), + QDir::toNativeSeparators(m_Settings.paths().overwrite()), dataPath, true, customOverwrite.isEmpty() diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index ddfe492e..33423225 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -191,7 +191,7 @@ void PluginList::refresh(const QString &profileName continue; } - bool forceEnabled = Settings::instance().forceEnableCoreFiles() && + bool forceEnabled = Settings::instance().game().forceEnableCoreFiles() && primaryPlugins.contains(filename, Qt::CaseInsensitive); //(std::find(primaryPlugins.begin(), primaryPlugins.end(), filename.toLower()) != primaryPlugins.end()); diff --git a/src/profile.cpp b/src/profile.cpp index 7f4ebcaa..e76060b9 100644 --- a/src/profile.cpp +++ b/src/profile.cpp @@ -73,7 +73,7 @@ Profile::Profile(const QString &name, IPluginGame const *gamePlugin, bool useDef : m_ModListWriter(std::bind(&Profile::doWriteModlist, this)) , m_GamePlugin(gamePlugin) { - QString profilesDir = Settings::instance().getProfileDirectory(); + QString profilesDir = Settings::instance().paths().profiles(); QDir profileBase(profilesDir); QString fixedName = name; if (!fixDirectoryName(fixedName)) { @@ -299,7 +299,7 @@ void Profile::createTweakedIniFile() // static void Profile::renameModInAllProfiles(const QString& oldName, const QString& newName) { - QDir profilesDir(Settings::instance().getProfileDirectory()); + QDir profilesDir(Settings::instance().paths().profiles()); profilesDir.setFilter(QDir::AllDirs | QDir::NoDotAndDotDot); QDirIterator profileIter(profilesDir); while (profileIter.hasNext()) { @@ -655,7 +655,7 @@ void Profile::setModPriority(unsigned int index, int &newPriority) Profile *Profile::createPtrFrom(const QString &name, const Profile &reference, MOBase::IPluginGame const *gamePlugin) { - QString profileDirectory = Settings::instance().getProfileDirectory() + "/" + name; + QString profileDirectory = Settings::instance().paths().profiles() + "/" + name; reference.copyFilesTo(profileDirectory); return new Profile(QDir(profileDirectory), gamePlugin); } @@ -906,7 +906,7 @@ QString Profile::savePath() const void Profile::rename(const QString &newName) { - QDir profileDir(Settings::instance().getProfileDirectory()); + QDir profileDir(Settings::instance().paths().profiles()); profileDir.rename(name(), newName); m_Directory.setPath(profileDir.absoluteFilePath(newName)); } diff --git a/src/profilesdialog.cpp b/src/profilesdialog.cpp index 2f1bd059..c91f48f4 100644 --- a/src/profilesdialog.cpp +++ b/src/profilesdialog.cpp @@ -57,7 +57,7 @@ ProfilesDialog::ProfilesDialog(const QString &profileName, MOBase::IPluginGame c { ui->setupUi(this); - QDir profilesDir(Settings::instance().getProfileDirectory()); + QDir profilesDir(Settings::instance().paths().profiles()); profilesDir.setFilter(QDir::AllDirs | QDir::NoDotAndDotDot); QDirIterator profileIter(profilesDir); @@ -203,7 +203,7 @@ void ProfilesDialog::on_removeProfileButton_clicked() if (confirmBox.exec() == QMessageBox::Yes) { QString profilePath; if (profileToDelete.get() == nullptr) { - profilePath = Settings::instance().getProfileDirectory() + profilePath = Settings::instance().paths().profiles() + "/" + ui->profilesList->currentItem()->text(); if (QMessageBox::question(this, tr("Profile broken"), tr("This profile you're about to delete seems to be broken or the path is invalid. " diff --git a/src/settings.cpp b/src/settings.cpp index 71288950..8b063efb 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -150,7 +150,7 @@ std::optional getOptional( template T get( const QSettings& settings, - const QString& section, const QString& key, T def={}) + const QString& section, const QString& key, T def) { if (auto v=getOptional(settings, section, key)) { return *v; @@ -453,22 +453,74 @@ void warnIfNotCheckable(const QAbstractButton* b) } +bool setWindowsCredential(const QString key, const QString data) +{ + QString finalKey("ModOrganizer2_" + key); + wchar_t* keyData = new wchar_t[finalKey.size()+1]; + finalKey.toWCharArray(keyData); + keyData[finalKey.size()] = L'\0'; + bool result = false; + if (data.isEmpty()) { + result = CredDeleteW(keyData, CRED_TYPE_GENERIC, 0); + if (!result) + if (GetLastError() == ERROR_NOT_FOUND) + result = true; + } else { + wchar_t* charData = new wchar_t[data.size()]; + data.toWCharArray(charData); + + CREDENTIALW cred = {}; + cred.Flags = 0; + cred.Type = CRED_TYPE_GENERIC; + cred.TargetName = keyData; + cred.CredentialBlob = (LPBYTE)charData; + cred.CredentialBlobSize = sizeof(wchar_t) * data.size(); + cred.Persist = CRED_PERSIST_LOCAL_MACHINE; + + result = CredWriteW(&cred, 0); + delete[] charData; + } + delete[] keyData; + return result; +} + +QString getWindowsCredential(const QString key) +{ + QString result; + QString finalKey("ModOrganizer2_" + key); + wchar_t* keyData = new wchar_t[finalKey.size()+1]; + finalKey.toWCharArray(keyData); + keyData[finalKey.size()] = L'\0'; + PCREDENTIALW creds; + if (CredReadW(keyData, 1, 0, &creds)) { + wchar_t *charData = (wchar_t *)creds->CredentialBlob; + result = QString::fromWCharArray(charData, creds->CredentialBlobSize / sizeof(wchar_t)); + CredFree(creds); + } else { + const auto e = GetLastError(); + if (e != ERROR_NOT_FOUND) { + log::error("Retrieving encrypted data failed: {}", formatSystemMessage(e)); + } + } + delete[] keyData; + return result; +} + + Settings *Settings::s_Instance = nullptr; Settings::Settings(const QString& path) : m_Settings(path, QSettings::IniFormat), - m_Geometry(m_Settings), m_Colors(m_Settings), m_Plugins(m_Settings) + m_Game(m_Settings), m_Geometry(m_Settings), m_Widgets(m_Settings), + m_Colors(m_Settings), m_Plugins(m_Settings), m_Paths(m_Settings), + m_Network(m_Settings), m_Nexus(*this, m_Settings), m_Steam(*this, m_Settings), + m_Interface(m_Settings), m_Diagnostics(m_Settings) { if (s_Instance != nullptr) { throw std::runtime_error("second instance of \"Settings\" created"); } else { s_Instance = this; } - - MOBase::QuestionBoxMemory::setCallbacks( - [this](auto&& w, auto&& f){ return getQuestionButton(w, f); }, - [this](auto&& w, auto&& b){ setQuestionWindowButton(w, b); }, - [this](auto&& w, auto&& f, auto&& b){ setQuestionFileButton(w, f, b); }); } Settings::~Settings() @@ -488,7 +540,7 @@ Settings &Settings::instance() void Settings::processUpdates( const QVersionNumber& currentVersion, const QVersionNumber& lastVersion) { - if (getFirstStart()) { + if (firstStart()) { return; } @@ -523,1569 +575,1670 @@ void Settings::processUpdates( set(m_Settings, "General", "version", currentVersion.toString()); } -QString Settings::getFilename() const +QString Settings::filename() const { return m_Settings.fileName(); } -void Settings::registerAsNXMHandler(bool force) +bool Settings::usePrereleases() const { - const auto nxmPath = QCoreApplication::applicationDirPath() + "/nxmhandler.exe"; - const auto executable = QCoreApplication::applicationFilePath(); - - QString mode = force ? "forcereg" : "reg"; - QString parameters = mode + " " + m_GamePlugin->gameShortName(); - for (const QString& altGame : m_GamePlugin->validShortNames()) { - parameters += "," + altGame; - } - parameters += " \"" + executable + "\""; - - if (!shell::Execute(nxmPath, parameters)) { - QMessageBox::critical( - nullptr, tr("Failed"), tr("Failed to start the helper application")); - } + return get(m_Settings, "Settings", "use_prereleases", false); } -bool Settings::colorSeparatorScrollbar() const +void Settings::setUsePrereleases(bool b) { - return get(m_Settings, "Settings", "colorSeparatorScrollbars", true); + set(m_Settings, "Settings", "use_prereleases", b); } -void Settings::setColorSeparatorScrollbar(bool b) +std::optional Settings::version() const { - set(m_Settings, "Settings", "colorSeparatorScrollbars", b); + if (auto v=getOptional(m_Settings, "General", "version")) { + return QVersionNumber::fromString(*v).normalized(); + } + + return {}; } -void Settings::managedGameChanged(IPluginGame const *gamePlugin) +bool Settings::firstStart() const { - m_GamePlugin = gamePlugin; + return get(m_Settings, "General", "first_start", true); } -bool Settings::obfuscate(const QString key, const QString data) +void Settings::setFirstStart(bool b) { - QString finalKey("ModOrganizer2_" + key); - wchar_t* keyData = new wchar_t[finalKey.size()+1]; - finalKey.toWCharArray(keyData); - keyData[finalKey.size()] = L'\0'; - bool result = false; - if (data.isEmpty()) { - result = CredDeleteW(keyData, CRED_TYPE_GENERIC, 0); - if (!result) - if (GetLastError() == ERROR_NOT_FOUND) - result = true; - } else { - wchar_t* charData = new wchar_t[data.size()]; - data.toWCharArray(charData); + set(m_Settings, "General", "first_start", b); +} - CREDENTIALW cred = {}; - cred.Flags = 0; - cred.Type = CRED_TYPE_GENERIC; - cred.TargetName = keyData; - cred.CredentialBlob = (LPBYTE)charData; - cred.CredentialBlobSize = sizeof(wchar_t) * data.size(); - cred.Persist = CRED_PERSIST_LOCAL_MACHINE; +QString Settings::executablesBlacklist() const +{ + static const QString def = (QStringList() + << "Chrome.exe" + << "Firefox.exe" + << "TSVNCache.exe" + << "TGitCache.exe" + << "Steam.exe" + << "GameOverlayUI.exe" + << "Discord.exe" + << "GalaxyClient.exe" + << "Spotify.exe" + ).join(";"); - result = CredWriteW(&cred, 0); - delete[] charData; - } - delete[] keyData; - return result; + return get(m_Settings, "Settings", "executable_blacklist", def); } -QString Settings::deObfuscate(const QString key) +void Settings::setExecutablesBlacklist(const QString& s) { - QString result; - QString finalKey("ModOrganizer2_" + key); - wchar_t* keyData = new wchar_t[finalKey.size()+1]; - finalKey.toWCharArray(keyData); - keyData[finalKey.size()] = L'\0'; - PCREDENTIALW creds; - if (CredReadW(keyData, 1, 0, &creds)) { - wchar_t *charData = (wchar_t *)creds->CredentialBlob; - result = QString::fromWCharArray(charData, creds->CredentialBlobSize / sizeof(wchar_t)); - CredFree(creds); - } else { - const auto e = GetLastError(); - if (e != ERROR_NOT_FOUND) { - log::error("Retrieving encrypted data failed: {}", formatSystemMessage(e)); - } - } - delete[] keyData; - return result; + set(m_Settings, "Settings", "executable_blacklist", s); } -QColor Settings::getIdealTextColor(const QColor& rBackgroundColor) +void Settings::setMotdHash(uint hash) { - if (rBackgroundColor.alpha() == 0) - return QColor(Qt::black); - - const int THRESHOLD = 106 * 255.0f / rBackgroundColor.alpha(); - int BackgroundDelta = (rBackgroundColor.red() * 0.299) + (rBackgroundColor.green() * 0.587) + (rBackgroundColor.blue() * 0.114); - return QColor((255 - BackgroundDelta <= THRESHOLD) ? Qt::black : Qt::white); + set(m_Settings, "General", "motd_hash", hash); } - -bool Settings::hideUncheckedPlugins() const +unsigned int Settings::motdHash() const { - return get(m_Settings, "Settings", "hide_unchecked_plugins", false); + return get(m_Settings, "General", "motd_hash", 0); } -void Settings::setHideUncheckedPlugins(bool b) +bool Settings::archiveParsing() const { - set(m_Settings, "Settings", "hide_unchecked_plugins", b); + return get(m_Settings, "Settings", "archive_parsing_experimental", false); } -bool Settings::forceEnableCoreFiles() const +void Settings::setArchiveParsing(bool b) { - return get(m_Settings, "Settings", "force_enable_core_files", true); + set(m_Settings, "Settings", "archive_parsing_experimental", b); } -void Settings::setForceEnableCoreFiles(bool b) +std::vector> Settings::executables() const { - set(m_Settings, "Settings", "force_enable_core_files", b); + ScopedReadArray sra(m_Settings, "customExecutables"); + std::vector> v; + + sra.for_each([&]{ + std::map map; + + for (auto&& key : sra.keys()) { + map[key] = m_Settings.value(key); + } + + v.push_back(map); + }); + + return v; } -bool Settings::lockGUI() const +void Settings::setExecutables(const std::vector>& v) { - return get(m_Settings, "Settings", "lock_gui", true); + removeSection(m_Settings, "customExecutables"); + + ScopedWriteArray swa(m_Settings, "customExecutables"); + + for (const auto& map : v) { + swa.next(); + + for (auto&& p : map) { + swa.set(p.first, p.second); + } + } } -void Settings::setLockGUI(bool b) +bool Settings::keepBackupOnInstall() const { - set(m_Settings, "Settings", "lock_gui", b); + return get(m_Settings, "General", "backup_install", false); } -bool Settings::automaticLoginEnabled() const +void Settings::setKeepBackupOnInstall(bool b) { - return get(m_Settings, "Settings", "nexus_login", false); + set(m_Settings, "General", "backup_install", b); } -QString Settings::getSteamAppID() const +GameSettings& Settings::game() { - return get(m_Settings, "Settings", "app_id", m_GamePlugin->steamAPPId()); + return m_Game; } -void Settings::setSteamAppID(const QString& id) +const GameSettings& Settings::game() const { - if (id.isEmpty()) { - remove(m_Settings, "Settings", "app_id"); - } else { - set(m_Settings, "Settings", "app_id", id); - } + return m_Game; } -bool Settings::usePrereleases() const +GeometrySettings& Settings::geometry() { - return get(m_Settings, "Settings", "use_prereleases", false); + return m_Geometry; } -void Settings::setUsePrereleases(bool b) +const GeometrySettings& Settings::geometry() const { - set(m_Settings, "Settings", "use_prereleases", b); + return m_Geometry; } -QString Settings::getConfigurablePath(const QString &key, - const QString &def, - bool resolve) const +WidgetSettings& Settings::widgets() { - QString result = QDir::fromNativeSeparators( - get(m_Settings, "Settings", key, QString("%BASE_DIR%/") + def)); - - if (resolve) { - result.replace("%BASE_DIR%", getBaseDirectory()); - } - - return result; + return m_Widgets; } -void Settings::setConfigurablePath(const QString &key, const QString& path) +const WidgetSettings& Settings::widgets() const { - if (path.isEmpty()) { - remove(m_Settings, "Settings", key); - } else { - set(m_Settings, "Settings", key, path); - } + return m_Widgets; } -QString Settings::getBaseDirectory() const +ColorSettings& Settings::colors() { - return QDir::fromNativeSeparators(get(m_Settings, - "Settings", "base_directory", qApp->property("dataPath").toString())); + return m_Colors; } -QString Settings::getDownloadDirectory(bool resolve) const +const ColorSettings& Settings::colors() const { - return getConfigurablePath( - "download_directory", - ToQString(AppConfig::downloadPath()), - resolve); + return m_Colors; } -QString Settings::getCacheDirectory(bool resolve) const +PluginSettings& Settings::plugins() { - return getConfigurablePath( - "cache_directory", - ToQString(AppConfig::cachePath()), - resolve); + return m_Plugins; } -QString Settings::getModDirectory(bool resolve) const +const PluginSettings& Settings::plugins() const { - return getConfigurablePath( - "mod_directory", - ToQString(AppConfig::modsPath()), - resolve); + return m_Plugins; } -QString Settings::getProfileDirectory(bool resolve) const +PathSettings& Settings::paths() { - return getConfigurablePath( - "profiles_directory", - ToQString(AppConfig::profilesPath()), - resolve); + return m_Paths; } -QString Settings::getOverwriteDirectory(bool resolve) const +const PathSettings& Settings::paths() const { - return getConfigurablePath( - "overwrite_directory", - ToQString(AppConfig::overwritePath()), - resolve); + return m_Paths; } -void Settings::setBaseDirectory(const QString& path) +NetworkSettings& Settings::network() { - if (path.isEmpty()) { - remove(m_Settings, "Settings", "base_directory"); - } else { - set(m_Settings, "Settings", "base_directory", path); - } + return m_Network; } -void Settings::setDownloadDirectory(const QString& path) +const NetworkSettings& Settings::network() const { - setConfigurablePath("download_directory", path); + return m_Network; } -void Settings::setModDirectory(const QString& path) +NexusSettings& Settings::nexus() { - setConfigurablePath("mod_directory", path); + return m_Nexus; } -void Settings::setCacheDirectory(const QString& path) +const NexusSettings& Settings::nexus() const { - setConfigurablePath("cache_directory", path); + return m_Nexus; } -void Settings::setProfileDirectory(const QString& path) +SteamSettings& Settings::steam() { - setConfigurablePath("profiles_directory", path); + return m_Steam; } -void Settings::setOverwriteDirectory(const QString& path) +const SteamSettings& Settings::steam() const { - setConfigurablePath("overwrite_directory", path); + return m_Steam; } -std::optional Settings::getManagedGameDirectory() const +InterfaceSettings& Settings::interface() { - if (auto v=getOptional(m_Settings, "General", "gamePath")) { - return QString::fromUtf8(*v); - } - - return {}; + return m_Interface; } -void Settings::setManagedGameDirectory(const QString& path) +const InterfaceSettings& Settings::interface() const { - set(m_Settings, "General", "gamePath", QDir::toNativeSeparators(path).toUtf8()); + return m_Interface; } -std::optional Settings::getManagedGameName() const +DiagnosticsSettings& Settings::diagnostics() { - return getOptional(m_Settings, "General", "gameName"); + return m_Diagnostics; } -void Settings::setManagedGameName(const QString& name) +const DiagnosticsSettings& Settings::diagnostics() const { - set(m_Settings, "General", "gameName", name); + return m_Diagnostics; } -std::optional Settings::getManagedGameEdition() const +QSettings::Status Settings::sync() const { - return getOptional(m_Settings, "General", "game_edition"); + m_Settings.sync(); + return m_Settings.status(); } -void Settings::setManagedGameEdition(const QString& name) +void Settings::dump() const { - set(m_Settings, "General", "game_edition", name); -} + static const QStringList ignore({ + "username", "password", "nexus_api_key" + }); -std::optional Settings::getSelectedProfileName() const -{ - if (auto v=getOptional(m_Settings, "General", "selected_profile")) { - return QString::fromUtf8(*v); + log::debug("settings:"); + + { + ScopedGroup sg(m_Settings, "Settings"); + + for (auto k : m_Settings.allKeys()) { + if (ignore.contains(k, Qt::CaseInsensitive)) { + continue; + } + + log::debug(" . {}={}", k, m_Settings.value(k).toString()); + } } - return {}; + m_Network.dump(); } -void Settings::setSelectedProfileName(const QString& name) +void Settings::managedGameChanged(IPluginGame const *gamePlugin) { - set(m_Settings, "General", "selected_profile", name.toUtf8()); + m_Game.setPlugin(gamePlugin); } -std::optional Settings::getStyleName() const + +GameSettings::GameSettings(QSettings& settings) + : m_Settings(settings), m_GamePlugin(nullptr) { - return getOptional(m_Settings, "Settings", "style"); } -void Settings::setStyleName(const QString& name) +const MOBase::IPluginGame* GameSettings::plugin() { - set(m_Settings, "Settings", "style", name); + return m_GamePlugin; } -bool Settings::getUseProxy() const +void GameSettings::setPlugin(const MOBase::IPluginGame* gamePlugin) { - return get(m_Settings, "Settings", "use_proxy", false); + m_GamePlugin = gamePlugin; } -void Settings::setUseProxy(bool b) +bool GameSettings::forceEnableCoreFiles() const { - set(m_Settings, "Settings", "use_proxy", b); + return get(m_Settings, "Settings", "force_enable_core_files", true); } -std::optional Settings::getVersion() const +void GameSettings::setForceEnableCoreFiles(bool b) { - if (auto v=getOptional(m_Settings, "General", "version")) { - return QVersionNumber::fromString(*v).normalized(); - } - - return {}; + set(m_Settings, "Settings", "force_enable_core_files", b); } -bool Settings::getFirstStart() const +std::optional GameSettings::directory() const { - return get(m_Settings, "General", "first_start", true); + if (auto v=getOptional(m_Settings, "General", "gamePath")) { + return QString::fromUtf8(*v); + } + + return {}; } -void Settings::setFirstStart(bool b) +void GameSettings::setDirectory(const QString& path) { - set(m_Settings, "General", "first_start", b); + set(m_Settings, "General", "gamePath", QDir::toNativeSeparators(path).toUtf8()); } -std::optional Settings::getPreviousSeparatorColor() const +std::optional GameSettings::name() const { - const auto c = getOptional(m_Settings, "General", "previousSeparatorColor"); - if (c && c->isValid()) { - return c; - } - - return {}; + return getOptional(m_Settings, "General", "gameName"); } -void Settings::setPreviousSeparatorColor(const QColor& c) const +void GameSettings::setName(const QString& name) { - set(m_Settings, "General", "previousSeparatorColor", c); + set(m_Settings, "General", "gameName", name); } -void Settings::removePreviousSeparatorColor() +std::optional GameSettings::edition() const { - remove(m_Settings, "General", "previousSeparatorColor"); + return getOptional(m_Settings, "General", "game_edition"); } -bool Settings::getNexusApiKey(QString &apiKey) const +void GameSettings::setEdition(const QString& name) { - QString tempKey = deObfuscate("APIKEY"); - if (tempKey.isEmpty()) - return false; - - apiKey = tempKey; - return true; + set(m_Settings, "General", "game_edition", name); } -bool Settings::setNexusApiKey(const QString& apiKey) +std::optional GameSettings::selectedProfileName() const { - if (!obfuscate("APIKEY", apiKey)) { - const auto e = GetLastError(); - log::error("Storing API key failed: {}", formatSystemMessage(e)); - return false; + if (auto v=getOptional(m_Settings, "General", "selected_profile")) { + return QString::fromUtf8(*v); } - return true; + return {}; } -bool Settings::clearNexusApiKey() +void GameSettings::setSelectedProfileName(const QString& name) { - return setNexusApiKey(""); + set(m_Settings, "General", "selected_profile", name.toUtf8()); } -bool Settings::hasNexusApiKey() const +LoadMechanism::EMechanism GameSettings::loadMechanismType() const { - return !deObfuscate("APIKEY").isEmpty(); -} + const auto def = LoadMechanism::LOAD_MODORGANIZER; -bool Settings::getSteamLogin(QString &username, QString &password) const -{ - username = get(m_Settings, "Settings", "steam_username", ""); - password = deObfuscate("steam_password"); + const auto i = get(m_Settings, + "Settings", "load_mechanism", def); - return !username.isEmpty() && !password.isEmpty(); -} + switch (i) + { + // ok + case LoadMechanism::LOAD_MODORGANIZER: // fall-through + { + break; + } -bool Settings::compactDownloads() const -{ - return get(m_Settings, "Settings", "compact_downloads", false); -} + default: + { + log::error( + "invalid load mechanism {}, reverting to {}", + static_cast(i), toString(def)); -void Settings::setCompactDownloads(bool b) -{ - set(m_Settings, "Settings", "compact_downloads", b); -} + set(m_Settings, "Settings", "load_mechanism", def); -bool Settings::metaDownloads() const -{ - return get(m_Settings, "Settings", "meta_downloads", false); -} + return def; + } + } -void Settings::setMetaDownloads(bool b) -{ - set(m_Settings, "Settings", "meta_downloads", b); + return i; } -bool Settings::offlineMode() const +void GameSettings::setLoadMechanism(LoadMechanism::EMechanism m) { - return get(m_Settings, "Settings/offline_mode", false); + set(m_Settings, "Settings", "load_mechanism", m); } -void Settings::setOfflineMode(bool b) +const LoadMechanism& GameSettings::loadMechanism() const { - set(m_Settings, "Settings", "offline_mode", b); + return m_LoadMechanism; } -log::Levels Settings::logLevel() const +void GameSettings::setupLoadMechanism() { - return get(m_Settings, "Settings", "log_level", log::Levels::Info); + m_LoadMechanism.activate(loadMechanismType()); } -void Settings::setLogLevel(log::Levels level) +bool GameSettings::hideUncheckedPlugins() const { - set(m_Settings, "Settings", "log_level", level); + return get(m_Settings, "Settings", "hide_unchecked_plugins", false); } -CrashDumpsType Settings::crashDumpsType() const +void GameSettings::setHideUncheckedPlugins(bool b) { - return get(m_Settings, - "Settings", "crash_dumps_type", CrashDumpsType::Mini); + set(m_Settings, "Settings", "hide_unchecked_plugins", b); } -void Settings::setCrashDumpsType(CrashDumpsType type) -{ - set(m_Settings, "Settings", "crash_dumps_type", type); -} -int Settings::crashDumpsMax() const +GeometrySettings::GeometrySettings(QSettings& s) + : m_Settings(s), m_Reset(false) { - return get(m_Settings, "Settings", "crash_dumps_max", 5); } -void Settings::setCrashDumpsMax(int n) +void GeometrySettings::requestReset() { - set(m_Settings, "Settings", "crash_dumps_max", n); + m_Reset = true; } -QString Settings::executablesBlacklist() const +void GeometrySettings::resetIfNeeded() { - static const QString def = (QStringList() - << "Chrome.exe" - << "Firefox.exe" - << "TSVNCache.exe" - << "TGitCache.exe" - << "Steam.exe" - << "GameOverlayUI.exe" - << "Discord.exe" - << "GalaxyClient.exe" - << "Spotify.exe" - ).join(";"); + if (!m_Reset) { + return; + } - return get(m_Settings, "Settings", "executable_blacklist", def); + removeSection(m_Settings, "Geometry"); } -void Settings::setExecutablesBlacklist(const QString& s) +void GeometrySettings::saveGeometry(const QWidget* w) { - set(m_Settings, "Settings", "executable_blacklist", s); + set(m_Settings, "Geometry", geoSettingName(w), w->saveGeometry()); } -void Settings::setSteamLogin(QString username, QString password) +bool GeometrySettings::restoreGeometry(QWidget* w) const { - if (username == "") { - remove(m_Settings, "Settings", "steam_username"); - password = ""; - } else { - set(m_Settings, "Settings", "steam_username", username); + if (auto v=getOptional(m_Settings, "Geometry", geoSettingName(w))) { + w->restoreGeometry(*v); + return true; } - if (!obfuscate("steam_password", password)) { - const auto e = GetLastError(); - log::error("Storing or deleting password failed: {}", formatSystemMessage(e)); - } + return false; } -LoadMechanism::EMechanism Settings::getLoadMechanism() const +void GeometrySettings::saveState(const QMainWindow* w) { - const auto def = LoadMechanism::LOAD_MODORGANIZER; - - const auto i = get(m_Settings, - "Settings", "load_mechanism", def); - - switch (i) - { - // ok - case LoadMechanism::LOAD_MODORGANIZER: // fall-through - { - break; - } - - default: - { - log::error( - "invalid load mechanism {}, reverting to {}", - static_cast(i), toString(def)); - - set(m_Settings, "Settings", "load_mechanism", def); - - return def; - } - } - - return i; + set(m_Settings, "Geometry", stateSettingName(w), w->saveState()); } -void Settings::setLoadMechanism(LoadMechanism::EMechanism m) +bool GeometrySettings::restoreState(QMainWindow* w) const { - set(m_Settings, "Settings", "load_mechanism", m); -} + if (auto v=getOptional(m_Settings, "Geometry", stateSettingName(w))) { + w->restoreState(*v); + return true; + } -void Settings::setupLoadMechanism() -{ - m_LoadMechanism.activate(getLoadMechanism()); + return false; } -bool Settings::endorsementIntegration() const +void GeometrySettings::saveState(const QHeaderView* w) { - return get(m_Settings, "Settings", "endorsement_integration", true); + set(m_Settings, "Geometry", stateSettingName(w), w->saveState()); } -void Settings::setEndorsementIntegration(bool b) const +bool GeometrySettings::restoreState(QHeaderView* w) const { - set(m_Settings, "Settings", "endorsement_integration", b); + if (auto v=getOptional(m_Settings, "Geometry", stateSettingName(w))) { + w->restoreState(*v); + return true; + } + + return false; } -EndorsementState Settings::endorsementState() const +void GeometrySettings::saveState(const QSplitter* w) { - return endorsementStateFromString( - get(m_Settings, "General", "endorse_state", "")); + set(m_Settings, "Geometry", stateSettingName(w), w->saveState()); } -void Settings::setEndorsementState(EndorsementState s) +bool GeometrySettings::restoreState(QSplitter* w) const { - const auto v = toString(s); - - if (v.isEmpty()) { - remove(m_Settings, "General", "endorse_state"); - } else { - set(m_Settings, "General", "endorse_state", v); + if (auto v=getOptional(m_Settings, "Geometry", stateSettingName(w))) { + w->restoreState(*v); + return true; } -} -bool Settings::hideAPICounter() const -{ - return get(m_Settings, "Settings", "hide_api_counter", false); + return false; } -void Settings::setHideAPICounter(bool b) +void GeometrySettings::saveState(const ExpanderWidget* expander) { - set(m_Settings, "Settings", "hide_api_counter", b); + set(m_Settings, "Geometry", stateSettingName(expander), expander->saveState()); } -bool Settings::displayForeign() const +bool GeometrySettings::restoreState(ExpanderWidget* expander) const { - return get(m_Settings, "Settings", "display_foreign", true); -} + if (auto v=getOptional(m_Settings, "Geometry", stateSettingName(expander))) { + expander->restoreState(*v); + return true; + } -void Settings::setDisplayForeign(bool b) -{ - set(m_Settings, "Settings", "display_foreign", b); + return false; } -void Settings::setMotDHash(uint hash) +void GeometrySettings::saveVisibility(const QWidget* w) { - set(m_Settings, "General", "motd_hash", hash); + set(m_Settings, "Geometry", visibilitySettingName(w), w->isVisible()); } -unsigned int Settings::getMotDHash() const +bool GeometrySettings::restoreVisibility(QWidget* w, std::optional def) const { - return get(m_Settings, "motd_hash", 0); -} + if (auto v=getOptional(m_Settings, "Geometry", visibilitySettingName(w), def)) { + w->setVisible(*v); + return true; + } -bool Settings::archiveParsing() const -{ - return get(m_Settings, "Settings", "archive_parsing_experimental", false); + return false; } -void Settings::setArchiveParsing(bool b) +void GeometrySettings::restoreToolbars(QMainWindow* w) const { - set(m_Settings, "Settings", "archive_parsing_experimental", b); -} + // all toolbars have the same size and button style settings + const auto size = getOptional(m_Settings, "Geometry", "toolbar_size"); + const auto style = getOptional(m_Settings, "Geometry", "toolbar_button_style"); -QString Settings::language() + for (auto* tb : w->findChildren()) { + if (size) { + tb->setIconSize(*size); + } + + if (style) { + tb->setToolButtonStyle(static_cast(*style)); + } + + restoreVisibility(tb); + } +} + +void GeometrySettings::saveToolbars(const QMainWindow* w) { - QString result = get(m_Settings, "Settings", "language", ""); + const auto tbs = w->findChildren(); - if (result.isEmpty()) { - QStringList languagePreferences = QLocale::system().uiLanguages(); + // save visibility for all + for (auto* tb : tbs) { + saveVisibility(tb); + } - if (languagePreferences.length() > 0) { - // the users most favoritest language - result = languagePreferences.at(0); - } else { - // fallback system locale - result = QLocale::system().name(); + // all toolbars have the same size and button style settings, just save the + // first one + if (!tbs.isEmpty()) { + const auto* tb = tbs[0]; + + set(m_Settings, "Geometry", "toolbar_size", tb->iconSize()); + set(m_Settings, "Geometry", "toolbar_button_style", static_cast(tb->toolButtonStyle())); + } +} + +QStringList GeometrySettings::modInfoTabOrder() const +{ + QStringList v; + + if (m_Settings.contains("mod_info_tabs")) { + // old byte array from 2.2.0 + QDataStream stream(m_Settings.value("mod_info_tabs").toByteArray()); + + int count = 0; + stream >> count; + + for (int i=0; i> s; + v.push_back(s); + } + } else { + // string list since 2.2.1 + QString string = m_Settings.value("mod_info_tab_order").toString(); + QTextStream stream(&string); + + while (!stream.atEnd()) { + QString s; + stream >> s; + v.push_back(s); } } - return result; + return v; } -void Settings::setLanguage(const QString& name) +void GeometrySettings::setModInfoTabOrder(const QString& names) { - set(m_Settings, "Settings", "language", name); + set(m_Settings, "Geometry", "mod_info_tab_order", names); } -void Settings::setDownloadSpeed(const QString& name, int bytesPerSecond) +void GeometrySettings::centerOnMainWindowMonitor(QWidget* w) { - auto servers = getServers(); + const auto monitor = getOptional( + m_Settings, "Geometry", "MainWindow_monitor"); - for (auto& server : servers) { - if (server.name() == name) { - server.addDownload(bytesPerSecond); - updateServers(servers); - return; + QPoint center; + + if (monitor && QGuiApplication::screens().size() > *monitor) { + center = QGuiApplication::screens().at(*monitor)->geometry().center(); + } else { + center = QGuiApplication::primaryScreen()->geometry().center(); + } + + w->move(center - w->rect().center()); +} + +void GeometrySettings::saveMainWindowMonitor(const QMainWindow* w) +{ + if (auto* handle=w->windowHandle()) { + if (auto* screen = handle->screen()) { + const int screenId = QGuiApplication::screens().indexOf(screen); + set(m_Settings, "Geometry", "MainWindow_monitor", screenId); } } +} - log::error( - "server '{}' not found while trying to add a download with bps {}", - name, bytesPerSecond); +Qt::Orientation dockOrientation(const QMainWindow* mw, const QDockWidget* d) +{ + // docks in these areas are horizontal + const auto horizontalAreas = + Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea; + + if (mw->dockWidgetArea(const_cast(d)) & horizontalAreas) { + return Qt::Horizontal; + } else { + return Qt::Vertical; + } } -ServerList Settings::getServers() const +void GeometrySettings::saveDocks(const QMainWindow* mw) { - // servers used to be a map of byte arrays until 2.2.1, it's now an array of - // individual values instead + // this attempts to fix https://bugreports.qt.io/browse/QTBUG-46620 where dock + // sizes are not restored when the main window is maximized; it is used in + // MainWindow::readSettings() and MainWindow::storeSettings() + // + // there's also https://stackoverflow.com/questions/44005852, which has what + // seems to be a popular fix, but it breaks the restored size of the window + // by setting it to the desktop's resolution, so that doesn't work + // + // the only fix I could find is to remember the sizes of the docks and manually + // setting them back; saving is straightforward, but restoring is messy + // + // this also depends on the window being visible before the timer in restore() + // is fired and the timer must be processed by application.exec(); therefore, + // the splash screen _must_ be closed before readSettings() is called, because + // it has its own event loop, which seems to interfere with this + // + // all of this should become unnecessary when QTBUG-46620 is fixed // - // so post 2.2.1, only one key is returned: "size", the size of the arrays; - // in 2.2.1, one key per server is returned - { - const QStringList keys = ScopedGroup(m_Settings, "Servers").keys(); - if (!keys.empty() && keys[0] != "size") { - // old format - return getServersFromOldMap(); + // saves the size of each dock + for (const auto* dock : mw->findChildren()) { + int size = 0; + + // save the width for horizontal docks, or the height for vertical + if (dockOrientation(mw, dock) == Qt::Horizontal) { + size = dock->size().width(); + } else { + size = dock->size().height(); } + + set(m_Settings, "Geometry", dockSettingName(dock), size); } +} +void GeometrySettings::restoreDocks(QMainWindow* mw) const +{ + struct DockInfo + { + QDockWidget* d; + int size = 0; + Qt::Orientation ori; + }; - // post 2.2.1 format, array of values + std::vector dockInfos; - ServerList list; + // for each dock + for (auto* dock : mw->findChildren()) { + if (auto size=getOptional(m_Settings, "Geometry", dockSettingName(dock))) { + // remember this dock, its size and orientation + dockInfos.push_back({dock, *size, dockOrientation(mw, dock)}); + } + } - { - ScopedReadArray sra(m_Settings, "Servers"); + // the main window must have had time to process the settings from + // readSettings() or it seems to override whatever is set here + // + // some people said a single processEvents() call is enough, but it doesn't + // look like it + QTimer::singleShot(5, [=] { + for (const auto& info : dockInfos) { + mw->resizeDocks({info.d}, {info.size}, info.ori); + } + }); +} - sra.for_each([&] { - ServerInfo::SpeedList lastDownloads; - const auto lastDownloadsString = sra.get("lastDownloads", ""); +WidgetSettings::WidgetSettings(QSettings& s) + : m_Settings(s) +{ + MOBase::QuestionBoxMemory::setCallbacks( + [this](auto&& w, auto&& f){ return questionButton(w, f); }, + [this](auto&& w, auto&& b){ setQuestionWindowButton(w, b); }, + [this](auto&& w, auto&& f, auto&& b){ setQuestionFileButton(w, f, b); }); +} - for (const auto& s : lastDownloadsString.split(" ")) { - const auto bytesPerSecond = s.toInt(); - if (bytesPerSecond > 0) { - lastDownloads.push_back(bytesPerSecond); - } - } +std::optional WidgetSettings::index(const QComboBox* cb) const +{ + return getOptional(m_Settings, "Widgets", indexSettingName(cb)); +} - ServerInfo server( - sra.get("name", ""), - sra.get("premium", false), - QDate::fromString(sra.get("lastSeen", ""), Qt::ISODate), - sra.get("preferred", 0), - lastDownloads); +void WidgetSettings::saveIndex(const QComboBox* cb) +{ + set(m_Settings, "Widgets", indexSettingName(cb), cb->currentIndex()); +} - list.add(std::move(server)); - }); +void WidgetSettings::restoreIndex(QComboBox* cb, std::optional def) const +{ + if (auto v=getOptional(m_Settings, "Widgets", indexSettingName(cb), def)) { + cb->setCurrentIndex(*v); } - - return list; } -ServerList Settings::getServersFromOldMap() const +std::optional WidgetSettings::index(const QTabWidget* w) const { - // for 2.2.1 and before + return getOptional(m_Settings, "Widgets", indexSettingName(w)); +} - ServerList list; - const ScopedGroup sg(m_Settings, "Servers"); +void WidgetSettings::saveIndex(const QTabWidget* w) +{ + set(m_Settings, "Widgets", indexSettingName(w), w->currentIndex()); +} - sg.for_each([&](auto&& serverKey) { - QVariantMap data = sg.get(serverKey); +void WidgetSettings::restoreIndex(QTabWidget* w, std::optional def) const +{ + if (auto v=getOptional(m_Settings, "Widgets", indexSettingName(w), def)) { + w->setCurrentIndex(*v); + } +} - ServerInfo server( - serverKey, - data["premium"].toBool(), - data["lastSeen"].toDate(), - data["preferred"].toInt(), - {}); +std::optional WidgetSettings::checked(const QAbstractButton* w) const +{ + warnIfNotCheckable(w); + return getOptional(m_Settings, "Widgets", checkedSettingName(w)); +} - // ignoring download count and speed, it's now a list of values instead of - // a total +void WidgetSettings::saveChecked(const QAbstractButton* w) +{ + warnIfNotCheckable(w); + set(m_Settings, "Widgets", checkedSettingName(w), w->isChecked()); +} - list.add(std::move(server)); - }); +void WidgetSettings::restoreChecked(QAbstractButton* w, std::optional def) const +{ + warnIfNotCheckable(w); - return list; + if (auto v=getOptional(m_Settings, "Widgets", checkedSettingName(w), def)) { + w->setChecked(*v); + } } -void Settings::updateServers(ServerList servers) +QuestionBoxMemory::Button WidgetSettings::questionButton( + const QString& windowName, const QString& filename) const { - // clean up unavailable servers - servers.cleanup(); - - removeSection(m_Settings, "Servers"); + const QString sectionName("DialogChoices"); - { - ScopedWriteArray swa(m_Settings, "Servers"); + if (!filename.isEmpty()) { + const auto fileSetting = windowName + "/" + filename; + if (auto v=getOptional(m_Settings, sectionName, filename)) { + return static_cast(*v); + } + } - for (const auto& server : servers) { - swa.next(); + if (auto v=getOptional(m_Settings, sectionName, windowName)) { + return static_cast(*v); + } - swa.set("name", server.name()); - swa.set("premium", server.isPremium()); - swa.set("lastSeen", server.lastSeen().toString(Qt::ISODate)); - swa.set("preferred", server.preferred()); + return QuestionBoxMemory::NoButton; +} - QString lastDownloads; - for (const auto& speed : server.lastDownloads()) { - if (speed > 0) { - lastDownloads += QString("%1 ").arg(speed); - } - } +void WidgetSettings::setQuestionWindowButton( + const QString& windowName, QuestionBoxMemory::Button button) +{ + const QString sectionName("DialogChoices"); - swa.set("lastDownloads", lastDownloads.trimmed()); - } + if (button == QuestionBoxMemory::NoButton) { + remove(m_Settings, sectionName, windowName); + } else { + set(m_Settings, sectionName, windowName, button); } } -std::map Settings::getRecentDirectories() const +void WidgetSettings::setQuestionFileButton( + const QString& windowName, const QString& filename, + QuestionBoxMemory::Button button) { - std::map map; + const QString sectionName("DialogChoices"); + const QString settingName(windowName + "/" + filename); - ScopedReadArray sra(m_Settings, "RecentDirectories"); + if (button == QuestionBoxMemory::NoButton) { + remove(m_Settings, sectionName, settingName); + } else { + set(m_Settings, sectionName, settingName, button); + } +} - sra.for_each([&] { - const QVariant name = sra.get("name"); - const QVariant dir = sra.get("directory"); +void WidgetSettings::resetQuestionButtons() +{ + removeSection(m_Settings, "DialogChoices"); +} - if (name.isValid() && dir.isValid()) { - map.emplace(name.toString(), dir.toString()); - } - }); - return map; +ColorSettings::ColorSettings(QSettings& s) + : m_Settings(s) +{ } -void Settings::setRecentDirectories(const std::map& map) +QColor ColorSettings::modlistOverwrittenLoose() const { - removeSection(m_Settings, "RecentDirectories"); + return get( + m_Settings, "Settings", "overwrittenLooseFilesColor", + QColor(0, 255, 0, 64)); +} - ScopedWriteArray swa(m_Settings, "recentDirectories"); +void ColorSettings::setModlistOverwrittenLoose(const QColor& c) +{ + set(m_Settings, "Settings", "overwrittenLooseFilesColor", c); +} - for (auto&& p : map) { - swa.next(); +QColor ColorSettings::modlistOverwritingLoose() const +{ + return get( + m_Settings, "Settings", "overwritingLooseFilesColor", + QColor(255, 0, 0, 64)); +} - swa.set("name", p.first); - swa.set("directory", p.second); - } +void ColorSettings::setModlistOverwritingLoose(const QColor& c) +{ + set(m_Settings, "Settings", "overwritingLooseFilesColor", c); } -std::vector> Settings::getExecutables() const +QColor ColorSettings::modlistOverwrittenArchive() const { - ScopedReadArray sra(m_Settings, "customExecutables"); - std::vector> v; + return get( + m_Settings, "Settings", "overwrittenArchiveFilesColor", + QColor(0, 255, 255, 64)); +} - sra.for_each([&]{ - std::map map; +void ColorSettings::setModlistOverwrittenArchive(const QColor& c) +{ + set(m_Settings, "Settings", "overwrittenArchiveFilesColor", c); +} - for (auto&& key : sra.keys()) { - map[key] = m_Settings.value(key); - } +QColor ColorSettings::modlistOverwritingArchive() const +{ + return get( + m_Settings, "Settings", "overwritingArchiveFilesColor", + QColor(255, 0, 255, 64)); +} - v.push_back(map); - }); +void ColorSettings::setModlistOverwritingArchive(const QColor& c) +{ + set(m_Settings, "Settings", "overwritingArchiveFilesColor", c); +} - return v; +QColor ColorSettings::modlistContainsPlugin() const +{ + return get( + m_Settings, "Settings", "containsPluginColor", + QColor(0, 0, 255, 64)); } -void Settings::setExecutables(const std::vector>& v) +void ColorSettings::setModlistContainsPlugin(const QColor& c) { - removeSection(m_Settings, "customExecutables"); + set(m_Settings, "Settings", "containsPluginColor", c); +} - ScopedWriteArray swa(m_Settings, "customExecutables"); +QColor ColorSettings::pluginListContained() const +{ + return get( + m_Settings, "Settings", "containedColor", + QColor(0, 0, 255, 64)); +} - for (const auto& map : v) { - swa.next(); +void ColorSettings::setPluginListContained(const QColor& c) +{ + set(m_Settings, "Settings", "containedColor", c); +} - for (auto&& p : map) { - swa.set(p.first, p.second); - } +std::optional ColorSettings::previousSeparatorColor() const +{ + const auto c = getOptional(m_Settings, "General", "previousSeparatorColor"); + if (c && c->isValid()) { + return c; } + + return {}; } -bool Settings::isTutorialCompleted(const QString& windowName) const +void ColorSettings::setPreviousSeparatorColor(const QColor& c) const { - return get(m_Settings, "CompletedWindowTutorials", windowName, false); + set(m_Settings, "General", "previousSeparatorColor", c); } -void Settings::setTutorialCompleted(const QString& windowName, bool b) +void ColorSettings::removePreviousSeparatorColor() { - set(m_Settings, "CompletedWindowTutorials", windowName, b); + remove(m_Settings, "General", "previousSeparatorColor"); } -bool Settings::keepBackupOnInstall() const +bool ColorSettings::colorSeparatorScrollbar() const { - return get(m_Settings, "backup_install", false); + return get(m_Settings, "Settings", "colorSeparatorScrollbars", true); } -void Settings::setKeepBackupOnInstall(bool b) +void ColorSettings::setColorSeparatorScrollbar(bool b) { - set(m_Settings, "General", "backup_install", b); + set(m_Settings, "Settings", "colorSeparatorScrollbars", b); } -QuestionBoxMemory::Button Settings::getQuestionButton( - const QString& windowName, const QString& filename) const +QColor ColorSettings::idealTextColor(const QColor& rBackgroundColor) { - const QString sectionName("DialogChoices"); + if (rBackgroundColor.alpha() == 0) + return QColor(Qt::black); - if (!filename.isEmpty()) { - const auto fileSetting = windowName + "/" + filename; - if (auto v=getOptional(m_Settings, sectionName, filename)) { - return static_cast(*v); + const int THRESHOLD = 106 * 255.0f / rBackgroundColor.alpha(); + int BackgroundDelta = (rBackgroundColor.red() * 0.299) + (rBackgroundColor.green() * 0.587) + (rBackgroundColor.blue() * 0.114); + return QColor((255 - BackgroundDelta <= THRESHOLD) ? Qt::black : Qt::white); +} + + + +PluginSettings::PluginSettings(QSettings& settings) + : m_Settings(settings) +{ +} + +void PluginSettings::clearPlugins() +{ + m_Plugins.clear(); + m_PluginSettings.clear(); + + m_PluginBlacklist.clear(); + + ScopedReadArray sra(m_Settings, "pluginBlacklist"); + sra.for_each([&]{ + m_PluginBlacklist.insert(sra.get("name")); + }); +} + +void PluginSettings::registerPlugin(IPlugin *plugin) +{ + m_Plugins.push_back(plugin); + m_PluginSettings.insert(plugin->name(), QVariantMap()); + m_PluginDescriptions.insert(plugin->name(), QVariantMap()); + + for (const PluginSetting &setting : plugin->settings()) { + const QString settingName = plugin->name() + "/" + setting.key; + + QVariant temp = get( + m_Settings, "Plugins", settingName, setting.defaultValue); + + if (!temp.convert(setting.defaultValue.type())) { + log::warn( + "failed to interpret \"{}\" as correct type for \"{}\" in plugin \"{}\", using default", + temp.toString(), setting.key, plugin->name()); + + temp = setting.defaultValue; } - } - if (auto v=getOptional(m_Settings, sectionName, windowName)) { - return static_cast(*v); + m_PluginSettings[plugin->name()][setting.key] = temp; + + m_PluginDescriptions[plugin->name()][setting.key] = QString("%1 (default: %2)") + .arg(setting.description) + .arg(setting.defaultValue.toString()); } +} - return QuestionBoxMemory::NoButton; +bool PluginSettings::pluginBlacklisted(const QString &fileName) const +{ + return m_PluginBlacklist.contains(fileName); } -void Settings::setQuestionWindowButton( - const QString& windowName, QuestionBoxMemory::Button button) +QVariant PluginSettings::pluginSetting(const QString &pluginName, const QString &key) const { - const QString sectionName("DialogChoices/"); + auto iterPlugin = m_PluginSettings.find(pluginName); + if (iterPlugin == m_PluginSettings.end()) { + return QVariant(); + } - if (button == QuestionBoxMemory::NoButton) { - remove(m_Settings, sectionName, windowName); - } else { - set(m_Settings, sectionName, windowName, button); + auto iterSetting = iterPlugin->find(key); + if (iterSetting == iterPlugin->end()) { + return QVariant(); } + + return *iterSetting; } -void Settings::setQuestionFileButton( - const QString& windowName, const QString& filename, - QuestionBoxMemory::Button button) +void PluginSettings::setPluginSetting(const QString &pluginName, const QString &key, const QVariant &value) { - const QString sectionName("DialogChoices"); - const QString settingName(windowName + "/" + filename); + auto iterPlugin = m_PluginSettings.find(pluginName); - if (button == QuestionBoxMemory::NoButton) { - remove(m_Settings, sectionName, settingName); - } else { - set(m_Settings, sectionName, settingName, button); + if (iterPlugin == m_PluginSettings.end()) { + throw MyException( + QObject::tr("attempt to store setting for unknown plugin \"%1\"") + .arg(pluginName)); } + + // store the new setting both in memory and in the ini + m_PluginSettings[pluginName][key] = value; + set(m_Settings, "Plugins", pluginName + "/" + key, value); } -void Settings::resetQuestionButtons() +QVariant PluginSettings::pluginPersistent(const QString &pluginName, const QString &key, const QVariant &def) const { - removeSection(m_Settings, "DialogChoices"); + if (!m_PluginSettings.contains(pluginName)) { + return def; + } + + return get(m_Settings, "PluginPersistance", pluginName + "/" + key, def); } -std::optional Settings::getIndex(const QComboBox* cb) const +void PluginSettings::setPluginPersistent( + const QString &pluginName, const QString &key, const QVariant &value, bool sync) { - return getOptional(m_Settings, "Widgets", indexSettingName(cb)); + if (!m_PluginSettings.contains(pluginName)) { + throw MyException( + QObject::tr("attempt to store setting for unknown plugin \"%1\"") + .arg(pluginName)); + } + + set(m_Settings, "PluginPersistance", pluginName + "/" + key, value); + + if (sync) { + m_Settings.sync(); + } } -void Settings::saveIndex(const QComboBox* cb) +void PluginSettings::addBlacklistPlugin(const QString &fileName) { - set(m_Settings, "Widgets", indexSettingName(cb), cb->currentIndex()); + m_PluginBlacklist.insert(fileName); + writePluginBlacklist(); } -void Settings::restoreIndex(QComboBox* cb, std::optional def) const +void PluginSettings::writePluginBlacklist() { - if (auto v=getOptional(m_Settings, "Widgets", indexSettingName(cb), def)) { - cb->setCurrentIndex(*v); + removeSection(m_Settings, "PluginBlacklist"); + + ScopedWriteArray swa(m_Settings, "PluginBlacklist"); + + for (const QString &plugin : m_PluginBlacklist) { + swa.next(); + swa.set("name", plugin); } } -std::optional Settings::getIndex(const QTabWidget* w) const +QVariantMap PluginSettings::pluginSettings(const QString &pluginName) const { - return getOptional(m_Settings, "Widgets", indexSettingName(w)); + return m_PluginSettings[pluginName]; } -void Settings::saveIndex(const QTabWidget* w) +void PluginSettings::setPluginSettings(const QString &pluginName, const QVariantMap& map) { - set(m_Settings, "Widgets", indexSettingName(w), w->currentIndex()); + m_PluginSettings[pluginName] = map; } -void Settings::restoreIndex(QTabWidget* w, std::optional def) const +QVariantMap PluginSettings::pluginDescriptions(const QString &pluginName) const { - if (auto v=getOptional(m_Settings, "Widgets", indexSettingName(w), def)) { - w->setCurrentIndex(*v); - } + return m_PluginDescriptions[pluginName]; } -std::optional Settings::getChecked(const QAbstractButton* w) const +void PluginSettings::pluginDescriptions(const QString &pluginName, const QVariantMap& map) { - warnIfNotCheckable(w); - return getOptional(m_Settings, "Widgets", checkedSettingName(w)); + m_PluginDescriptions[pluginName] = map; } -void Settings::saveChecked(const QAbstractButton* w) +const QSet& PluginSettings::pluginBlacklist() const { - warnIfNotCheckable(w); - set(m_Settings, "Widgets", checkedSettingName(w), w->isChecked()); + return m_PluginBlacklist; } -void Settings::restoreChecked(QAbstractButton* w, std::optional def) const +void PluginSettings::setPluginBlacklist(const QStringList& pluginNames) { - warnIfNotCheckable(w); + m_PluginBlacklist.clear(); - if (auto v=getOptional(m_Settings, "Widgets", checkedSettingName(w), def)) { - w->setChecked(*v); + for (const auto& name : pluginNames) { + m_PluginBlacklist.insert(name); } } -GeometrySettings& Settings::geometry() +void PluginSettings::save() { - return m_Geometry; -} + for (auto iterPlugins=m_PluginSettings.begin(); iterPlugins!=m_PluginSettings.end(); ++iterPlugins) { + for (auto iterSettings=iterPlugins->begin(); iterSettings!=iterPlugins->end(); ++iterSettings) { + const auto key = iterPlugins.key() + "/" + iterSettings.key(); + set(m_Settings, "Plugins", key, iterSettings.value()); + } + } -const GeometrySettings& Settings::geometry() const -{ - return m_Geometry; + writePluginBlacklist(); } -ColorSettings& Settings::colors() -{ - return m_Colors; -} -const ColorSettings& Settings::colors() const +PathSettings::PathSettings(QSettings& settings) + : m_Settings(settings) { - return m_Colors; } -PluginSettings& Settings::plugins() +std::map PathSettings::recent() const { - return m_Plugins; -} + std::map map; -const PluginSettings& Settings::plugins() const -{ - return m_Plugins; -} + ScopedReadArray sra(m_Settings, "RecentDirectories"); -QSettings::Status Settings::sync() const -{ - m_Settings.sync(); - return m_Settings.status(); -} + sra.for_each([&] { + const QVariant name = sra.get("name"); + const QVariant dir = sra.get("directory"); -void Settings::dump() const -{ - static const QStringList ignore({ - "username", "password", "nexus_api_key" + if (name.isValid() && dir.isValid()) { + map.emplace(name.toString(), dir.toString()); + } }); - log::debug("settings:"); + return map; +} - { - ScopedGroup sg(m_Settings, "Settings"); +void PathSettings::setRecent(const std::map& map) +{ + removeSection(m_Settings, "RecentDirectories"); - for (auto k : m_Settings.allKeys()) { - if (ignore.contains(k, Qt::CaseInsensitive)) { - continue; - } + ScopedWriteArray swa(m_Settings, "recentDirectories"); - log::debug(" . {}={}", k, m_Settings.value(k).toString()); - } - } + for (auto&& p : map) { + swa.next(); - log::debug("servers:"); + swa.set("name", p.first); + swa.set("directory", p.second); + } +} - for (const auto& server : getServers()) { - QString lastDownloads; - for (auto speed : server.lastDownloads()) { - lastDownloads += QString("%1 ").arg(speed); - } +QString PathSettings::getConfigurablePath(const QString &key, + const QString &def, + bool resolve) const +{ + QString result = QDir::fromNativeSeparators( + get(m_Settings, "Settings", key, QString("%BASE_DIR%/") + def)); - log::debug( - " . {} premium={} lastSeen={} preferred={} lastDownloads={}", - server.name(), - server.isPremium() ? "yes" : "no", - server.lastSeen().toString(Qt::ISODate), - server.preferred(), - lastDownloads.trimmed()); + if (resolve) { + result.replace("%BASE_DIR%", base()); } -} + return result; +} -GeometrySettings::GeometrySettings(QSettings& s) - : m_Settings(s), m_Reset(false) +void PathSettings::setConfigurablePath(const QString &key, const QString& path) { + if (path.isEmpty()) { + remove(m_Settings, "Settings", key); + } else { + set(m_Settings, "Settings", key, path); + } } -void GeometrySettings::requestReset() +QString PathSettings::base() const { - m_Reset = true; + return QDir::fromNativeSeparators(get(m_Settings, + "Settings", "base_directory", qApp->property("dataPath").toString())); } -void GeometrySettings::resetIfNeeded() +QString PathSettings::downloads(bool resolve) const { - if (!m_Reset) { - return; - } - - removeSection(m_Settings, "Geometry"); + return getConfigurablePath( + "download_directory", + ToQString(AppConfig::downloadPath()), + resolve); } -void GeometrySettings::saveGeometry(const QWidget* w) +QString PathSettings::cache(bool resolve) const { - set(m_Settings, "Geometry", geoSettingName(w), w->saveGeometry()); + return getConfigurablePath( + "cache_directory", + ToQString(AppConfig::cachePath()), + resolve); } -bool GeometrySettings::restoreGeometry(QWidget* w) const +QString PathSettings::mods(bool resolve) const { - if (auto v=getOptional(m_Settings, "Geometry", geoSettingName(w))) { - w->restoreGeometry(*v); - return true; - } + return getConfigurablePath( + "mod_directory", + ToQString(AppConfig::modsPath()), + resolve); +} - return false; +QString PathSettings::profiles(bool resolve) const +{ + return getConfigurablePath( + "profiles_directory", + ToQString(AppConfig::profilesPath()), + resolve); } -void GeometrySettings::saveState(const QMainWindow* w) +QString PathSettings::overwrite(bool resolve) const { - set(m_Settings, "Geometry", stateSettingName(w), w->saveState()); + return getConfigurablePath( + "overwrite_directory", + ToQString(AppConfig::overwritePath()), + resolve); } -bool GeometrySettings::restoreState(QMainWindow* w) const +void PathSettings::setBase(const QString& path) { - if (auto v=getOptional(m_Settings, "Geometry", stateSettingName(w))) { - w->restoreState(*v); - return true; + if (path.isEmpty()) { + remove(m_Settings, "Settings", "base_directory"); + } else { + set(m_Settings, "Settings", "base_directory", path); } - - return false; } -void GeometrySettings::saveState(const QHeaderView* w) +void PathSettings::setDownloads(const QString& path) { - set(m_Settings, "Geometry", stateSettingName(w), w->saveState()); + setConfigurablePath("download_directory", path); } -bool GeometrySettings::restoreState(QHeaderView* w) const +void PathSettings::setMods(const QString& path) { - if (auto v=getOptional(m_Settings, "Geometry", stateSettingName(w))) { - w->restoreState(*v); - return true; - } - - return false; + setConfigurablePath("mod_directory", path); } -void GeometrySettings::saveState(const QSplitter* w) +void PathSettings::setCache(const QString& path) { - set(m_Settings, "Geometry", stateSettingName(w), w->saveState()); + setConfigurablePath("cache_directory", path); } -bool GeometrySettings::restoreState(QSplitter* w) const +void PathSettings::setProfiles(const QString& path) { - if (auto v=getOptional(m_Settings, "Geometry", stateSettingName(w))) { - w->restoreState(*v); - return true; - } - - return false; + setConfigurablePath("profiles_directory", path); } -void GeometrySettings::saveState(const ExpanderWidget* expander) +void PathSettings::setOverwrite(const QString& path) { - set(m_Settings, "Geometry", stateSettingName(expander), expander->saveState()); + setConfigurablePath("overwrite_directory", path); } -bool GeometrySettings::restoreState(ExpanderWidget* expander) const -{ - if (auto v=getOptional(m_Settings, "Geometry", stateSettingName(expander))) { - expander->restoreState(*v); - return true; - } - return false; +NetworkSettings::NetworkSettings(QSettings& settings) + : m_Settings(settings) +{ } -void GeometrySettings::saveVisibility(const QWidget* w) +bool NetworkSettings::offlineMode() const { - set(m_Settings, "Geometry", visibilitySettingName(w), w->isVisible()); + return get(m_Settings, "Settings", "offline_mode", false); } -bool GeometrySettings::restoreVisibility(QWidget* w, std::optional def) const +void NetworkSettings::setOfflineMode(bool b) { - if (auto v=getOptional(m_Settings, "Geometry", visibilitySettingName(w), def)) { - w->setVisible(*v); - return true; - } + set(m_Settings, "Settings", "offline_mode", b); +} - return false; +bool NetworkSettings::useProxy() const +{ + return get(m_Settings, "Settings", "use_proxy", false); } -void GeometrySettings::restoreToolbars(QMainWindow* w) const +void NetworkSettings::setUseProxy(bool b) { - // all toolbars have the same size and button style settings - const auto size = getOptional(m_Settings, "Geometry", "toolbar_size"); - const auto style = getOptional(m_Settings, "Geometry", "toolbar_button_style"); + set(m_Settings, "Settings", "use_proxy", b); +} - for (auto* tb : w->findChildren()) { - if (size) { - tb->setIconSize(*size); - } +void NetworkSettings::setDownloadSpeed(const QString& name, int bytesPerSecond) +{ + auto current = servers(); - if (style) { - tb->setToolButtonStyle(static_cast(*style)); + for (auto& server : current) { + if (server.name() == name) { + server.addDownload(bytesPerSecond); + updateServers(current); + return; } - - restoreVisibility(tb); } + + log::error( + "server '{}' not found while trying to add a download with bps {}", + name, bytesPerSecond); } -void GeometrySettings::saveToolbars(const QMainWindow* w) +ServerList NetworkSettings::servers() const { - const auto tbs = w->findChildren(); + // servers used to be a map of byte arrays until 2.2.1, it's now an array of + // individual values instead + // + // so post 2.2.1, only one key is returned: "size", the size of the arrays; + // in 2.2.1, one key per server is returned + { + const QStringList keys = ScopedGroup(m_Settings, "Servers").keys(); - // save visibility for all - for (auto* tb : tbs) { - saveVisibility(tb); + if (!keys.empty() && keys[0] != "size") { + // old format + return serversFromOldMap(); + } } - // all toolbars have the same size and button style settings, just save the - // first one - if (!tbs.isEmpty()) { - const auto* tb = tbs[0]; - set(m_Settings, "Geometry", "toolbar_size", tb->iconSize()); - set(m_Settings, "Geometry", "toolbar_button_style", static_cast(tb->toolButtonStyle())); - } -} + // post 2.2.1 format, array of values -QStringList GeometrySettings::getModInfoTabOrder() const -{ - QStringList v; + ServerList list; - if (m_Settings.contains("mod_info_tabs")) { - // old byte array from 2.2.0 - QDataStream stream(m_Settings.value("mod_info_tabs").toByteArray()); + { + ScopedReadArray sra(m_Settings, "Servers"); - int count = 0; - stream >> count; + sra.for_each([&] { + ServerInfo::SpeedList lastDownloads; - for (int i=0; i> s; - v.push_back(s); - } - } else { - // string list since 2.2.1 - QString string = m_Settings.value("mod_info_tab_order").toString(); - QTextStream stream(&string); + const auto lastDownloadsString = sra.get("lastDownloads", ""); - while (!stream.atEnd()) { - QString s; - stream >> s; - v.push_back(s); - } + for (const auto& s : lastDownloadsString.split(" ")) { + const auto bytesPerSecond = s.toInt(); + if (bytesPerSecond > 0) { + lastDownloads.push_back(bytesPerSecond); + } + } + + ServerInfo server( + sra.get("name", ""), + sra.get("premium", false), + QDate::fromString(sra.get("lastSeen", ""), Qt::ISODate), + sra.get("preferred", 0), + lastDownloads); + + list.add(std::move(server)); + }); } - return v; + return list; } -void GeometrySettings::setModInfoTabOrder(const QString& names) +ServerList NetworkSettings::serversFromOldMap() const { - set(m_Settings, "Geometry", "mod_info_tab_order", names); -} + // for 2.2.1 and before -void GeometrySettings::centerOnMainWindowMonitor(QWidget* w) -{ - const auto monitor = getOptional( - m_Settings, "Geometry", "MainWindow_monitor"); + ServerList list; + const ScopedGroup sg(m_Settings, "Servers"); - QPoint center; + sg.for_each([&](auto&& serverKey) { + QVariantMap data = sg.get(serverKey); - if (monitor && QGuiApplication::screens().size() > *monitor) { - center = QGuiApplication::screens().at(*monitor)->geometry().center(); - } else { - center = QGuiApplication::primaryScreen()->geometry().center(); - } + ServerInfo server( + serverKey, + data["premium"].toBool(), + data["lastSeen"].toDate(), + data["preferred"].toInt(), + {}); - w->move(center - w->rect().center()); -} + // ignoring download count and speed, it's now a list of values instead of + // a total -void GeometrySettings::saveMainWindowMonitor(const QMainWindow* w) -{ - if (auto* handle=w->windowHandle()) { - if (auto* screen = handle->screen()) { - const int screenId = QGuiApplication::screens().indexOf(screen); - set(m_Settings, "Geometry", "MainWindow_monitor", screenId); - } - } + list.add(std::move(server)); + }); + + return list; } -Qt::Orientation dockOrientation(const QMainWindow* mw, const QDockWidget* d) +void NetworkSettings::updateServers(ServerList servers) { - // docks in these areas are horizontal - const auto horizontalAreas = - Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea; + // clean up unavailable servers + servers.cleanup(); - if (mw->dockWidgetArea(const_cast(d)) & horizontalAreas) { - return Qt::Horizontal; - } else { - return Qt::Vertical; - } -} + removeSection(m_Settings, "Servers"); -void GeometrySettings::saveDocks(const QMainWindow* mw) -{ - // this attempts to fix https://bugreports.qt.io/browse/QTBUG-46620 where dock - // sizes are not restored when the main window is maximized; it is used in - // MainWindow::readSettings() and MainWindow::storeSettings() - // - // there's also https://stackoverflow.com/questions/44005852, which has what - // seems to be a popular fix, but it breaks the restored size of the window - // by setting it to the desktop's resolution, so that doesn't work - // - // the only fix I could find is to remember the sizes of the docks and manually - // setting them back; saving is straightforward, but restoring is messy - // - // this also depends on the window being visible before the timer in restore() - // is fired and the timer must be processed by application.exec(); therefore, - // the splash screen _must_ be closed before readSettings() is called, because - // it has its own event loop, which seems to interfere with this - // - // all of this should become unnecessary when QTBUG-46620 is fixed - // + { + ScopedWriteArray swa(m_Settings, "Servers"); - // saves the size of each dock - for (const auto* dock : mw->findChildren()) { - int size = 0; + for (const auto& server : servers) { + swa.next(); - // save the width for horizontal docks, or the height for vertical - if (dockOrientation(mw, dock) == Qt::Horizontal) { - size = dock->size().width(); - } else { - size = dock->size().height(); - } + swa.set("name", server.name()); + swa.set("premium", server.isPremium()); + swa.set("lastSeen", server.lastSeen().toString(Qt::ISODate)); + swa.set("preferred", server.preferred()); - set(m_Settings, "Geometry", dockSettingName(dock), size); + QString lastDownloads; + for (const auto& speed : server.lastDownloads()) { + if (speed > 0) { + lastDownloads += QString("%1 ").arg(speed); + } + } + + swa.set("lastDownloads", lastDownloads.trimmed()); + } } } -void GeometrySettings::restoreDocks(QMainWindow* mw) const +void NetworkSettings::dump() const { - struct DockInfo - { - QDockWidget* d; - int size = 0; - Qt::Orientation ori; - }; - - std::vector dockInfos; + log::debug("servers:"); - // for each dock - for (auto* dock : mw->findChildren()) { - if (auto size=getOptional(m_Settings, "Geometry", dockSettingName(dock))) { - // remember this dock, its size and orientation - dockInfos.push_back({dock, *size, dockOrientation(mw, dock)}); + for (const auto& server : servers()) { + QString lastDownloads; + for (auto speed : server.lastDownloads()) { + lastDownloads += QString("%1 ").arg(speed); } - } - // the main window must have had time to process the settings from - // readSettings() or it seems to override whatever is set here - // - // some people said a single processEvents() call is enough, but it doesn't - // look like it - QTimer::singleShot(5, [=] { - for (const auto& info : dockInfos) { - mw->resizeDocks({info.d}, {info.size}, info.ori); - } - }); + log::debug( + " . {} premium={} lastSeen={} preferred={} lastDownloads={}", + server.name(), + server.isPremium() ? "yes" : "no", + server.lastSeen().toString(Qt::ISODate), + server.preferred(), + lastDownloads.trimmed()); + } } -ColorSettings::ColorSettings(QSettings& s) - : m_Settings(s) +NexusSettings::NexusSettings(Settings& parent, QSettings& settings) + : m_Parent(parent), m_Settings(settings) { } -QColor ColorSettings::modlistOverwrittenLoose() const +bool NexusSettings::automaticLoginEnabled() const { - return get( - m_Settings, "Settings", "overwrittenLooseFilesColor", - QColor(0, 255, 0, 64)); + return get(m_Settings, "Settings", "nexus_login", false); } -void ColorSettings::setModlistOverwrittenLoose(const QColor& c) +bool NexusSettings::apiKey(QString &apiKey) const { - set(m_Settings, "Settings", "overwrittenLooseFilesColor", c); + QString tempKey = getWindowsCredential("APIKEY"); + if (tempKey.isEmpty()) + return false; + + apiKey = tempKey; + return true; } -QColor ColorSettings::modlistOverwritingLoose() const +bool NexusSettings::setApiKey(const QString& apiKey) { - return get( - m_Settings, "Settings", "overwritingLooseFilesColor", - QColor(255, 0, 0, 64)); + if (!setWindowsCredential("APIKEY", apiKey)) { + const auto e = GetLastError(); + log::error("Storing API key failed: {}", formatSystemMessage(e)); + return false; + } + + return true; } -void ColorSettings::setModlistOverwritingLoose(const QColor& c) +bool NexusSettings::clearApiKey() { - set(m_Settings, "Settings", "overwritingLooseFilesColor", c); + return setApiKey(""); } -QColor ColorSettings::modlistOverwrittenArchive() const +bool NexusSettings::hasApiKey() const { - return get( - m_Settings, "Settings", "overwrittenArchiveFilesColor", - QColor(0, 255, 255, 64)); + return !getWindowsCredential("APIKEY").isEmpty(); } -void ColorSettings::setModlistOverwrittenArchive(const QColor& c) +bool NexusSettings::endorsementIntegration() const { - set(m_Settings, "Settings", "overwrittenArchiveFilesColor", c); + return get(m_Settings, "Settings", "endorsement_integration", true); } -QColor ColorSettings::modlistOverwritingArchive() const +void NexusSettings::setEndorsementIntegration(bool b) const { - return get( - m_Settings, "Settings", "overwritingArchiveFilesColor", - QColor(255, 0, 255, 64)); + set(m_Settings, "Settings", "endorsement_integration", b); } -void ColorSettings::setModlistOverwritingArchive(const QColor& c) +EndorsementState NexusSettings::endorsementState() const { - set(m_Settings, "Settings", "overwritingArchiveFilesColor", c); + return endorsementStateFromString( + get(m_Settings, "General", "endorse_state", "")); } -QColor ColorSettings::modlistContainsPlugin() const +void NexusSettings::setEndorsementState(EndorsementState s) { - return get( - m_Settings, "Settings", "containsPluginColor", - QColor(0, 0, 255, 64)); + const auto v = toString(s); + + if (v.isEmpty()) { + remove(m_Settings, "General", "endorse_state"); + } else { + set(m_Settings, "General", "endorse_state", v); + } } -void ColorSettings::setModlistContainsPlugin(const QColor& c) +void NexusSettings::registerAsNXMHandler(bool force) { - set(m_Settings, "Settings", "containsPluginColor", c); + const auto nxmPath = QCoreApplication::applicationDirPath() + "/nxmhandler.exe"; + const auto executable = QCoreApplication::applicationFilePath(); + + QString mode = force ? "forcereg" : "reg"; + QString parameters = mode + " " + m_Parent.game().plugin()->gameShortName(); + for (const QString& altGame : m_Parent.game().plugin()->validShortNames()) { + parameters += "," + altGame; + } + parameters += " \"" + executable + "\""; + + if (!shell::Execute(nxmPath, parameters)) { + QMessageBox::critical( + nullptr, QObject::tr("Failed"), + QObject::tr("Failed to start the helper application")); + } } -QColor ColorSettings::pluginListContained() const + +SteamSettings::SteamSettings(Settings& parent, QSettings& settings) + : m_Parent(parent), m_Settings(settings) { - return get( - m_Settings, "Settings", "containedColor", - QColor(0, 0, 255, 64)); } -void ColorSettings::setPluginListContained(const QColor& c) +QString SteamSettings::appID() const { - set(m_Settings, "Settings", "containedColor", c); + return get( + m_Settings, "Settings", "app_id", m_Parent.game().plugin()->steamAPPId()); } - -PluginSettings::PluginSettings(QSettings& settings) - : m_Settings(settings) +void SteamSettings::setAppID(const QString& id) { + if (id.isEmpty()) { + remove(m_Settings, "Settings", "app_id"); + } else { + set(m_Settings, "Settings", "app_id", id); + } } -void PluginSettings::clearPlugins() +bool SteamSettings::login(QString &username, QString &password) const { - m_Plugins.clear(); - m_PluginSettings.clear(); - - m_PluginBlacklist.clear(); + username = get(m_Settings, "Settings", "steam_username", ""); + password = getWindowsCredential("steam_password"); - ScopedReadArray sra(m_Settings, "pluginBlacklist"); - sra.for_each([&]{ - m_PluginBlacklist.insert(sra.get("name")); - }); + return !username.isEmpty() && !password.isEmpty(); } -void PluginSettings::registerPlugin(IPlugin *plugin) +void SteamSettings::setLogin(QString username, QString password) { - m_Plugins.push_back(plugin); - m_PluginSettings.insert(plugin->name(), QVariantMap()); - m_PluginDescriptions.insert(plugin->name(), QVariantMap()); + if (username == "") { + remove(m_Settings, "Settings", "steam_username"); + password = ""; + } else { + set(m_Settings, "Settings", "steam_username", username); + } - for (const PluginSetting &setting : plugin->settings()) { - const QString settingName = plugin->name() + "/" + setting.key; + if (!setWindowsCredential("steam_password", password)) { + const auto e = GetLastError(); + log::error("Storing or deleting password failed: {}", formatSystemMessage(e)); + } +} - QVariant temp = get( - m_Settings, "Plugins", settingName, setting.defaultValue); - if (!temp.convert(setting.defaultValue.type())) { - log::warn( - "failed to interpret \"{}\" as correct type for \"{}\" in plugin \"{}\", using default", - temp.toString(), setting.key, plugin->name()); +InterfaceSettings::InterfaceSettings(QSettings& settings) + : m_Settings(settings) +{ +} - temp = setting.defaultValue; - } +bool InterfaceSettings::lockGUI() const +{ + return get(m_Settings, "Settings", "lock_gui", true); +} - m_PluginSettings[plugin->name()][setting.key] = temp; +void InterfaceSettings::setLockGUI(bool b) +{ + set(m_Settings, "Settings", "lock_gui", b); +} - m_PluginDescriptions[plugin->name()][setting.key] = QString("%1 (default: %2)") - .arg(setting.description) - .arg(setting.defaultValue.toString()); - } +std::optional InterfaceSettings::styleName() const +{ + return getOptional(m_Settings, "Settings", "style"); } -bool PluginSettings::pluginBlacklisted(const QString &fileName) const +void InterfaceSettings::setStyleName(const QString& name) { - return m_PluginBlacklist.contains(fileName); + set(m_Settings, "Settings", "style", name); } -QVariant PluginSettings::pluginSetting(const QString &pluginName, const QString &key) const +bool InterfaceSettings::compactDownloads() const { - auto iterPlugin = m_PluginSettings.find(pluginName); - if (iterPlugin == m_PluginSettings.end()) { - return QVariant(); - } + return get(m_Settings, "Settings", "compact_downloads", false); +} - auto iterSetting = iterPlugin->find(key); - if (iterSetting == iterPlugin->end()) { - return QVariant(); - } +void InterfaceSettings::setCompactDownloads(bool b) +{ + set(m_Settings, "Settings", "compact_downloads", b); +} - return *iterSetting; +bool InterfaceSettings::metaDownloads() const +{ + return get(m_Settings, "Settings", "meta_downloads", false); } -void PluginSettings::setPluginSetting(const QString &pluginName, const QString &key, const QVariant &value) +void InterfaceSettings::setMetaDownloads(bool b) { - auto iterPlugin = m_PluginSettings.find(pluginName); + set(m_Settings, "Settings", "meta_downloads", b); +} - if (iterPlugin == m_PluginSettings.end()) { - throw MyException( - QObject::tr("attempt to store setting for unknown plugin \"%1\"") - .arg(pluginName)); - } +bool InterfaceSettings::hideAPICounter() const +{ + return get(m_Settings, "Settings", "hide_api_counter", false); +} - // store the new setting both in memory and in the ini - m_PluginSettings[pluginName][key] = value; - set(m_Settings, "Plugins", pluginName + "/" + key, value); +void InterfaceSettings::setHideAPICounter(bool b) +{ + set(m_Settings, "Settings", "hide_api_counter", b); } -QVariant PluginSettings::pluginPersistent(const QString &pluginName, const QString &key, const QVariant &def) const +bool InterfaceSettings::displayForeign() const { - if (!m_PluginSettings.contains(pluginName)) { - return def; - } + return get(m_Settings, "Settings", "display_foreign", true); +} - return get(m_Settings, "PluginPersistance", pluginName + "/" + key, def); +void InterfaceSettings::setDisplayForeign(bool b) +{ + set(m_Settings, "Settings", "display_foreign", b); } -void PluginSettings::setPluginPersistent( - const QString &pluginName, const QString &key, const QVariant &value, bool sync) +QString InterfaceSettings::language() { - if (!m_PluginSettings.contains(pluginName)) { - throw MyException( - QObject::tr("attempt to store setting for unknown plugin \"%1\"") - .arg(pluginName)); - } + QString result = get(m_Settings, "Settings", "language", ""); - set(m_Settings, "PluginPersistance", pluginName + "/" + key, value); + if (result.isEmpty()) { + QStringList languagePreferences = QLocale::system().uiLanguages(); - if (sync) { - m_Settings.sync(); + if (languagePreferences.length() > 0) { + // the users most favoritest language + result = languagePreferences.at(0); + } else { + // fallback system locale + result = QLocale::system().name(); + } } + + return result; } -void PluginSettings::addBlacklistPlugin(const QString &fileName) +void InterfaceSettings::setLanguage(const QString& name) { - m_PluginBlacklist.insert(fileName); - writePluginBlacklist(); + set(m_Settings, "Settings", "language", name); } -void PluginSettings::writePluginBlacklist() +bool InterfaceSettings::isTutorialCompleted(const QString& windowName) const { - removeSection(m_Settings, "PluginBlacklist"); - - ScopedWriteArray swa(m_Settings, "PluginBlacklist"); - - for (const QString &plugin : m_PluginBlacklist) { - swa.next(); - swa.set("name", plugin); - } + return get(m_Settings, "CompletedWindowTutorials", windowName, false); } -QVariantMap PluginSettings::pluginSettings(const QString &pluginName) const +void InterfaceSettings::setTutorialCompleted(const QString& windowName, bool b) { - return m_PluginSettings[pluginName]; + set(m_Settings, "CompletedWindowTutorials", windowName, b); } -void PluginSettings::setPluginSettings(const QString &pluginName, const QVariantMap& map) + +DiagnosticsSettings::DiagnosticsSettings(QSettings& settings) + : m_Settings(settings) { - m_PluginSettings[pluginName] = map; } -QVariantMap PluginSettings::pluginDescriptions(const QString &pluginName) const +log::Levels DiagnosticsSettings::logLevel() const { - return m_PluginDescriptions[pluginName]; + return get(m_Settings, "Settings", "log_level", log::Levels::Info); } -void PluginSettings::pluginDescriptions(const QString &pluginName, const QVariantMap& map) +void DiagnosticsSettings::setLogLevel(log::Levels level) { - m_PluginDescriptions[pluginName] = map; + set(m_Settings, "Settings", "log_level", level); } -const QSet& PluginSettings::pluginBlacklist() const +CrashDumpsType DiagnosticsSettings::crashDumpsType() const { - return m_PluginBlacklist; + return get(m_Settings, + "Settings", "crash_dumps_type", CrashDumpsType::Mini); } -void PluginSettings::setPluginBlacklist(const QStringList& pluginNames) +void DiagnosticsSettings::setCrashDumpsType(CrashDumpsType type) { - m_PluginBlacklist.clear(); - - for (const auto& name : pluginNames) { - m_PluginBlacklist.insert(name); - } + set(m_Settings, "Settings", "crash_dumps_type", type); } -void PluginSettings::save() +int DiagnosticsSettings::crashDumpsMax() const { - for (auto iterPlugins=m_PluginSettings.begin(); iterPlugins!=m_PluginSettings.end(); ++iterPlugins) { - for (auto iterSettings=iterPlugins->begin(); iterSettings!=iterPlugins->end(); ++iterSettings) { - const auto key = iterPlugins.key() + "/" + iterSettings.key(); - set(m_Settings, "Plugins", key, iterSettings.value()); - } - } + return get(m_Settings, "Settings", "crash_dumps_max", 5); +} - writePluginBlacklist(); +void DiagnosticsSettings::setCrashDumpsMax(int n) +{ + set(m_Settings, "Settings", "crash_dumps_max", n); } diff --git a/src/settings.h b/src/settings.h index 403c2d71..5c0a2542 100644 --- a/src/settings.h +++ b/src/settings.h @@ -25,6 +25,10 @@ along with Mod Organizer. If not, see . #include #include +#ifdef interface + #undef interface +#endif + namespace MOBase { class IPlugin; class IPluginGame; @@ -50,6 +54,58 @@ private: }; +class GameSettings +{ +public: + GameSettings(QSettings& setting); + + const MOBase::IPluginGame* plugin(); + void setPlugin(const MOBase::IPluginGame* gamePlugin); + + /** + * whether files of the core game are forced-enabled so the user can't + * accidentally disable them + */ + bool forceEnableCoreFiles() const; + void setForceEnableCoreFiles(bool b); + + /** + * the directory where the managed game is stored (with native separators) + **/ + std::optional directory() const; + void setDirectory(const QString& path); + + std::optional name() const; + void setName(const QString& name); + + std::optional edition() const; + void setEdition(const QString& name); + + std::optional selectedProfileName() const; + void setSelectedProfileName(const QString& name); + + /** + * @return the load mechanism to be used + **/ + LoadMechanism::EMechanism loadMechanismType() const; + void setLoadMechanism(LoadMechanism::EMechanism m); + const LoadMechanism& loadMechanism() const; + void setupLoadMechanism(); + + /** + * @return true if the user wants unchecked plugins (esp, esm) should be hidden from + * the virtual data directory + **/ + bool hideUncheckedPlugins() const; + void setHideUncheckedPlugins(bool b); + +private: + QSettings& m_Settings; + const MOBase::IPluginGame* m_GamePlugin; + LoadMechanism m_LoadMechanism; +}; + + class GeometrySettings { public: @@ -83,7 +139,7 @@ public: void saveDocks(const QMainWindow* w); void restoreDocks(QMainWindow* w) const; - QStringList getModInfoTabOrder() const; + QStringList modInfoTabOrder() const; void setModInfoTabOrder(const QString& names); void centerOnMainWindowMonitor(QWidget* w); @@ -95,6 +151,40 @@ private: }; +class WidgetSettings +{ +public: + WidgetSettings(QSettings& s); + + std::optional index(const QComboBox* cb) const; + void saveIndex(const QComboBox* cb); + void restoreIndex(QComboBox* cb, std::optional def={}) const; + + std::optional index(const QTabWidget* w) const; + void saveIndex(const QTabWidget* w); + void restoreIndex(QTabWidget* w, std::optional def={}) const; + + std::optional checked(const QAbstractButton* w) const; + void saveChecked(const QAbstractButton* w); + void restoreChecked(QAbstractButton* w, std::optional def={}) const; + + MOBase::QuestionBoxMemory::Button questionButton( + const QString& windowName, const QString& filename) const; + + void setQuestionWindowButton( + const QString& windowName, MOBase::QuestionBoxMemory::Button button); + + void setQuestionFileButton( + const QString& windowName, const QString& filename, + MOBase::QuestionBoxMemory::Button choice); + + void resetQuestionButtons(); + +private: + QSettings& m_Settings; +}; + + class ColorSettings { public: @@ -120,6 +210,19 @@ public: QColor pluginListContained() const; void setPluginListContained(const QColor& c) ; + std::optional previousSeparatorColor() const; + void setPreviousSeparatorColor(const QColor& c) const; + void removePreviousSeparatorColor(); + + /** + * @brief color the scrollbar of the mod list for custom separator colors? + * @return the state of the setting + */ + bool colorSeparatorScrollbar() const; + void setColorSeparatorScrollbar(bool b); + + static QColor idealTextColor(const QColor& rBackgroundColor); + private: QSettings& m_Settings; }; @@ -165,210 +268,229 @@ private: }; -enum class EndorsementState -{ - Accepted = 1, - Refused, - NoDecision -}; - -EndorsementState endorsementStateFromString(const QString& s); -QString toString(EndorsementState s); - - -/** - * manages the settings for Mod Organizer. The settings are not cached - * inside the class but read/written directly from/to disc - **/ -class Settings : public QObject +class PathSettings { - Q_OBJECT; - public: - Settings(const QString& path); - ~Settings(); + PathSettings(QSettings& settings); - static Settings &instance(); + QString base() const; + QString downloads(bool resolve = true) const; + QString mods(bool resolve = true) const; + QString cache(bool resolve = true) const; + QString profiles(bool resolve = true) const; + QString overwrite(bool resolve = true) const; - void processUpdates( - const QVersionNumber& currentVersion, const QVersionNumber& lastVersion); + void setBase(const QString& path); + void setDownloads(const QString& path); + void setMods(const QString& path); + void setCache(const QString& path); + void setProfiles(const QString& path); + void setOverwrite(const QString& path); - QString getFilename() const; + std::map recent() const; + void setRecent(const std::map& map); - /** - * @return true if the user wants unchecked plugins (esp, esm) should be hidden from - * the virtual dat adirectory - **/ - bool hideUncheckedPlugins() const; - void setHideUncheckedPlugins(bool b); +private: + QSettings& m_Settings; - /** - * @return true if files of the core game are forced-enabled so the user can't accidentally disable them - */ - bool forceEnableCoreFiles() const; - void setForceEnableCoreFiles(bool b); + QString getConfigurablePath(const QString &key, const QString &def, bool resolve) const; + void setConfigurablePath(const QString &key, const QString& path); +}; - /** - * @return true if the GUI should be locked when running executables - */ - bool lockGUI() const; - void setLockGUI(bool b); - /** - * the steam appid is assigned by the steam platform to each product sold there. - * The appid may differ between different versions of a game so it may be impossible - * for Mod Organizer to automatically recognize it, though usually it does - * @return the steam appid for the game - **/ - QString getSteamAppID() const; - void setSteamAppID(const QString& id); - - QString getBaseDirectory() const; - QString getDownloadDirectory(bool resolve = true) const; - QString getModDirectory(bool resolve = true) const; - QString getCacheDirectory(bool resolve = true) const; - QString getProfileDirectory(bool resolve = true) const; - QString getOverwriteDirectory(bool resolve = true) const; - - void setBaseDirectory(const QString& path); - void setDownloadDirectory(const QString& path); - void setModDirectory(const QString& path); - void setCacheDirectory(const QString& path); - void setProfileDirectory(const QString& path); - void setOverwriteDirectory(const QString& path); +class NetworkSettings +{ +public: + NetworkSettings(QSettings& settings); /** - * retrieve the directory where the managed game is stored (with native separators) - **/ - std::optional getManagedGameDirectory() const; - void setManagedGameDirectory(const QString& path); - - std::optional getManagedGameName() const; - void setManagedGameName(const QString& name); - - std::optional getManagedGameEdition() const; - void setManagedGameEdition(const QString& name); + * @return true if the user disabled internet features + */ + bool offlineMode() const; + void setOfflineMode(bool b); - std::optional getSelectedProfileName() const; - void setSelectedProfileName(const QString& name); + /** + * @return true if the user configured the use of a network proxy + */ + bool useProxy() const; + void setUseProxy(bool b); - std::optional getStyleName() const; - void setStyleName(const QString& name); + void setDownloadSpeed(const QString &serverName, int bytesPerSecond); + ServerList servers() const; + void updateServers(ServerList servers); - std::optional getVersion() const; + void dump() const; - bool getFirstStart() const; - void setFirstStart(bool b); +private: + QSettings& m_Settings; - std::optional getPreviousSeparatorColor() const; - void setPreviousSeparatorColor(const QColor& c) const; - void removePreviousSeparatorColor(); + ServerList serversFromOldMap() const; +}; - std::map getRecentDirectories() const; - void setRecentDirectories(const std::map& map); - std::vector> getExecutables() const; - void setExecutables(const std::vector>& v); +enum class EndorsementState +{ + Accepted = 1, + Refused, + NoDecision +}; - bool isTutorialCompleted(const QString& windowName) const; - void setTutorialCompleted(const QString& windowName, bool b=true); +EndorsementState endorsementStateFromString(const QString& s); +QString toString(EndorsementState s); - bool keepBackupOnInstall() const; - void setKeepBackupOnInstall(bool b); +class NexusSettings +{ +public: + NexusSettings(Settings& parent, QSettings& settings); - MOBase::QuestionBoxMemory::Button getQuestionButton( - const QString& windowName, const QString& filename) const; + /** + * @return true if the user has set up automatic login to nexus + **/ + bool automaticLoginEnabled() const; - void setQuestionWindowButton( - const QString& windowName, MOBase::QuestionBoxMemory::Button button); + /** + * @brief retrieve the login information for nexus + * + * @param username (out) receives the user name for nexus + * @param password (out) received the password for nexus + * @return true if automatic login is active, false otherwise + **/ + bool apiKey(QString &apiKey) const; - void setQuestionFileButton( - const QString& windowName, const QString& filename, - MOBase::QuestionBoxMemory::Button choice); + /** + * @brief set the nexus login information + * + * @param username username + * @param password password + */ + bool setApiKey(const QString& apiKey); - void resetQuestionButtons(); + /** + * @brief clears the nexus login information + */ + bool clearApiKey(); - std::optional getIndex(const QComboBox* cb) const; - void saveIndex(const QComboBox* cb); - void restoreIndex(QComboBox* cb, std::optional def={}) const; + /** + * @brief returns whether an API key is currently stored + */ + bool hasApiKey() const; - std::optional getIndex(const QTabWidget* w) const; - void saveIndex(const QTabWidget* w); - void restoreIndex(QTabWidget* w, std::optional def={}) const; + /** + * @return true if endorsement integration is enabled + */ + bool endorsementIntegration() const; + void setEndorsementIntegration(bool b) const; - std::optional getChecked(const QAbstractButton* w) const; - void saveChecked(const QAbstractButton* w); - void restoreChecked(QAbstractButton* w, std::optional def={}) const; + EndorsementState endorsementState() const; + void setEndorsementState(EndorsementState s); - GeometrySettings& geometry(); - const GeometrySettings& geometry() const; + /** + * @brief register MO as the handler for nxm links + * @param force set to true to enforce the registration dialog to show up, + * even if the user said earlier not to + */ + void registerAsNXMHandler(bool force); - ColorSettings& colors(); - const ColorSettings& colors() const; +private: + Settings& m_Parent; + QSettings& m_Settings; +}; - PluginSettings& plugins(); - const PluginSettings& plugins() const; +class SteamSettings +{ +public: + SteamSettings(Settings& parent, QSettings& settings); /** - * @return true if the user has set up automatic login to nexus - **/ - bool automaticLoginEnabled() const; + * the steam appid is assigned by the steam platform to each product sold there. + * The appid may differ between different versions of a game so it may be impossible + * for Mod Organizer to automatically recognize it, though usually it does + * @return the steam appid for the game + **/ + QString appID() const; + void setAppID(const QString& id); /** - * @brief retrieve the login information for nexus - * - * @param username (out) receives the user name for nexus - * @param password (out) received the password for nexus - * @return true if automatic login is active, false otherwise - **/ - bool getNexusApiKey(QString &apiKey) const; + * @brief retrieve the login information for steam + * + * @param username (out) receives the user name for nexus + * @param password (out) received the password for nexus + * @return true if a username has been specified, false otherwise + **/ + bool login(QString &username, QString &password) const; /** - * @brief set the nexus login information + * @brief set the steam login information * * @param username username * @param password password */ - bool setNexusApiKey(const QString& apiKey); + void setLogin(QString username, QString password); - /** - * @brief clears the nexus login information - */ - bool clearNexusApiKey(); +private: + Settings& m_Parent; + QSettings& m_Settings; +}; - /** - * @brief returns whether an API key is currently stored - */ - bool hasNexusApiKey() const; - /** - * @brief retrieve the login information for steam - * - * @param username (out) receives the user name for nexus - * @param password (out) received the password for nexus - * @return true if a username has been specified, false otherwise - **/ - bool getSteamLogin(QString &username, QString &password) const; +class InterfaceSettings +{ +public: + InterfaceSettings(QSettings& settings); /** - * @return true if the user disabled internet features - */ - bool offlineMode() const; - void setOfflineMode(bool b); + * @return true if the GUI should be locked when running executables + */ + bool lockGUI() const; + void setLockGUI(bool b); + + std::optional styleName() const; + void setStyleName(const QString& name); /** - * @return true if the user chose compact downloads - */ + * @return true if the user chose compact downloads + */ bool compactDownloads() const; void setCompactDownloads(bool b); /** - * @return true if the user chose meta downloads - */ + * @return true if the user chose meta downloads + */ bool metaDownloads() const; void setMetaDownloads(bool b); + /** + * @return true if the API counter should be hidden + */ + bool hideAPICounter() const; + void setHideAPICounter(bool b); + + /** + * @return true if the user wants to see non-official plugins installed outside MO in his mod list + */ + bool displayForeign() const; + void setDisplayForeign(bool b); + + /** + * @return short code of the configured language (corresponding to the translation files) + */ + QString language(); + void setLanguage(const QString& name); + + bool isTutorialCompleted(const QString& windowName) const; + void setTutorialCompleted(const QString& windowName, bool b=true); + +private: + QSettings& m_Settings; +}; + + +class DiagnosticsSettings +{ +public: + DiagnosticsSettings(QSettings& settings); + MOBase::log::Levels logLevel() const; void setLogLevel(MOBase::log::Levels level); @@ -378,60 +500,48 @@ public: int crashDumpsMax() const; void setCrashDumpsMax(int n); - QString executablesBlacklist() const; - void setExecutablesBlacklist(const QString& s); +private: + QSettings& m_Settings; +}; - /** - * @brief set the steam login information - * - * @param username username - * @param password password - */ - void setSteamLogin(QString username, QString password); - /** - * @return the load mechanism to be used - **/ - LoadMechanism::EMechanism getLoadMechanism() const; - void setLoadMechanism(LoadMechanism::EMechanism m); - /** - * @brief activate the load mechanism selected by the user - **/ - void setupLoadMechanism(); +/** + * manages the settings for Mod Organizer. The settings are not cached + * inside the class but read/written directly from/to disc + **/ +class Settings : public QObject +{ + Q_OBJECT; - /** - * @return true if the user configured the use of a network proxy - */ - bool getUseProxy() const; - void setUseProxy(bool b); +public: + Settings(const QString& path); + ~Settings(); - /** - * @return true if endorsement integration is enabled - */ - bool endorsementIntegration() const; - void setEndorsementIntegration(bool b) const; + static Settings &instance(); - EndorsementState endorsementState() const; - void setEndorsementState(EndorsementState s); + QString filename() const; - /** - * @return true if the API counter should be hidden - */ - bool hideAPICounter() const; - void setHideAPICounter(bool b); + std::optional version() const; + void processUpdates(const QVersionNumber& current, const QVersionNumber& last); - /** - * @return true if the user wants to see non-official plugins installed outside MO in his mod list - */ - bool displayForeign() const; - void setDisplayForeign(bool b); + bool firstStart() const; + void setFirstStart(bool b); + + std::vector> executables() const; + void setExecutables(const std::vector>& v); + + bool keepBackupOnInstall() const; + void setKeepBackupOnInstall(bool b); + + QString executablesBlacklist() const; + void setExecutablesBlacklist(const QString& s); /** * @brief sets the new motd hash **/ - unsigned int getMotDHash() const; - void setMotDHash(unsigned int hash); + unsigned int motdHash() const; + void setMotdHash(unsigned int hash); /** * @return true if the user wants to have archives being parsed to show conflicts and contents @@ -439,41 +549,45 @@ public: bool archiveParsing() const; void setArchiveParsing(bool b); - /** - * @return short code of the configured language (corresponding to the translation files) - */ - QString language(); - void setLanguage(const QString& name); - - void setDownloadSpeed(const QString &serverName, int bytesPerSecond); - ServerList getServers() const; - ServerList getServersFromOldMap() const; - void updateServers(ServerList servers); - bool usePrereleases() const; void setUsePrereleases(bool b); - /** - * @brief register MO as the handler for nxm links - * @param force set to true to enforce the registration dialog to show up, - * even if the user said earlier not to - */ - void registerAsNXMHandler(bool force); - /** - * @brief color the scrollbar of the mod list for custom separator colors? - * @return the state of the setting - */ - bool colorSeparatorScrollbar() const; - void setColorSeparatorScrollbar(bool b); + GameSettings& game(); + const GameSettings& game() const; + + GeometrySettings& geometry(); + const GeometrySettings& geometry() const; - static QColor getIdealTextColor(const QColor& rBackgroundColor); + WidgetSettings& widgets(); + const WidgetSettings& widgets() const; - MOBase::IPluginGame const *gamePlugin() { return m_GamePlugin; } - const LoadMechanism& loadMechanism() const { return m_LoadMechanism; } + ColorSettings& colors(); + const ColorSettings& colors() const; - QSettings::Status sync() const; + PluginSettings& plugins(); + const PluginSettings& plugins() const; + + PathSettings& paths(); + const PathSettings& paths() const; + + NetworkSettings& network(); + const NetworkSettings& network() const; + + NexusSettings& nexus(); + const NexusSettings& nexus() const; + + SteamSettings& steam(); + const SteamSettings& steam() const; + InterfaceSettings& interface(); + const InterfaceSettings& interface() const; + + DiagnosticsSettings& diagnostics(); + const DiagnosticsSettings& diagnostics() const; + + + QSettings::Status sync() const; void dump() const; public slots: @@ -485,18 +599,19 @@ signals: private: static Settings *s_Instance; - MOBase::IPluginGame const *m_GamePlugin; mutable QSettings m_Settings; + + GameSettings m_Game; GeometrySettings m_Geometry; + WidgetSettings m_Widgets; ColorSettings m_Colors; PluginSettings m_Plugins; - LoadMechanism m_LoadMechanism; - - static bool obfuscate(const QString key, const QString data); - static QString deObfuscate(const QString key); - - QString getConfigurablePath(const QString &key, const QString &def, bool resolve) const; - void setConfigurablePath(const QString &key, const QString& path); + PathSettings m_Paths; + NetworkSettings m_Network; + NexusSettings m_Nexus; + SteamSettings m_Steam; + InterfaceSettings m_Interface; + DiagnosticsSettings m_Diagnostics; }; #endif // SETTINGS_H diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index 35d14644..1d3d4a39 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -51,11 +51,11 @@ int SettingsDialog::exec() { GeometrySaver gs(m_settings, this); - m_settings.restoreIndex(ui->tabWidget); + m_settings.widgets().restoreIndex(ui->tabWidget); auto ret = TutorableDialog::exec(); - m_settings.saveIndex(ui->tabWidget); + m_settings.widgets().saveIndex(ui->tabWidget); if (ret == QDialog::Accepted) { for (auto&& tab : m_tabs) { @@ -109,7 +109,7 @@ void SettingsDialog::accept() if ((QDir::fromNativeSeparators(newModPath) != QDir::fromNativeSeparators( - Settings::instance().getModDirectory(true))) && + Settings::instance().paths().mods(true))) && (QMessageBox::question( nullptr, tr("Confirm"), tr("Changing the mod directory affects all your profiles! " diff --git a/src/settingsdialogdiagnostics.cpp b/src/settingsdialogdiagnostics.cpp index 278da0bf..386c7425 100644 --- a/src/settingsdialogdiagnostics.cpp +++ b/src/settingsdialogdiagnostics.cpp @@ -12,7 +12,7 @@ DiagnosticsSettingsTab::DiagnosticsSettingsTab(Settings& s, SettingsDialog& d) setLevelsBox(); setCrashDumpTypesBox(); - ui->dumpsMaxEdit->setValue(settings().crashDumpsMax()); + ui->dumpsMaxEdit->setValue(settings().diagnostics().crashDumpsMax()); QString logsPath = qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::logPath()); @@ -36,7 +36,7 @@ void DiagnosticsSettingsTab::setLevelsBox() ui->logLevelBox->addItem(QObject::tr("Error"), log::Error); for (int i=0; ilogLevelBox->count(); ++i) { - if (ui->logLevelBox->itemData(i) == settings().logLevel()) { + if (ui->logLevelBox->itemData(i) == settings().diagnostics().logLevel()) { ui->logLevelBox->setCurrentIndex(i); break; } @@ -56,7 +56,8 @@ void DiagnosticsSettingsTab::setCrashDumpTypesBox() add(QObject::tr("Data"), CrashDumpsType::Data); add(QObject::tr("Full"), CrashDumpsType::Full); - const auto current = static_cast(settings().crashDumpsType()); + const auto current = static_cast( + settings().diagnostics().crashDumpsType()); for (int i=0; idumpsTypeBox->count(); ++i) { if (ui->dumpsTypeBox->itemData(i) == current) { @@ -68,11 +69,11 @@ void DiagnosticsSettingsTab::setCrashDumpTypesBox() void DiagnosticsSettingsTab::update() { - settings().setLogLevel( + settings().diagnostics().setLogLevel( static_cast(ui->logLevelBox->currentData().toInt())); - settings().setCrashDumpsType( + settings().diagnostics().setCrashDumpsType( static_cast(ui->dumpsTypeBox->currentData().toInt())); - settings().setCrashDumpsMax(ui->dumpsMaxEdit->value()); + settings().diagnostics().setCrashDumpsMax(ui->dumpsMaxEdit->value()); } diff --git a/src/settingsdialoggeneral.cpp b/src/settingsdialoggeneral.cpp index e3d73037..3f7ece38 100644 --- a/src/settingsdialoggeneral.cpp +++ b/src/settingsdialoggeneral.cpp @@ -11,7 +11,7 @@ GeneralSettingsTab::GeneralSettingsTab(Settings& s, SettingsDialog& d) { addLanguages(); { - QString languageCode = settings().language(); + QString languageCode = settings().interface().language(); int currentID = ui->languageBox->findData(languageCode); // I made a mess. :( Most languages are stored with only the iso country // code (2 characters like "de") but chinese @@ -29,7 +29,7 @@ GeneralSettingsTab::GeneralSettingsTab(Settings& s, SettingsDialog& d) { const int currentID = ui->styleBox->findData( - settings().getStyleName().value_or("")); + settings().interface().styleName().value_or("")); if (currentID != -1) { ui->styleBox->setCurrentIndex(currentID); @@ -51,10 +51,10 @@ GeneralSettingsTab::GeneralSettingsTab(Settings& s, SettingsDialog& d) setContainsColor(settings().colors().modlistContainsPlugin()); setContainedColor(settings().colors().pluginListContained()); - ui->compactBox->setChecked(settings().compactDownloads()); - ui->showMetaBox->setChecked(settings().metaDownloads()); + ui->compactBox->setChecked(settings().interface().compactDownloads()); + ui->showMetaBox->setChecked(settings().interface().metaDownloads()); ui->usePrereleaseBox->setChecked(settings().usePrereleases()); - ui->colorSeparatorsBox->setChecked(settings().colorSeparatorScrollbar()); + ui->colorSeparatorsBox->setChecked(settings().colors().colorSeparatorScrollbar()); QObject::connect(ui->overwritingArchiveBtn, &QPushButton::clicked, [&]{ on_overwritingArchiveBtn_clicked(); }); QObject::connect(ui->overwritingBtn, &QPushButton::clicked, [&]{ on_overwritingBtn_clicked(); }); @@ -69,18 +69,18 @@ GeneralSettingsTab::GeneralSettingsTab(Settings& s, SettingsDialog& d) void GeneralSettingsTab::update() { - const QString oldLanguage = settings().language(); + const QString oldLanguage = settings().interface().language(); const QString newLanguage = ui->languageBox->itemData(ui->languageBox->currentIndex()).toString(); if (newLanguage != oldLanguage) { - settings().setLanguage(newLanguage); + settings().interface().setLanguage(newLanguage); emit settings().languageChanged(newLanguage); } - const QString oldStyle = settings().getStyleName().value_or(""); + const QString oldStyle = settings().interface().styleName().value_or(""); const QString newStyle = ui->styleBox->itemData(ui->styleBox->currentIndex()).toString(); if (oldStyle != newStyle) { - settings().setStyleName(newStyle); + settings().interface().setStyleName(newStyle); emit settings().styleChanged(newStyle); } @@ -91,10 +91,10 @@ void GeneralSettingsTab::update() settings().colors().setModlistContainsPlugin(getContainsColor()); settings().colors().setPluginListContained(getContainedColor()); - settings().setCompactDownloads(ui->compactBox->isChecked()); - settings().setMetaDownloads(ui->showMetaBox->isChecked()); + settings().interface().setCompactDownloads(ui->compactBox->isChecked()); + settings().interface().setMetaDownloads(ui->showMetaBox->isChecked()); settings().setUsePrereleases(ui->usePrereleaseBox->isChecked()); - settings().setColorSeparatorScrollbar(ui->colorSeparatorsBox->isChecked()); + settings().colors().setColorSeparatorScrollbar(ui->colorSeparatorsBox->isChecked()); } void GeneralSettingsTab::addLanguages() @@ -145,7 +145,7 @@ void GeneralSettingsTab::addStyles() void GeneralSettingsTab::resetDialogs() { - settings().resetQuestionButtons(); + settings().widgets().resetQuestionButtons(); } void GeneralSettingsTab::setButtonColor(QPushButton *button, const QColor &color) @@ -161,7 +161,7 @@ void GeneralSettingsTab::setButtonColor(QPushButton *button, const QColor &color .arg(color.green()) .arg(color.blue()) .arg(color.alpha()) - .arg(Settings::getIdealTextColor(color).name()) + .arg(ColorSettings::idealTextColor(color).name()) ); }; diff --git a/src/settingsdialognexus.cpp b/src/settingsdialognexus.cpp index 8822200e..0b08f13f 100644 --- a/src/settingsdialognexus.cpp +++ b/src/settingsdialognexus.cpp @@ -74,13 +74,13 @@ private: NexusSettingsTab::NexusSettingsTab(Settings& s, SettingsDialog& d) : SettingsTab(s, d) { - ui->offlineBox->setChecked(settings().offlineMode()); - ui->proxyBox->setChecked(settings().getUseProxy()); - ui->endorsementBox->setChecked(settings().endorsementIntegration()); - ui->hideAPICounterBox->setChecked(settings().hideAPICounter()); + ui->offlineBox->setChecked(settings().network().offlineMode()); + ui->proxyBox->setChecked(settings().network().useProxy()); + ui->endorsementBox->setChecked(settings().nexus().endorsementIntegration()); + ui->hideAPICounterBox->setChecked(settings().interface().hideAPICounter()); // display server preferences - for (const auto& server : s.getServers()) { + for (const auto& server : s.network().servers()) { QString descriptor = server.name(); if (!descriptor.compare("CDN", Qt::CaseInsensitive)) { @@ -117,12 +117,12 @@ NexusSettingsTab::NexusSettingsTab(Settings& s, SettingsDialog& d) void NexusSettingsTab::update() { - settings().setOfflineMode(ui->offlineBox->isChecked()); - settings().setUseProxy(ui->proxyBox->isChecked()); - settings().setEndorsementIntegration(ui->endorsementBox->isChecked()); - settings().setHideAPICounter(ui->hideAPICounterBox->isChecked()); + settings().network().setOfflineMode(ui->offlineBox->isChecked()); + settings().network().setUseProxy(ui->proxyBox->isChecked()); + settings().nexus().setEndorsementIntegration(ui->endorsementBox->isChecked()); + settings().interface().setHideAPICounter(ui->hideAPICounterBox->isChecked()); - auto servers = settings().getServers(); + auto servers = settings().network().servers(); // store server preference for (int i = 0; i < ui->knownServersList->count(); ++i) { @@ -167,7 +167,7 @@ void NexusSettingsTab::update() } } - settings().updateServers(servers); + settings().network().updateServers(servers); } void NexusSettingsTab::on_nexusConnect_clicked() @@ -225,13 +225,13 @@ void NexusSettingsTab::on_nexusDisconnect_clicked() void NexusSettingsTab::on_clearCacheButton_clicked() { - QDir(Settings::instance().getCacheDirectory()).removeRecursively(); + QDir(Settings::instance().paths().cache()).removeRecursively(); NexusInterface::instance(dialog().m_PluginContainer)->clearCache(); } void NexusSettingsTab::on_associateButton_clicked() { - Settings::instance().registerAsNXMHandler(true); + Settings::instance().nexus().registerAsNXMHandler(true); } void NexusSettingsTab::validateKey(const QString& key) @@ -312,7 +312,7 @@ void NexusSettingsTab::addNexusLog(const QString& s) bool NexusSettingsTab::setKey(const QString& key) { dialog().m_keyChanged = true; - const bool ret = settings().setNexusApiKey(key); + const bool ret = settings().nexus().setApiKey(key); updateNexusState(); return ret; } @@ -320,7 +320,7 @@ bool NexusSettingsTab::setKey(const QString& key) bool NexusSettingsTab::clearKey() { dialog().m_keyChanged = true; - const auto ret = settings().clearNexusApiKey(); + const auto ret = settings().nexus().clearApiKey(); NexusInterface::instance(dialog().m_PluginContainer)->getAccessManager()->clearApiKey(); updateNexusState(); @@ -352,7 +352,7 @@ void NexusSettingsTab::updateNexusButtons() ui->nexusManualKey->setText(QObject::tr("Cancel")); ui->nexusManualKey->setEnabled(true); } - else if (settings().hasNexusApiKey()) { + else if (settings().nexus().hasApiKey()) { // api key is present ui->nexusConnect->setText(QObject::tr("Connect to Nexus")); ui->nexusConnect->setEnabled(false); diff --git a/src/settingsdialogpaths.cpp b/src/settingsdialogpaths.cpp index 32aaf4bf..aeb4dd5d 100644 --- a/src/settingsdialogpaths.cpp +++ b/src/settingsdialogpaths.cpp @@ -6,17 +6,23 @@ PathsSettingsTab::PathsSettingsTab(Settings& s, SettingsDialog& d) : SettingsTab(s, d) { - ui->baseDirEdit->setText(settings().getBaseDirectory()); - ui->managedGameDirEdit->setText(settings().gamePlugin()->gameDirectory().absoluteFilePath(settings().gamePlugin()->binaryName())); - QString basePath = settings().getBaseDirectory(); + ui->baseDirEdit->setText(settings().paths().base()); + + ui->managedGameDirEdit->setText( + settings().game().plugin()->gameDirectory().absoluteFilePath( + settings().game().plugin()->binaryName())); + + QString basePath = settings().paths().base(); QDir baseDir(basePath); + for (const auto &dir : { - std::make_pair(ui->downloadDirEdit, settings().getDownloadDirectory(false)), - std::make_pair(ui->modDirEdit, settings().getModDirectory(false)), - std::make_pair(ui->cacheDirEdit, settings().getCacheDirectory(false)), - std::make_pair(ui->profilesDirEdit, settings().getProfileDirectory(false)), - std::make_pair(ui->overwriteDirEdit, settings().getOverwriteDirectory(false)) + std::make_pair(ui->downloadDirEdit, settings().paths().downloads(false)), + std::make_pair(ui->modDirEdit, settings().paths().mods(false)), + std::make_pair(ui->cacheDirEdit, settings().paths().cache(false)), + std::make_pair(ui->profilesDirEdit, settings().paths().profiles(false)), + std::make_pair(ui->overwriteDirEdit, settings().paths().overwrite(false)) }) { + QString storePath = baseDir.relativeFilePath(dir.second); storePath = dir.second; dir.first->setText(storePath); @@ -40,17 +46,17 @@ PathsSettingsTab::PathsSettingsTab(Settings& s, SettingsDialog& d) void PathsSettingsTab::update() { - using Setter = void (Settings::*)(const QString&); + using Setter = void (PathSettings::*)(const QString&); using Directory = std::tuple; - QString basePath = settings().getBaseDirectory(); + QString basePath = settings().paths().base(); for (const Directory &dir :{ - Directory{ui->downloadDirEdit->text(), &Settings::setDownloadDirectory, AppConfig::downloadPath()}, - Directory{ui->cacheDirEdit->text(), &Settings::setCacheDirectory, AppConfig::cachePath()}, - Directory{ui->modDirEdit->text(), &Settings::setModDirectory, AppConfig::modsPath()}, - Directory{ui->overwriteDirEdit->text(), &Settings::setOverwriteDirectory, AppConfig::overwritePath()}, - Directory{ui->profilesDirEdit->text(), &Settings::setProfileDirectory, AppConfig::profilesPath()} + Directory{ui->downloadDirEdit->text(), &PathSettings::setDownloads, AppConfig::downloadPath()}, + Directory{ui->cacheDirEdit->text(), &PathSettings::setCache, AppConfig::cachePath()}, + Directory{ui->modDirEdit->text(), &PathSettings::setMods, AppConfig::modsPath()}, + Directory{ui->overwriteDirEdit->text(), &PathSettings::setOverwrite, AppConfig::overwritePath()}, + Directory{ui->profilesDirEdit->text(), &PathSettings::setProfiles, AppConfig::profilesPath()} }) { QString path; Setter setter; @@ -70,22 +76,26 @@ void PathsSettingsTab::update() } if (QFileInfo(realPath) != QFileInfo(basePath + "/" + QString::fromStdWString(defaultName))) { - (settings().*setter)(path); + (settings().paths().*setter)(path); } else { - (settings().*setter)(""); + (settings().paths().*setter)(""); } } if (QFileInfo(ui->baseDirEdit->text()) != QFileInfo(qApp->property("dataPath").toString())) { - settings().setBaseDirectory(ui->baseDirEdit->text()); + settings().paths().setBase(ui->baseDirEdit->text()); } else { - settings().setBaseDirectory(""); + settings().paths().setBase(""); } - QFileInfo oldGameExe(settings().gamePlugin()->gameDirectory().absoluteFilePath(settings().gamePlugin()->binaryName())); + QFileInfo oldGameExe( + settings().game().plugin()->gameDirectory().absoluteFilePath( + settings().game().plugin()->binaryName())); + QFileInfo newGameExe(ui->managedGameDirEdit->text()); + if (oldGameExe != newGameExe) { - settings().setManagedGameDirectory(newGameExe.absolutePath()); + settings().game().setDirectory(newGameExe.absolutePath()); } } diff --git a/src/settingsdialogsteam.cpp b/src/settingsdialogsteam.cpp index 9ed93e47..3c4c5de6 100644 --- a/src/settingsdialogsteam.cpp +++ b/src/settingsdialogsteam.cpp @@ -5,7 +5,7 @@ SteamSettingsTab::SteamSettingsTab(Settings& s, SettingsDialog& d) : SettingsTab(s, d) { QString username, password; - settings().getSteamLogin(username, password); + settings().steam().login(username, password); ui->steamUserEdit->setText(username); ui->steamPassEdit->setText(password); @@ -13,5 +13,5 @@ SteamSettingsTab::SteamSettingsTab(Settings& s, SettingsDialog& d) void SteamSettingsTab::update() { - settings().setSteamLogin(ui->steamUserEdit->text(), ui->steamPassEdit->text()); + settings().steam().setLogin(ui->steamUserEdit->text(), ui->steamPassEdit->text()); } diff --git a/src/settingsdialogworkarounds.cpp b/src/settingsdialogworkarounds.cpp index b06bd77c..4d811e40 100644 --- a/src/settingsdialogworkarounds.cpp +++ b/src/settingsdialogworkarounds.cpp @@ -6,12 +6,12 @@ WorkaroundsSettingsTab::WorkaroundsSettingsTab(Settings& s, SettingsDialog& d) : SettingsTab(s, d) { - ui->appIDEdit->setText(settings().getSteamAppID()); + ui->appIDEdit->setText(settings().steam().appID()); - LoadMechanism::EMechanism mechanismID = settings().getLoadMechanism(); + LoadMechanism::EMechanism mechanismID = settings().game().loadMechanismType(); int index = 0; - if (settings().loadMechanism().isDirectLoadingSupported()) { + if (settings().game().loadMechanism().isDirectLoadingSupported()) { ui->mechanismBox->addItem(QObject::tr("Mod Organizer"), LoadMechanism::LOAD_MODORGANIZER); if (mechanismID == LoadMechanism::LOAD_MODORGANIZER) { index = ui->mechanismBox->count() - 1; @@ -20,10 +20,10 @@ WorkaroundsSettingsTab::WorkaroundsSettingsTab(Settings& s, SettingsDialog& d) ui->mechanismBox->setCurrentIndex(index); - ui->hideUncheckedBox->setChecked(settings().hideUncheckedPlugins()); - ui->forceEnableBox->setChecked(settings().forceEnableCoreFiles()); - ui->displayForeignBox->setChecked(settings().displayForeign()); - ui->lockGUIBox->setChecked(settings().lockGUI()); + ui->hideUncheckedBox->setChecked(settings().game().hideUncheckedPlugins()); + ui->forceEnableBox->setChecked(settings().game().forceEnableCoreFiles()); + ui->displayForeignBox->setChecked(settings().interface().displayForeign()); + ui->lockGUIBox->setChecked(settings().interface().lockGUI()); ui->enableArchiveParsingBox->setChecked(settings().archiveParsing()); setExecutableBlacklist(settings().executablesBlacklist()); @@ -35,19 +35,19 @@ WorkaroundsSettingsTab::WorkaroundsSettingsTab(Settings& s, SettingsDialog& d) void WorkaroundsSettingsTab::update() { - if (ui->appIDEdit->text() != settings().gamePlugin()->steamAPPId()) { - settings().setSteamAppID(ui->appIDEdit->text()); + if (ui->appIDEdit->text() != settings().game().plugin()->steamAPPId()) { + settings().steam().setAppID(ui->appIDEdit->text()); } else { - settings().setSteamAppID(""); + settings().steam().setAppID(""); } - settings().setLoadMechanism(static_cast( + settings().game().setLoadMechanism(static_cast( ui->mechanismBox->itemData(ui->mechanismBox->currentIndex()).toInt())); - settings().setHideUncheckedPlugins(ui->hideUncheckedBox->isChecked()); - settings().setForceEnableCoreFiles(ui->forceEnableBox->isChecked()); - settings().setDisplayForeign(ui->displayForeignBox->isChecked()); - settings().setLockGUI(ui->lockGUIBox->isChecked()); + settings().game().setHideUncheckedPlugins(ui->hideUncheckedBox->isChecked()); + settings().game().setForceEnableCoreFiles(ui->forceEnableBox->isChecked()); + settings().interface().setDisplayForeign(ui->displayForeignBox->isChecked()); + settings().interface().setLockGUI(ui->lockGUIBox->isChecked()); settings().setArchiveParsing(ui->enableArchiveParsingBox->isChecked()); settings().setExecutablesBlacklist(getExecutableBlacklist()); } diff --git a/src/statusbar.cpp b/src/statusbar.cpp index d22010a5..3734aa87 100644 --- a/src/statusbar.cpp +++ b/src/statusbar.cpp @@ -129,7 +129,7 @@ void StatusBar::setUpdateAvailable(bool b) void StatusBar::checkSettings(const Settings& settings) { - m_api->setVisible(!settings.hideAPICounter()); + m_api->setVisible(!settings.interface().hideAPICounter()); } void StatusBar::showEvent(QShowEvent*) diff --git a/src/usvfsconnector.cpp b/src/usvfsconnector.cpp index 41f58308..4315ed92 100644 --- a/src/usvfsconnector.cpp +++ b/src/usvfsconnector.cpp @@ -163,8 +163,8 @@ QString toString(CrashDumpsType t) UsvfsConnector::UsvfsConnector() { USVFSParameters params; - LogLevel level = toUsvfsLogLevel(Settings::instance().logLevel()); - CrashDumpsType dumpType = Settings::instance().crashDumpsType(); + LogLevel level = toUsvfsLogLevel(Settings::instance().diagnostics().logLevel()); + CrashDumpsType dumpType = Settings::instance().diagnostics().crashDumpsType(); std::string dumpPath = MOShared::ToString(OrganizerCore::crashDumpsPath(), true); USVFSInitParameters(¶ms, SHMID, false, level, dumpType, dumpPath.c_str()); -- cgit v1.3.1 From f92e2c376d36132a9676b30f0b08543f27a13064 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Thu, 19 Sep 2019 16:27:41 -0400 Subject: TaskDialog for restarting as admin for steam added a parent widget parameter to a bunch of places fixed paths still getting changed even if folders can't be created made private the member variables that were temporarily public during rework --- src/settingsdialog.cpp | 44 ++++++++++------ src/settingsdialog.h | 20 +++---- src/settingsdialoggeneral.cpp | 7 ++- src/settingsdialognexus.cpp | 14 ++--- src/settingsdialogpaths.cpp | 6 ++- src/settingsdialogworkarounds.cpp | 6 ++- src/spawn.cpp | 106 ++++++++++++++++++++------------------ src/spawn.h | 7 ++- 8 files changed, 121 insertions(+), 89 deletions(-) (limited to 'src/settingsdialognexus.cpp') diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index 1d3d4a39..8fb25b1c 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -33,8 +33,8 @@ SettingsDialog::SettingsDialog(PluginContainer *pluginContainer, Settings& setti : TutorableDialog("SettingsDialog", parent) , ui(new Ui::SettingsDialog) , m_settings(settings) - , m_PluginContainer(pluginContainer) - , m_keyChanged(false) + , m_pluginContainer(pluginContainer) + , m_restartNeeded(false) { ui->setupUi(this); @@ -47,6 +47,25 @@ SettingsDialog::SettingsDialog(PluginContainer *pluginContainer, Settings& setti m_tabs.push_back(std::unique_ptr(new WorkaroundsSettingsTab(settings, *this))); } +PluginContainer* SettingsDialog::pluginContainer() +{ + return m_pluginContainer; +} + +QWidget* SettingsDialog::parentWidgetForDialogs() +{ + if (isVisible()) { + return this; + } else { + return parentWidget(); + } +} + +void SettingsDialog::setRestartNeeded() +{ + m_restartNeeded = true; +} + int SettingsDialog::exec() { GeometrySaver gs(m_settings, this); @@ -68,13 +87,8 @@ int SettingsDialog::exec() } } - bool restartNeeded = false; - if (getApiKeyChanged()) { - restartNeeded = true; - } - - if (restartNeeded) { - if (QMessageBox::question(nullptr, + if (m_restartNeeded) { + if (QMessageBox::question(parentWidgetForDialogs(), tr("Restart Mod Organizer?"), tr("In order to finish configuration changes, MO must be restarted.\n" "Restart it now?"), @@ -111,7 +125,7 @@ void SettingsDialog::accept() QDir::fromNativeSeparators( Settings::instance().paths().mods(true))) && (QMessageBox::question( - nullptr, tr("Confirm"), + parentWidgetForDialogs(), tr("Confirm"), tr("Changing the mod directory affects all your profiles! " "Mods not present (or named differently) in the new location " "will be disabled in all profiles. " @@ -124,11 +138,6 @@ void SettingsDialog::accept() TutorableDialog::accept(); } -bool SettingsDialog::getApiKeyChanged() -{ - return m_keyChanged; -} - SettingsTab::SettingsTab(Settings& s, SettingsDialog& d) : ui(d.ui), m_settings(s), m_dialog(d) @@ -146,3 +155,8 @@ SettingsDialog& SettingsTab::dialog() { return m_dialog; } + +QWidget* SettingsTab::parentWidget() +{ + return m_dialog.parentWidgetForDialogs(); +} diff --git a/src/settingsdialog.h b/src/settingsdialog.h index 6a99cb8d..e89da665 100644 --- a/src/settingsdialog.h +++ b/src/settingsdialog.h @@ -42,6 +42,7 @@ protected: Settings& settings(); SettingsDialog& dialog(); + QWidget* parentWidget(); private: Settings& m_settings; @@ -56,11 +57,12 @@ private: **/ class SettingsDialog : public MOBase::TutorableDialog { - Q_OBJECT + Q_OBJECT; + friend class SettingsTab; public: explicit SettingsDialog( - PluginContainer *pluginContainer, Settings& settings, QWidget *parent = 0); + PluginContainer* pluginContainer, Settings& settings, QWidget* parent = 0); ~SettingsDialog(); @@ -70,23 +72,21 @@ public: */ QString getColoredButtonStyleSheet() const; - // temp - Ui::SettingsDialog *ui; - bool m_keyChanged; - PluginContainer *m_PluginContainer; + PluginContainer* pluginContainer(); + QWidget* parentWidgetForDialogs(); + void setRestartNeeded(); int exec() override; public slots: virtual void accept(); -public: - bool getApiKeyChanged(); - private: Settings& m_settings; std::vector> m_tabs; - + Ui::SettingsDialog* ui; + bool m_restartNeeded; + PluginContainer* m_pluginContainer; }; #endif // SETTINGSDIALOG_H diff --git a/src/settingsdialoggeneral.cpp b/src/settingsdialoggeneral.cpp index 3f7ece38..8ecdcbb9 100644 --- a/src/settingsdialoggeneral.cpp +++ b/src/settingsdialoggeneral.cpp @@ -238,8 +238,11 @@ void GeneralSettingsTab::on_resetColorsBtn_clicked() void GeneralSettingsTab::on_resetDialogsButton_clicked() { - if (QMessageBox::question(&dialog(), QObject::tr("Confirm?"), - QObject::tr("This will make all dialogs show up again where you checked the \"Remember selection\"-box. Continue?"), + if (QMessageBox::question( + parentWidget(), QObject::tr("Confirm?"), + QObject::tr( + "This will reset all the choices you made to dialogs and make them all " + "visible again. Continue?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { resetDialogs(); } diff --git a/src/settingsdialognexus.cpp b/src/settingsdialognexus.cpp index 0b08f13f..826075c0 100644 --- a/src/settingsdialognexus.cpp +++ b/src/settingsdialognexus.cpp @@ -226,7 +226,7 @@ void NexusSettingsTab::on_nexusDisconnect_clicked() void NexusSettingsTab::on_clearCacheButton_clicked() { QDir(Settings::instance().paths().cache()).removeRecursively(); - NexusInterface::instance(dialog().m_PluginContainer)->clearCache(); + NexusInterface::instance(dialog().pluginContainer())->clearCache(); } void NexusSettingsTab::on_associateButton_clicked() @@ -238,7 +238,7 @@ void NexusSettingsTab::validateKey(const QString& key) { if (!m_nexusValidator) { m_nexusValidator.reset(new NexusKeyValidator( - *NexusInterface::instance(dialog().m_PluginContainer)->getAccessManager())); + *NexusInterface::instance(dialog().pluginContainer())->getAccessManager())); m_nexusValidator->stateChanged = [&](auto&& s, auto&& e){ onValidatorStateChanged(s, e); @@ -294,7 +294,7 @@ void NexusSettingsTab::onValidatorStateChanged( void NexusSettingsTab::onValidatorFinished(const APIUserAccount& user) { - NexusInterface::instance(dialog().m_PluginContainer)->setUserAccount(user); + NexusInterface::instance(dialog().pluginContainer())->setUserAccount(user); if (!user.apiKey().isEmpty()) { if (setKey(user.apiKey())) { @@ -311,7 +311,7 @@ void NexusSettingsTab::addNexusLog(const QString& s) bool NexusSettingsTab::setKey(const QString& key) { - dialog().m_keyChanged = true; + dialog().setRestartNeeded(); const bool ret = settings().nexus().setApiKey(key); updateNexusState(); return ret; @@ -319,10 +319,10 @@ bool NexusSettingsTab::setKey(const QString& key) bool NexusSettingsTab::clearKey() { - dialog().m_keyChanged = true; + dialog().setRestartNeeded(); const auto ret = settings().nexus().clearApiKey(); - NexusInterface::instance(dialog().m_PluginContainer)->getAccessManager()->clearApiKey(); + NexusInterface::instance(dialog().pluginContainer())->getAccessManager()->clearApiKey(); updateNexusState(); return ret; @@ -371,7 +371,7 @@ void NexusSettingsTab::updateNexusButtons() void NexusSettingsTab::updateNexusData() { - const auto user = NexusInterface::instance(dialog().m_PluginContainer) + const auto user = NexusInterface::instance(dialog().pluginContainer()) ->getAPIUserAccount(); if (user.isValid()) { diff --git a/src/settingsdialogpaths.cpp b/src/settingsdialogpaths.cpp index aeb4dd5d..c6fd40a7 100644 --- a/src/settingsdialogpaths.cpp +++ b/src/settingsdialogpaths.cpp @@ -68,10 +68,12 @@ void PathsSettingsTab::update() if (!QDir(realPath).exists()) { if (!QDir().mkpath(realPath)) { - QMessageBox::warning(qApp->activeWindow(), QObject::tr("Error"), + QMessageBox::warning(parentWidget(), QObject::tr("Error"), QObject::tr("Failed to create \"%1\", you may not have the " - "necessary permission. path remains unchanged.") + "necessary permissions. Path remains unchanged.") .arg(realPath)); + + continue; } } diff --git a/src/settingsdialogworkarounds.cpp b/src/settingsdialogworkarounds.cpp index ccbfcbfe..5e70e5a6 100644 --- a/src/settingsdialogworkarounds.cpp +++ b/src/settingsdialogworkarounds.cpp @@ -83,7 +83,9 @@ void WorkaroundsSettingsTab::on_bsaDateBtn_clicked() const auto* game = qApp->property("managed_game").value(); QDir dir = game->dataDirectory(); - helper::backdateBSAs(qApp->applicationDirPath().toStdWString(), + helper::backdateBSAs( + parentWidget(), + qApp->applicationDirPath().toStdWString(), dir.absolutePath().toStdWString()); } @@ -95,7 +97,7 @@ void WorkaroundsSettingsTab::on_resetGeometryBtn_clicked() "Restart now?"); const auto res = QMessageBox::question( - nullptr, caption, text, QMessageBox::Yes | QMessageBox::Cancel); + parentWidget(), caption, text, QMessageBox::Yes | QMessageBox::Cancel); if (res == QMessageBox::Yes) { settings().geometry().requestReset(); diff --git a/src/spawn.cpp b/src/spawn.cpp index e18e6bb3..a0cf9fb6 100644 --- a/src/spawn.cpp +++ b/src/spawn.cpp @@ -215,7 +215,7 @@ QMessageBox::StandardButton startSteamFailed( .exec(); } -void spawnFailed(const SpawnParameters& sp, DWORD code) +void spawnFailed(QWidget* parent, const SpawnParameters& sp, DWORD code) { const auto details = makeDetails(sp, code); log::error("{}", details); @@ -225,12 +225,7 @@ void spawnFailed(const SpawnParameters& sp, DWORD code) const auto mainText = QObject::tr("Cannot start %1") .arg(sp.binary.fileName()); - QWidget *window = qApp->activeWindow(); - if ((window != nullptr) && (!window->isVisible())) { - window = nullptr; - } - - MOBase::TaskDialog(window, title) + MOBase::TaskDialog(parent, title) .main(mainText) .content(makeContent(sp, code)) .details(details) @@ -239,7 +234,7 @@ void spawnFailed(const SpawnParameters& sp, DWORD code) } void helperFailed( - DWORD code, const QString& why, const std::wstring& binary, + QWidget* parent, DWORD code, const QString& why, const std::wstring& binary, const std::wstring& cwd, const std::wstring& args) { SpawnParameters sp; @@ -255,12 +250,7 @@ void helperFailed( const auto mainText = QObject::tr("Cannot start %1") .arg(sp.binary.fileName()); - QWidget *window = qApp->activeWindow(); - if ((window != nullptr) && (!window->isVisible())) { - window = nullptr; - } - - MOBase::TaskDialog(window, title) + MOBase::TaskDialog(parent, title) .main(mainText) .content(makeContent(sp, code)) .details(details) @@ -268,7 +258,7 @@ void helperFailed( .exec(); } -bool confirmRestartAsAdmin(const SpawnParameters& sp) +bool confirmRestartAsAdmin(QWidget* parent, const SpawnParameters& sp) { const auto details = makeDetails(sp, ERROR_ELEVATION_REQUIRED); @@ -287,15 +277,9 @@ bool confirmRestartAsAdmin(const SpawnParameters& sp) "You can restart Mod Organizer as administrator and try launching the " "program again."); - - QWidget *window = qApp->activeWindow(); - if ((window != nullptr) && (!window->isVisible())) { - window = nullptr; - } - log::debug("asking user to restart MO as administrator"); - const auto r = MOBase::TaskDialog(window, title) + const auto r = MOBase::TaskDialog(parent, title) .main(mainText) .content(content) .details(details) @@ -313,7 +297,7 @@ bool confirmRestartAsAdmin(const SpawnParameters& sp) } QMessageBox::StandardButton confirmStartSteam( - QWidget* window, const SpawnParameters& sp, const QString& details) + QWidget* parent, const SpawnParameters& sp, const QString& details) { const auto title = QObject::tr("Launch Steam"); const auto mainText = QObject::tr("This program requires Steam"); @@ -321,7 +305,7 @@ QMessageBox::StandardButton confirmStartSteam( "Mod Organizer has detected that this program likely requires Steam to be " "running to function properly."); - return MOBase::TaskDialog(window, title) + return MOBase::TaskDialog(parent, title) .main(mainText) .content(content) .details(details) @@ -340,17 +324,35 @@ QMessageBox::StandardButton confirmStartSteam( .exec(); } -QuestionBoxMemory::Button confirmRestartAsAdminForSteam(QWidget* parent, const SpawnParameters& sp) +QMessageBox::StandardButton confirmRestartAsAdminForSteam( + QWidget* parent, const SpawnParameters& sp) { - return QuestionBoxMemory::query( - parent, "steamAdminQuery", sp.binary.fileName(), - QObject::tr("Steam: Access Denied"), - QObject::tr("MO was denied access to the Steam process. This normally indicates that " - "Steam is being run as administrator while MO is not. This can cause issues " - "launching the game. It is recommended to not run Steam as administrator unless " - "absolutely necessary.\n\n" - "Restart MO as administrator?"), - QDialogButtonBox::Yes | QDialogButtonBox::No | QDialogButtonBox::Cancel); + const auto title = QObject::tr("Elevation required"); + const auto mainText = QObject::tr("Steam is running as administrator"); + const auto content = QObject::tr( + "Running Steam as administrator is typically unnecessary and can cause " + "problems when Mod Organizer is not running as administrato\r\n\r\n" + "You can restart Mod Organizer as administrator and try launching the " + "program again."); + + return MOBase::TaskDialog(parent, title) + .main(mainText) + .content(content) + .details("") + .icon(QMessageBox::Question) + .button({ + QObject::tr("Restart Mod Organizer as administrator"), + QObject::tr("You must allow \"helper.exe\" to make changes to the system."), + QMessageBox::Yes}) + .button({ + QObject::tr("Continue"), + QObject::tr("The program might fail to run."), + QMessageBox::No}) + .button({ + QObject::tr("Cancel"), + QMessageBox::Cancel}) + .remember("steamAdminQuery", sp.binary.fileName()) + .exec(); } bool eventLogNotRunning( @@ -447,7 +449,7 @@ DWORD spawn(const SpawnParameters& sp, HANDLE& processHandle, HANDLE& threadHand return ERROR_SUCCESS; } -bool restartAsAdmin() +bool restartAsAdmin(QWidget* parent) { WCHAR cwd[MAX_PATH] = {}; if (!GetCurrentDirectory(MAX_PATH, cwd)) { @@ -455,6 +457,7 @@ bool restartAsAdmin() } if (!helper::adminLaunch( + parent, qApp->applicationDirPath().toStdWString(), qApp->applicationFilePath().toStdWString(), std::wstring(cwd))) @@ -469,21 +472,21 @@ bool restartAsAdmin() return true; } -void startBinaryAdmin(const SpawnParameters& sp) +void startBinaryAdmin(QWidget* parent, const SpawnParameters& sp) { - if (!dialogs::confirmRestartAsAdmin(sp)) { + if (!dialogs::confirmRestartAsAdmin(parent, sp)) { log::debug("user declined"); return; } log::info("restarting MO as administrator"); - restartAsAdmin(); + restartAsAdmin(parent); } bool checkBinary(QWidget* parent, const SpawnParameters& sp) { if (!sp.binary.exists()) { - dialogs::spawnFailed(sp, ERROR_FILE_NOT_FOUND); + dialogs::spawnFailed(parent, sp, ERROR_FILE_NOT_FOUND); return false; } @@ -660,7 +663,7 @@ bool checkSteam( const auto c = dialogs::confirmRestartAsAdminForSteam(parent, sp); if (c == QDialogButtonBox::Yes) { - restartAsAdmin(); + restartAsAdmin(parent); return false; } else if (c == QDialogButtonBox::No) { log::debug("user declined to restart MO, continuing"); @@ -720,13 +723,13 @@ HANDLE startBinary(QWidget* parent, const SpawnParameters& sp) case ERROR_ELEVATION_REQUIRED: { - startBinaryAdmin(sp); + startBinaryAdmin(parent, sp); return INVALID_HANDLE_VALUE; } default: { - dialogs::spawnFailed(sp, e); + dialogs::spawnFailed(parent, sp, e); return INVALID_HANDLE_VALUE; } } @@ -740,6 +743,7 @@ namespace helper { bool helperExec( + QWidget* parent, const std::wstring& moDirectory, const std::wstring& commandLine, BOOL async) { const std::wstring fileName = moDirectory + L"\\helper.exe"; @@ -766,7 +770,7 @@ bool helperExec( const auto e = GetLastError(); spawn::dialogs::helperFailed( - e, "ShellExecuteExW()", fileName, moDirectory, commandLine); + parent, e, "ShellExecuteExW()", fileName, moDirectory, commandLine); return false; } @@ -788,7 +792,8 @@ bool helperExec( ERROR_ABANDONED_WAIT_0 : GetLastError()); spawn::dialogs::helperFailed( - code, "WaitForSingleObject()", fileName, moDirectory, commandLine); + parent, code, "WaitForSingleObject()", + fileName, moDirectory, commandLine); return false; } @@ -798,7 +803,7 @@ bool helperExec( const auto e = GetLastError(); spawn::dialogs::helperFailed( - e, "GetExitCodeProcess()", fileName, moDirectory, commandLine); + parent, e, "GetExitCodeProcess()", fileName, moDirectory, commandLine); return false; } @@ -806,21 +811,24 @@ bool helperExec( return (exitCode == 0); } -bool backdateBSAs(const std::wstring &moPath, const std::wstring &dataPath) +bool backdateBSAs( + QWidget* parent, const std::wstring &moPath, const std::wstring &dataPath) { const std::wstring commandLine = fmt::format( L"backdateBSA \"{}\"", dataPath); - return helperExec(moPath, commandLine, FALSE); + return helperExec(parent, moPath, commandLine, FALSE); } -bool adminLaunch(const std::wstring &moPath, const std::wstring &moFile, const std::wstring &workingDir) +bool adminLaunch( + QWidget* parent, const std::wstring &moPath, + const std::wstring &moFile, const std::wstring &workingDir) { const std::wstring commandLine = fmt::format( L"adminLaunch {} \"{}\" \"{}\"", ::GetCurrentProcessId(), moFile, workingDir); - return helperExec(moPath, commandLine, true); + return helperExec(parent, moPath, commandLine, true); } } // namespace diff --git a/src/spawn.h b/src/spawn.h index da626329..9e1e2539 100644 --- a/src/spawn.h +++ b/src/spawn.h @@ -84,7 +84,8 @@ namespace helper * @param moPath absolute path to the modOrganizer base directory * @param dataPath the path taht contains the .bsa-files, usually the data directory of the game **/ -bool backdateBSAs(const std::wstring &moPath, const std::wstring &dataPath); +bool backdateBSAs( + QWidget* parent, const std::wstring &moPath, const std::wstring &dataPath); /** * @brief waits for the current process to exit and restarts it as an administrator @@ -92,7 +93,9 @@ bool backdateBSAs(const std::wstring &moPath, const std::wstring &dataPath); * @param moFile file name of modOrganizer * @param workingDir current working directory **/ -bool adminLaunch(const std::wstring &moPath, const std::wstring &moFile, const std::wstring &workingDir); +bool adminLaunch( + QWidget* parent, const std::wstring &moPath, + const std::wstring &moFile, const std::wstring &workingDir); } // namespace -- cgit v1.3.1 From c4dd23abb7a37531040d6348c491dc868919013c Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Fri, 4 Oct 2019 09:40:37 -0400 Subject: added error messages to FileRenamer and a few more fixes for shell functions changing names --- src/downloadmanager.cpp | 10 +++++----- src/filerenamer.cpp | 42 +++++++++++++++++++++++++++++------------- src/filerenamer.h | 8 ++++++-- src/mainwindow.cpp | 36 ++++++++++++++++++------------------ src/modinfodialogconflicts.cpp | 2 +- src/modinfodialogfiletree.cpp | 8 ++++---- src/modinfodialogimages.cpp | 2 +- src/modinfodialognexus.cpp | 6 +++--- src/motddialog.cpp | 2 +- src/nxmaccessmanager.cpp | 2 +- src/overwriteinfodialog.cpp | 4 ++-- src/problemsdialog.cpp | 2 +- src/selfupdater.cpp | 7 +++++-- src/settings.cpp | 6 ++++-- src/settingsdialognexus.cpp | 2 +- src/texteditor.cpp | 2 +- 16 files changed, 83 insertions(+), 58 deletions(-) (limited to 'src/settingsdialognexus.cpp') diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 35f60d7a..b4a7b57d 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -1059,11 +1059,11 @@ void DownloadManager::openFile(int index) QDir path = QDir(m_OutputDirectory); if (path.exists(getFileName(index))) { - shell::OpenFile(getFilePath(index)); + shell::Open(getFilePath(index)); return; } - shell::ExploreFile(m_OutputDirectory); + shell::Explore(m_OutputDirectory); return; } @@ -1077,18 +1077,18 @@ void DownloadManager::openInDownloadsFolder(int index) const auto path = getFilePath(index); if (QFile::exists(path)) { - shell::ExploreFile(path); + shell::Explore(path); return; } else { const auto unfinished = path + ".unfinished"; if (QFile::exists(unfinished)) { - shell::ExploreFile(unfinished); + shell::Explore(unfinished); return; } } - shell::ExploreFile(m_OutputDirectory); + shell::Explore(m_OutputDirectory); } diff --git a/src/filerenamer.cpp b/src/filerenamer.cpp index a97d7742..7fc90eb2 100644 --- a/src/filerenamer.cpp +++ b/src/filerenamer.cpp @@ -1,4 +1,5 @@ #include "filerenamer.h" +#include #include #include #include @@ -37,10 +38,13 @@ FileRenamer::RenameResults FileRenamer::rename(const QString& oldName, const QSt log::debug("removing {}", newName); // user wants to replace the file, so remove it - if (!QFile(newName).remove()) { - log::warn("failed to remove '{}'", newName); + const auto r = shell::Delete(newName); + + if (!r.success()) { + log::error("failed to remove '{}': {}", newName, r.toString()); + // removal failed, warn the user and allow canceling - if (!removeFailed(newName)) { + if (!removeFailed(newName, r)) { log::debug("canceling {}", oldName); // user wants to cancel return RESULT_CANCEL; @@ -64,12 +68,15 @@ FileRenamer::RenameResults FileRenamer::rename(const QString& oldName, const QSt } // target either didn't exist or was removed correctly + const auto r = shell::Rename(oldName, newName); - if (!QFile::rename(oldName, newName)) { - log::warn("failed to rename '{}' to '{}'", oldName, newName); + if (!r.success()) { + log::error( + "failed to rename '{}' to '{}': {}", + oldName, newName, r.toString()); // renaming failed, warn the user and allow canceling - if (!renameFailed(oldName, newName)) { + if (!renameFailed(oldName, newName, r)) { // user wants to cancel log::debug("canceling"); return RESULT_CANCEL; @@ -144,7 +151,7 @@ FileRenamer::RenameDecision FileRenamer::confirmReplace(const QString& newName) } } -bool FileRenamer::removeFailed(const QString& name) +bool FileRenamer::removeFailed(const QString& name, const shell::Result& r) { QMessageBox::StandardButtons buttons = QMessageBox::Ok; if (m_flags & MULTIPLE) { @@ -153,8 +160,9 @@ bool FileRenamer::removeFailed(const QString& name) } const auto answer = QMessageBox::critical( - m_parent, QObject::tr("File operation failed"), - QObject::tr("Failed to remove \"%1\". Maybe you lack the required file permissions?").arg(name), + m_parent, + QObject::tr("File operation failed"), + QObject::tr("Failed to remove \"%1\": %2").arg(name).arg(r.toString()), buttons); if (answer == QMessageBox::Cancel) { @@ -168,7 +176,8 @@ bool FileRenamer::removeFailed(const QString& name) return true; } -bool FileRenamer::renameFailed(const QString& oldName, const QString& newName) +bool FileRenamer::renameFailed( + const QString& oldName, const QString& newName, const shell::Result& r) { QMessageBox::StandardButtons buttons = QMessageBox::Ok; if (m_flags & MULTIPLE) { @@ -177,9 +186,16 @@ bool FileRenamer::renameFailed(const QString& oldName, const QString& newName) } const auto answer = QMessageBox::critical( - m_parent, QObject::tr("File operation failed"), - QObject::tr("failed to rename %1 to %2").arg(oldName).arg(QDir::toNativeSeparators(newName)), - buttons); + m_parent, + QObject::tr("File operation failed"), + QObject::tr( + "Failed to rename file: %1.\r\n\r\n" + "Source:\r\n\"%2\"\r\n\r\n" + "Destination:\r\n\"%3\"") + .arg(r.toString()) + .arg(QDir::toNativeSeparators(oldName)) + .arg(QDir::toNativeSeparators(newName)), + buttons); if (answer == QMessageBox::Cancel) { // user wants to stop diff --git a/src/filerenamer.h b/src/filerenamer.h index cd57244c..5583ecbd 100644 --- a/src/filerenamer.h +++ b/src/filerenamer.h @@ -3,6 +3,8 @@ #include +namespace MOBase::shell { class Result; } + /** * Renames individual files and handles dialog boxes to confirm replacements and * failures with the user @@ -126,7 +128,7 @@ private: * @param name The name of the file that failed to be removed * @return true to continue, false to stop **/ - bool removeFailed(const QString& name); + bool removeFailed(const QString& name, const MOBase::shell::Result& r); /** * renaming a file failed, ask the user to continue or cancel @@ -134,7 +136,9 @@ private: * @param newName new filename * @return true to continue, false to stop **/ - bool renameFailed(const QString& oldName, const QString& newName); + bool renameFailed( + const QString& oldName, const QString& newName, + const MOBase::shell::Result& r); }; #endif // FILERENAMER_H diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 8ab28d22..b29ae11c 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3313,12 +3313,12 @@ void MainWindow::openExplorer_clicked() if (selection->hasSelection() && selection->selectedRows().count() > 1) { for (QModelIndex idx : selection->selectedRows()) { ModInfo::Ptr info = ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt()); - shell::ExploreFile(info->absolutePath()); + shell::Explore(info->absolutePath()); } } else { ModInfo::Ptr modInfo = ModInfo::getByIndex(m_ContextRow); - shell::ExploreFile(modInfo->absolutePath()); + shell::Explore(modInfo->absolutePath()); } } @@ -3333,14 +3333,14 @@ void MainWindow::openPluginOriginExplorer_clicked() continue; } ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); - shell::ExploreFile(modInfo->absolutePath()); + shell::Explore(modInfo->absolutePath()); } } else { QModelIndex idx = selection->currentIndex(); QString fileName = idx.data().toString(); ModInfo::Ptr modInfo = ModInfo::getByIndex(ModInfo::getIndex(m_OrganizerCore.pluginList()->origin(fileName))); - shell::ExploreFile(modInfo->absolutePath()); + shell::Explore(modInfo->absolutePath()); } } @@ -3355,7 +3355,7 @@ void MainWindow::openExplorer_activated() std::vector flags = modInfo->getFlags(); if (modInfo->isRegular() || (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end())) { - shell::ExploreFile(modInfo->absolutePath()); + shell::Explore(modInfo->absolutePath()); } } @@ -3376,7 +3376,7 @@ void MainWindow::openExplorer_activated() std::vector flags = modInfo->getFlags(); if (modInfo->isRegular() || (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end())) { - shell::ExploreFile(modInfo->absolutePath()); + shell::Explore(modInfo->absolutePath()); } } } @@ -4344,61 +4344,61 @@ void MainWindow::disableVisibleMods() void MainWindow::openInstanceFolder() { QString dataPath = qApp->property("dataPath").toString(); - shell::ExploreFile(dataPath); + shell::Explore(dataPath); } void MainWindow::openLogsFolder() { QString logsPath = qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::logPath()); - shell::ExploreFile(logsPath); + shell::Explore(logsPath); } void MainWindow::openInstallFolder() { - shell::ExploreFile(qApp->applicationDirPath()); + shell::Explore(qApp->applicationDirPath()); } void MainWindow::openPluginsFolder() { QString pluginsPath = QCoreApplication::applicationDirPath() + "/" + ToQString(AppConfig::pluginPath()); - shell::ExploreFile(pluginsPath); + shell::Explore(pluginsPath); } void MainWindow::openProfileFolder() { - shell::ExploreFile(m_OrganizerCore.currentProfile()->absolutePath()); + shell::Explore(m_OrganizerCore.currentProfile()->absolutePath()); } void MainWindow::openIniFolder() { if (m_OrganizerCore.currentProfile()->localSettingsEnabled()) { - shell::ExploreFile(m_OrganizerCore.currentProfile()->absolutePath()); + shell::Explore(m_OrganizerCore.currentProfile()->absolutePath()); } else { - shell::ExploreFile(m_OrganizerCore.managedGame()->documentsDirectory()); + shell::Explore(m_OrganizerCore.managedGame()->documentsDirectory()); } } void MainWindow::openDownloadsFolder() { - shell::ExploreFile(m_OrganizerCore.settings().paths().downloads()); + shell::Explore(m_OrganizerCore.settings().paths().downloads()); } void MainWindow::openModsFolder() { - shell::ExploreFile(m_OrganizerCore.settings().paths().mods()); + shell::Explore(m_OrganizerCore.settings().paths().mods()); } void MainWindow::openGameFolder() { - shell::ExploreFile(m_OrganizerCore.managedGame()->gameDirectory()); + shell::Explore(m_OrganizerCore.managedGame()->gameDirectory()); } void MainWindow::openMyGamesFolder() { - shell::ExploreFile(m_OrganizerCore.managedGame()->documentsDirectory()); + shell::Explore(m_OrganizerCore.managedGame()->documentsDirectory()); } @@ -5391,7 +5391,7 @@ void MainWindow::openDataOriginExplorer_clicked() const auto fullPath = m_ContextItem->data(0, Qt::UserRole).toString(); log::debug("opening in explorer: {}", fullPath); - shell::ExploreFile(fullPath); + shell::Explore(fullPath); } void MainWindow::updateAvailable() diff --git a/src/modinfodialogconflicts.cpp b/src/modinfodialogconflicts.cpp index 3a71b405..36559a75 100644 --- a/src/modinfodialogconflicts.cpp +++ b/src/modinfodialogconflicts.cpp @@ -547,7 +547,7 @@ void ConflictsTab::exploreItems(QTreeView* tree) // the menu item is only shown for a single selection, but handle all of them // in case this changes for_each_in_selection(tree, [&](const ConflictItem* item) { - shell::ExploreFile(item->fileName()); + shell::Explore(item->fileName()); return true; }); } diff --git a/src/modinfodialogfiletree.cpp b/src/modinfodialogfiletree.cpp index 207c792d..71ea9210 100644 --- a/src/modinfodialogfiletree.cpp +++ b/src/modinfodialogfiletree.cpp @@ -128,7 +128,7 @@ void FileTreeTab::onOpen() return; } - shell::OpenFile(m_fs->filePath(selection)); + shell::Open(m_fs->filePath(selection)); } void FileTreeTab::onPreview() @@ -146,9 +146,9 @@ void FileTreeTab::onExplore() auto selection = singleSelection(); if (selection.isValid()) { - shell::ExploreFile(m_fs->filePath(selection)); + shell::Explore(m_fs->filePath(selection)); } else { - shell::ExploreFile(mod().absolutePath()); + shell::Explore(mod().absolutePath()); } } @@ -204,7 +204,7 @@ void FileTreeTab::onUnhide() void FileTreeTab::onOpenInExplorer() { - shell::ExploreFile(mod().absolutePath()); + shell::Explore(mod().absolutePath()); } bool FileTreeTab::deleteFile(const QModelIndex& index) diff --git a/src/modinfodialogimages.cpp b/src/modinfodialogimages.cpp index 9d347f57..c5b04538 100644 --- a/src/modinfodialogimages.cpp +++ b/src/modinfodialogimages.cpp @@ -547,7 +547,7 @@ void ImagesTab::showTooltip(QHelpEvent* e) void ImagesTab::onExplore() { if (auto* f=m_files.selectedFile()) { - MOBase::shell::ExploreFile(f->path()); + shell::Explore(f->path()); } } diff --git a/src/modinfodialognexus.cpp b/src/modinfodialognexus.cpp index 95e62328..59bfe930 100644 --- a/src/modinfodialognexus.cpp +++ b/src/modinfodialognexus.cpp @@ -95,7 +95,7 @@ void NexusTab::update() connect( page, &NexusTabWebpage::linkClicked, - [&](const QUrl& url){ shell::OpenLink(url); }); + [&](const QUrl& url){ shell::Open(url); }); ui->endorse->setEnabled( (mod().endorsedState() == ModInfo::ENDORSED_FALSE) || @@ -363,7 +363,7 @@ void NexusTab::onVisitNexus() const QString nexusLink = NexusInterface::instance(&plugin()) ->getModURL(modID, mod().getGameName()); - shell::OpenLink(QUrl(nexusLink)); + shell::Open(QUrl(nexusLink)); } } @@ -412,6 +412,6 @@ void NexusTab::onVisitCustomURL() { const auto url = mod().parseCustomURL(); if (url.isValid()) { - shell::OpenLink(url); + shell::Open(url); } } diff --git a/src/motddialog.cpp b/src/motddialog.cpp index ca1e60ad..eee80205 100644 --- a/src/motddialog.cpp +++ b/src/motddialog.cpp @@ -47,5 +47,5 @@ void MotDDialog::on_okButton_clicked() void MotDDialog::linkClicked(const QUrl &url) { - shell::OpenLink(url); + shell::Open(url); } diff --git a/src/nxmaccessmanager.cpp b/src/nxmaccessmanager.cpp index c6ef7bc7..3cc1b7d9 100644 --- a/src/nxmaccessmanager.cpp +++ b/src/nxmaccessmanager.cpp @@ -286,7 +286,7 @@ void NexusSSOLogin::onMessage(const QString& s) // open browser const auto url = NexusSSOPage.arg(m_guid); - shell::OpenLink(url); + shell::Open(url); m_timeout.stop(); setState(WaitingForBrowser); diff --git a/src/overwriteinfodialog.cpp b/src/overwriteinfodialog.cpp index fe1d8825..078bcfc9 100644 --- a/src/overwriteinfodialog.cpp +++ b/src/overwriteinfodialog.cpp @@ -229,7 +229,7 @@ void OverwriteInfoDialog::renameTriggered() void OverwriteInfoDialog::openFile(const QModelIndex &index) { - shell::OpenFile(m_FileSystemModel->filePath(index)); + shell::Open(m_FileSystemModel->filePath(index)); } @@ -270,7 +270,7 @@ void OverwriteInfoDialog::createDirectoryTriggered() void OverwriteInfoDialog::on_explorerButton_clicked() { - shell::ExploreFile(m_ModInfo->absolutePath()); + shell::Explore(m_ModInfo->absolutePath()); } void OverwriteInfoDialog::on_filesView_customContextMenuRequested(const QPoint &pos) diff --git a/src/problemsdialog.cpp b/src/problemsdialog.cpp index 63d58295..ea23beec 100644 --- a/src/problemsdialog.cpp +++ b/src/problemsdialog.cpp @@ -112,5 +112,5 @@ void ProblemsDialog::startFix() void ProblemsDialog::urlClicked(const QUrl &url) { - shell::OpenLink(url); + shell::Open(url); } diff --git a/src/selfupdater.cpp b/src/selfupdater.cpp index 8887927a..5a70568e 100644 --- a/src/selfupdater.cpp +++ b/src/selfupdater.cpp @@ -341,11 +341,14 @@ void SelfUpdater::downloadCancel() void SelfUpdater::installUpdate() { const QString parameters = "/DIR=\"" + qApp->applicationDirPath() + "\" "; + const auto r = shell::Execute(m_UpdateFile.fileName(), parameters); - if (shell::Execute(m_UpdateFile.fileName(), parameters)) { + if (r.success()) { QCoreApplication::quit(); } else { - reportError(tr("Failed to start %1").arg(m_UpdateFile.fileName())); + reportError(tr("Failed to start %1: %2") + .arg(m_UpdateFile.fileName()) + .arg(r.toString())); } m_UpdateFile.remove(); diff --git a/src/settings.cpp b/src/settings.cpp index 462cd92a..15bc801a 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1808,10 +1808,12 @@ void NexusSettings::registerAsNXMHandler(bool force) } parameters += " \"" + executable + "\""; - if (!shell::Execute(nxmPath, parameters)) { + const auto r = shell::Execute(nxmPath, parameters); + + if (!r.success()) { QMessageBox::critical( nullptr, QObject::tr("Failed"), - QObject::tr("Failed to start the helper application")); + QObject::tr("Failed to start the helper application: %1").arg(r.toString())); } } diff --git a/src/settingsdialognexus.cpp b/src/settingsdialognexus.cpp index 826075c0..2021bdc1 100644 --- a/src/settingsdialognexus.cpp +++ b/src/settingsdialognexus.cpp @@ -49,7 +49,7 @@ public: void openBrowser() { - shell::OpenLink(QUrl("https://www.nexusmods.com/users/myaccount?tab=api")); + shell::Open(QUrl("https://www.nexusmods.com/users/myaccount?tab=api")); } void paste() diff --git a/src/texteditor.cpp b/src/texteditor.cpp index 0c0eb1cc..4a8080f4 100644 --- a/src/texteditor.cpp +++ b/src/texteditor.cpp @@ -199,7 +199,7 @@ void TextEditor::explore() return; } - MOBase::shell::ExploreFile(m_filename); + shell::Explore(m_filename); } void TextEditor::onModified(bool b) -- cgit v1.3.1 From 8269ac1b489d30ca6065ec2b97542ddbb22298b3 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 5 Oct 2019 13:36:27 -0400 Subject: rework of the nexus key validator to allow multiple attempts before failing --- src/nxmaccessmanager.cpp | 495 ++++++++++++++++++++++++++++++-------------- src/nxmaccessmanager.h | 100 ++++++--- src/settingsdialognexus.cpp | 45 ++-- src/settingsdialognexus.h | 5 +- 4 files changed, 428 insertions(+), 217 deletions(-) (limited to 'src/settingsdialognexus.cpp') diff --git a/src/nxmaccessmanager.cpp b/src/nxmaccessmanager.cpp index 036b22ff..99b93048 100644 --- a/src/nxmaccessmanager.cpp +++ b/src/nxmaccessmanager.cpp @@ -44,7 +44,6 @@ using namespace MOBase; using namespace std::chrono_literals; const QString NexusBaseUrl("https://api.nexusmods.com/v1"); -const std::chrono::seconds NXMAccessManager::ValidationTimeout = 10s; const QString NexusSSO("wss://sso.nexusmods.com"); const QString NexusSSOPage("https://www.nexusmods.com/sso?id=%1&application=modorganizer2"); @@ -69,6 +68,7 @@ void ValidationProgressDialog::setParentWidget(QWidget* w) if (w && wasVisible) { setVisible(true); + raise(); } } @@ -80,9 +80,7 @@ void ValidationProgressDialog::start() m_updateTimer->setInterval(100ms); } - ui->progress->setRange(0, m_validator.timeout().count()); - ui->progress->setValue(0); - + updateProgress(); m_updateTimer->start(); show(); @@ -123,14 +121,35 @@ void ValidationProgressDialog::onCancel() void ValidationProgressDialog::onTimer() { - ui->progress->setValue(m_validator.elapsed().elapsed() / 1000); + updateProgress(); +} + +void ValidationProgressDialog::updateProgress() +{ + const auto* current = m_validator.currentAttempt(); + + if (current) { + ui->progress->setRange(0, current->timeout().count()); + ui->progress->setValue(current->elapsed().elapsed() / 1000); + } else { + // indeterminate + ui->progress->setRange(0, 0); + } + + if (const auto* a=m_validator.lastAttempt()) { + ui->label->setText(a->message() + ". " + tr("Trying again...")); + } else if (current) { + ui->label->setText(tr("Connecting to Nexus...")); + } else { + ui->label->setText("?"); + } } NexusSSOLogin::NexusSSOLogin() : m_keyReceived(false), m_active(false) { - m_timeout.setInterval(NXMAccessManager::ValidationTimeout); + m_timeout.setInterval(10s); m_timeout.setSingleShot(true); QObject::connect( @@ -167,20 +186,22 @@ QString NexusSSOLogin::stateToString(States s, const QString& e) return QObject::tr("Waiting for Nexus..."); case WaitingForBrowser: - return QObject::tr( - "Opened Nexus in browser.\n" - "Switch to your browser and accept the request."); + return + QObject::tr("Opened Nexus in browser.") + "\n" + + QObject::tr("Switch to your browser and accept the request."); case Finished: return QObject::tr("Finished."); case Timeout: - return QObject::tr( - "No answer from Nexus.\n" - "A firewall might be blocking Mod Organizer."); + return + QObject::tr("No answer from Nexus.") + "\n" + + QObject::tr("A firewall might be blocking Mod Organizer."); case ClosedByRemote: - return QObject::tr("Nexus closed the connection."); + return + QObject::tr("Nexus closed the connection.") + "\n" + + QObject::tr("A firewall might be blocking Mod Organizer."); case Cancelled: return QObject::tr("Cancelled."); @@ -300,10 +321,11 @@ void NexusSSOLogin::onMessage(const QString& s) void NexusSSOLogin::onDisconnected() { if (m_active) { - m_active = false; - if (!m_keyReceived) { + close(); setState(ClosedByRemote); + } else { + m_active = false; } } } @@ -332,84 +354,48 @@ void NexusSSOLogin::onTimeout() } -NexusKeyValidator::NexusKeyValidator(NXMAccessManager& am) - : m_manager(am), m_reply(nullptr), m_active(false) +ValidationAttempt::ValidationAttempt(std::chrono::seconds timeout) + : m_reply(nullptr), m_result(None) { - m_timeout.setInterval(NXMAccessManager::ValidationTimeout); m_timeout.setSingleShot(true); + m_timeout.setInterval(timeout); QObject::connect(&m_timeout, &QTimer::timeout, [&]{ onTimeout(); }); } -NexusKeyValidator::~NexusKeyValidator() -{ - abort(); -} - -QString NexusKeyValidator::stateToString(States s, const QString& e) -{ - switch (s) - { - case NexusKeyValidator::Connecting: - return QObject::tr("Connecting to Nexus..."); - - case NexusKeyValidator::Finished: - return QObject::tr("Finished."); - - case NexusKeyValidator::InvalidJson: - return QObject::tr("Invalid JSON"); - - case NexusKeyValidator::BadResponse: - return QObject::tr("Bad response"); - - case NexusKeyValidator::Timeout: - return QObject::tr("There was a timeout during the request"); - - case NexusKeyValidator::Cancelled: - return QObject::tr("Cancelled"); - - case NexusKeyValidator::Error: // fall-through - default: - { - if (e.isEmpty()) { - return QString("%1").arg(s); - } else { - return e; - } - } - } -} - -void NexusKeyValidator::start(const QString& key) +void ValidationAttempt::start(NXMAccessManager& m, const QString& key) { - if (m_reply) { - abort(); + if (!sendRequest(m, key)) { return; } - m_active = true; - setState(Connecting); m_elapsed.start(); + m_timeout.start(); + + log::debug( + "validator: attempt started with timeout of {} seconds", timeout().count()); +} +bool ValidationAttempt::sendRequest( + NXMAccessManager& m, const QString& key) +{ const QString requestUrl(NexusBaseUrl + "/users/validate"); QNetworkRequest request(requestUrl); request.setRawHeader("APIKEY", key.toUtf8()); - request.setHeader(QNetworkRequest::KnownHeaders::UserAgentHeader, m_manager.userAgent().toUtf8()); + request.setHeader(QNetworkRequest::KnownHeaders::UserAgentHeader, m.userAgent().toUtf8()); request.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, "application/json"); request.setRawHeader("Protocol-Version", "1.0.0"); request.setRawHeader("Application-Name", "MO2"); - request.setRawHeader("Application-Version", m_manager.MOVersion().toUtf8()); + request.setRawHeader("Application-Version", m.MOVersion().toUtf8()); + + m_reply = m.get(request); - m_reply = m_manager.get(request); if (!m_reply) { - close(); - setState(Error, QObject::tr("Failed to request %1").arg(requestUrl)); - return; + setFailure(SoftError, QObject::tr("Failed to request %1").arg(requestUrl)); + return false; } - m_timeout.start(NXMAccessManager::ValidationTimeout); - QObject::connect( m_reply, &QNetworkReply::finished, [&]{ onFinished(); }); @@ -417,93 +403,114 @@ void NexusKeyValidator::start(const QString& key) QObject::connect( m_reply, &QNetworkReply::sslErrors, [&](auto&& errors){ onSslErrors(errors); }); + + return true; } -void NexusKeyValidator::cancel() +void ValidationAttempt::cancel() { - if (m_active) { - abort(); - setState(Cancelled); + if (!m_reply || m_result != None) { + // not running + return; } + + setFailure(Cancelled, QObject::tr("Cancelled")); + + if (m_reply) { + m_reply->abort(); + } + + cleanup(); } -bool NexusKeyValidator::isActive() const +bool ValidationAttempt::done() const { - return m_active; + return (m_result != None); } -QElapsedTimer NexusKeyValidator::elapsed() const +ValidationAttempt::Result ValidationAttempt::result() const { - return m_elapsed; + return m_result; } -std::chrono::seconds NexusKeyValidator::timeout() const +const QString& ValidationAttempt::message() const { - return NXMAccessManager::ValidationTimeout; + return m_message; } -void NexusKeyValidator::close() +std::chrono::seconds ValidationAttempt::timeout() const { - m_active = false; - m_timeout.stop(); - - if (m_reply) { - m_reply->disconnect(); - m_reply->deleteLater(); - m_reply = nullptr; - } + return std::chrono::duration_cast( + m_timeout.intervalAsDuration()); } -void NexusKeyValidator::abort() +QElapsedTimer ValidationAttempt::elapsed() const { - m_active = false; - m_timeout.stop(); - - if (m_reply) { - m_reply->disconnect(); - m_reply->abort(); - m_reply->deleteLater(); - m_reply = nullptr; - } + return m_elapsed; } -void NexusKeyValidator::setState(States s, const QString& error) +void ValidationAttempt::onFinished() { - if (stateChanged) { - stateChanged(s, error); + if (m_result == Cancelled) { + return; } -} -void NexusKeyValidator::onFinished() -{ + log::debug("validator attempt: request has finished"); + if (!m_reply) { // shouldn't happen + log::error("validator attempt: reply is null"); + setFailure(HardError, QObject::tr("Internal error")); return; } - m_timeout.stop(); + const auto code = m_reply->attribute( + QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + if (code == 0) { + // request wasn't even sent + log::error("validator attempt: code is 0"); + setFailure(SoftError, m_reply->errorString()); + return; + } - const auto code = m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); const auto doc = QJsonDocument::fromJson(m_reply->readAll()); const auto headers = m_reply->rawHeaderPairs(); - const auto error = m_reply->errorString(); - - close(); + const auto httpError = m_reply->errorString(); const QJsonObject data = doc.object(); if (code != 200) { - handleError(code, data.value("message").toString(), error); + // http request failed + + QString s = m_reply->errorString(); + + const auto nexusMessage = data.value("message").toString(); + if (!nexusMessage.isEmpty()) { + if (!s.isEmpty()) { + s += ", "; + } + + s += nexusMessage; + } + + if (s.isEmpty()) { + s = QObject::tr("HTTP code %1").arg(code); + } else { + s += QString(" (%1)").arg(code); + } + + setFailure(SoftError, s); return; } if (doc.isNull()) { - setState(InvalidJson); + setFailure(HardError, QObject::tr("Invalid JSON")); return; } if (!data.contains("user_id")) { - setState(BadResponse); + setFailure(HardError, QObject::tr("Bad response")); return; } @@ -512,6 +519,11 @@ void NexusKeyValidator::onFinished() const QString name = data.value("name").toString(); const bool premium = data.value("is_premium").toBool(); + if (key.isEmpty()) { + setFailure(HardError, QObject::tr("API key is empty")); + return; + } + const auto user = APIUserAccount() .apiKey(key) .id(QString("%1").arg(id)) @@ -519,51 +531,218 @@ void NexusKeyValidator::onFinished() .type(premium ? APIUserAccountTypes::Premium : APIUserAccountTypes::Regular) .limits(NexusInterface::parseLimits(headers)); - if (finished) { - setState(Finished); - finished(user); + setSuccess(user); +} + +void ValidationAttempt::onSslErrors(const QList& errors) +{ + log::error("validator attempt: ssl errors"); + + for (auto& e : errors) { + log::error(" . {}", e.errorString()); } + + setFailure(HardError, QObject::tr("SSL error")); } -void NexusKeyValidator::onSslErrors(const QList& errors) +void ValidationAttempt::onTimeout() { - if (m_active) { - for (const auto& e : errors) { - setState(Error, e.errorString()); - } + setFailure(SoftError, QObject::tr("Timed out")); +} + +void ValidationAttempt::setFailure(Result r, const QString& error) +{ + if (r != Cancelled) { + // don't spam the log + log::error("validator attempt: {}", error); + } + + cleanup(); + + m_result = r; + m_message = error; + + if (failure) { + failure(); } } -void NexusKeyValidator::onTimeout() +void ValidationAttempt::setSuccess(const APIUserAccount& user) +{ + log::debug("validator attempt successful"); + cleanup(); + + m_result = Success; + m_message = ""; + + if (success) { + success(user); + } +} + +void ValidationAttempt::cleanup() +{ + m_timeout.stop(); + + if (m_reply) { + m_reply->disconnect(); + m_reply->deleteLater(); + m_reply = nullptr; + } +} + + +NexusKeyValidator::NexusKeyValidator(NXMAccessManager& am) + : m_manager(am) { - abort(); - setState(Timeout); } -void NexusKeyValidator::handleError( - int code, const QString& nexusMessage, const QString& httpError) +NexusKeyValidator::~NexusKeyValidator() { - QString s = httpError; + cancel(); +} - if (!nexusMessage.isEmpty()) { - if (!s.isEmpty()) { - s += ", "; +void NexusKeyValidator::start(const QString& key, Behaviour b) +{ + if (isActive()) { + log::debug("validator: trying to start while ongoing; ignoring"); + return; + } + + m_key = key; + + switch (b) + { + case OneShot: + { + createAttempts({10s}); + break; } - s += nexusMessage; + case Retry: + { + createAttempts({5s, 5s, 10s}); + break; + } } - if (code != 0) { - if (s.isEmpty()) { - s = QString("HTTP code %1").arg(code); + nextTry(); +} + +void NexusKeyValidator::createAttempts( + const std::vector& timeouts) +{ + m_attempts.clear(); + + for (auto&& t : timeouts) { + m_attempts.push_back(std::make_unique(t)); + } +} + +void NexusKeyValidator::cancel() +{ + log::debug("validator: cancelled"); + + for (auto&& a : m_attempts) { + a->cancel(); + } +} + +bool NexusKeyValidator::isActive() const +{ + for (auto&& a : m_attempts) { + if (!a->done()) { + return true; + } + } + + return false; +} + +const ValidationAttempt* NexusKeyValidator::lastAttempt() const +{ + const ValidationAttempt* last = nullptr; + + for (auto&& a : m_attempts) { + if (a->done()) { + last = a.get(); } else { - s += QString(" (%1)").arg(code); + break; } } - setState(Error, s); + return last; } +const ValidationAttempt* NexusKeyValidator::currentAttempt() const +{ + for (auto&& a : m_attempts) { + if (!a->done()) { + return a.get(); + } + } + + return nullptr; +} + +bool NexusKeyValidator::nextTry() +{ + for (auto&& a : m_attempts) { + if (!a->done()) { + a->success = [&](auto&& user){ onAttemptSuccess(*a, user); }; + a->failure = [&]{ onAttemptFailure(*a); }; + + a->start(m_manager, m_key); + return true; + } + } + + // no more + return false; +} + +void NexusKeyValidator::onAttemptSuccess( + const ValidationAttempt&, const APIUserAccount& u) +{ + setFinished(ValidationAttempt::Success, "", u); +} + +void NexusKeyValidator::onAttemptFailure(const ValidationAttempt& a) +{ + switch (a.result()) + { + case ValidationAttempt::SoftError: + { + if (!nextTry()) { + setFinished(a.result(), a.message(), {}); + } + + break; + } + + case ValidationAttempt::HardError: + { + cancel(); + setFinished(a.result(), a.message(), {}); + break; + } + + case ValidationAttempt::Cancelled: + { + setFinished(ValidationAttempt::Cancelled, QObject::tr("Cancelled"), {}); + break; + } + } +} + +void NexusKeyValidator::setFinished( + ValidationAttempt::Result r, const QString& message, + std::optional user) +{ + if (finished) { + finished(r, message, user); + } +} NXMAccessManager::NXMAccessManager(QObject *parent, const QString &moVersion) @@ -572,8 +751,9 @@ NXMAccessManager::NXMAccessManager(QObject *parent, const QString &moVersion) , m_validator(*this) , m_validationState(NotChecked) { - m_validator.stateChanged = [&](auto&& s, auto&& e){ onValidatorState(s, e); }; - m_validator.finished = [&](auto&& user){ onValidatorFinished(user); }; + m_validator.finished = [&](auto&& r, auto&& m, auto&& u) { + onValidatorFinished(r, m, u); + }; setCookieJar(new PersistentCookieJar(QDir::fromNativeSeparators( Settings::instance().paths().cache() + "/nexus_cookies.dat"))); @@ -587,7 +767,9 @@ NXMAccessManager::NXMAccessManager(QObject *parent, const QString &moVersion) void NXMAccessManager::setTopLevelWidget(QWidget* w) { if (w) { - m_ProgressDialog->setParentWidget(w); + if (m_ProgressDialog) { + m_ProgressDialog->setParentWidget(w); + } } else { m_ProgressDialog.reset(); m_validator.cancel(); @@ -637,37 +819,30 @@ void NXMAccessManager::clearCookies() void NXMAccessManager::startValidationCheck(const QString& key) { m_validationState = NotChecked; - m_validator.start(key); + m_validator.start(key, NexusKeyValidator::Retry); startProgress(); } -void NXMAccessManager::onValidatorState( - NexusKeyValidator::States s, const QString& e) +void NXMAccessManager::onValidatorFinished( + ValidationAttempt::Result r, const QString& message, + std::optional user) { - if (s == NexusKeyValidator::Connecting || s == NexusKeyValidator::Finished) { - // no-op, success is handled in onValidatorFinished() - return; - } - stopProgress(); - if (s == NexusKeyValidator::Cancelled) { - m_validationState = NotChecked; + if (user) { + m_validationState = Valid; + emit credentialsReceived(*user); + emit validateSuccessful(true); } else { - m_validationState = Invalid; - emit validateFailed(NexusKeyValidator::stateToString(s, e)); + if (r == ValidationAttempt::Cancelled) { + m_validationState = NotChecked; + } else { + m_validationState = Invalid; + emit validateFailed(message); + } } } -void NXMAccessManager::onValidatorFinished(const APIUserAccount& user) -{ - stopProgress(); - - m_validationState = Valid; - emit credentialsReceived(user); - emit validateSuccessful(true); -} - bool NXMAccessManager::validated() const { if (m_validator.isActive()) { diff --git a/src/nxmaccessmanager.h b/src/nxmaccessmanager.h index f0bdd8a6..b0ea45e7 100644 --- a/src/nxmaccessmanager.h +++ b/src/nxmaccessmanager.h @@ -82,53 +82,93 @@ private: }; -class NexusKeyValidator +class ValidationAttempt { public: - enum States + enum Result { - Connecting, - Finished, - InvalidJson, - BadResponse, - Timeout, - Cancelled, - Error + None, + Success, + SoftError, + HardError, + Cancelled }; - std::function finished; - std::function stateChanged; - - static QString stateToString(States s, const QString& e); + std::function success; + std::function failure; - NexusKeyValidator(NXMAccessManager& am); - ~NexusKeyValidator(); + ValidationAttempt(std::chrono::seconds timeout); + ValidationAttempt(const ValidationAttempt&) = delete; + ValidationAttempt& operator=(const ValidationAttempt&) = delete; - void start(const QString& key); + void start(NXMAccessManager& m, const QString& key); void cancel(); - bool isActive() const; - QElapsedTimer elapsed() const; + bool done() const; + Result result() const; + const QString& message() const; std::chrono::seconds timeout() const; + QElapsedTimer elapsed() const; private: - NXMAccessManager& m_manager; QNetworkReply* m_reply; + Result m_result; + QString m_message; QTimer m_timeout; - bool m_active; QElapsedTimer m_elapsed; - void setState(States s, const QString& error={}); - - void close(); - void abort(); + bool sendRequest(NXMAccessManager& m, const QString& key); void onFinished(); void onSslErrors(const QList& errors); void onTimeout(); - void handleError( - int code, const QString& nexusMessage, const QString& httpError); + void setFailure(Result r, const QString& error); + void setSuccess(const APIUserAccount& user); + + void cleanup(); +}; + + +class NexusKeyValidator +{ +public: + enum Behaviour + { + OneShot = 0, + Retry + }; + + using FinishedCallback = void ( + ValidationAttempt::Result, const QString&, + std::optional); + + std::function finished; + + NexusKeyValidator(NXMAccessManager& am); + ~NexusKeyValidator(); + + void start(const QString& key, Behaviour b); + void cancel(); + + bool isActive() const; + const ValidationAttempt* lastAttempt() const; + const ValidationAttempt* currentAttempt() const; + +private: + NXMAccessManager& m_manager; + QString m_key; + std::vector> m_attempts; + + void createAttempts(const std::vector& timeouts); + + bool nextTry(); + void onAttemptSuccess(const ValidationAttempt& a, const APIUserAccount& u); + void onAttemptFailure(const ValidationAttempt& a); + + void setFinished( + ValidationAttempt::Result r, const QString& message, + std::optional user); }; @@ -157,6 +197,7 @@ private: void onHide(); void onCancel(); void onTimer(); + void updateProgress(); }; @@ -167,8 +208,6 @@ class NXMAccessManager : public QNetworkAccessManager { Q_OBJECT public: - static const std::chrono::seconds ValidationTimeout; - NXMAccessManager(QObject *parent, const QString &moVersion); void setTopLevelWidget(QWidget* w); @@ -230,8 +269,9 @@ private: States m_validationState; void startValidationCheck(const QString& key); - void onValidatorState(NexusKeyValidator::States s, const QString& e); - void onValidatorFinished(const APIUserAccount& user); + void onValidatorFinished( + ValidationAttempt::Result r, const QString& message, + std::optional); void startProgress(); void stopProgress(); diff --git a/src/settingsdialognexus.cpp b/src/settingsdialognexus.cpp index 2021bdc1..209ed661 100644 --- a/src/settingsdialognexus.cpp +++ b/src/settingsdialognexus.cpp @@ -240,17 +240,13 @@ void NexusSettingsTab::validateKey(const QString& key) m_nexusValidator.reset(new NexusKeyValidator( *NexusInterface::instance(dialog().pluginContainer())->getAccessManager())); - m_nexusValidator->stateChanged = [&](auto&& s, auto&& e){ - onValidatorStateChanged(s, e); - }; - - m_nexusValidator->finished = [&](auto&& user) { - onValidatorFinished(user); + m_nexusValidator->finished = [&](auto&& r, auto&& m, auto&& u) { + onValidatorFinished(r, m, u); }; } addNexusLog(QObject::tr("Checking API key...")); - m_nexusValidator->start(key); + m_nexusValidator->start(key, NexusKeyValidator::OneShot); } void NexusSettingsTab::onSSOKeyChanged(const QString& key) @@ -277,32 +273,31 @@ void NexusSettingsTab::onSSOStateChanged(NexusSSOLogin::States s, const QString& updateNexusState(); } -void NexusSettingsTab::onValidatorStateChanged( - NexusKeyValidator::States s, const QString& e) +void NexusSettingsTab::onValidatorFinished( + ValidationAttempt::Result r, const QString& message, + std::optional user) { - if (s != NexusKeyValidator::Finished) { - // finished state is handled in onValidatorFinished() - const auto log = NexusKeyValidator::stateToString(s, e); + if (user) { + NexusInterface::instance(dialog().pluginContainer())->setUserAccount(*user); + addNexusLog(QObject::tr("Received user acount information")); - for (auto&& line : log.split("\n")) { - addNexusLog(line); + if (setKey(user->apiKey())) { + addNexusLog(QObject::tr("Linked with Nexus successfully.")); + } else { + addNexusLog(QObject::tr("Failed to set API key")); + } + } else { + if (message.isEmpty()) { + // shouldn't happen + addNexusLog("Unknown error"); + } else { + addNexusLog(message); } } updateNexusState(); } -void NexusSettingsTab::onValidatorFinished(const APIUserAccount& user) -{ - NexusInterface::instance(dialog().pluginContainer())->setUserAccount(user); - - if (!user.apiKey().isEmpty()) { - if (setKey(user.apiKey())) { - addNexusLog(QObject::tr("Linked with Nexus successfully.")); - } - } -} - void NexusSettingsTab::addNexusLog(const QString& s) { ui->nexusLog->addItem(s); diff --git a/src/settingsdialognexus.h b/src/settingsdialognexus.h index 89a6618f..2cb1cc1e 100644 --- a/src/settingsdialognexus.h +++ b/src/settingsdialognexus.h @@ -32,8 +32,9 @@ private: void onSSOKeyChanged(const QString& key); void onSSOStateChanged(NexusSSOLogin::States s, const QString& e); - void onValidatorStateChanged(NexusKeyValidator::States s, const QString& e); - void onValidatorFinished(const APIUserAccount& user); + void onValidatorFinished( + ValidationAttempt::Result r, const QString& message, + std::optional useR); void addNexusLog(const QString& s); }; -- cgit v1.3.1 From 925dbcba875477e1111453878f9b69e069bc8fd2 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 5 Oct 2019 13:52:04 -0400 Subject: at least put one message in the list when opening the dialog to avoid having an empty widget --- src/settingsdialognexus.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/settingsdialognexus.cpp') diff --git a/src/settingsdialognexus.cpp b/src/settingsdialognexus.cpp index 209ed661..2dd8b998 100644 --- a/src/settingsdialognexus.cpp +++ b/src/settingsdialognexus.cpp @@ -112,6 +112,12 @@ NexusSettingsTab::NexusSettingsTab(Settings& s, SettingsDialog& d) QObject::connect(ui->clearCacheButton, &QPushButton::clicked, [&]{ on_clearCacheButton_clicked(); }); QObject::connect(ui->associateButton, &QPushButton::clicked, [&]{ on_associateButton_clicked(); }); + if (settings().nexus().hasApiKey()) { + addNexusLog(QObject::tr("Connected.")); + } else { + addNexusLog(QObject::tr("Not connected.")); + } + updateNexusState(); } -- cgit v1.3.1 From 3d97b0efd04326c0252411fc6639c4c6df2f2160 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 7 Oct 2019 04:27:53 -0400 Subject: added ExitModOrganizer(), used instead of qApp->exit() reset geometry uses TaskDialog moved restart code for the settings dialog to MainWindow fixed settings sometimes not being saved when restarting don't log "crash dumps present" every time the settings dialog is closed --- src/main.cpp | 12 +++- src/mainwindow.cpp | 113 +++++++++++++++++++++++++------------- src/mainwindow.h | 11 ++-- src/settingsdialog.cpp | 21 +++---- src/settingsdialog.h | 9 ++- src/settingsdialognexus.cpp | 4 +- src/settingsdialogworkarounds.cpp | 23 ++++---- src/shared/util.cpp | 47 ++++++++++++++++ src/shared/util.h | 18 ++++++ src/spawn.cpp | 12 ++-- 10 files changed, 189 insertions(+), 81 deletions(-) (limited to 'src/settingsdialognexus.cpp') diff --git a/src/main.cpp b/src/main.cpp index a6918d99..f08ba066 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -563,10 +563,12 @@ int runApplication(MOApplication &application, SingleInstance &instance, if (game == nullptr) { InstanceManager &instance = InstanceManager::instance(); QString instanceName = instance.currentInstance(); + if (instanceName.compare("Portable", Qt::CaseInsensitive) != 0) { instance.clearCurrentInstance(); - return INT_MAX; + return RestartExitCode; } + return 1; } @@ -710,6 +712,8 @@ int runApplication(MOApplication &application, SingleInstance &instance, splash.finish(&mainWindow); res = application.exec(); + mainWindow.onBeforeClose(); + mainWindow.close(); NexusInterface::instance(&pluginContainer) ->getAccessManager()->setTopLevelWidget(nullptr); @@ -867,6 +871,7 @@ int main(int argc, char *argv[]) do { LogModel::instance().clear(); + ResetExitFlag(); // make sure the log file isn't locked in case MO was restarted and // the previous instance gets deleted @@ -904,10 +909,11 @@ int main(int argc, char *argv[]) splash = ":/MO/gui/splash"; } - int result = runApplication(application, instance, splash); - if (result != INT_MAX) { + const int result = runApplication(application, instance, splash); + if (result != RestartExitCode) { return result; } + argc = 1; moshortcut = MOShortcut(""); } while (true); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 295c60a6..23733cac 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1217,7 +1217,7 @@ void MainWindow::showEvent(QShowEvent *event) QMainWindow::showEvent(event); if (!m_WasVisible) { - readSettings(m_OrganizerCore.settings()); + readSettings(); refreshFilters(); // this needs to be connected here instead of in the constructor because the @@ -1267,26 +1267,44 @@ void MainWindow::showEvent(QShowEvent *event) } } +void MainWindow::onBeforeClose() +{ + storeSettings(); +} void MainWindow::closeEvent(QCloseEvent* event) { - if (!confirmExit()) { + // this happens for two reasons: + // 1) the user requested to close the window, such as clicking the X + // 2) close() is called in runApplication() after application.exec() + // returns, which happens when qApp->exit() is called + // + // the window must never actually close for 1), because settings haven't been + // saved yet: the state of many widgets is saved to the ini, which relies on + // the window still being onscreen (or else everything is considered hidden) + // + // for 2), the settings have been saved and the window can just close + + if (ModOrganizerExiting()) { + // the user has confirmed if necessary and all settings have been saved, + // just close it + QMainWindow::closeEvent(event); + } else { + // never close the window because settings might need to be changed event->ignore(); - return; - } - storeSettings(m_OrganizerCore.settings()); + // start the process of exiting, which may require confirmation by calling + // canExit(), among other things + ExitModOrganizer(); + } } -bool MainWindow::confirmExit() +bool MainWindow::canExit() { - m_closing = true; - if (m_OrganizerCore.downloadManager()->downloadsInProgressNoPause()) { if (QMessageBox::question(this, tr("Downloads in progress"), tr("There are still downloads in progress, do you really want to quit?"), QMessageBox::Yes | QMessageBox::Cancel) == QMessageBox::Cancel) { - m_closing = false; return false; } else { m_OrganizerCore.downloadManager()->pauseAll(); @@ -1298,8 +1316,9 @@ bool MainWindow::confirmExit() HANDLE injected_process_still_running = m_OrganizerCore.findAndOpenAUSVFSProcess(hiddenList, GetCurrentProcessId()); if (injected_process_still_running != INVALID_HANDLE_VALUE) { + m_exitAfterWait = true; m_OrganizerCore.waitForApplication(injected_process_still_running); - if (!m_closing) { // if operation cancelled + if (!m_exitAfterWait) { // if operation cancelled return false; } } @@ -2138,20 +2157,22 @@ void MainWindow::activateProxy(bool activate) busyDialog.hide(); } -void MainWindow::readSettings(const Settings& settings) +void MainWindow::readSettings() { - settings.geometry().restoreGeometry(this); - settings.geometry().restoreState(this); - settings.geometry().restoreDocks(this); - settings.geometry().restoreToolbars(this); - settings.geometry().restoreState(ui->splitter); - settings.geometry().restoreState(ui->categoriesSplitter); - settings.geometry().restoreVisibility(ui->menuBar); - settings.geometry().restoreVisibility(ui->statusBar); + const auto& s = m_OrganizerCore.settings(); + + s.geometry().restoreGeometry(this); + s.geometry().restoreState(this); + s.geometry().restoreDocks(this); + s.geometry().restoreToolbars(this); + s.geometry().restoreState(ui->splitter); + s.geometry().restoreState(ui->categoriesSplitter); + s.geometry().restoreVisibility(ui->menuBar); + s.geometry().restoreVisibility(ui->statusBar); { // special case in case someone puts 0 in the INI - auto v = settings.widgets().index(ui->executablesListBox); + auto v = s.widgets().index(ui->executablesListBox); if (!v || v == 0) { v = 1; } @@ -2159,16 +2180,16 @@ void MainWindow::readSettings(const Settings& settings) ui->executablesListBox->setCurrentIndex(*v); } - settings.widgets().restoreIndex(ui->groupCombo); + s.widgets().restoreIndex(ui->groupCombo); { - settings.geometry().restoreVisibility(ui->categoriesGroup, false); + s.geometry().restoreVisibility(ui->categoriesGroup, false); const auto v = ui->categoriesGroup->isVisible(); setCategoryListVisible(v); ui->displayCategoriesBtn->setChecked(v); } - if (settings.network().useProxy()) { + if (s.network().useProxy()) { activateProxy(true); } } @@ -2215,8 +2236,10 @@ void MainWindow::processUpdates(Settings& settings) { } } -void MainWindow::storeSettings(Settings& s) +void MainWindow::storeSettings() { + auto& s = m_OrganizerCore.settings(); + s.geometry().saveState(this); s.geometry().saveGeometry(this); s.geometry().saveDocks(this); @@ -2244,7 +2267,7 @@ ILockedWaitingForProcess* MainWindow::lock() ++m_LockCount; return m_LockDialog; } - if (m_closing) + if (m_exitAfterWait) m_LockDialog = new WaitingOnCloseDialog(this); else m_LockDialog = new LockedDialog(this, true); @@ -2265,8 +2288,8 @@ void MainWindow::unlock() } --m_LockCount; if (m_LockCount == 0) { - if (m_closing && m_LockDialog->canceled()) - m_closing = false; + if (m_exitAfterWait && m_LockDialog->canceled()) + m_exitAfterWait = false; m_LockDialog->hide(); m_LockDialog->deleteLater(); m_LockDialog = nullptr; @@ -5018,18 +5041,31 @@ void MainWindow::on_actionSettings_triggered() bool proxy = settings.network().useProxy(); DownloadManager *dlManager = m_OrganizerCore.downloadManager(); const bool oldCheckForUpdates = settings.checkForUpdates(); + const int oldMaxDumps = settings.diagnostics().crashDumpsMax(); SettingsDialog dialog(&m_PluginContainer, settings, this); dialog.exec(); + auto e = dialog.exitNeeded(); + if (oldManagedGameDirectory != settings.game().directory()) { - QMessageBox::about(this, tr("Restarting MO"), - tr("Changing the managed game directory requires restarting MO.\n" - "Any pending downloads will be paused.\n\n" - "Click OK to restart MO now.")); - dlManager->pauseAll(); - qApp->exit(INT_MAX); + e |= Exit::Restart; + } + + if (e.testFlag(Exit::Restart)) { + const auto r = MOBase::TaskDialog(this) + .title(tr("Restart Mod Organizer")) + .main("Restart Mod Organizer") + .content(tr("Mod Organizer must restart to finish configuration changes")) + .icon(QMessageBox::Question) + .button({tr("Restart"), QMessageBox::Yes}) + .button({tr("Continue"), tr("Some things might be weird."), QMessageBox::No}) + .exec(); + + if (r == QMessageBox::Yes) { + ExitModOrganizer(e); + } } InstallationManager *instManager = m_OrganizerCore.installationManager(); @@ -5092,7 +5128,10 @@ void MainWindow::on_actionSettings_triggered() updateDownloadView(); m_OrganizerCore.setLogLevel(settings.diagnostics().logLevel()); - m_OrganizerCore.cycleDiagnostics(); + + if (settings.diagnostics().crashDumpsMax() != oldMaxDumps) { + m_OrganizerCore.cycleDiagnostics(); + } toggleMO2EndorseState(); @@ -5475,9 +5514,7 @@ void MainWindow::on_actionUpdate_triggered() void MainWindow::on_actionExit_triggered() { - if (confirmExit()) { - qApp->exit(); - } + ExitModOrganizer(); } void MainWindow::actionEndorseMO() @@ -6121,7 +6158,7 @@ void MainWindow::on_actionChange_Game_triggered() if (r == QMessageBox::Yes) { InstanceManager::instance().clearCurrentInstance(); - qApp->exit(INT_MAX); + ExitModOrganizer(Exit::Restart); } } diff --git a/src/mainwindow.h b/src/mainwindow.h index 524e2b6e..b04096be 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -111,7 +111,6 @@ class MainWindow : public QMainWindow, public IUserInterface friend class OrganizerProxy; public: - explicit MainWindow(Settings &settings, OrganizerCore &organizerCore, PluginContainer &pluginContainer, QWidget *parent = 0); @@ -154,7 +153,8 @@ public: void displayModInformation( ModInfo::Ptr modInfo, unsigned int modIndex, ModInfoTabIDs tabID) override; - bool confirmExit(); + bool canExit(); + void onBeforeClose(); virtual bool closeWindow(); virtual void setWindowEnabled(bool enabled); @@ -383,9 +383,8 @@ private: LockedDialogBase *m_LockDialog { nullptr }; uint64_t m_LockCount { 0 }; - bool m_closing{ false }; - bool m_showArchiveData{ true }; + bool m_exitAfterWait{ false }; MOBase::DelayedFileWriter m_ArchiveListWriter; @@ -672,8 +671,8 @@ private slots: // ui slots void on_categoriesOrBtn_toggled(bool checked); void on_managedArchiveLabel_linkHovered(const QString &link); - void storeSettings(Settings& settings); - void readSettings(const Settings& settings); + void storeSettings(); + void readSettings(); void setupModList(); }; diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index 8fb25b1c..daccfba9 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -33,8 +33,8 @@ SettingsDialog::SettingsDialog(PluginContainer *pluginContainer, Settings& setti : TutorableDialog("SettingsDialog", parent) , ui(new Ui::SettingsDialog) , m_settings(settings) + , m_exit(Exit::None) , m_pluginContainer(pluginContainer) - , m_restartNeeded(false) { ui->setupUi(this); @@ -61,9 +61,14 @@ QWidget* SettingsDialog::parentWidgetForDialogs() } } -void SettingsDialog::setRestartNeeded() +void SettingsDialog::setExitNeeded(ExitFlags e) { - m_restartNeeded = true; + m_exit = e; +} + +ExitFlags SettingsDialog::exitNeeded() const +{ + return m_exit; } int SettingsDialog::exec() @@ -87,16 +92,6 @@ int SettingsDialog::exec() } } - if (m_restartNeeded) { - if (QMessageBox::question(parentWidgetForDialogs(), - tr("Restart Mod Organizer?"), - tr("In order to finish configuration changes, MO must be restarted.\n" - "Restart it now?"), - QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { - qApp->exit(INT_MAX); - } - } - return ret; } diff --git a/src/settingsdialog.h b/src/settingsdialog.h index e89da665..bae4a469 100644 --- a/src/settingsdialog.h +++ b/src/settingsdialog.h @@ -21,6 +21,7 @@ along with Mod Organizer. If not, see . #define SETTINGSDIALOG_H #include "tutorabledialog.h" +#include "util.h" class PluginContainer; class Settings; @@ -74,7 +75,9 @@ public: PluginContainer* pluginContainer(); QWidget* parentWidgetForDialogs(); - void setRestartNeeded(); + + void setExitNeeded(ExitFlags e); + ExitFlags exitNeeded() const; int exec() override; @@ -82,10 +85,10 @@ public slots: virtual void accept(); private: + Ui::SettingsDialog* ui; Settings& m_settings; std::vector> m_tabs; - Ui::SettingsDialog* ui; - bool m_restartNeeded; + ExitFlags m_exit; PluginContainer* m_pluginContainer; }; diff --git a/src/settingsdialognexus.cpp b/src/settingsdialognexus.cpp index 2dd8b998..d49e0a33 100644 --- a/src/settingsdialognexus.cpp +++ b/src/settingsdialognexus.cpp @@ -312,7 +312,7 @@ void NexusSettingsTab::addNexusLog(const QString& s) bool NexusSettingsTab::setKey(const QString& key) { - dialog().setRestartNeeded(); + dialog().setExitNeeded(Exit::Restart); const bool ret = settings().nexus().setApiKey(key); updateNexusState(); return ret; @@ -320,7 +320,7 @@ bool NexusSettingsTab::setKey(const QString& key) bool NexusSettingsTab::clearKey() { - dialog().setRestartNeeded(); + dialog().setExitNeeded(Exit::Restart); const auto ret = settings().nexus().clearApiKey(); NexusInterface::instance(dialog().pluginContainer())->getAccessManager()->clearApiKey(); diff --git a/src/settingsdialogworkarounds.cpp b/src/settingsdialogworkarounds.cpp index 0e31fc4b..1c5fbe26 100644 --- a/src/settingsdialogworkarounds.cpp +++ b/src/settingsdialogworkarounds.cpp @@ -2,6 +2,7 @@ #include "ui_settingsdialog.h" #include "spawn.h" #include "settings.h" +#include #include WorkaroundsSettingsTab::WorkaroundsSettingsTab(Settings& s, SettingsDialog& d) @@ -118,16 +119,18 @@ void WorkaroundsSettingsTab::on_bsaDateBtn_clicked() void WorkaroundsSettingsTab::on_resetGeometryBtn_clicked() { - const auto caption = QObject::tr("Restart Mod Organizer?"); - const auto text = QObject::tr( - "In order to reset the geometry, Mod Organizer must be restarted.\n" - "Restart now?"); - - const auto res = QMessageBox::question( - parentWidget(), caption, text, QMessageBox::Yes | QMessageBox::Cancel); - - if (res == QMessageBox::Yes) { + const auto r = MOBase::TaskDialog(parentWidget()) + .title(QObject::tr("Restart Mod Organizer")) + .main(QObject::tr("Restart Mod Organizer")) + .content(QObject::tr("Geometries will be reset to their default values.")) + .icon(QMessageBox::Question) + .button({QObject::tr("Restart Mod Organizer"), QMessageBox::Ok}) + .button({QObject::tr("Cancel"), QMessageBox::Cancel}) + .exec(); + + if (r == QMessageBox::Ok) { settings().geometry().requestReset(); - qApp->exit(INT_MAX); + ExitModOrganizer(Exit::Restart); + dialog().close(); } } diff --git a/src/shared/util.cpp b/src/shared/util.cpp index 07983e12..58f4eb2b 100644 --- a/src/shared/util.cpp +++ b/src/shared/util.cpp @@ -19,6 +19,7 @@ along with Mod Organizer. If not, see . #include "util.h" #include "windows_error.h" +#include "mainwindow.h" namespace MOShared { @@ -244,3 +245,49 @@ MOBase::VersionInfo createVersionInfo() } } // namespace MOShared + + +static bool g_exiting = false; + +MainWindow* findMainWindow() +{ + for (auto* tl : qApp->topLevelWidgets()) { + if (auto* mw=dynamic_cast(tl)) { + return mw; + } + } + + return nullptr; +} + +bool ExitModOrganizer(ExitFlags e) +{ + if (g_exiting) { + return true; + } + + if (!e.testFlag(Exit::Force)) { + if (auto* mw=findMainWindow()) { + if (!mw->canExit()) { + return false; + } + } + } + + g_exiting = true; + + const int code = (e.testFlag(Exit::Restart) ? RestartExitCode : 0); + qApp->exit(code); + + return true; +} + +bool ModOrganizerExiting() +{ + return g_exiting; +} + +void ResetExitFlag() +{ + g_exiting = false; +} diff --git a/src/shared/util.h b/src/shared/util.h index 7bae96f2..aea6f200 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -48,4 +48,22 @@ MOBase::VersionInfo createVersionInfo(); } // namespace MOShared + +enum class Exit +{ + None = 0x00, + Normal = 0x01, + Restart = 0x02, + Force = 0x04 +}; + +const int RestartExitCode = INT_MAX; + +using ExitFlags = QFlags; +Q_DECLARE_OPERATORS_FOR_FLAGS(ExitFlags); + +bool ExitModOrganizer(ExitFlags e=Exit::Normal); +bool ModOrganizerExiting(); +void ResetExitFlag(); + #endif // UTIL_H diff --git a/src/spawn.cpp b/src/spawn.cpp index 079677f4..a34230b2 100644 --- a/src/spawn.cpp +++ b/src/spawn.cpp @@ -186,12 +186,12 @@ QMessageBox::StandardButton badSteamReg( .details(details) .icon(QMessageBox::Critical) .button({ - QObject::tr("Continue without starting Steam"), - QObject::tr("The program may fail to launch."), - QMessageBox::Yes}) + QObject::tr("Continue without starting Steam"), + QObject::tr("The program may fail to launch."), + QMessageBox::Yes}) .button({ - QObject::tr("Cancel"), - QMessageBox::Cancel}) + QObject::tr("Cancel"), + QMessageBox::Cancel}) .exec(); } @@ -517,7 +517,7 @@ bool restartAsAdmin(QWidget* parent) } log::debug("exiting MO"); - qApp->exit(0); + ExitModOrganizer(Exit::Force); return true; } -- cgit v1.3.1