summaryrefslogtreecommitdiff
path: root/src/settings.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/settings.cpp')
-rw-r--r--src/settings.cpp1761
1 files changed, 957 insertions, 804 deletions
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<T> getOptional(
template <class T>
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<T>(settings, section, key)) {
return *v;
@@ -453,115 +453,7 @@ void warnIfNotCheckable(const QAbstractButton* b)
}
-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)
-{
- 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()
-{
- MOBase::QuestionBoxMemory::setCallbacks({}, {}, {});
- s_Instance = nullptr;
-}
-
-Settings &Settings::instance()
-{
- if (s_Instance == nullptr) {
- throw std::runtime_error("no instance of \"Settings\"");
- }
- return *s_Instance;
-}
-
-void Settings::processUpdates(
- const QVersionNumber& currentVersion, const QVersionNumber& lastVersion)
-{
- if (getFirstStart()) {
- return;
- }
-
- if (lastVersion < QVersionNumber(2, 2, 0)) {
- 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");
-
- removeSection(m_Settings, "Servers");
- }
-
- if (lastVersion < QVersionNumber(2, 2, 1)) {
- 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
- remove(m_Settings, "General", "log_split");
- }
-
- //save version in all case
- set(m_Settings, "General", "version", currentVersion.toString());
-}
-
-QString Settings::getFilename() const
-{
- return m_Settings.fileName();
-}
-
-void Settings::registerAsNXMHandler(bool force)
-{
- 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"));
- }
-}
-
-bool Settings::colorSeparatorScrollbar() const
-{
- return get<bool>(m_Settings, "Settings", "colorSeparatorScrollbars", true);
-}
-
-void Settings::setColorSeparatorScrollbar(bool b)
-{
- set(m_Settings, "Settings", "colorSeparatorScrollbars", b);
-}
-
-void Settings::managedGameChanged(IPluginGame const *gamePlugin)
-{
- m_GamePlugin = gamePlugin;
-}
-
-bool Settings::obfuscate(const QString key, const QString data)
+bool setWindowsCredential(const QString key, const QString data)
{
QString finalKey("ModOrganizer2_" + key);
wchar_t* keyData = new wchar_t[finalKey.size()+1];
@@ -592,7 +484,7 @@ bool Settings::obfuscate(const QString key, const QString data)
return result;
}
-QString Settings::deObfuscate(const QString key)
+QString getWindowsCredential(const QString key)
{
QString result;
QString finalKey("ModOrganizer2_" + key);
@@ -614,248 +506,91 @@ QString Settings::deObfuscate(const QString key)
return result;
}
-QColor Settings::getIdealTextColor(const QColor& rBackgroundColor)
-{
- 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);
-}
-
-
-bool Settings::hideUncheckedPlugins() const
-{
- return get<bool>(m_Settings, "Settings", "hide_unchecked_plugins", false);
-}
-
-void Settings::setHideUncheckedPlugins(bool b)
-{
- set(m_Settings, "Settings", "hide_unchecked_plugins", b);
-}
-
-bool Settings::forceEnableCoreFiles() const
-{
- return get<bool>(m_Settings, "Settings", "force_enable_core_files", true);
-}
-
-void Settings::setForceEnableCoreFiles(bool b)
-{
- set(m_Settings, "Settings", "force_enable_core_files", b);
-}
-
-bool Settings::lockGUI() const
-{
- return get<bool>(m_Settings, "Settings", "lock_gui", true);
-}
-
-void Settings::setLockGUI(bool b)
-{
- set(m_Settings, "Settings", "lock_gui", b);
-}
-
-bool Settings::automaticLoginEnabled() const
-{
- return get<bool>(m_Settings, "Settings", "nexus_login", false);
-}
-QString Settings::getSteamAppID() const
-{
- return get<QString>(m_Settings, "Settings", "app_id", m_GamePlugin->steamAPPId());
-}
+Settings *Settings::s_Instance = nullptr;
-void Settings::setSteamAppID(const QString& id)
+Settings::Settings(const QString& path) :
+ m_Settings(path, QSettings::IniFormat),
+ 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 (id.isEmpty()) {
- remove(m_Settings, "Settings", "app_id");
+ if (s_Instance != nullptr) {
+ throw std::runtime_error("second instance of \"Settings\" created");
} else {
- set(m_Settings, "Settings", "app_id", id);
+ s_Instance = this;
}
}
-bool Settings::usePrereleases() const
-{
- return get<bool>(m_Settings, "Settings", "use_prereleases", false);
-}
-
-void Settings::setUsePrereleases(bool b)
+Settings::~Settings()
{
- set(m_Settings, "Settings", "use_prereleases", b);
+ MOBase::QuestionBoxMemory::setCallbacks({}, {}, {});
+ s_Instance = nullptr;
}
-QString Settings::getConfigurablePath(const QString &key,
- const QString &def,
- bool resolve) const
+Settings &Settings::instance()
{
- QString result = QDir::fromNativeSeparators(
- get<QString>(m_Settings, "Settings", key, QString("%BASE_DIR%/") + def));
-
- if (resolve) {
- result.replace("%BASE_DIR%", getBaseDirectory());
+ if (s_Instance == nullptr) {
+ throw std::runtime_error("no instance of \"Settings\"");
}
-
- return result;
+ return *s_Instance;
}
-void Settings::setConfigurablePath(const QString &key, const QString& path)
+void Settings::processUpdates(
+ const QVersionNumber& currentVersion, const QVersionNumber& lastVersion)
{
- if (path.isEmpty()) {
- remove(m_Settings, "Settings", key);
- } else {
- set(m_Settings, "Settings", key, path);
+ if (firstStart()) {
+ return;
}
-}
-QString Settings::getBaseDirectory() const
-{
- return QDir::fromNativeSeparators(get<QString>(m_Settings,
- "Settings", "base_directory", qApp->property("dataPath").toString()));
-}
-
-QString Settings::getDownloadDirectory(bool resolve) const
-{
- return getConfigurablePath(
- "download_directory",
- ToQString(AppConfig::downloadPath()),
- resolve);
-}
-
-QString Settings::getCacheDirectory(bool resolve) const
-{
- return getConfigurablePath(
- "cache_directory",
- ToQString(AppConfig::cachePath()),
- resolve);
-}
-
-QString Settings::getModDirectory(bool resolve) const
-{
- return getConfigurablePath(
- "mod_directory",
- ToQString(AppConfig::modsPath()),
- resolve);
-}
-
-QString Settings::getProfileDirectory(bool resolve) const
-{
- return getConfigurablePath(
- "profiles_directory",
- ToQString(AppConfig::profilesPath()),
- resolve);
-}
-
-QString Settings::getOverwriteDirectory(bool resolve) const
-{
- return getConfigurablePath(
- "overwrite_directory",
- ToQString(AppConfig::overwritePath()),
- resolve);
-}
+ if (lastVersion < QVersionNumber(2, 2, 0)) {
+ 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");
-void Settings::setBaseDirectory(const QString& path)
-{
- if (path.isEmpty()) {
- remove(m_Settings, "Settings", "base_directory");
- } else {
- set(m_Settings, "Settings", "base_directory", path);
+ removeSection(m_Settings, "Servers");
}
-}
-
-void Settings::setDownloadDirectory(const QString& path)
-{
- setConfigurablePath("download_directory", path);
-}
-void Settings::setModDirectory(const QString& path)
-{
- setConfigurablePath("mod_directory", path);
-}
-
-void Settings::setCacheDirectory(const QString& path)
-{
- setConfigurablePath("cache_directory", path);
-}
-
-void Settings::setProfileDirectory(const QString& path)
-{
- setConfigurablePath("profiles_directory", path);
-}
-
-void Settings::setOverwriteDirectory(const QString& path)
-{
- setConfigurablePath("overwrite_directory", path);
-}
-
-std::optional<QString> Settings::getManagedGameDirectory() const
-{
- if (auto v=getOptional<QByteArray>(m_Settings, "General", "gamePath")) {
- return QString::fromUtf8(*v);
+ if (lastVersion < QVersionNumber(2, 2, 1)) {
+ 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");
}
- return {};
-}
-
-void Settings::setManagedGameDirectory(const QString& path)
-{
- set(m_Settings, "General", "gamePath", QDir::toNativeSeparators(path).toUtf8());
-}
-
-std::optional<QString> Settings::getManagedGameName() const
-{
- return getOptional<QString>(m_Settings, "General", "gameName");
-}
-
-void Settings::setManagedGameName(const QString& name)
-{
- set(m_Settings, "General", "gameName", name);
-}
-
-std::optional<QString> Settings::getManagedGameEdition() const
-{
- return getOptional<QString>(m_Settings, "General", "game_edition");
-}
-
-void Settings::setManagedGameEdition(const QString& name)
-{
- set(m_Settings, "General", "game_edition", name);
-}
-
-std::optional<QString> Settings::getSelectedProfileName() const
-{
- if (auto v=getOptional<QByteArray>(m_Settings, "General", "selected_profile")) {
- return QString::fromUtf8(*v);
+ if (lastVersion < QVersionNumber(2, 2, 2)) {
+ // log splitter is gone, it's a dock now
+ remove(m_Settings, "General", "log_split");
}
- return {};
-}
-
-void Settings::setSelectedProfileName(const QString& name)
-{
- set(m_Settings, "General", "selected_profile", name.toUtf8());
-}
-
-std::optional<QString> Settings::getStyleName() const
-{
- return getOptional<QString>(m_Settings, "Settings", "style");
+ //save version in all case
+ set(m_Settings, "General", "version", currentVersion.toString());
}
-void Settings::setStyleName(const QString& name)
+QString Settings::filename() const
{
- set(m_Settings, "Settings", "style", name);
+ return m_Settings.fileName();
}
-bool Settings::getUseProxy() const
+bool Settings::usePrereleases() const
{
- return get<bool>(m_Settings, "Settings", "use_proxy", false);
+ return get<bool>(m_Settings, "Settings", "use_prereleases", false);
}
-void Settings::setUseProxy(bool b)
+void Settings::setUsePrereleases(bool b)
{
- set(m_Settings, "Settings", "use_proxy", b);
+ set(m_Settings, "Settings", "use_prereleases", b);
}
-std::optional<QVersionNumber> Settings::getVersion() const
+std::optional<QVersionNumber> Settings::version() const
{
if (auto v=getOptional<QString>(m_Settings, "General", "version")) {
return QVersionNumber::fromString(*v).normalized();
@@ -864,7 +599,7 @@ std::optional<QVersionNumber> Settings::getVersion() const
return {};
}
-bool Settings::getFirstStart() const
+bool Settings::firstStart() const
{
return get<bool>(m_Settings, "General", "first_start", true);
}
@@ -874,679 +609,362 @@ void Settings::setFirstStart(bool b)
set(m_Settings, "General", "first_start", b);
}
-std::optional<QColor> Settings::getPreviousSeparatorColor() const
+QString Settings::executablesBlacklist() const
{
- const auto c = getOptional<QColor>(m_Settings, "General", "previousSeparatorColor");
- if (c && c->isValid()) {
- return c;
- }
-
- return {};
-}
+ static const QString def = (QStringList()
+ << "Chrome.exe"
+ << "Firefox.exe"
+ << "TSVNCache.exe"
+ << "TGitCache.exe"
+ << "Steam.exe"
+ << "GameOverlayUI.exe"
+ << "Discord.exe"
+ << "GalaxyClient.exe"
+ << "Spotify.exe"
+ ).join(";");
-void Settings::setPreviousSeparatorColor(const QColor& c) const
-{
- set(m_Settings, "General", "previousSeparatorColor", c);
+ return get<QString>(m_Settings, "Settings", "executable_blacklist", def);
}
-void Settings::removePreviousSeparatorColor()
+void Settings::setExecutablesBlacklist(const QString& s)
{
- remove(m_Settings, "General", "previousSeparatorColor");
+ set(m_Settings, "Settings", "executable_blacklist", s);
}
-bool Settings::getNexusApiKey(QString &apiKey) const
+void Settings::setMotdHash(uint hash)
{
- QString tempKey = deObfuscate("APIKEY");
- if (tempKey.isEmpty())
- return false;
-
- apiKey = tempKey;
- return true;
+ set(m_Settings, "General", "motd_hash", hash);
}
-bool Settings::setNexusApiKey(const QString& apiKey)
+unsigned int Settings::motdHash() const
{
- if (!obfuscate("APIKEY", apiKey)) {
- const auto e = GetLastError();
- log::error("Storing API key failed: {}", formatSystemMessage(e));
- return false;
- }
-
- return true;
+ return get<unsigned int>(m_Settings, "General", "motd_hash", 0);
}
-bool Settings::clearNexusApiKey()
+bool Settings::archiveParsing() const
{
- return setNexusApiKey("");
+ return get<bool>(m_Settings, "Settings", "archive_parsing_experimental", false);
}
-bool Settings::hasNexusApiKey() const
+void Settings::setArchiveParsing(bool b)
{
- return !deObfuscate("APIKEY").isEmpty();
+ set(m_Settings, "Settings", "archive_parsing_experimental", b);
}
-bool Settings::getSteamLogin(QString &username, QString &password) const
+std::vector<std::map<QString, QVariant>> Settings::executables() const
{
- username = get<QString>(m_Settings, "Settings", "steam_username", "");
- password = deObfuscate("steam_password");
-
- return !username.isEmpty() && !password.isEmpty();
-}
+ ScopedReadArray sra(m_Settings, "customExecutables");
+ std::vector<std::map<QString, QVariant>> v;
-bool Settings::compactDownloads() const
-{
- return get<bool>(m_Settings, "Settings", "compact_downloads", false);
-}
+ sra.for_each([&]{
+ std::map<QString, QVariant> map;
-void Settings::setCompactDownloads(bool b)
-{
- set(m_Settings, "Settings", "compact_downloads", b);
-}
+ for (auto&& key : sra.keys()) {
+ map[key] = m_Settings.value(key);
+ }
-bool Settings::metaDownloads() const
-{
- return get<bool>(m_Settings, "Settings", "meta_downloads", false);
-}
+ v.push_back(map);
+ });
-void Settings::setMetaDownloads(bool b)
-{
- set(m_Settings, "Settings", "meta_downloads", b);
+ return v;
}
-bool Settings::offlineMode() const
+void Settings::setExecutables(const std::vector<std::map<QString, QVariant>>& v)
{
- return get<bool>(m_Settings, "Settings/offline_mode", false);
-}
+ removeSection(m_Settings, "customExecutables");
-void Settings::setOfflineMode(bool b)
-{
- set(m_Settings, "Settings", "offline_mode", b);
-}
+ ScopedWriteArray swa(m_Settings, "customExecutables");
-log::Levels Settings::logLevel() const
-{
- return get<log::Levels>(m_Settings, "Settings", "log_level", log::Levels::Info);
-}
+ for (const auto& map : v) {
+ swa.next();
-void Settings::setLogLevel(log::Levels level)
-{
- set(m_Settings, "Settings", "log_level", level);
+ for (auto&& p : map) {
+ swa.set(p.first, p.second);
+ }
+ }
}
-CrashDumpsType Settings::crashDumpsType() const
+bool Settings::keepBackupOnInstall() const
{
- return get<CrashDumpsType>(m_Settings,
- "Settings", "crash_dumps_type", CrashDumpsType::Mini);
+ return get<bool>(m_Settings, "General", "backup_install", false);
}
-void Settings::setCrashDumpsType(CrashDumpsType type)
+void Settings::setKeepBackupOnInstall(bool b)
{
- set(m_Settings, "Settings", "crash_dumps_type", type);
+ set(m_Settings, "General", "backup_install", b);
}
-int Settings::crashDumpsMax() const
+GameSettings& Settings::game()
{
- return get<int>(m_Settings, "Settings", "crash_dumps_max", 5);
+ return m_Game;
}
-void Settings::setCrashDumpsMax(int n)
+const GameSettings& Settings::game() const
{
- set(m_Settings, "Settings", "crash_dumps_max", n);
+ return m_Game;
}
-QString Settings::executablesBlacklist() const
+GeometrySettings& Settings::geometry()
{
- 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<QString>(m_Settings, "Settings", "executable_blacklist", def);
+ return m_Geometry;
}
-void Settings::setExecutablesBlacklist(const QString& s)
+const GeometrySettings& Settings::geometry() const
{
- set(m_Settings, "Settings", "executable_blacklist", s);
+ return m_Geometry;
}
-void Settings::setSteamLogin(QString username, QString password)
+WidgetSettings& Settings::widgets()
{
- if (username == "") {
- remove(m_Settings, "Settings", "steam_username");
- password = "";
- } else {
- 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));
- }
+ return m_Widgets;
}
-LoadMechanism::EMechanism Settings::getLoadMechanism() const
+const WidgetSettings& Settings::widgets() const
{
- const auto def = LoadMechanism::LOAD_MODORGANIZER;
-
- const auto i = get<LoadMechanism::EMechanism>(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<int>(i), toString(def));
-
- set(m_Settings, "Settings", "load_mechanism", def);
-
- return def;
- }
- }
-
- return i;
+ return m_Widgets;
}
-void Settings::setLoadMechanism(LoadMechanism::EMechanism m)
+ColorSettings& Settings::colors()
{
- set(m_Settings, "Settings", "load_mechanism", m);
+ return m_Colors;
}
-void Settings::setupLoadMechanism()
+const ColorSettings& Settings::colors() const
{
- m_LoadMechanism.activate(getLoadMechanism());
+ return m_Colors;
}
-bool Settings::endorsementIntegration() const
+PluginSettings& Settings::plugins()
{
- return get<bool>(m_Settings, "Settings", "endorsement_integration", true);
+ return m_Plugins;
}
-void Settings::setEndorsementIntegration(bool b) const
+const PluginSettings& Settings::plugins() const
{
- set(m_Settings, "Settings", "endorsement_integration", b);
+ return m_Plugins;
}
-EndorsementState Settings::endorsementState() const
+PathSettings& Settings::paths()
{
- return endorsementStateFromString(
- get<QString>(m_Settings, "General", "endorse_state", ""));
+ return m_Paths;
}
-void Settings::setEndorsementState(EndorsementState s)
+const PathSettings& Settings::paths() const
{
- const auto v = toString(s);
-
- if (v.isEmpty()) {
- remove(m_Settings, "General", "endorse_state");
- } else {
- set(m_Settings, "General", "endorse_state", v);
- }
+ return m_Paths;
}
-bool Settings::hideAPICounter() const
+NetworkSettings& Settings::network()
{
- return get<bool>(m_Settings, "Settings", "hide_api_counter", false);
+ return m_Network;
}
-void Settings::setHideAPICounter(bool b)
+const NetworkSettings& Settings::network() const
{
- set(m_Settings, "Settings", "hide_api_counter", b);
+ return m_Network;
}
-bool Settings::displayForeign() const
+NexusSettings& Settings::nexus()
{
- return get<bool>(m_Settings, "Settings", "display_foreign", true);
+ return m_Nexus;
}
-void Settings::setDisplayForeign(bool b)
+const NexusSettings& Settings::nexus() const
{
- set(m_Settings, "Settings", "display_foreign", b);
+ return m_Nexus;
}
-void Settings::setMotDHash(uint hash)
+SteamSettings& Settings::steam()
{
- set(m_Settings, "General", "motd_hash", hash);
+ return m_Steam;
}
-unsigned int Settings::getMotDHash() const
+const SteamSettings& Settings::steam() const
{
- return get<unsigned int>(m_Settings, "motd_hash", 0);
+ return m_Steam;
}
-bool Settings::archiveParsing() const
+InterfaceSettings& Settings::interface()
{
- return get<bool>(m_Settings, "Settings", "archive_parsing_experimental", false);
+ return m_Interface;
}
-void Settings::setArchiveParsing(bool b)
+const InterfaceSettings& Settings::interface() const
{
- set(m_Settings, "Settings", "archive_parsing_experimental", b);
+ return m_Interface;
}
-QString Settings::language()
+DiagnosticsSettings& Settings::diagnostics()
{
- QString result = get<QString>(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);
- } else {
- // fallback system locale
- result = QLocale::system().name();
- }
- }
-
- return result;
+ return m_Diagnostics;
}
-void Settings::setLanguage(const QString& name)
+const DiagnosticsSettings& Settings::diagnostics() const
{
- set(m_Settings, "Settings", "language", name);
+ return m_Diagnostics;
}
-void Settings::setDownloadSpeed(const QString& name, int bytesPerSecond)
+QSettings::Status Settings::sync() const
{
- auto servers = getServers();
-
- for (auto& server : servers) {
- if (server.name() == name) {
- server.addDownload(bytesPerSecond);
- updateServers(servers);
- return;
- }
- }
-
- log::error(
- "server '{}' not found while trying to add a download with bps {}",
- name, bytesPerSecond);
+ m_Settings.sync();
+ return m_Settings.status();
}
-ServerList Settings::getServers() const
+void Settings::dump() const
{
- // 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();
-
- if (!keys.empty() && keys[0] != "size") {
- // old format
- return getServersFromOldMap();
- }
- }
-
-
- // post 2.2.1 format, array of values
-
- ServerList list;
-
- {
- ScopedReadArray sra(m_Settings, "Servers");
-
- sra.for_each([&] {
- ServerInfo::SpeedList lastDownloads;
-
- const auto lastDownloadsString = sra.get<QString>("lastDownloads", "");
-
- for (const auto& s : lastDownloadsString.split(" ")) {
- const auto bytesPerSecond = s.toInt();
- if (bytesPerSecond > 0) {
- lastDownloads.push_back(bytesPerSecond);
- }
- }
-
- ServerInfo server(
- sra.get<QString>("name", ""),
- sra.get<bool>("premium", false),
- QDate::fromString(sra.get<QString>("lastSeen", ""), Qt::ISODate),
- sra.get<int>("preferred", 0),
- lastDownloads);
-
- list.add(std::move(server));
+ static const QStringList ignore({
+ "username", "password", "nexus_api_key"
});
- }
-
- return list;
-}
-
-ServerList Settings::getServersFromOldMap() const
-{
- // for 2.2.1 and before
-
- ServerList list;
- const ScopedGroup sg(m_Settings, "Servers");
-
- sg.for_each([&](auto&& serverKey) {
- QVariantMap data = sg.get<QVariantMap>(serverKey);
-
- ServerInfo server(
- serverKey,
- data["premium"].toBool(),
- data["lastSeen"].toDate(),
- data["preferred"].toInt(),
- {});
-
- // ignoring download count and speed, it's now a list of values instead of
- // a total
-
- list.add(std::move(server));
- });
-
- return list;
-}
-
-void Settings::updateServers(ServerList servers)
-{
- // clean up unavailable servers
- servers.cleanup();
- removeSection(m_Settings, "Servers");
+ log::debug("settings:");
{
- ScopedWriteArray swa(m_Settings, "Servers");
-
- for (const auto& server : servers) {
- swa.next();
-
- swa.set("name", server.name());
- swa.set("premium", server.isPremium());
- swa.set("lastSeen", server.lastSeen().toString(Qt::ISODate));
- swa.set("preferred", server.preferred());
+ ScopedGroup sg(m_Settings, "Settings");
- QString lastDownloads;
- for (const auto& speed : server.lastDownloads()) {
- if (speed > 0) {
- lastDownloads += QString("%1 ").arg(speed);
- }
+ for (auto k : m_Settings.allKeys()) {
+ if (ignore.contains(k, Qt::CaseInsensitive)) {
+ continue;
}
- swa.set("lastDownloads", lastDownloads.trimmed());
+ log::debug(" . {}={}", k, m_Settings.value(k).toString());
}
}
-}
-
-std::map<QString, QString> Settings::getRecentDirectories() const
-{
- std::map<QString, QString> map;
-
- ScopedReadArray sra(m_Settings, "RecentDirectories");
-
- sra.for_each([&] {
- const QVariant name = sra.get<QVariant>("name");
- const QVariant dir = sra.get<QVariant>("directory");
-
- if (name.isValid() && dir.isValid()) {
- map.emplace(name.toString(), dir.toString());
- }
- });
- return map;
+ m_Network.dump();
}
-void Settings::setRecentDirectories(const std::map<QString, QString>& map)
+void Settings::managedGameChanged(IPluginGame const *gamePlugin)
{
- removeSection(m_Settings, "RecentDirectories");
-
- ScopedWriteArray swa(m_Settings, "recentDirectories");
-
- for (auto&& p : map) {
- swa.next();
-
- swa.set("name", p.first);
- swa.set("directory", p.second);
- }
+ m_Game.setPlugin(gamePlugin);
}
-std::vector<std::map<QString, QVariant>> Settings::getExecutables() const
-{
- ScopedReadArray sra(m_Settings, "customExecutables");
- std::vector<std::map<QString, QVariant>> v;
-
- sra.for_each([&]{
- std::map<QString, QVariant> map;
-
- for (auto&& key : sra.keys()) {
- map[key] = m_Settings.value(key);
- }
-
- v.push_back(map);
- });
-
- return v;
-}
-void Settings::setExecutables(const std::vector<std::map<QString, QVariant>>& v)
+GameSettings::GameSettings(QSettings& settings)
+ : m_Settings(settings), m_GamePlugin(nullptr)
{
- 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);
- }
- }
}
-bool Settings::isTutorialCompleted(const QString& windowName) const
+const MOBase::IPluginGame* GameSettings::plugin()
{
- return get<bool>(m_Settings, "CompletedWindowTutorials", windowName, false);
+ return m_GamePlugin;
}
-void Settings::setTutorialCompleted(const QString& windowName, bool b)
+void GameSettings::setPlugin(const MOBase::IPluginGame* gamePlugin)
{
- set(m_Settings, "CompletedWindowTutorials", windowName, b);
+ m_GamePlugin = gamePlugin;
}
-bool Settings::keepBackupOnInstall() const
+bool GameSettings::forceEnableCoreFiles() const
{
- return get<bool>(m_Settings, "backup_install", false);
+ return get<bool>(m_Settings, "Settings", "force_enable_core_files", true);
}
-void Settings::setKeepBackupOnInstall(bool b)
+void GameSettings::setForceEnableCoreFiles(bool b)
{
- set(m_Settings, "General", "backup_install", b);
+ set(m_Settings, "Settings", "force_enable_core_files", b);
}
-QuestionBoxMemory::Button Settings::getQuestionButton(
- const QString& windowName, const QString& filename) const
+std::optional<QString> GameSettings::directory() const
{
- const QString sectionName("DialogChoices");
-
- if (!filename.isEmpty()) {
- const auto fileSetting = windowName + "/" + filename;
- if (auto v=getOptional<int>(m_Settings, sectionName, filename)) {
- return static_cast<QuestionBoxMemory::Button>(*v);
- }
- }
-
- if (auto v=getOptional<int>(m_Settings, sectionName, windowName)) {
- return static_cast<QuestionBoxMemory::Button>(*v);
+ if (auto v=getOptional<QByteArray>(m_Settings, "General", "gamePath")) {
+ return QString::fromUtf8(*v);
}
- return QuestionBoxMemory::NoButton;
+ return {};
}
-void Settings::setQuestionWindowButton(
- const QString& windowName, QuestionBoxMemory::Button button)
+void GameSettings::setDirectory(const QString& path)
{
- const QString sectionName("DialogChoices/");
-
- if (button == QuestionBoxMemory::NoButton) {
- remove(m_Settings, sectionName, windowName);
- } else {
- set(m_Settings, sectionName, windowName, button);
- }
+ set(m_Settings, "General", "gamePath", QDir::toNativeSeparators(path).toUtf8());
}
-void Settings::setQuestionFileButton(
- const QString& windowName, const QString& filename,
- QuestionBoxMemory::Button button)
+std::optional<QString> GameSettings::name() const
{
- const QString sectionName("DialogChoices");
- const QString settingName(windowName + "/" + filename);
-
- if (button == QuestionBoxMemory::NoButton) {
- remove(m_Settings, sectionName, settingName);
- } else {
- set(m_Settings, sectionName, settingName, button);
- }
+ return getOptional<QString>(m_Settings, "General", "gameName");
}
-void Settings::resetQuestionButtons()
+void GameSettings::setName(const QString& name)
{
- removeSection(m_Settings, "DialogChoices");
+ set(m_Settings, "General", "gameName", name);
}
-std::optional<int> Settings::getIndex(const QComboBox* cb) const
+std::optional<QString> GameSettings::edition() const
{
- return getOptional<int>(m_Settings, "Widgets", indexSettingName(cb));
+ return getOptional<QString>(m_Settings, "General", "game_edition");
}
-void Settings::saveIndex(const QComboBox* cb)
+void GameSettings::setEdition(const QString& name)
{
- set(m_Settings, "Widgets", indexSettingName(cb), cb->currentIndex());
+ set(m_Settings, "General", "game_edition", name);
}
-void Settings::restoreIndex(QComboBox* cb, std::optional<int> def) const
+std::optional<QString> GameSettings::selectedProfileName() const
{
- if (auto v=getOptional<int>(m_Settings, "Widgets", indexSettingName(cb), def)) {
- cb->setCurrentIndex(*v);
+ if (auto v=getOptional<QByteArray>(m_Settings, "General", "selected_profile")) {
+ return QString::fromUtf8(*v);
}
-}
-std::optional<int> Settings::getIndex(const QTabWidget* w) const
-{
- return getOptional<int>(m_Settings, "Widgets", indexSettingName(w));
-}
-
-void Settings::saveIndex(const QTabWidget* w)
-{
- set(m_Settings, "Widgets", indexSettingName(w), w->currentIndex());
+ return {};
}
-void Settings::restoreIndex(QTabWidget* w, std::optional<int> def) const
+void GameSettings::setSelectedProfileName(const QString& name)
{
- if (auto v=getOptional<int>(m_Settings, "Widgets", indexSettingName(w), def)) {
- w->setCurrentIndex(*v);
- }
+ set(m_Settings, "General", "selected_profile", name.toUtf8());
}
-std::optional<bool> Settings::getChecked(const QAbstractButton* w) const
+LoadMechanism::EMechanism GameSettings::loadMechanismType() const
{
- warnIfNotCheckable(w);
- return getOptional<bool>(m_Settings, "Widgets", checkedSettingName(w));
-}
+ const auto def = LoadMechanism::LOAD_MODORGANIZER;
-void Settings::saveChecked(const QAbstractButton* w)
-{
- warnIfNotCheckable(w);
- set(m_Settings, "Widgets", checkedSettingName(w), w->isChecked());
-}
+ const auto i = get<LoadMechanism::EMechanism>(m_Settings,
+ "Settings", "load_mechanism", def);
-void Settings::restoreChecked(QAbstractButton* w, std::optional<bool> def) const
-{
- warnIfNotCheckable(w);
+ switch (i)
+ {
+ // ok
+ case LoadMechanism::LOAD_MODORGANIZER: // fall-through
+ {
+ break;
+ }
- if (auto v=getOptional<bool>(m_Settings, "Widgets", checkedSettingName(w), def)) {
- w->setChecked(*v);
- }
-}
+ default:
+ {
+ log::error(
+ "invalid load mechanism {}, reverting to {}",
+ static_cast<int>(i), toString(def));
-GeometrySettings& Settings::geometry()
-{
- return m_Geometry;
-}
+ set(m_Settings, "Settings", "load_mechanism", def);
-const GeometrySettings& Settings::geometry() const
-{
- return m_Geometry;
-}
+ return def;
+ }
+ }
-ColorSettings& Settings::colors()
-{
- return m_Colors;
+ return i;
}
-const ColorSettings& Settings::colors() const
+void GameSettings::setLoadMechanism(LoadMechanism::EMechanism m)
{
- return m_Colors;
+ set(m_Settings, "Settings", "load_mechanism", m);
}
-PluginSettings& Settings::plugins()
+const LoadMechanism& GameSettings::loadMechanism() const
{
- return m_Plugins;
+ return m_LoadMechanism;
}
-const PluginSettings& Settings::plugins() const
+void GameSettings::setupLoadMechanism()
{
- return m_Plugins;
+ m_LoadMechanism.activate(loadMechanismType());
}
-QSettings::Status Settings::sync() const
+bool GameSettings::hideUncheckedPlugins() const
{
- m_Settings.sync();
- return m_Settings.status();
+ return get<bool>(m_Settings, "Settings", "hide_unchecked_plugins", false);
}
-void Settings::dump() const
+void GameSettings::setHideUncheckedPlugins(bool b)
{
- static const QStringList ignore({
- "username", "password", "nexus_api_key"
- });
-
- 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());
- }
- }
-
- 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());
- }
+ set(m_Settings, "Settings", "hide_unchecked_plugins", b);
}
@@ -1697,7 +1115,7 @@ void GeometrySettings::saveToolbars(const QMainWindow* w)
}
}
-QStringList GeometrySettings::getModInfoTabOrder() const
+QStringList GeometrySettings::modInfoTabOrder() const
{
QStringList v;
@@ -1840,6 +1258,121 @@ void GeometrySettings::restoreDocks(QMainWindow* mw) const
}
+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); });
+}
+
+std::optional<int> WidgetSettings::index(const QComboBox* cb) const
+{
+ return getOptional<int>(m_Settings, "Widgets", indexSettingName(cb));
+}
+
+void WidgetSettings::saveIndex(const QComboBox* cb)
+{
+ set(m_Settings, "Widgets", indexSettingName(cb), cb->currentIndex());
+}
+
+void WidgetSettings::restoreIndex(QComboBox* cb, std::optional<int> def) const
+{
+ if (auto v=getOptional<int>(m_Settings, "Widgets", indexSettingName(cb), def)) {
+ cb->setCurrentIndex(*v);
+ }
+}
+
+std::optional<int> WidgetSettings::index(const QTabWidget* w) const
+{
+ return getOptional<int>(m_Settings, "Widgets", indexSettingName(w));
+}
+
+void WidgetSettings::saveIndex(const QTabWidget* w)
+{
+ set(m_Settings, "Widgets", indexSettingName(w), w->currentIndex());
+}
+
+void WidgetSettings::restoreIndex(QTabWidget* w, std::optional<int> def) const
+{
+ if (auto v=getOptional<int>(m_Settings, "Widgets", indexSettingName(w), def)) {
+ w->setCurrentIndex(*v);
+ }
+}
+
+std::optional<bool> WidgetSettings::checked(const QAbstractButton* w) const
+{
+ warnIfNotCheckable(w);
+ return getOptional<bool>(m_Settings, "Widgets", checkedSettingName(w));
+}
+
+void WidgetSettings::saveChecked(const QAbstractButton* w)
+{
+ warnIfNotCheckable(w);
+ set(m_Settings, "Widgets", checkedSettingName(w), w->isChecked());
+}
+
+void WidgetSettings::restoreChecked(QAbstractButton* w, std::optional<bool> def) const
+{
+ warnIfNotCheckable(w);
+
+ if (auto v=getOptional<bool>(m_Settings, "Widgets", checkedSettingName(w), def)) {
+ w->setChecked(*v);
+ }
+}
+
+QuestionBoxMemory::Button WidgetSettings::questionButton(
+ const QString& windowName, const QString& filename) const
+{
+ const QString sectionName("DialogChoices");
+
+ if (!filename.isEmpty()) {
+ const auto fileSetting = windowName + "/" + filename;
+ if (auto v=getOptional<int>(m_Settings, sectionName, filename)) {
+ return static_cast<QuestionBoxMemory::Button>(*v);
+ }
+ }
+
+ if (auto v=getOptional<int>(m_Settings, sectionName, windowName)) {
+ return static_cast<QuestionBoxMemory::Button>(*v);
+ }
+
+ return QuestionBoxMemory::NoButton;
+}
+
+void WidgetSettings::setQuestionWindowButton(
+ const QString& windowName, QuestionBoxMemory::Button button)
+{
+ const QString sectionName("DialogChoices");
+
+ if (button == QuestionBoxMemory::NoButton) {
+ remove(m_Settings, sectionName, windowName);
+ } else {
+ set(m_Settings, sectionName, windowName, button);
+ }
+}
+
+void WidgetSettings::setQuestionFileButton(
+ const QString& windowName, const QString& filename,
+ QuestionBoxMemory::Button button)
+{
+ const QString sectionName("DialogChoices");
+ const QString settingName(windowName + "/" + filename);
+
+ if (button == QuestionBoxMemory::NoButton) {
+ remove(m_Settings, sectionName, settingName);
+ } else {
+ set(m_Settings, sectionName, settingName, button);
+ }
+}
+
+void WidgetSettings::resetQuestionButtons()
+{
+ removeSection(m_Settings, "DialogChoices");
+}
+
+
ColorSettings::ColorSettings(QSettings& s)
: m_Settings(s)
{
@@ -1917,6 +1450,47 @@ void ColorSettings::setPluginListContained(const QColor& c)
set(m_Settings, "Settings", "containedColor", c);
}
+std::optional<QColor> ColorSettings::previousSeparatorColor() const
+{
+ const auto c = getOptional<QColor>(m_Settings, "General", "previousSeparatorColor");
+ if (c && c->isValid()) {
+ return c;
+ }
+
+ return {};
+}
+
+void ColorSettings::setPreviousSeparatorColor(const QColor& c) const
+{
+ set(m_Settings, "General", "previousSeparatorColor", c);
+}
+
+void ColorSettings::removePreviousSeparatorColor()
+{
+ remove(m_Settings, "General", "previousSeparatorColor");
+}
+
+bool ColorSettings::colorSeparatorScrollbar() const
+{
+ return get<bool>(m_Settings, "Settings", "colorSeparatorScrollbars", true);
+}
+
+void ColorSettings::setColorSeparatorScrollbar(bool b)
+{
+ set(m_Settings, "Settings", "colorSeparatorScrollbars", b);
+}
+
+QColor ColorSettings::idealTextColor(const QColor& rBackgroundColor)
+{
+ 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);
+}
+
+
PluginSettings::PluginSettings(QSettings& settings)
: m_Settings(settings)
@@ -2089,6 +1663,585 @@ void PluginSettings::save()
}
+PathSettings::PathSettings(QSettings& settings)
+ : m_Settings(settings)
+{
+}
+
+std::map<QString, QString> PathSettings::recent() const
+{
+ std::map<QString, QString> map;
+
+ ScopedReadArray sra(m_Settings, "RecentDirectories");
+
+ sra.for_each([&] {
+ const QVariant name = sra.get<QVariant>("name");
+ const QVariant dir = sra.get<QVariant>("directory");
+
+ if (name.isValid() && dir.isValid()) {
+ map.emplace(name.toString(), dir.toString());
+ }
+ });
+
+ return map;
+}
+
+void PathSettings::setRecent(const std::map<QString, QString>& map)
+{
+ removeSection(m_Settings, "RecentDirectories");
+
+ ScopedWriteArray swa(m_Settings, "recentDirectories");
+
+ for (auto&& p : map) {
+ swa.next();
+
+ swa.set("name", p.first);
+ swa.set("directory", p.second);
+ }
+}
+
+QString PathSettings::getConfigurablePath(const QString &key,
+ const QString &def,
+ bool resolve) const
+{
+ QString result = QDir::fromNativeSeparators(
+ get<QString>(m_Settings, "Settings", key, QString("%BASE_DIR%/") + def));
+
+ if (resolve) {
+ result.replace("%BASE_DIR%", base());
+ }
+
+ return result;
+}
+
+void PathSettings::setConfigurablePath(const QString &key, const QString& path)
+{
+ if (path.isEmpty()) {
+ remove(m_Settings, "Settings", key);
+ } else {
+ set(m_Settings, "Settings", key, path);
+ }
+}
+
+QString PathSettings::base() const
+{
+ return QDir::fromNativeSeparators(get<QString>(m_Settings,
+ "Settings", "base_directory", qApp->property("dataPath").toString()));
+}
+
+QString PathSettings::downloads(bool resolve) const
+{
+ return getConfigurablePath(
+ "download_directory",
+ ToQString(AppConfig::downloadPath()),
+ resolve);
+}
+
+QString PathSettings::cache(bool resolve) const
+{
+ return getConfigurablePath(
+ "cache_directory",
+ ToQString(AppConfig::cachePath()),
+ resolve);
+}
+
+QString PathSettings::mods(bool resolve) const
+{
+ return getConfigurablePath(
+ "mod_directory",
+ ToQString(AppConfig::modsPath()),
+ resolve);
+}
+
+QString PathSettings::profiles(bool resolve) const
+{
+ return getConfigurablePath(
+ "profiles_directory",
+ ToQString(AppConfig::profilesPath()),
+ resolve);
+}
+
+QString PathSettings::overwrite(bool resolve) const
+{
+ return getConfigurablePath(
+ "overwrite_directory",
+ ToQString(AppConfig::overwritePath()),
+ resolve);
+}
+
+void PathSettings::setBase(const QString& path)
+{
+ if (path.isEmpty()) {
+ remove(m_Settings, "Settings", "base_directory");
+ } else {
+ set(m_Settings, "Settings", "base_directory", path);
+ }
+}
+
+void PathSettings::setDownloads(const QString& path)
+{
+ setConfigurablePath("download_directory", path);
+}
+
+void PathSettings::setMods(const QString& path)
+{
+ setConfigurablePath("mod_directory", path);
+}
+
+void PathSettings::setCache(const QString& path)
+{
+ setConfigurablePath("cache_directory", path);
+}
+
+void PathSettings::setProfiles(const QString& path)
+{
+ setConfigurablePath("profiles_directory", path);
+}
+
+void PathSettings::setOverwrite(const QString& path)
+{
+ setConfigurablePath("overwrite_directory", path);
+}
+
+
+NetworkSettings::NetworkSettings(QSettings& settings)
+ : m_Settings(settings)
+{
+}
+
+bool NetworkSettings::offlineMode() const
+{
+ return get<bool>(m_Settings, "Settings", "offline_mode", false);
+}
+
+void NetworkSettings::setOfflineMode(bool b)
+{
+ set(m_Settings, "Settings", "offline_mode", b);
+}
+
+bool NetworkSettings::useProxy() const
+{
+ return get<bool>(m_Settings, "Settings", "use_proxy", false);
+}
+
+void NetworkSettings::setUseProxy(bool b)
+{
+ set(m_Settings, "Settings", "use_proxy", b);
+}
+
+void NetworkSettings::setDownloadSpeed(const QString& name, int bytesPerSecond)
+{
+ auto current = servers();
+
+ for (auto& server : current) {
+ if (server.name() == name) {
+ server.addDownload(bytesPerSecond);
+ updateServers(current);
+ return;
+ }
+ }
+
+ log::error(
+ "server '{}' not found while trying to add a download with bps {}",
+ name, bytesPerSecond);
+}
+
+ServerList NetworkSettings::servers() const
+{
+ // 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();
+
+ if (!keys.empty() && keys[0] != "size") {
+ // old format
+ return serversFromOldMap();
+ }
+ }
+
+
+ // post 2.2.1 format, array of values
+
+ ServerList list;
+
+ {
+ ScopedReadArray sra(m_Settings, "Servers");
+
+ sra.for_each([&] {
+ ServerInfo::SpeedList lastDownloads;
+
+ const auto lastDownloadsString = sra.get<QString>("lastDownloads", "");
+
+ for (const auto& s : lastDownloadsString.split(" ")) {
+ const auto bytesPerSecond = s.toInt();
+ if (bytesPerSecond > 0) {
+ lastDownloads.push_back(bytesPerSecond);
+ }
+ }
+
+ ServerInfo server(
+ sra.get<QString>("name", ""),
+ sra.get<bool>("premium", false),
+ QDate::fromString(sra.get<QString>("lastSeen", ""), Qt::ISODate),
+ sra.get<int>("preferred", 0),
+ lastDownloads);
+
+ list.add(std::move(server));
+ });
+ }
+
+ return list;
+}
+
+ServerList NetworkSettings::serversFromOldMap() const
+{
+ // for 2.2.1 and before
+
+ ServerList list;
+ const ScopedGroup sg(m_Settings, "Servers");
+
+ sg.for_each([&](auto&& serverKey) {
+ QVariantMap data = sg.get<QVariantMap>(serverKey);
+
+ ServerInfo server(
+ serverKey,
+ data["premium"].toBool(),
+ data["lastSeen"].toDate(),
+ data["preferred"].toInt(),
+ {});
+
+ // ignoring download count and speed, it's now a list of values instead of
+ // a total
+
+ list.add(std::move(server));
+ });
+
+ return list;
+}
+
+void NetworkSettings::updateServers(ServerList servers)
+{
+ // clean up unavailable servers
+ servers.cleanup();
+
+ removeSection(m_Settings, "Servers");
+
+ {
+ ScopedWriteArray swa(m_Settings, "Servers");
+
+ for (const auto& server : servers) {
+ swa.next();
+
+ 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()) {
+ if (speed > 0) {
+ lastDownloads += QString("%1 ").arg(speed);
+ }
+ }
+
+ swa.set("lastDownloads", lastDownloads.trimmed());
+ }
+ }
+}
+
+void NetworkSettings::dump() const
+{
+ log::debug("servers:");
+
+ for (const auto& server : servers()) {
+ 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());
+ }
+}
+
+
+NexusSettings::NexusSettings(Settings& parent, QSettings& settings)
+ : m_Parent(parent), m_Settings(settings)
+{
+}
+
+bool NexusSettings::automaticLoginEnabled() const
+{
+ return get<bool>(m_Settings, "Settings", "nexus_login", false);
+}
+
+bool NexusSettings::apiKey(QString &apiKey) const
+{
+ QString tempKey = getWindowsCredential("APIKEY");
+ if (tempKey.isEmpty())
+ return false;
+
+ apiKey = tempKey;
+ return true;
+}
+
+bool NexusSettings::setApiKey(const QString& apiKey)
+{
+ if (!setWindowsCredential("APIKEY", apiKey)) {
+ const auto e = GetLastError();
+ log::error("Storing API key failed: {}", formatSystemMessage(e));
+ return false;
+ }
+
+ return true;
+}
+
+bool NexusSettings::clearApiKey()
+{
+ return setApiKey("");
+}
+
+bool NexusSettings::hasApiKey() const
+{
+ return !getWindowsCredential("APIKEY").isEmpty();
+}
+
+bool NexusSettings::endorsementIntegration() const
+{
+ return get<bool>(m_Settings, "Settings", "endorsement_integration", true);
+}
+
+void NexusSettings::setEndorsementIntegration(bool b) const
+{
+ set(m_Settings, "Settings", "endorsement_integration", b);
+}
+
+EndorsementState NexusSettings::endorsementState() const
+{
+ return endorsementStateFromString(
+ get<QString>(m_Settings, "General", "endorse_state", ""));
+}
+
+void NexusSettings::setEndorsementState(EndorsementState s)
+{
+ const auto v = toString(s);
+
+ if (v.isEmpty()) {
+ remove(m_Settings, "General", "endorse_state");
+ } else {
+ set(m_Settings, "General", "endorse_state", v);
+ }
+}
+
+void NexusSettings::registerAsNXMHandler(bool force)
+{
+ 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"));
+ }
+}
+
+
+SteamSettings::SteamSettings(Settings& parent, QSettings& settings)
+ : m_Parent(parent), m_Settings(settings)
+{
+}
+
+QString SteamSettings::appID() const
+{
+ return get<QString>(
+ m_Settings, "Settings", "app_id", m_Parent.game().plugin()->steamAPPId());
+}
+
+void SteamSettings::setAppID(const QString& id)
+{
+ if (id.isEmpty()) {
+ remove(m_Settings, "Settings", "app_id");
+ } else {
+ set(m_Settings, "Settings", "app_id", id);
+ }
+}
+
+bool SteamSettings::login(QString &username, QString &password) const
+{
+ username = get<QString>(m_Settings, "Settings", "steam_username", "");
+ password = getWindowsCredential("steam_password");
+
+ return !username.isEmpty() && !password.isEmpty();
+}
+
+void SteamSettings::setLogin(QString username, QString password)
+{
+ if (username == "") {
+ remove(m_Settings, "Settings", "steam_username");
+ password = "";
+ } else {
+ set(m_Settings, "Settings", "steam_username", username);
+ }
+
+ if (!setWindowsCredential("steam_password", password)) {
+ const auto e = GetLastError();
+ log::error("Storing or deleting password failed: {}", formatSystemMessage(e));
+ }
+}
+
+
+InterfaceSettings::InterfaceSettings(QSettings& settings)
+ : m_Settings(settings)
+{
+}
+
+bool InterfaceSettings::lockGUI() const
+{
+ return get<bool>(m_Settings, "Settings", "lock_gui", true);
+}
+
+void InterfaceSettings::setLockGUI(bool b)
+{
+ set(m_Settings, "Settings", "lock_gui", b);
+}
+
+std::optional<QString> InterfaceSettings::styleName() const
+{
+ return getOptional<QString>(m_Settings, "Settings", "style");
+}
+
+void InterfaceSettings::setStyleName(const QString& name)
+{
+ set(m_Settings, "Settings", "style", name);
+}
+
+bool InterfaceSettings::compactDownloads() const
+{
+ return get<bool>(m_Settings, "Settings", "compact_downloads", false);
+}
+
+void InterfaceSettings::setCompactDownloads(bool b)
+{
+ set(m_Settings, "Settings", "compact_downloads", b);
+}
+
+bool InterfaceSettings::metaDownloads() const
+{
+ return get<bool>(m_Settings, "Settings", "meta_downloads", false);
+}
+
+void InterfaceSettings::setMetaDownloads(bool b)
+{
+ set(m_Settings, "Settings", "meta_downloads", b);
+}
+
+bool InterfaceSettings::hideAPICounter() const
+{
+ return get<bool>(m_Settings, "Settings", "hide_api_counter", false);
+}
+
+void InterfaceSettings::setHideAPICounter(bool b)
+{
+ set(m_Settings, "Settings", "hide_api_counter", b);
+}
+
+bool InterfaceSettings::displayForeign() const
+{
+ return get<bool>(m_Settings, "Settings", "display_foreign", true);
+}
+
+void InterfaceSettings::setDisplayForeign(bool b)
+{
+ set(m_Settings, "Settings", "display_foreign", b);
+}
+
+QString InterfaceSettings::language()
+{
+ QString result = get<QString>(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);
+ } else {
+ // fallback system locale
+ result = QLocale::system().name();
+ }
+ }
+
+ return result;
+}
+
+void InterfaceSettings::setLanguage(const QString& name)
+{
+ set(m_Settings, "Settings", "language", name);
+}
+
+bool InterfaceSettings::isTutorialCompleted(const QString& windowName) const
+{
+ return get<bool>(m_Settings, "CompletedWindowTutorials", windowName, false);
+}
+
+void InterfaceSettings::setTutorialCompleted(const QString& windowName, bool b)
+{
+ set(m_Settings, "CompletedWindowTutorials", windowName, b);
+}
+
+
+DiagnosticsSettings::DiagnosticsSettings(QSettings& settings)
+ : m_Settings(settings)
+{
+}
+
+log::Levels DiagnosticsSettings::logLevel() const
+{
+ return get<log::Levels>(m_Settings, "Settings", "log_level", log::Levels::Info);
+}
+
+void DiagnosticsSettings::setLogLevel(log::Levels level)
+{
+ set(m_Settings, "Settings", "log_level", level);
+}
+
+CrashDumpsType DiagnosticsSettings::crashDumpsType() const
+{
+ return get<CrashDumpsType>(m_Settings,
+ "Settings", "crash_dumps_type", CrashDumpsType::Mini);
+}
+
+void DiagnosticsSettings::setCrashDumpsType(CrashDumpsType type)
+{
+ set(m_Settings, "Settings", "crash_dumps_type", type);
+}
+
+int DiagnosticsSettings::crashDumpsMax() const
+{
+ return get<int>(m_Settings, "Settings", "crash_dumps_max", 5);
+}
+
+void DiagnosticsSettings::setCrashDumpsMax(int n)
+{
+ set(m_Settings, "Settings", "crash_dumps_max", n);
+}
+
+
GeometrySaver::GeometrySaver(Settings& s, QDialog* dialog)
: m_settings(s), m_dialog(dialog)
{