summaryrefslogtreecommitdiff
path: root/src/settings.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/settings.cpp')
-rw-r--r--src/settings.cpp2424
1 files changed, 1615 insertions, 809 deletions
diff --git a/src/settings.cpp b/src/settings.cpp
index 5cb2524f..5170a5de 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -18,75 +18,55 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
*/
#include "settings.h"
-
-#include "pluginsetting.h"
+#include "settingsutilities.h"
#include "serverinfo.h"
-#include "settingsdialog.h"
-#include "versioninfo.h"
+#include "executableslist.h"
#include "appconfig.h"
-#include "organizercore.h"
+#include "env.h"
+#include "envmetrics.h"
+#include <expanderwidget.h>
#include <utility.h>
-#include <iplugin.h>
#include <iplugingame.h>
-#include <questionboxmemory.h>
-#include <usvfsparameters.h>
-
-#include <QCheckBox>
-#include <QCoreApplication>
-#include <QComboBox>
-#include <QDate>
-#include <QDialog>
-#include <QDir>
-#include <QDirIterator>
-#include <QFileInfo>
-#include <QLineEdit>
-#include <QSpinBox>
-#include <QListWidgetItem>
-#include <QLocale>
-#include <QMessageBox>
-#include <QApplication>
-#include <QRegExp>
-#include <QDir>
-#include <QStringList>
-#include <QVariantMap>
-#include <QLabel>
-#include <QPushButton>
-#include <QPalette>
-#include <Qt> // for Qt::UserRole, etc
-#include <QtDebug> // for qDebug, qWarning
-
-#include <Windows.h> // For ShellExecuteW, HINSTANCE, etc
-#include <wincred.h> // For storage
+using namespace MOBase;
-#include <algorithm> // for sort
-#include <memory>
-#include <stdexcept> // for runtime_error
-#include <string>
-#include <utility> // for pair, make_pair
+EndorsementState endorsementStateFromString(const QString& s)
+{
+ if (s == "Endorsed") {
+ return EndorsementState::Accepted;
+ } else if (s == "Abstained") {
+ return EndorsementState::Refused;
+ } else {
+ return EndorsementState::NoDecision;
+ }
+}
-using namespace MOBase;
+QString toString(EndorsementState s)
+{
+ switch (s)
+ {
+ case EndorsementState::Accepted:
+ return "Endorsed";
-template <typename T>
-class QListWidgetItemEx : public QListWidgetItem {
-public:
- QListWidgetItemEx(const QString &text, int sortRole = Qt::DisplayRole, QListWidget *parent = 0, int type = Type)
- : QListWidgetItem(text, parent, type), m_SortRole(sortRole) {}
+ case EndorsementState::Refused:
+ return "Abstained";
- virtual bool operator< ( const QListWidgetItem & other ) const {
- return this->data(m_SortRole).value<T>() < other.data(m_SortRole).value<T>();
+ case EndorsementState::NoDecision: // fall-through
+ default:
+ return {};
}
-private:
- int m_SortRole;
-};
+}
Settings *Settings::s_Instance = nullptr;
-
-Settings::Settings(const QSettings &settingsSource)
- : m_Settings(settingsSource.fileName(), settingsSource.format())
+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 (s_Instance != nullptr) {
throw std::runtime_error("second instance of \"Settings\" created");
@@ -95,13 +75,12 @@ Settings::Settings(const QSettings &settingsSource)
}
}
-
Settings::~Settings()
{
+ MOBase::QuestionBoxMemory::setCallbacks({}, {}, {});
s_Instance = nullptr;
}
-
Settings &Settings::instance()
{
if (s_Instance == nullptr) {
@@ -110,447 +89,1188 @@ Settings &Settings::instance()
return *s_Instance;
}
-void Settings::clearPlugins()
+void Settings::processUpdates(
+ const QVersionNumber& currentVersion, const QVersionNumber& lastVersion)
{
- m_Plugins.clear();
- m_PluginSettings.clear();
+ if (firstStart()) {
+ return;
+ }
- m_PluginBlacklist.clear();
- int count = m_Settings.beginReadArray("pluginBlacklist");
- for (int i = 0; i < count; ++i) {
- m_Settings.setArrayIndex(i);
- m_PluginBlacklist.insert(m_Settings.value("name").toString());
+ if (currentVersion == lastVersion) {
+ return;
}
- m_Settings.endArray();
+
+ log::info(
+ "updating from {} to {}",
+ lastVersion.toString(), currentVersion.toString());
+
+ auto version = [&](const QVersionNumber& v, auto&& f) {
+ if (lastVersion < v) {
+ log::debug("processing updates for {}", v.toString());
+ f();
+ }
+ };
+
+ version({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");
+ });
+
+ version({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");
+ });
+
+ version({2, 2, 2}, [&]{
+ // log splitter is gone, it's a dock now
+ remove(m_Settings, "General", "log_split");
+
+ // moved to widgets
+ remove(m_Settings, "General", "mod_info_conflicts_tab");
+ remove(m_Settings, "General", "mod_info_conflicts_general_expanders");
+ remove(m_Settings, "General", "mod_info_conflicts_general_overwrite");
+ remove(m_Settings, "General", "mod_info_conflicts_general_noconflict");
+ remove(m_Settings, "General", "mod_info_conflicts_general_overwritten");
+ remove(m_Settings, "General", "mod_info_conflicts_advanced_list");
+ remove(m_Settings, "General", "mod_info_conflicts_advanced_options");
+ remove(m_Settings, "General", "mod_info_tab_order");
+ remove(m_Settings, "General", "mod_info_dialog_images_show_dds");
+
+ // moved to geometry
+ remove(m_Settings, "General", "window_geometry");
+ remove(m_Settings, "General", "window_state");
+ remove(m_Settings, "General", "toolbar_size");
+ remove(m_Settings, "General", "toolbar_button_style");
+ remove(m_Settings, "General", "menubar_visible");
+ remove(m_Settings, "General", "statusbar_visible");
+ remove(m_Settings, "General", "window_split");
+ remove(m_Settings, "General", "window_monitor");
+ remove(m_Settings, "General", "browser_geometry");
+ remove(m_Settings, "General", "filters_visible");
+
+ // this was supposed to have been removed above when updating from 2.2.0,
+ // but it wasn't in Settings, it was in General
+ remove(m_Settings, "General", "ask_for_nexuspw");
+
+ m_Network.updateFromOldMap();
+ });
+
+ //save version in all case
+ set(m_Settings, "General", "version", currentVersion.toString());
+
+ log::debug("updating done");
}
-bool Settings::pluginBlacklisted(const QString &fileName) const
+QString Settings::filename() const
{
- return m_PluginBlacklist.contains(fileName);
+ return m_Settings.fileName();
}
-void Settings::registerAsNXMHandler(bool force)
+bool Settings::checkForUpdates() const
{
- const auto nxmPath = QCoreApplication::applicationDirPath() + "/nxmhandler.exe";
- const auto executable = QCoreApplication::applicationFilePath();
+ return get<bool>(m_Settings, "Settings", "check_for_updates", true);
+}
- QString mode = force ? "forcereg" : "reg";
- QString parameters = mode + " " + m_GamePlugin->gameShortName();
- for (const QString& altGame : m_GamePlugin->validShortNames()) {
- parameters += "," + altGame;
+void Settings::setCheckForUpdates(bool b)
+{
+ set(m_Settings, "Settings", "check_for_updates", b);
+}
+
+bool Settings::usePrereleases() const
+{
+ return get<bool>(m_Settings, "Settings", "use_prereleases", false);
+}
+
+void Settings::setUsePrereleases(bool b)
+{
+ set(m_Settings, "Settings", "use_prereleases", b);
+}
+
+std::optional<QVersionNumber> Settings::version() const
+{
+ if (auto v=getOptional<QString>(m_Settings, "General", "version")) {
+ return QVersionNumber::fromString(*v).normalized();
}
- parameters += " \"" + executable + "\"";
- if (!shell::Execute(nxmPath, parameters)) {
- QMessageBox::critical(
- nullptr, tr("Failed"), tr("Failed to start the helper application"));
+ return {};
+}
+
+bool Settings::firstStart() const
+{
+ return get<bool>(m_Settings, "General", "first_start", true);
+}
+
+void Settings::setFirstStart(bool b)
+{
+ set(m_Settings, "General", "first_start", b);
+}
+
+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(";");
+
+ return get<QString>(m_Settings, "Settings", "executable_blacklist", def);
+}
+
+bool Settings::isExecutableBlacklisted(const QString& s) const
+{
+ for (auto exec : executablesBlacklist().split(";")) {
+ if (exec.compare(s, Qt::CaseInsensitive) == 0) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void Settings::setExecutablesBlacklist(const QString& s)
+{
+ set(m_Settings, "Settings", "executable_blacklist", s);
+}
+
+void Settings::setMotdHash(uint hash)
+{
+ set(m_Settings, "General", "motd_hash", hash);
+}
+
+unsigned int Settings::motdHash() const
+{
+ return get<unsigned int>(m_Settings, "General", "motd_hash", 0);
+}
+
+bool Settings::archiveParsing() const
+{
+ return get<bool>(m_Settings, "Settings", "archive_parsing_experimental", false);
+}
+
+void Settings::setArchiveParsing(bool b)
+{
+ set(m_Settings, "Settings", "archive_parsing_experimental", b);
+}
+
+std::vector<std::map<QString, QVariant>> Settings::executables() 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] = sra.get<QVariant>(key);
+ }
+
+ v.push_back(map);
+ });
+
+ return v;
+}
+
+void Settings::setExecutables(const std::vector<std::map<QString, QVariant>>& v)
+{
+ const auto current = executables();
+
+ if (current == v) {
+ // no change
+ return;
}
+
+ if (current.size() > v.size()) {
+ // Qt can't remove array elements, the section must be cleared
+ removeSection(m_Settings, "customExecutables");
+ }
+
+ ScopedWriteArray swa(m_Settings, "customExecutables", v.size());
+
+ for (const auto& map : v) {
+ swa.next();
+
+ for (auto&& p : map) {
+ swa.set(p.first, p.second);
+ }
+ }
+}
+
+bool Settings::keepBackupOnInstall() const
+{
+ return get<bool>(m_Settings, "General", "backup_install", false);
+}
+
+void Settings::setKeepBackupOnInstall(bool b)
+{
+ set(m_Settings, "General", "backup_install", b);
+}
+
+GameSettings& Settings::game()
+{
+ return m_Game;
+}
+
+const GameSettings& Settings::game() const
+{
+ return m_Game;
+}
+
+GeometrySettings& Settings::geometry()
+{
+ return m_Geometry;
+}
+
+const GeometrySettings& Settings::geometry() const
+{
+ return m_Geometry;
+}
+
+WidgetSettings& Settings::widgets()
+{
+ return m_Widgets;
+}
+
+const WidgetSettings& Settings::widgets() const
+{
+ return m_Widgets;
+}
+
+ColorSettings& Settings::colors()
+{
+ return m_Colors;
+}
+
+const ColorSettings& Settings::colors() const
+{
+ return m_Colors;
+}
+
+PluginSettings& Settings::plugins()
+{
+ return m_Plugins;
+}
+
+const PluginSettings& Settings::plugins() const
+{
+ return m_Plugins;
+}
+
+PathSettings& Settings::paths()
+{
+ return m_Paths;
+}
+
+const PathSettings& Settings::paths() const
+{
+ return m_Paths;
}
-bool Settings::colorSeparatorScrollbar() const
+NetworkSettings& Settings::network()
{
- return m_Settings.value("Settings/colorSeparatorScrollbars", true).toBool();
+ return m_Network;
+}
+
+const NetworkSettings& Settings::network() const
+{
+ return m_Network;
+}
+
+NexusSettings& Settings::nexus()
+{
+ return m_Nexus;
+}
+
+const NexusSettings& Settings::nexus() const
+{
+ return m_Nexus;
+}
+
+SteamSettings& Settings::steam()
+{
+ return m_Steam;
+}
+
+const SteamSettings& Settings::steam() const
+{
+ return m_Steam;
+}
+
+InterfaceSettings& Settings::interface()
+{
+ return m_Interface;
+}
+
+const InterfaceSettings& Settings::interface() const
+{
+ return m_Interface;
+}
+
+DiagnosticsSettings& Settings::diagnostics()
+{
+ return m_Diagnostics;
+}
+
+const DiagnosticsSettings& Settings::diagnostics() const
+{
+ return m_Diagnostics;
+}
+
+QSettings::Status Settings::sync() const
+{
+ m_Settings.sync();
+ return m_Settings.status();
+}
+
+void Settings::dump() const
+{
+ 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());
+ }
+ }
+
+ m_Network.dump();
}
void Settings::managedGameChanged(IPluginGame const *gamePlugin)
{
+ m_Game.setPlugin(gamePlugin);
+}
+
+
+GameSettings::GameSettings(QSettings& settings)
+ : m_Settings(settings), m_GamePlugin(nullptr)
+{
+}
+
+const MOBase::IPluginGame* GameSettings::plugin()
+{
+ return m_GamePlugin;
+}
+
+void GameSettings::setPlugin(const MOBase::IPluginGame* gamePlugin)
+{
m_GamePlugin = gamePlugin;
}
-void Settings::registerPlugin(IPlugin *plugin)
+bool GameSettings::forceEnableCoreFiles() const
{
- 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);
- if (!temp.convert(setting.defaultValue.type())) {
- qWarning("failed to interpret \"%s\" as correct type for \"%s\" in plugin \"%s\", using default",
- qUtf8Printable(temp.toString()), qUtf8Printable(setting.key), qUtf8Printable(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());
+ return get<bool>(m_Settings, "Settings", "force_enable_core_files", true);
+}
+
+void GameSettings::setForceEnableCoreFiles(bool b)
+{
+ set(m_Settings, "Settings", "force_enable_core_files", b);
+}
+
+std::optional<QString> GameSettings::directory() const
+{
+ if (auto v=getOptional<QByteArray>(m_Settings, "General", "gamePath")) {
+ return QString::fromUtf8(*v);
}
+
+ return {};
}
-bool Settings::obfuscate(const QString key, const QString data)
+void GameSettings::setDirectory(const QString& path)
{
- 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", "gamePath", QDir::toNativeSeparators(path).toUtf8());
+}
- 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;
+std::optional<QString> GameSettings::name() const
+{
+ return getOptional<QString>(m_Settings, "General", "gameName");
+}
- result = CredWriteW(&cred, 0);
- delete[] charData;
+void GameSettings::setName(const QString& name)
+{
+ set(m_Settings, "General", "gameName", name);
+}
+
+std::optional<QString> GameSettings::edition() const
+{
+ return getOptional<QString>(m_Settings, "General", "game_edition");
+}
+
+void GameSettings::setEdition(const QString& name)
+{
+ set(m_Settings, "General", "game_edition", name);
+}
+
+std::optional<QString> GameSettings::selectedProfileName() const
+{
+ if (auto v=getOptional<QByteArray>(m_Settings, "General", "selected_profile")) {
+ return QString::fromUtf8(*v);
}
- delete[] keyData;
- return result;
+
+ return {};
}
-QString Settings::deObfuscate(const QString key)
+void GameSettings::setSelectedProfileName(const QString& name)
{
- 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) {
- qCritical().nospace()
- << "Retrieving encrypted data failed: "
- << formatSystemMessageQ(e);
+ set(m_Settings, "General", "selected_profile", name.toUtf8());
+}
+
+LoadMechanism::EMechanism GameSettings::loadMechanismType() 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;
}
}
- delete[] keyData;
- return result;
+
+ return i;
}
-QColor Settings::getIdealTextColor(const QColor& rBackgroundColor)
+void GameSettings::setLoadMechanism(LoadMechanism::EMechanism m)
{
- if (rBackgroundColor.alpha() == 0)
- return QColor(Qt::black);
+ set(m_Settings, "Settings", "load_mechanism", m);
+}
- 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);
+const LoadMechanism& GameSettings::loadMechanism() const
+{
+ return m_LoadMechanism;
}
+LoadMechanism& GameSettings::loadMechanism()
+{
+ return m_LoadMechanism;
+}
-bool Settings::hideUncheckedPlugins() const
+bool GameSettings::hideUncheckedPlugins() const
{
- return m_Settings.value("Settings/hide_unchecked_plugins", false).toBool();
+ return get<bool>(m_Settings, "Settings", "hide_unchecked_plugins", false);
}
-bool Settings::forceEnableCoreFiles() const
+void GameSettings::setHideUncheckedPlugins(bool b)
{
- return m_Settings.value("Settings/force_enable_core_files", true).toBool();
+ set(m_Settings, "Settings", "hide_unchecked_plugins", b);
}
-bool Settings::lockGUI() const
+
+GeometrySettings::GeometrySettings(QSettings& s)
+ : m_Settings(s), m_Reset(false)
{
- return m_Settings.value("Settings/lock_gui", true).toBool();
}
-bool Settings::automaticLoginEnabled() const
+void GeometrySettings::requestReset()
{
- return m_Settings.value("Settings/nexus_login", false).toBool();
+ m_Reset = true;
}
-QString Settings::getSteamAppID() const
+void GeometrySettings::resetIfNeeded()
{
- return m_Settings.value("Settings/app_id", m_GamePlugin->steamAPPId()).toString();
+ if (!m_Reset) {
+ return;
+ }
+
+ removeSection(m_Settings, "Geometry");
}
-bool Settings::usePrereleases() const
+void GeometrySettings::saveGeometry(const QMainWindow* w)
{
- return m_Settings.value("Settings/use_prereleases", false).toBool();
+ saveWindowGeometry(w);
}
-void Settings::setDownloadSpeed(const QString &serverName, int bytesPerSecond)
+bool GeometrySettings::restoreGeometry(QMainWindow* w) const
{
- m_Settings.beginGroup("Servers");
+ return restoreWindowGeometry(w);
+}
- 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<double>(bytesPerSecond);
- m_Settings.setValue(serverKey, data);
- }
+void GeometrySettings::saveGeometry(const QDialog* d)
+{
+ saveWindowGeometry(d);
+}
+
+bool GeometrySettings::restoreGeometry(QDialog* d) const
+{
+ const auto r = restoreWindowGeometry(d);
+
+ if (centerDialogs()) {
+ centerOnParent(d);
}
- m_Settings.endGroup();
- m_Settings.sync();
+ return r;
+}
+
+void GeometrySettings::saveWindowGeometry(const QWidget* w)
+{
+ set(m_Settings, "Geometry", geoSettingName(w), w->saveGeometry());
}
-std::map<QString, int> Settings::getPreferredServers()
+bool GeometrySettings::restoreWindowGeometry(QWidget* w) const
{
- std::map<QString, int> result;
- m_Settings.beginGroup("Servers");
+ if (auto v=getOptional<QByteArray>(m_Settings, "Geometry", geoSettingName(w))) {
+ w->restoreGeometry(*v);
+ ensureWindowOnScreen(w);
+ return true;
+ }
- for (const QString &serverKey : m_Settings.childKeys()) {
- QVariantMap data = m_Settings.value(serverKey).toMap();
- int preference = data["preferred"].toInt();
- if (preference > 0) {
- result[serverKey] = preference;
+ return false;
+}
+
+void GeometrySettings::ensureWindowOnScreen(QWidget* w) const
+{
+ // users report that the main window and/or dialogs are displayed off-screen;
+ // the usual workaround is keyboard navigation to move it
+ //
+ // qt should have code that deals with multiple monitors and off-screen
+ // geometries, but there seems to be bugs or inconsistencies that can't be
+ // reproduced
+ //
+ // the closest would probably be https://bugreports.qt.io/browse/QTBUG-64498,
+ // which is about multiple monitors and high dpi, but it seems fixed as of
+ // 5.12.4, which is shipped with 2.2.1
+ //
+ // without being to reproduce the problem, some simple checks are made in a
+ // timer, which may mitigate the issues
+
+ QTimer::singleShot(100, w, [w] {
+ const auto borders = 20;
+
+ // desktop geometry, made smaller to make sure there isn't just a few pixels
+ const auto originalDg = env::Environment().metrics().desktopGeometry();
+ const auto dg = originalDg.adjusted(borders, borders, -borders, -borders);
+
+ const auto g = w->geometry();
+
+ if (!dg.intersects(g)) {
+ log::warn(
+ "window '{}' is offscreen, moving to main monitor; geo={}, desktop={}",
+ w->objectName(), g, originalDg);
+
+ // widget is off-screen, center it on main monitor
+ centerOnMonitor(w, -1);
+
+ log::warn("window '{}' now at {}", w->objectName(), w->geometry());
}
+ });
+}
+
+void GeometrySettings::saveState(const QMainWindow* w)
+{
+ set(m_Settings, "Geometry", stateSettingName(w), w->saveState());
+}
+
+bool GeometrySettings::restoreState(QMainWindow* w) const
+{
+ if (auto v=getOptional<QByteArray>(m_Settings, "Geometry", stateSettingName(w))) {
+ w->restoreState(*v);
+ return true;
}
- m_Settings.endGroup();
- return result;
+ return false;
}
-QString Settings::getConfigurablePath(const QString &key,
- const QString &def,
- bool resolve) const
+void GeometrySettings::saveState(const QHeaderView* w)
{
- QString result = QDir::fromNativeSeparators(
- m_Settings.value(QString("settings/") + key, QString("%BASE_DIR%/") + def)
- .toString());
- if (resolve) {
- result.replace("%BASE_DIR%", getBaseDirectory());
+ set(m_Settings, "Geometry", stateSettingName(w), w->saveState());
+}
+
+bool GeometrySettings::restoreState(QHeaderView* w) const
+{
+ if (auto v=getOptional<QByteArray>(m_Settings, "Geometry", stateSettingName(w))) {
+ w->restoreState(*v);
+ return true;
}
- return result;
+
+ return false;
}
-QString Settings::getBaseDirectory() const
+void GeometrySettings::saveState(const QSplitter* w)
{
- return QDir::fromNativeSeparators(m_Settings.value(
- "settings/base_directory", qApp->property("dataPath").toString()).toString());
+ set(m_Settings, "Geometry", stateSettingName(w), w->saveState());
}
-QString Settings::getDownloadDirectory(bool resolve) const
+bool GeometrySettings::restoreState(QSplitter* w) const
{
- return getConfigurablePath("download_directory", ToQString(AppConfig::downloadPath()), resolve);
+ if (auto v=getOptional<QByteArray>(m_Settings, "Geometry", stateSettingName(w))) {
+ w->restoreState(*v);
+ return true;
+ }
+
+ return false;
}
-QString Settings::getCacheDirectory(bool resolve) const
+void GeometrySettings::saveState(const ExpanderWidget* expander)
{
- return getConfigurablePath("cache_directory", ToQString(AppConfig::cachePath()), resolve);
+ set(m_Settings, "Geometry", stateSettingName(expander), expander->saveState());
}
-QString Settings::getModDirectory(bool resolve) const
+bool GeometrySettings::restoreState(ExpanderWidget* expander) const
{
- return getConfigurablePath("mod_directory", ToQString(AppConfig::modsPath()), resolve);
+ if (auto v=getOptional<QByteArray>(m_Settings, "Geometry", stateSettingName(expander))) {
+ expander->restoreState(*v);
+ return true;
+ }
+
+ return false;
}
-QString Settings::getManagedGameDirectory() const
+void GeometrySettings::saveVisibility(const QWidget* w)
{
- return m_Settings.value("gamePath", "").toString();
+ set(m_Settings, "Geometry", visibilitySettingName(w), w->isVisible());
}
-QString Settings::getProfileDirectory(bool resolve) const
+bool GeometrySettings::restoreVisibility(QWidget* w, std::optional<bool> def) const
{
- return getConfigurablePath("profiles_directory", ToQString(AppConfig::profilesPath()), resolve);
+ if (auto v=getOptional<bool>(m_Settings, "Geometry", visibilitySettingName(w), def)) {
+ w->setVisible(*v);
+ return true;
+ }
+
+ return false;
}
-QString Settings::getOverwriteDirectory(bool resolve) const
+void GeometrySettings::restoreToolbars(QMainWindow* w) const
{
- return getConfigurablePath("overwrite_directory",
- ToQString(AppConfig::overwritePath()), resolve);
+ // all toolbars have the same size and button style settings
+ const auto size = getOptional<QSize>(m_Settings, "Geometry", "toolbar_size");
+ const auto style = getOptional<int>(m_Settings, "Geometry", "toolbar_button_style");
+
+ for (auto* tb : w->findChildren<QToolBar*>()) {
+ if (size) {
+ tb->setIconSize(*size);
+ }
+
+ if (style) {
+ tb->setToolButtonStyle(static_cast<Qt::ToolButtonStyle>(*style));
+ }
+
+ restoreVisibility(tb);
+ }
}
-bool Settings::getNexusApiKey(QString &apiKey) const
+void GeometrySettings::saveToolbars(const QMainWindow* w)
{
- QString tempKey = deObfuscate("APIKEY");
- if (tempKey.isEmpty())
- return false;
- apiKey = tempKey;
- return true;
+ const auto tbs = w->findChildren<QToolBar*>();
+
+ // save visibility for all
+ for (auto* tb : tbs) {
+ saveVisibility(tb);
+ }
+
+ // 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<int>(tb->toolButtonStyle()));
+ }
}
-bool Settings::setNexusApiKey(const QString& apiKey)
+QStringList GeometrySettings::modInfoTabOrder() const
{
- if (!obfuscate("APIKEY", apiKey)) {
- const auto e = GetLastError();
+ QStringList v;
- qCritical().nospace()
- << "Storing API key failed: "
- << formatSystemMessageQ(e);
+ if (m_Settings.contains("mod_info_tabs")) {
+ // old byte array from 2.2.0
+ QDataStream stream(m_Settings.value("mod_info_tabs").toByteArray());
- return false;
+ int count = 0;
+ stream >> count;
+
+ for (int i=0; i<count; ++i) {
+ QString s;
+ stream >> s;
+ v.push_back(s);
+ }
+ } else {
+ // string list since 2.2.1
+ QString string = get<QString>(m_Settings, "Widgets", "ModInfoTabOrder", "");
+ QTextStream stream(&string);
+
+ while (!stream.atEnd()) {
+ QString s;
+ stream >> s;
+ v.push_back(s);
+ }
}
- return true;
+ return v;
}
-bool Settings::clearNexusApiKey()
+void GeometrySettings::setModInfoTabOrder(const QString& names)
{
- return setNexusApiKey("");
+ set(m_Settings, "Widgets", "ModInfoTabOrder", names);
}
-bool Settings::hasNexusApiKey() const
+bool GeometrySettings::centerDialogs() const
{
- return !deObfuscate("APIKEY").isEmpty();
+ return get<bool>(m_Settings, "Settings", "center_dialogs", false);
}
-bool Settings::getSteamLogin(QString &username, QString &password) const
+void GeometrySettings::setCenterDialogs(bool b)
{
- if (m_Settings.contains("Settings/steam_username")) {
- QString tempPass = deObfuscate("steam_password");
- if (!tempPass.isEmpty()) {
- username = m_Settings.value("Settings/steam_username").toString();
- password = tempPass;
- return true;
+ set(m_Settings, "Settings", "center_dialogs", b);
+}
+
+void GeometrySettings::centerOnMainWindowMonitor(QWidget* w)
+{
+ const auto monitor = getOptional<int>(
+ m_Settings, "Geometry", "MainWindow_monitor").value_or(-1);
+
+ centerOnMonitor(w, monitor);
+}
+
+void GeometrySettings::centerOnMonitor(QWidget* w, int monitor)
+{
+ QPoint center;
+
+ if (monitor >= 0 && monitor < QGuiApplication::screens().size()) {
+ center = QGuiApplication::screens().at(monitor)->geometry().center();
+ } else {
+ center = QGuiApplication::primaryScreen()->geometry().center();
+ }
+
+ w->move(center - w->rect().center());
+}
+
+void GeometrySettings::centerOnParent(QWidget* w, QWidget* parent)
+{
+ if (!parent) {
+ parent = w->parentWidget();
+
+ if (!parent) {
+ parent = qApp->activeWindow();
+ }
+ }
+
+ if (parent && parent->isVisible()) {
+ const auto pr = parent->geometry();
+ w->move(pr.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);
}
}
- return false;
}
-bool Settings::compactDownloads() const
+
+Qt::Orientation dockOrientation(const QMainWindow* mw, const QDockWidget* d)
{
- return m_Settings.value("Settings/compact_downloads", false).toBool();
+ // docks in these areas are horizontal
+ const auto horizontalAreas =
+ Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea;
+
+ if (mw->dockWidgetArea(const_cast<QDockWidget*>(d)) & horizontalAreas) {
+ return Qt::Horizontal;
+ } else {
+ return Qt::Vertical;
+ }
}
-bool Settings::metaDownloads() const
+void GeometrySettings::saveDocks(const QMainWindow* mw)
{
- return m_Settings.value("Settings/meta_downloads", false).toBool();
+ // 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
+ //
+
+ // saves the size of each dock
+ for (const auto* dock : mw->findChildren<QDockWidget*>()) {
+ 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);
+ }
}
-bool Settings::offlineMode() const
+void GeometrySettings::restoreDocks(QMainWindow* mw) const
{
- return m_Settings.value("Settings/offline_mode", false).toBool();
+ struct DockInfo
+ {
+ QDockWidget* d;
+ int size = 0;
+ Qt::Orientation ori;
+ };
+
+ std::vector<DockInfo> dockInfos;
+
+ // for each dock
+ for (auto* dock : mw->findChildren<QDockWidget*>()) {
+ if (auto size=getOptional<int>(m_Settings, "Geometry", dockSettingName(dock))) {
+ // remember this dock, its size and orientation
+ dockInfos.push_back({dock, *size, dockOrientation(mw, dock)});
+ }
+ }
+
+ // 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);
+ }
+ });
}
-int Settings::logLevel() const
+
+WidgetSettings::WidgetSettings(QSettings& s)
+ : m_Settings(s)
{
- return m_Settings.value("Settings/log_level", static_cast<int>(LogLevel::Info)).toInt();
+ 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); });
}
-int Settings::crashDumpsType() const
+std::optional<int> WidgetSettings::index(const QComboBox* cb) const
{
- return m_Settings.value("Settings/crash_dumps_type", static_cast<int>(CrashDumpsType::Mini)).toInt();
+ return getOptional<int>(m_Settings, "Widgets", indexSettingName(cb));
}
-int Settings::crashDumpsMax() const
+void WidgetSettings::saveIndex(const QComboBox* cb)
{
- return m_Settings.value("Settings/crash_dumps_max", 5).toInt();
+ set(m_Settings, "Widgets", indexSettingName(cb), cb->currentIndex());
}
-QColor Settings::modlistOverwrittenLooseColor() const
+void WidgetSettings::restoreIndex(QComboBox* cb, std::optional<int> def) const
{
- return m_Settings.value("Settings/overwrittenLooseFilesColor", QColor(0, 255, 0, 64)).value<QColor>();
+ if (auto v=getOptional<int>(m_Settings, "Widgets", indexSettingName(cb), def)) {
+ cb->setCurrentIndex(*v);
+ }
}
-QColor Settings::modlistOverwritingLooseColor() const
+std::optional<int> WidgetSettings::index(const QTabWidget* w) const
{
- return m_Settings.value("Settings/overwritingLooseFilesColor", QColor(255, 0, 0, 64)).value<QColor>();
+ return getOptional<int>(m_Settings, "Widgets", indexSettingName(w));
}
-QColor Settings::modlistOverwrittenArchiveColor() const
+void WidgetSettings::saveIndex(const QTabWidget* w)
{
- return m_Settings.value("Settings/overwrittenArchiveFilesColor", QColor(0, 255, 255, 64)).value<QColor>();
+ set(m_Settings, "Widgets", indexSettingName(w), w->currentIndex());
}
-QColor Settings::modlistOverwritingArchiveColor() const
+void WidgetSettings::restoreIndex(QTabWidget* w, std::optional<int> def) const
{
- return m_Settings.value("Settings/overwritingArchiveFilesColor", QColor(255, 0, 255, 64)).value<QColor>();
+ if (auto v=getOptional<int>(m_Settings, "Widgets", indexSettingName(w), def)) {
+ w->setCurrentIndex(*v);
+ }
}
-QColor Settings::modlistContainsPluginColor() const
+std::optional<bool> WidgetSettings::checked(const QAbstractButton* w) const
{
- return m_Settings.value("Settings/containsPluginColor", QColor(0, 0, 255, 64)).value<QColor>();
+ warnIfNotCheckable(w);
+ return getOptional<bool>(m_Settings, "Widgets", checkedSettingName(w));
}
-QColor Settings::pluginListContainedColor() const
+void WidgetSettings::saveChecked(const QAbstractButton* w)
{
- return m_Settings.value("Settings/containedColor", QColor(0, 0, 255, 64)).value<QColor>();
+ warnIfNotCheckable(w);
+ set(m_Settings, "Widgets", checkedSettingName(w), w->isChecked());
}
-QString Settings::executablesBlacklist() const
+void WidgetSettings::restoreChecked(QAbstractButton* w, std::optional<bool> def) 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();
+ warnIfNotCheckable(w);
+
+ if (auto v=getOptional<bool>(m_Settings, "Widgets", checkedSettingName(w), def)) {
+ w->setChecked(*v);
+ }
}
-void Settings::setSteamLogin(QString username, QString password)
+QuestionBoxMemory::Button WidgetSettings::questionButton(
+ const QString& windowName, const QString& filename) const
{
- if (username == "") {
- m_Settings.remove("Settings/steam_username");
- password = "";
- } else {
- m_Settings.setValue("Settings/steam_username", username);
+ const QString sectionName("DialogChoices");
+
+ if (!filename.isEmpty()) {
+ const auto fileSetting = windowName + "/" + filename;
+ if (auto v=getOptional<int>(m_Settings, sectionName, fileSetting)) {
+ return static_cast<QuestionBoxMemory::Button>(*v);
+ }
}
- if (!obfuscate("steam_password", password)) {
- const auto e = GetLastError();
- qCritical().nospace()
- << "Storing or deleting password failed: "
- << formatSystemMessageQ(e);
+
+ if (auto v=getOptional<int>(m_Settings, sectionName, windowName)) {
+ return static_cast<QuestionBoxMemory::Button>(*v);
}
+
+ return QuestionBoxMemory::NoButton;
}
-LoadMechanism::EMechanism Settings::getLoadMechanism() const
+void WidgetSettings::setQuestionWindowButton(
+ const QString& windowName, QuestionBoxMemory::Button button)
{
- switch (m_Settings.value("Settings/load_mechanism").toInt()) {
- case LoadMechanism::LOAD_MODORGANIZER: return LoadMechanism::LOAD_MODORGANIZER;
- case LoadMechanism::LOAD_SCRIPTEXTENDER: return LoadMechanism::LOAD_SCRIPTEXTENDER;
- case LoadMechanism::LOAD_PROXYDLL: return LoadMechanism::LOAD_PROXYDLL;
+ const QString sectionName("DialogChoices");
+
+ if (button == QuestionBoxMemory::NoButton) {
+ remove(m_Settings, sectionName, windowName);
+ } else {
+ set(m_Settings, sectionName, windowName, button);
}
- throw std::runtime_error("invalid load mechanism");
}
+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 Settings::setupLoadMechanism()
+void WidgetSettings::resetQuestionButtons()
{
- m_LoadMechanism.activate(getLoadMechanism());
+ removeSection(m_Settings, "DialogChoices");
}
-bool Settings::useProxy() const
+ColorSettings::ColorSettings(QSettings& s)
+ : m_Settings(s)
{
- return m_Settings.value("Settings/use_proxy", false).toBool();
}
-bool Settings::endorsementIntegration() const
+QColor ColorSettings::modlistOverwrittenLoose() const
{
- return m_Settings.value("Settings/endorsement_integration", true).toBool();
+ return get<QColor>(
+ m_Settings, "Settings", "overwrittenLooseFilesColor",
+ QColor(0, 255, 0, 64));
}
-bool Settings::hideAPICounter() const
+void ColorSettings::setModlistOverwrittenLoose(const QColor& c)
{
- return m_Settings.value("Settings/hide_api_counter", false).toBool();
+ set(m_Settings, "Settings", "overwrittenLooseFilesColor", c);
}
-bool Settings::displayForeign() const
+QColor ColorSettings::modlistOverwritingLoose() const
{
- return m_Settings.value("Settings/display_foreign", true).toBool();
+ return get<QColor>(
+ m_Settings, "Settings", "overwritingLooseFilesColor",
+ QColor(255, 0, 0, 64));
}
-void Settings::setMotDHash(uint hash)
+void ColorSettings::setModlistOverwritingLoose(const QColor& c)
{
- m_Settings.setValue("motd_hash", hash);
+ set(m_Settings, "Settings", "overwritingLooseFilesColor", c);
}
-uint Settings::getMotDHash() const
+QColor ColorSettings::modlistOverwrittenArchive() const
{
- return m_Settings.value("motd_hash", 0).toUInt();
+ return get<QColor>(
+ m_Settings, "Settings", "overwrittenArchiveFilesColor",
+ QColor(0, 255, 255, 64));
}
-bool Settings::archiveParsing() const
+void ColorSettings::setModlistOverwrittenArchive(const QColor& c)
{
- return m_Settings.value("Settings/archive_parsing_experimental", false).toBool();
+ set(m_Settings, "Settings", "overwrittenArchiveFilesColor", c);
}
-QVariant Settings::pluginSetting(const QString &pluginName, const QString &key) const
+QColor ColorSettings::modlistOverwritingArchive() const
+{
+ return get<QColor>(
+ m_Settings, "Settings", "overwritingArchiveFilesColor",
+ QColor(255, 0, 255, 64));
+}
+
+void ColorSettings::setModlistOverwritingArchive(const QColor& c)
+{
+ set(m_Settings, "Settings", "overwritingArchiveFilesColor", c);
+}
+
+QColor ColorSettings::modlistContainsPlugin() const
+{
+ return get<QColor>(
+ m_Settings, "Settings", "containsPluginColor",
+ QColor(0, 0, 255, 64));
+}
+
+void ColorSettings::setModlistContainsPlugin(const QColor& c)
+{
+ set(m_Settings, "Settings", "containsPluginColor", c);
+}
+
+QColor ColorSettings::pluginListContained() const
+{
+ return get<QColor>(
+ m_Settings, "Settings", "containedColor",
+ QColor(0, 0, 255, 64));
+}
+
+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)
+{
+}
+
+void PluginSettings::clearPlugins()
+{
+ m_Plugins.clear();
+ m_PluginSettings.clear();
+ m_PluginBlacklist.clear();
+
+ m_PluginBlacklist = readBlacklist();
+}
+
+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<QVariant>(
+ 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());
+ }
+}
+
+std::vector<MOBase::IPlugin*> PluginSettings::plugins() const
+{
+ return m_Plugins;
+}
+
+QVariant PluginSettings::setting(const QString &pluginName, const QString &key) const
{
auto iterPlugin = m_PluginSettings.find(pluginName);
if (iterPlugin == m_PluginSettings.end()) {
return QVariant();
}
+
auto iterSetting = iterPlugin->find(key);
if (iterSetting == iterPlugin->end()) {
return QVariant();
@@ -559,677 +1279,763 @@ QVariant Settings::pluginSetting(const QString &pluginName, const QString &key)
return *iterSetting;
}
-void Settings::setPluginSetting(const QString &pluginName, const QString &key, const QVariant &value)
+void PluginSettings::setSetting(const QString &pluginName, const QString &key, const QVariant &value)
{
auto iterPlugin = m_PluginSettings.find(pluginName);
+
if (iterPlugin == m_PluginSettings.end()) {
- throw MyException(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);
+}
+
+QVariantMap PluginSettings::settings(const QString &pluginName) const
+{
+ return m_PluginSettings[pluginName];
+}
+
+void PluginSettings::setSettings(const QString &pluginName, const QVariantMap& map)
+{
+ m_PluginSettings[pluginName] = map;
+}
+
+QVariantMap PluginSettings::descriptions(const QString &pluginName) const
+{
+ return m_PluginDescriptions[pluginName];
+}
+
+void PluginSettings::setDescriptions(const QString &pluginName, const QVariantMap& map)
+{
+ m_PluginDescriptions[pluginName] = map;
}
-QVariant Settings::pluginPersistent(const QString &pluginName, const QString &key, const QVariant &def) const
+QVariant PluginSettings::persistent(const QString &pluginName, const QString &key, const QVariant &def) const
{
if (!m_PluginSettings.contains(pluginName)) {
return def;
}
- return m_Settings.value("PluginPersistance/" + pluginName + "/" + key, def);
+
+ return get<QVariant>(m_Settings, "PluginPersistance", pluginName + "/" + key, def);
}
-void Settings::setPluginPersistent(const QString &pluginName, const QString &key, const QVariant &value, bool sync)
+void PluginSettings::setPersistent(
+ const QString &pluginName, const QString &key, const QVariant &value, bool sync)
{
if (!m_PluginSettings.contains(pluginName)) {
- throw MyException(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();
}
}
-QString Settings::language()
+void PluginSettings::addBlacklist(const QString &fileName)
{
- QString result = m_Settings.value("Settings/language", "").toString();
- 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;
+ m_PluginBlacklist.insert(fileName);
+ writeBlacklist();
}
-void Settings::updateServers(const QList<ServerInfo> &servers)
+bool PluginSettings::blacklisted(const QString &fileName) const
{
- m_Settings.beginGroup("Servers");
- QStringList oldServerKeys = m_Settings.childKeys();
+ return m_PluginBlacklist.contains(fileName);
+}
- for (const ServerInfo &server : servers) {
- if (!oldServerKeys.contains(server.name)) {
- // not yet known server
- QVariantMap newVal;
- newVal["premium"] = server.premium;
- newVal["preferred"] = server.preferred ? 1 : 0;
- newVal["lastSeen"] = server.lastSeen;
- newVal["downloadCount"] = 0;
- newVal["downloadSpeed"] = 0.0;
+void PluginSettings::setBlacklist(const QStringList& pluginNames)
+{
+ m_PluginBlacklist.clear();
- m_Settings.setValue(server.name, newVal);
- } else {
- QVariantMap data = m_Settings.value(server.name).toMap();
- data["lastSeen"] = server.lastSeen;
- data["premium"] = server.premium;
+ for (const auto& name : pluginNames) {
+ m_PluginBlacklist.insert(name);
+ }
+}
+
+const QSet<QString>& PluginSettings::blacklist() const
+{
+ return m_PluginBlacklist;
+}
- m_Settings.setValue(server.name, data);
+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 = iterPlugins.key() + "/" + iterSettings.key();
+ set(m_Settings, "Plugins", key, iterSettings.value());
}
}
- // clean up unavailable servers
- QDate now = QDate::currentDate();
- for (const QString &key : m_Settings.childKeys()) {
- QVariantMap val = m_Settings.value(key).toMap();
- QDate lastSeen = val["lastSeen"].toDate();
- if (lastSeen.daysTo(now) > 30) {
- qDebug("removing server %s since it hasn't been available for downloads in over a month", qUtf8Printable(key));
- m_Settings.remove(key);
- }
+ writeBlacklist();
+}
+
+void PluginSettings::writeBlacklist()
+{
+ const auto current = readBlacklist();
+
+ if (current.size() > m_PluginBlacklist.size()) {
+ // Qt can't remove array elements, the section must be cleared
+ removeSection(m_Settings, "pluginBlacklist");
}
- m_Settings.endGroup();
+ ScopedWriteArray swa(m_Settings, "pluginBlacklist", m_PluginBlacklist.size());
- m_Settings.sync();
+ for (const QString &plugin : m_PluginBlacklist) {
+ swa.next();
+ swa.set("name", plugin);
+ }
}
-void Settings::addBlacklistPlugin(const QString &fileName)
+QSet<QString> PluginSettings::readBlacklist() const
{
- m_PluginBlacklist.insert(fileName);
- writePluginBlacklist();
+ QSet<QString> set;
+
+ ScopedReadArray sra(m_Settings, "pluginBlacklist");
+ sra.for_each([&]{
+ set.insert(sra.get<QString>("name"));
+ });
+
+ return set;
}
-void Settings::writePluginBlacklist()
-{
- m_Settings.remove("pluginBlacklist");
- m_Settings.beginWriteArray("pluginBlacklist");
- int idx = 0;
- for (const QString &plugin : m_PluginBlacklist) {
- m_Settings.setArrayIndex(idx++);
- m_Settings.setValue("name", plugin);
- }
- m_Settings.endArray();
+PathSettings::PathSettings(QSettings& settings)
+ : m_Settings(settings)
+{
}
-void Settings::addLanguages(QComboBox *languageBox)
+std::map<QString, QString> PathSettings::recent() const
{
- std::vector<std::pair<QString, QString>> languages;
+ std::map<QString, QString> map;
- QDirIterator langIter(QCoreApplication::applicationDirPath() + "/translations", QDir::Files);
- QString pattern = ToQString(AppConfig::translationPrefix()) + "_([a-z]{2,3}(_[A-Z]{2,2})?).qm";
- QRegExp exp(pattern);
- while (langIter.hasNext()) {
- langIter.next();
- QString file = langIter.fileName();
- if (exp.exactMatch(file)) {
- QString languageCode = exp.cap(1);
- QLocale locale(languageCode);
- QString languageString = QString("%1 (%2)").arg(locale.nativeLanguageName()).arg(locale.nativeCountryName()); //QLocale::languageToString(locale.language());
- if (locale.language() == QLocale::Chinese) {
- if (languageCode == "zh_TW") {
- languageString = "Chinese (traditional)";
- } else {
- languageString = "Chinese (simplified)";
- }
- }
- languages.push_back(std::make_pair(QString("%1").arg(languageString), exp.cap(1)));
- //languageBox->addItem(QString("%1").arg(languageString), exp.cap(1));
+ 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)
+{
+ const auto current = recent();
+
+ if (current.size() > map.size()) {
+ // Qt can't remove array elements, the section must be cleared
+ removeSection(m_Settings, "recentDirectories");
}
- if (!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) {
- languageBox->addItem(lang.first, lang.second);
+
+ ScopedWriteArray swa(m_Settings, "recentDirectories", map.size());
+
+ for (auto&& p : map) {
+ swa.next();
+
+ swa.set("name", p.first);
+ swa.set("directory", p.second);
}
}
-void Settings::addStyles(QComboBox *styleBox)
+QString PathSettings::getConfigurablePath(const QString &key,
+ const QString &def,
+ bool resolve) const
{
- styleBox->addItem("None", "");
- styleBox->addItem("Fusion", "Fusion");
+ QString result = QDir::fromNativeSeparators(
+ get<QString>(m_Settings, "Settings", key, QString("%BASE_DIR%/") + def));
- QDirIterator langIter(QCoreApplication::applicationDirPath() + "/" + ToQString(AppConfig::stylesheetsPath()), QStringList("*.qss"), QDir::Files);
- while (langIter.hasNext()) {
- langIter.next();
- QString style = langIter.fileName();
- styleBox->addItem(style, style);
+ 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);
}
}
-void Settings::resetDialogs()
+QString PathSettings::base() const
+{
+ return QDir::fromNativeSeparators(get<QString>(m_Settings,
+ "Settings", "base_directory", qApp->property("dataPath").toString()));
+}
+
+QString PathSettings::downloads(bool resolve) const
{
- QuestionBoxMemory::resetDialogs();
+ return getConfigurablePath(
+ "download_directory",
+ ToQString(AppConfig::downloadPath()),
+ resolve);
}
-void Settings::query(PluginContainer *pluginContainer, QWidget *parent)
+QString PathSettings::cache(bool resolve) const
{
- SettingsDialog dialog(pluginContainer, this, parent);
- connect(&dialog, SIGNAL(resetDialogs()), this, SLOT(resetDialogs()));
+ return getConfigurablePath(
+ "cache_directory",
+ ToQString(AppConfig::cachePath()),
+ resolve);
+}
- std::vector<std::unique_ptr<SettingsTab>> tabs;
+QString PathSettings::mods(bool resolve) const
+{
+ return getConfigurablePath(
+ "mod_directory",
+ ToQString(AppConfig::modsPath()),
+ resolve);
+}
- tabs.push_back(std::unique_ptr<SettingsTab>(new GeneralTab(this, dialog)));
- tabs.push_back(std::unique_ptr<SettingsTab>(new PathsTab(this, dialog)));
- tabs.push_back(std::unique_ptr<SettingsTab>(new DiagnosticsTab(this, dialog)));
- tabs.push_back(std::unique_ptr<SettingsTab>(new NexusTab(this, dialog)));
- tabs.push_back(std::unique_ptr<SettingsTab>(new SteamTab(this, dialog)));
- tabs.push_back(std::unique_ptr<SettingsTab>(new PluginsTab(this, dialog)));
- tabs.push_back(std::unique_ptr<SettingsTab>(new WorkaroundsTab(this, dialog)));
+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);
+}
- QString key = QString("geometry/%1").arg(dialog.objectName());
- if (m_Settings.contains(key)) {
- dialog.restoreGeometry(m_Settings.value(key).toByteArray());
+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);
+}
- if (dialog.exec() == QDialog::Accepted) {
- // remember settings before change
- QMap<QString, QString> before;
- m_Settings.beginGroup("Settings");
- for (auto k : m_Settings.allKeys())
- before[k] = m_Settings.value(k).toString();
- m_Settings.endGroup();
+void NetworkSettings::setUseProxy(bool b)
+{
+ set(m_Settings, "Settings", "use_proxy", b);
+}
- // transfer modified settings to configuration file
- for (std::unique_ptr<SettingsTab> const &tab: tabs) {
- tab->update();
+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
+{
+ ServerList list;
+
+ {
+ ScopedReadArray sra(m_Settings, "Servers");
+
+ sra.for_each([&] {
+ ServerInfo::SpeedList lastDownloads;
- // print "changed" settings
- m_Settings.beginGroup("Settings");
- bool first_update = true;
- for (auto k : m_Settings.allKeys())
- if (m_Settings.value(k).toString() != before[k] && !k.contains("username") && !k.contains("password"))
- {
- if (first_update) {
- qDebug("Changed settings:");
- first_update = false;
+ 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);
}
- qDebug(" %s=%s", k.toUtf8().data(), m_Settings.value(k).toString().toUtf8().data());
}
- m_Settings.endGroup();
+
+ 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));
+ });
}
- m_Settings.setValue(key, dialog.saveGeometry());
- // These changes happen regardless of accepted or rejected
- bool restartNeeded = false;
- if (dialog.getApiKeyChanged()) {
- restartNeeded = true;
+ return list;
+}
+
+void NetworkSettings::updateServers(ServerList newServers)
+{
+ // clean up unavailable servers
+ newServers.cleanup();
+
+ const auto current = servers();
+
+ if (current.size() > newServers.size()) {
+ // Qt can't remove array elements, the section must be cleared
+ removeSection(m_Settings, "Servers");
}
- if (dialog.getResetGeometries()) {
- restartNeeded = true;
- m_Settings.setValue("reset_geometry", true);
+
+
+ ScopedWriteArray swa(m_Settings, "Servers", newServers.size());
+
+ for (const auto& server : newServers) {
+ 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());
}
- if (restartNeeded) {
- if (QMessageBox::question(nullptr,
- 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);
+}
+
+void NetworkSettings::updateFromOldMap()
+{
+ // 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
+
+ // sanity check that this is really 2.2.1
+ {
+ const QStringList keys = ScopedGroup(m_Settings, "Servers").keys();
+
+ for (auto&& k : keys) {
+ if (k == "size") {
+ // this looks like an array, so the upgrade was probably already done
+ return;
+ }
}
}
+ const auto servers = serversFromOldMap();
+ removeSection(m_Settings, "Servers");
+ updateServers(servers);
}
-Settings::SettingsTab::SettingsTab(Settings *m_parent, SettingsDialog &m_dialog)
- : m_parent(m_parent)
- , m_Settings(m_parent->m_Settings)
- , m_dialog(m_dialog)
+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));
+ });
-Settings::SettingsTab::~SettingsTab()
-{}
+ return list;
+}
-Settings::GeneralTab::GeneralTab(Settings *m_parent, SettingsDialog &m_dialog)
- : Settings::SettingsTab(m_parent, m_dialog)
- , m_languageBox(m_dialog.findChild<QComboBox *>("languageBox"))
- , m_styleBox(m_dialog.findChild<QComboBox *>("styleBox"))
- , m_compactBox(m_dialog.findChild<QCheckBox *>("compactBox"))
- , m_showMetaBox(m_dialog.findChild<QCheckBox *>("showMetaBox"))
- , m_usePrereleaseBox(m_dialog.findChild<QCheckBox *>("usePrereleaseBox"))
- , m_overwritingBtn(m_dialog.findChild<QPushButton *>("overwritingBtn"))
- , m_overwrittenBtn(m_dialog.findChild<QPushButton *>("overwrittenBtn"))
- , m_overwritingArchiveBtn(m_dialog.findChild<QPushButton *>("overwritingArchiveBtn"))
- , m_overwrittenArchiveBtn(m_dialog.findChild<QPushButton *>("overwrittenArchiveBtn"))
- , m_containsBtn(m_dialog.findChild<QPushButton *>("containsBtn"))
- , m_containedBtn(m_dialog.findChild<QPushButton *>("containedBtn"))
- , m_colorSeparatorsBox(m_dialog.findChild<QCheckBox *>("colorSeparatorsBox"))
+void NetworkSettings::dump() const
{
- // FIXME I think 'addLanguages' lives in here not in parent
- m_parent->addLanguages(m_languageBox);
- {
- QString languageCode = m_parent->language();
- int currentID = m_languageBox->findData(languageCode);
- // I made a mess. :( Most languages are stored with only the iso country
- // code (2 characters like "de") but chinese
- // with the exact language variant (zh_TW) so I have to search for both
- // variants
- if (currentID == -1) {
- currentID = m_languageBox->findData(languageCode.mid(0, 2));
- }
- if (currentID != -1) {
- m_languageBox->setCurrentIndex(currentID);
- }
- }
+ log::debug("servers:");
- // FIXME I think addStyles lives in here not in parent
- m_parent->addStyles(m_styleBox);
- {
- int currentID = m_styleBox->findData(
- m_Settings.value("Settings/style", "").toString());
- if (currentID != -1) {
- m_styleBox->setCurrentIndex(currentID);
+ 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());
}
- /* 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
- m_dialog.setButtonColor(m_overwritingBtn, m_parent->modlistOverwritingLooseColor());
- m_dialog.setButtonColor(m_overwrittenBtn, m_parent->modlistOverwrittenLooseColor());
- m_dialog.setButtonColor(m_overwritingArchiveBtn, m_parent->modlistOverwritingArchiveColor());
- m_dialog.setButtonColor(m_overwrittenArchiveBtn, m_parent->modlistOverwrittenArchiveColor());
- m_dialog.setButtonColor(m_containsBtn, m_parent->modlistContainsPluginColor());
- m_dialog.setButtonColor(m_containedBtn, m_parent->pluginListContainedColor());
- m_dialog.setOverwritingColor(m_parent->modlistOverwritingLooseColor());
- m_dialog.setOverwrittenColor(m_parent->modlistOverwrittenLooseColor());
- m_dialog.setOverwritingArchiveColor(m_parent->modlistOverwritingArchiveColor());
- m_dialog.setOverwrittenArchiveColor(m_parent->modlistOverwrittenArchiveColor());
- m_dialog.setContainsColor(m_parent->modlistContainsPluginColor());
- m_dialog.setContainedColor(m_parent->pluginListContainedColor());
+NexusSettings::NexusSettings(Settings& parent, QSettings& settings)
+ : m_Parent(parent), m_Settings(settings)
+{
+}
- m_compactBox->setChecked(m_parent->compactDownloads());
- m_showMetaBox->setChecked(m_parent->metaDownloads());
- m_usePrereleaseBox->setChecked(m_parent->usePrereleases());
- m_colorSeparatorsBox->setChecked(m_parent->colorSeparatorScrollbar());
+bool NexusSettings::apiKey(QString& apiKey) const
+{
+ QString tempKey = getWindowsCredential("APIKEY");
+ if (tempKey.isEmpty())
+ return false;
+
+ apiKey = tempKey;
+ return true;
}
-void Settings::GeneralTab::update()
+bool NexusSettings::setApiKey(const QString& apiKey)
{
- QString oldLanguage = m_parent->language();
- QString newLanguage = m_languageBox->itemData(m_languageBox->currentIndex()).toString();
- if (newLanguage != oldLanguage) {
- m_Settings.setValue("Settings/language", newLanguage);
- emit m_parent->languageChanged(newLanguage);
+ if (!setWindowsCredential("APIKEY", apiKey)) {
+ const auto e = GetLastError();
+ log::error("Storing API key failed: {}", formatSystemMessage(e));
+ return false;
}
- QString oldStyle = m_Settings.value("Settings/style", "").toString();
- QString newStyle = m_styleBox->itemData(m_styleBox->currentIndex()).toString();
- if (oldStyle != newStyle) {
- m_Settings.setValue("Settings/style", newStyle);
- emit m_parent->styleChanged(newStyle);
- }
+ 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);
+}
- m_Settings.setValue("Settings/overwritingLooseFilesColor", m_dialog.getOverwritingColor());
- m_Settings.setValue("Settings/overwrittenLooseFilesColor", m_dialog.getOverwrittenColor());
- m_Settings.setValue("Settings/overwritingArchiveFilesColor", m_dialog.getOverwritingArchiveColor());
- m_Settings.setValue("Settings/overwrittenArchiveFilesColor", m_dialog.getOverwrittenArchiveColor());
- m_Settings.setValue("Settings/containsPluginColor", m_dialog.getContainsColor());
- m_Settings.setValue("Settings/containedColor", m_dialog.getContainedColor());
- m_Settings.setValue("Settings/compact_downloads", m_compactBox->isChecked());
- m_Settings.setValue("Settings/meta_downloads", m_showMetaBox->isChecked());
- m_Settings.setValue("Settings/use_prereleases", m_usePrereleaseBox->isChecked());
- m_Settings.setValue("Settings/colorSeparatorScrollbars", m_colorSeparatorsBox->isChecked());
+void NexusSettings::setEndorsementIntegration(bool b) const
+{
+ set(m_Settings, "Settings", "endorsement_integration", b);
}
-Settings::PathsTab::PathsTab(Settings *parent, SettingsDialog &dialog)
- : SettingsTab(parent, dialog)
- , m_baseDirEdit(m_dialog.findChild<QLineEdit *>("baseDirEdit"))
- , m_downloadDirEdit(m_dialog.findChild<QLineEdit *>("downloadDirEdit"))
- , m_modDirEdit(m_dialog.findChild<QLineEdit *>("modDirEdit"))
- , m_cacheDirEdit(m_dialog.findChild<QLineEdit *>("cacheDirEdit"))
- , m_profilesDirEdit(m_dialog.findChild<QLineEdit *>("profilesDirEdit"))
- , m_overwriteDirEdit(m_dialog.findChild<QLineEdit *>("overwriteDirEdit"))
- , m_managedGameDirEdit(m_dialog.findChild<QLineEdit *>("managedGameDirEdit"))
+EndorsementState NexusSettings::endorsementState() const
{
- m_baseDirEdit->setText(m_parent->getBaseDirectory());
- m_managedGameDirEdit->setText(m_parent->m_GamePlugin->gameDirectory().absoluteFilePath(m_parent->m_GamePlugin->binaryName()));
- QString basePath = parent->getBaseDirectory();
- QDir baseDir(basePath);
- for (const auto &dir : {
- std::make_pair(m_downloadDirEdit, m_parent->getDownloadDirectory(false)),
- std::make_pair(m_modDirEdit, m_parent->getModDirectory(false)),
- std::make_pair(m_cacheDirEdit, m_parent->getCacheDirectory(false)),
- std::make_pair(m_profilesDirEdit, m_parent->getProfileDirectory(false)),
- std::make_pair(m_overwriteDirEdit, m_parent->getOverwriteDirectory(false))
- }) {
- QString storePath = baseDir.relativeFilePath(dir.second);
- storePath = dir.second;
- dir.first->setText(storePath);
+ 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 Settings::PathsTab::update()
+void NexusSettings::registerAsNXMHandler(bool force)
{
- typedef std::tuple<QString, QString, std::wstring> Directory;
+ 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 + "\"";
- QString basePath = m_parent->getBaseDirectory();
+ const auto r = shell::Execute(nxmPath, parameters);
- for (const Directory &dir :{
- Directory{m_downloadDirEdit->text(), "download_directory", AppConfig::downloadPath()},
- Directory{m_cacheDirEdit->text(), "cache_directory", AppConfig::cachePath()},
- Directory{m_modDirEdit->text(), "mod_directory", AppConfig::modsPath()},
- Directory{m_overwriteDirEdit->text(), "overwrite_directory", AppConfig::overwritePath()},
- Directory{m_profilesDirEdit->text(), "profiles_directory", AppConfig::profilesPath()}
- }) {
- QString path, settingsKey;
- std::wstring defaultName;
- std::tie(path, settingsKey, defaultName) = dir;
+ if (!r.success()) {
+ QMessageBox::critical(
+ nullptr, QObject::tr("Failed"),
+ QObject::tr("Failed to start the helper application: %1").arg(r.toString()));
+ }
+}
- settingsKey = QString("Settings/%1").arg(settingsKey);
- QString realPath = path;
- realPath.replace("%BASE_DIR%", m_baseDirEdit->text());
+SteamSettings::SteamSettings(Settings& parent, QSettings& settings)
+ : m_Parent(parent), m_Settings(settings)
+{
+}
- if (!QDir(realPath).exists()) {
- if (!QDir().mkpath(realPath)) {
- QMessageBox::warning(qApp->activeWindow(), tr("Error"),
- tr("Failed to create \"%1\", you may not have the "
- "necessary permission. path remains unchanged.")
- .arg(realPath));
- }
- }
+QString SteamSettings::appID() const
+{
+ return get<QString>(
+ m_Settings, "Settings", "app_id", m_Parent.game().plugin()->steamAPPId());
+}
- if (QFileInfo(realPath)
- != QFileInfo(basePath + "/" + QString::fromStdWString(defaultName))) {
- m_Settings.setValue(settingsKey, path);
- } else {
- m_Settings.remove(settingsKey);
- }
+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");
- if (QFileInfo(m_baseDirEdit->text()) !=
- QFileInfo(qApp->property("dataPath").toString())) {
- m_Settings.setValue("Settings/base_directory", m_baseDirEdit->text());
+ return !username.isEmpty() && !password.isEmpty();
+}
+
+void SteamSettings::setLogin(QString username, QString password)
+{
+ if (username == "") {
+ remove(m_Settings, "Settings", "steam_username");
+ password = "";
} else {
- m_Settings.remove("Settings/base_directory");
+ set(m_Settings, "Settings", "steam_username", username);
}
- QFileInfo oldGameExe(m_parent->m_GamePlugin->gameDirectory().absoluteFilePath(m_parent->m_GamePlugin->binaryName()));
- QFileInfo newGameExe(m_managedGameDirEdit->text());
- if (oldGameExe != newGameExe) {
- m_Settings.setValue("gamePath", newGameExe.absolutePath());
+ if (!setWindowsCredential("steam_password", password)) {
+ const auto e = GetLastError();
+ log::error("Storing or deleting password failed: {}", formatSystemMessage(e));
}
}
-Settings::DiagnosticsTab::DiagnosticsTab(Settings *m_parent, SettingsDialog &m_dialog)
- : Settings::SettingsTab(m_parent, m_dialog)
- , m_logLevelBox(m_dialog.findChild<QComboBox *>("logLevelBox"))
- , m_dumpsTypeBox(m_dialog.findChild<QComboBox *>("dumpsTypeBox"))
- , m_dumpsMaxEdit(m_dialog.findChild<QSpinBox *>("dumpsMaxEdit"))
- , m_diagnosticsExplainedLabel(m_dialog.findChild<QLabel *>("diagnosticsExplainedLabel"))
+
+InterfaceSettings::InterfaceSettings(QSettings& settings)
+ : m_Settings(settings)
{
- m_logLevelBox->setCurrentIndex(m_parent->logLevel());
- m_dumpsTypeBox->setCurrentIndex(m_parent->crashDumpsType());
- m_dumpsMaxEdit->setValue(m_parent->crashDumpsMax());
- QString logsPath = qApp->property("dataPath").toString()
- + "/" + QString::fromStdWString(AppConfig::logPath());
- m_diagnosticsExplainedLabel->setText(
- m_diagnosticsExplainedLabel->text()
- .replace("LOGS_FULL_PATH", logsPath)
- .replace("LOGS_DIR", QString::fromStdWString(AppConfig::logPath()))
- .replace("DUMPS_FULL_PATH", QString::fromStdWString(OrganizerCore::crashDumpsPath()))
- .replace("DUMPS_DIR", QString::fromStdWString(AppConfig::dumpsDir()))
- );
}
-void Settings::DiagnosticsTab::update()
+bool InterfaceSettings::lockGUI() const
{
- m_Settings.setValue("Settings/log_level", m_logLevelBox->currentIndex());
- m_Settings.setValue("Settings/crash_dumps_type", m_dumpsTypeBox->currentIndex());
- m_Settings.setValue("Settings/crash_dumps_max", m_dumpsMaxEdit->value());
+ return get<bool>(m_Settings, "Settings", "lock_gui", true);
}
-Settings::NexusTab::NexusTab(Settings *parent, SettingsDialog &dialog)
- : Settings::SettingsTab(parent, dialog)
- , m_offlineBox(dialog.findChild<QCheckBox *>("offlineBox"))
- , m_proxyBox(dialog.findChild<QCheckBox *>("proxyBox"))
- , m_knownServersList(dialog.findChild<QListWidget *>("knownServersList"))
- , m_preferredServersList(
- dialog.findChild<QListWidget *>("preferredServersList"))
- , m_endorsementBox(dialog.findChild<QCheckBox *>("endorsementBox"))
- , m_hideAPICounterBox(dialog.findChild<QCheckBox *>("hideAPICounterBox"))
+void InterfaceSettings::setLockGUI(bool b)
{
- m_offlineBox->setChecked(parent->offlineMode());
- m_proxyBox->setChecked(parent->useProxy());
- m_endorsementBox->setChecked(parent->endorsementIntegration());
- m_hideAPICounterBox->setChecked(parent->hideAPICounter());
+ set(m_Settings, "Settings", "lock_gui", b);
+}
- // 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<int>(val["downloadSpeed"].toDouble() / val["downloadCount"].toInt());
- descriptor += QString(" (%1 kbps)").arg(bps / 1024);
- }
+std::optional<QString> InterfaceSettings::styleName() const
+{
+ return getOptional<QString>(m_Settings, "Settings", "style");
+}
- QListWidgetItem *newItem = new QListWidgetItemEx<int>(descriptor, Qt::UserRole + 1);
+void InterfaceSettings::setStyleName(const QString& name)
+{
+ set(m_Settings, "Settings", "style", name);
+}
- newItem->setData(Qt::UserRole, key);
- newItem->setData(Qt::UserRole + 1, val["preferred"].toInt());
- if (val["preferred"].toInt() > 0) {
- m_preferredServersList->addItem(newItem);
- } else {
- m_knownServersList->addItem(newItem);
- }
- m_preferredServersList->sortItems(Qt::DescendingOrder);
- }
- m_Settings.endGroup();
+bool InterfaceSettings::compactDownloads() const
+{
+ return get<bool>(m_Settings, "Settings", "compact_downloads", false);
}
-void Settings::NexusTab::update()
+void InterfaceSettings::setCompactDownloads(bool b)
{
- /*
- 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", m_offlineBox->isChecked());
- m_Settings.setValue("Settings/use_proxy", m_proxyBox->isChecked());
- m_Settings.setValue("Settings/endorsement_integration", m_endorsementBox->isChecked());
- m_Settings.setValue("Settings/hide_api_counter", m_hideAPICounterBox->isChecked());
+ set(m_Settings, "Settings", "compact_downloads", b);
+}
- // store server preference
- m_Settings.beginGroup("Servers");
- for (int i = 0; i < m_knownServersList->count(); ++i) {
- QString key = m_knownServersList->item(i)->data(Qt::UserRole).toString();
- QVariantMap val = m_Settings.value(key).toMap();
- val["preferred"] = 0;
- m_Settings.setValue(key, val);
- }
- int count = m_preferredServersList->count();
- for (int i = 0; i < count; ++i) {
- QString key = m_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();
+bool InterfaceSettings::metaDownloads() const
+{
+ return get<bool>(m_Settings, "Settings", "meta_downloads", false);
}
-Settings::SteamTab::SteamTab(Settings *m_parent, SettingsDialog &m_dialog)
- : Settings::SettingsTab(m_parent, m_dialog)
- , m_steamUserEdit(m_dialog.findChild<QLineEdit *>("steamUserEdit"))
- , m_steamPassEdit(m_dialog.findChild<QLineEdit *>("steamPassEdit"))
+void InterfaceSettings::setMetaDownloads(bool b)
{
- if (m_Settings.contains("Settings/steam_username")) {
- m_steamUserEdit->setText(m_Settings.value("Settings/steam_username", "").toString());
- QString password = deObfuscate("steam_password");
- if (!password.isEmpty()) {
- m_steamPassEdit->setText(password);
- }
- }
+ set(m_Settings, "Settings", "meta_downloads", b);
}
-void Settings::SteamTab::update()
+bool InterfaceSettings::hideAPICounter() const
{
- //FIXME this should be inlined here?
- m_parent->setSteamLogin(m_steamUserEdit->text(), m_steamPassEdit->text());
+ return get<bool>(m_Settings, "Settings", "hide_api_counter", false);
}
-Settings::PluginsTab::PluginsTab(Settings *m_parent, SettingsDialog &m_dialog)
- : Settings::SettingsTab(m_parent, m_dialog)
- , m_pluginsList(m_dialog.findChild<QListWidget *>("pluginsList"))
- , m_pluginBlacklistList(m_dialog.findChild<QListWidget *>("pluginBlacklist"))
+void InterfaceSettings::setHideAPICounter(bool b)
{
- // display plugin settings
- QSet<QString> handledNames;
- for (IPlugin *plugin : m_parent->m_Plugins) {
- if (handledNames.contains(plugin->name()))
- continue;
- QListWidgetItem *listItem = new QListWidgetItem(plugin->name(), m_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()]);
- m_pluginsList->addItem(listItem);
- handledNames.insert(plugin->name());
- }
+ set(m_Settings, "Settings", "hide_api_counter", b);
+}
- // display plugin blacklist
- for (const QString &pluginName : m_parent->m_PluginBlacklist) {
- m_pluginBlacklistList->addItem(pluginName);
- }
+bool InterfaceSettings::displayForeign() const
+{
+ return get<bool>(m_Settings, "Settings", "display_foreign", true);
}
-void Settings::PluginsTab::update()
+void InterfaceSettings::setDisplayForeign(bool b)
{
- // transfer plugin settings to in-memory structure
- for (int i = 0; i < m_pluginsList->count(); ++i) {
- QListWidgetItem *item = m_pluginsList->item(i);
- m_parent->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 iterSettings = iterPlugins->begin(); iterSettings != iterPlugins->end(); ++iterSettings) {
- m_Settings.setValue("Plugins/" + iterPlugins.key() + "/" + iterSettings.key(), iterSettings.value());
+ 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();
}
}
- // store plugin blacklist
- m_parent->m_PluginBlacklist.clear();
- for (QListWidgetItem *item : m_pluginBlacklistList->findItems("*", Qt::MatchWildcard)) {
- m_parent->m_PluginBlacklist.insert(item->text());
- }
- m_parent->writePluginBlacklist();
+ return result;
}
-Settings::WorkaroundsTab::WorkaroundsTab(Settings *m_parent,
- SettingsDialog &m_dialog)
- : Settings::SettingsTab(m_parent, m_dialog)
- , m_appIDEdit(m_dialog.findChild<QLineEdit *>("appIDEdit"))
- , m_mechanismBox(m_dialog.findChild<QComboBox *>("mechanismBox"))
- , m_hideUncheckedBox(m_dialog.findChild<QCheckBox *>("hideUncheckedBox"))
- , m_forceEnableBox(m_dialog.findChild<QCheckBox *>("forceEnableBox"))
- , m_displayForeignBox(m_dialog.findChild<QCheckBox *>("displayForeignBox"))
- , m_lockGUIBox(m_dialog.findChild<QCheckBox *>("lockGUIBox"))
- , m_enableArchiveParsingBox(m_dialog.findChild<QCheckBox *>("enableArchiveParsingBox"))
- , m_resetGeometriesBtn(m_dialog.findChild<QPushButton *>("resetGeometryBtn"))
+void InterfaceSettings::setLanguage(const QString& name)
{
- m_appIDEdit->setText(m_parent->getSteamAppID());
+ set(m_Settings, "Settings", "language", name);
+}
- LoadMechanism::EMechanism mechanismID = m_parent->getLoadMechanism();
- int index = 0;
+bool InterfaceSettings::isTutorialCompleted(const QString& windowName) const
+{
+ return get<bool>(m_Settings, "CompletedWindowTutorials", windowName, false);
+}
- if (m_parent->m_LoadMechanism.isDirectLoadingSupported()) {
- m_mechanismBox->addItem(QObject::tr("Mod Organizer"), LoadMechanism::LOAD_MODORGANIZER);
- if (mechanismID == LoadMechanism::LOAD_MODORGANIZER) {
- index = m_mechanismBox->count() - 1;
- }
- }
+void InterfaceSettings::setTutorialCompleted(const QString& windowName, bool b)
+{
+ set(m_Settings, "CompletedWindowTutorials", windowName, b);
+}
- if (m_parent->m_LoadMechanism.isScriptExtenderSupported()) {
- m_mechanismBox->addItem(QObject::tr("Script Extender"), LoadMechanism::LOAD_SCRIPTEXTENDER);
- if (mechanismID == LoadMechanism::LOAD_SCRIPTEXTENDER) {
- index = m_mechanismBox->count() - 1;
- }
- }
+bool InterfaceSettings::showChangeGameConfirmation() const
+{
+ return get<bool>(m_Settings, "Settings", "show_change_game_confirmation", true);
+}
- if (m_parent->m_LoadMechanism.isProxyDLLSupported()) {
- m_mechanismBox->addItem(QObject::tr("Proxy DLL"), LoadMechanism::LOAD_PROXYDLL);
- if (mechanismID == LoadMechanism::LOAD_PROXYDLL) {
- index = m_mechanismBox->count() - 1;
- }
- }
+void InterfaceSettings::setShowChangeGameConfirmation(bool b)
+{
+ set(m_Settings, "Settings", "show_change_game_confirmation", b);
+}
- m_mechanismBox->setCurrentIndex(index);
+bool InterfaceSettings::doubleClicksOpenPreviews() const
+{
+ return get<bool>(m_Settings, "Settings", "double_click_previews", true);
+}
- m_hideUncheckedBox->setChecked(m_parent->hideUncheckedPlugins());
- m_forceEnableBox->setChecked(m_parent->forceEnableCoreFiles());
- m_displayForeignBox->setChecked(m_parent->displayForeign());
- m_lockGUIBox->setChecked(m_parent->lockGUI());
- m_enableArchiveParsingBox->setChecked(m_parent->archiveParsing());
+void InterfaceSettings::setDoubleClicksOpenPreviews(bool b)
+{
+ set(m_Settings, "Settings", "double_click_previews", b);
+}
- m_resetGeometriesBtn->setChecked(m_parent->directInterface().value("reset_geometry", false).toBool());
- m_dialog.setExecutableBlacklist(m_parent->executablesBlacklist());
+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 Settings::WorkaroundsTab::update()
+void DiagnosticsSettings::setLogLevel(log::Levels level)
{
- if (m_appIDEdit->text() != m_parent->m_GamePlugin->steamAPPId()) {
- m_Settings.setValue("Settings/app_id", m_appIDEdit->text());
- } else {
- m_Settings.remove("Settings/app_id");
- }
- m_Settings.setValue("Settings/load_mechanism", m_mechanismBox->itemData(m_mechanismBox->currentIndex()).toInt());
- m_Settings.setValue("Settings/hide_unchecked_plugins", m_hideUncheckedBox->isChecked());
- m_Settings.setValue("Settings/force_enable_core_files", m_forceEnableBox->isChecked());
- m_Settings.setValue("Settings/display_foreign", m_displayForeignBox->isChecked());
- m_Settings.setValue("Settings/lock_gui", m_lockGUIBox->isChecked());
- m_Settings.setValue("Settings/archive_parsing_experimental", m_enableArchiveParsingBox->isChecked());
+ set(m_Settings, "Settings", "log_level", level);
+}
+
+lootcli::LogLevels DiagnosticsSettings::lootLogLevel() const
+{
+ return get<lootcli::LogLevels>(
+ m_Settings, "Settings", "loot_log_level", lootcli::LogLevels::Info);
+}
+
+void DiagnosticsSettings::setLootLogLevel(lootcli::LogLevels level)
+{
+ set(m_Settings, "Settings", "loot_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);
+}
+
+std::chrono::seconds DiagnosticsSettings::spawnDelay() const
+{
+ return std::chrono::seconds(
+ get<int>(m_Settings, "Settings", "spawn_delay", 0));
+}
- m_Settings.setValue("Settings/executable_blacklist", m_dialog.getExecutableBlacklist());
+void DiagnosticsSettings::setSpawnDelay(std::chrono::seconds t)
+{
+ set(m_Settings, "Settings", "spawn_delay", t.count());
}