/* Copyright (C) 2012 Sebastian Herbord. All rights reserved. This file is part of Mod Organizer. Mod Organizer is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Mod Organizer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Mod Organizer. If not, see . */ #ifndef SETTINGS_H #define SETTINGS_H #include "envdump.h" #include "nexusoauthtokens.h" #include #include #include #include #include #ifdef interface #undef interface #endif namespace MOBase { class IPlugin; class IPluginGame; class ExpanderWidget; } // namespace MOBase class QSplitter; class ServerList; class Settings; // setting for the currently managed game // class GameSettings { public: GameSettings(QSettings& setting); // game plugin // const MOBase::IPluginGame* plugin(); void setPlugin(const MOBase::IPluginGame* gamePlugin); // whether files of the core game are forced-enabled so the user can't // accidentally disable them // bool forceEnableCoreFiles() const; void setForceEnableCoreFiles(bool b); // the directory where the managed game is stored // std::optional directory() const; void setDirectory(const QString& path); // the name of the managed game // std::optional name() const; void setName(const QString& name); // the edition of the managed game // std::optional edition() const; void setEdition(const QString& name); // the current profile name // std::optional selectedProfileName() const; void setSelectedProfileName(const QString& name); private: QSettings& m_Settings; const MOBase::IPluginGame* m_GamePlugin; }; // geometry settings for various widgets; this should contain any setting that // can get invalid through UI changes or when users change display settings // (resolution, monitors, etc.); see WidgetSettings for the counterpart // // all these settings are stored under [Geometry] and get wiped when the // "reset geometry settings" button is clicked in the settings // // saveGeometry(), restoreGeometry(), saveState() and restoreState() call the // same functions on the given widget // class GeometrySettings { public: GeometrySettings(QSettings& s); // asks the settings to get reset // // this gets called from the settings dialog and gets picked up in // resetIfNeeded(), called from runApplication() just before exiting // void requestReset(); void resetIfNeeded(); void saveGeometry(const QMainWindow* w); bool restoreGeometry(QMainWindow* w) const; void saveGeometry(const QDialog* d); bool restoreGeometry(QDialog* d) const; void saveState(const QMainWindow* window); bool restoreState(QMainWindow* window) const; void saveState(const QHeaderView* header); bool restoreState(QHeaderView* header) const; void saveState(const QSplitter* splitter); bool restoreState(QSplitter* splitter) const; void saveState(const MOBase::ExpanderWidget* expander); bool restoreState(MOBase::ExpanderWidget* expander) const; void saveVisibility(const QWidget* w); bool restoreVisibility(QWidget* w, std::optional def = {}) const; void saveToolbars(const QMainWindow* w); void restoreToolbars(QMainWindow* w) const; void saveDocks(const QMainWindow* w); void restoreDocks(QMainWindow* w) const; // this should be a generic "tab order" setting, but it only happens for the // mod info dialog right now // QStringList modInfoTabOrder() const; void setModInfoTabOrder(const QString& names); // whether dialogs should be centered on their parent // bool centerDialogs() const; void setCenterDialogs(bool b); // assumes the given widget is a top-level // void centerOnMainWindowMonitor(QWidget* w) const; // saves the monitor number of the given window // void saveMainWindowMonitor(const QMainWindow* w); private: QSettings& m_Settings; bool m_Reset; void saveWindowGeometry(const QWidget* w); bool restoreWindowGeometry(QWidget* w) const; void ensureWindowOnScreen(QWidget* w) const; static void centerOnMonitor(QWidget* w, int monitor); static void centerOnParent(QWidget* w, QWidget* parent = nullptr); }; // widget settings that should stay valid regardless of UI changes or when users // change display settings (resolution, monitors, etc.); see GeometrySettings // for the counterpart // class WidgetSettings { public: // globalInstance is forwarded from the Settings constructor; WidgetSettings // has the callbacks used by QuestionBoxMemory and those are global, so they // should only be set by the global instance // WidgetSettings(QSettings& s, bool globalInstance); // tree item check - this saves the list of expanded items based on the given role // void saveTreeCheckState(const QTreeView* tv, int role = Qt::CheckStateRole); void restoreTreeCheckState(QTreeView* tv, int role = Qt::CheckStateRole) const; // tree state - this saves the list of expanded items based on the given role // void saveTreeExpandState(const QTreeView* tv, int role = Qt::DisplayRole); void restoreTreeExpandState(QTreeView* tv, int role = Qt::DisplayRole) const; // selected index for a combobox // std::optional index(const QComboBox* cb) const; void saveIndex(const QComboBox* cb); void restoreIndex(QComboBox* cb, std::optional def = {}) const; // selected tab index for a tab widget // std::optional index(const QTabWidget* w) const; void saveIndex(const QTabWidget* w); void restoreIndex(QTabWidget* w, std::optional def = {}) const; // check state for a checkable button // std::optional checked(const QAbstractButton* w) const; void saveChecked(const QAbstractButton* w); void restoreChecked(QAbstractButton* w, std::optional def = {}) const; // returns the remembered button for a question dialog, or NoButton if the // user hasn't saved the choice // MOBase::QuestionBoxMemory::Button questionButton(const QString& windowName, const QString& filename) const; // sets the button to be remembered for the given window // void setQuestionWindowButton(const QString& windowName, MOBase::QuestionBoxMemory::Button button); // sets the button to be remembered for the given file // void setQuestionFileButton(const QString& windowName, const QString& filename, MOBase::QuestionBoxMemory::Button choice); // wipes all the remembered buttons // void resetQuestionButtons(); private: QSettings& m_Settings; }; // various color settings // class ColorSettings { public: ColorSettings(QSettings& s); QColor modlistOverwrittenLoose() const; void setModlistOverwrittenLoose(const QColor& c); QColor modlistOverwritingLoose() const; void setModlistOverwritingLoose(const QColor& c); QColor modlistOverwrittenArchive() const; void setModlistOverwrittenArchive(const QColor& c); QColor modlistOverwritingArchive() const; void setModlistOverwritingArchive(const QColor& c); QColor modlistContainsFile() const; void setModlistContainsFile(const QColor& c); QColor pluginListContained() const; void setPluginListContained(const QColor& c); QColor pluginListMaster() const; void setPluginListMaster(const QColor& c); std::optional previousSeparatorColor() const; void setPreviousSeparatorColor(const QColor& c) const; void removePreviousSeparatorColor(); // whether the scrollbar of the mod list should have colors for custom // separator colors // bool colorSeparatorScrollbar() const; void setColorSeparatorScrollbar(bool b); // returns a color with a good contrast for the given background // static QColor idealTextColor(const QColor& rBackgroundColor); private: QSettings& m_Settings; }; // settings about plugins // class PluginSettings : public QObject { Q_OBJECT public: PluginSettings(QSettings& settings); // forgets all the plugins // void clearPlugins(); // adds/removes the given plugin to the list and loads all of its settings // void registerPlugin(MOBase::IPlugin* plugin); void unregisterPlugin(MOBase::IPlugin* plugin); // returns all the registered plugins // std::vector plugins() const; // returns the plugin setting for the given key // QVariant setting(const QString& pluginName, const QString& key) const; // sets the plugin setting for the given key // void setSetting(const QString& pluginName, const QString& key, const QVariant& value); // returns all settings // QVariantMap settings(const QString& pluginName) const; // overwrites all settings // void setSettings(const QString& pluginName, const QVariantMap& map); // returns all descriptions // QVariantMap descriptions(const QString& pluginName) const; // overwrites all descriptions // void setDescriptions(const QString& pluginName, const QVariantMap& map); // ? QVariant persistent(const QString& pluginName, const QString& key, const QVariant& def) const; void setPersistent(const QString& pluginName, const QString& key, const QVariant& value, bool sync); // adds the given plugin to the blacklist // void addBlacklist(const QString& fileName); // returns whether the given plugin is blacklisted // bool blacklisted(const QString& fileName) const; // overwrites the whole blacklist // void setBlacklist(const QStringList& pluginNames); // returns the blacklist // const QSet& blacklist() const; // commits all the settings to the ini // void save(); Q_SIGNALS: /** * Emitted when a plugin setting changes. */ void pluginSettingChanged(QString const& pluginName, const QString& key, const QVariant& oldValue, const QVariant& newValue); private: QSettings& m_Settings; std::vector m_Plugins; QMap m_PluginSettings; QMap m_PluginDescriptions; QSet m_PluginBlacklist; // commits the blacklist to the ini // void writeBlacklist(); // reads the blacklist from the ini // QSet readBlacklist() const; }; // paths for the game and various components // // if the 'resolve' parameter is true, %BASE_DIR% is expanded; it's set to // false mostly in the settings dialog // class PathSettings { public: // %BASE_DIR% static const QString BaseDirVariable; PathSettings(QSettings& settings); QString base() const; void setBase(const QString& path); QString downloads(bool resolve = true) const; void setDownloads(const QString& path); QString mods(bool resolve = true) const; void setMods(const QString& path); QString cache(bool resolve = true) const; void setCache(const QString& path); QString profiles(bool resolve = true) const; void setProfiles(const QString& path); QString overwrite(bool resolve = true) const; void setOverwrite(const QString& path); // map of names to directories, used to remember the last directory used in // various file pickers // std::map recent() const; void setRecent(const std::map& map); // resolves %BASE_DIR% // static QString resolve(const QString& path, const QString& baseDir); // returns %BASE_DIR%/dirName // static QString makeDefaultPath(const QString dirName); private: QSettings& m_Settings; QString getConfigurablePath(const QString& key, const QString& def, bool resolve) const; void setConfigurablePath(const QString& key, const QString& path); }; class NetworkSettings { public: // globalInstance is forwarded from the Settings constructor; NetworkSettings // will set the global URL custom command // NetworkSettings(QSettings& settings, bool globalInstance); // whether the user has disabled online features // bool offlineMode() const; void setOfflineMode(bool b); // whether the user wants to use the system proxy // bool useProxy() const; void setUseProxy(bool b); // add a new download speed to the list for the given server; each server // remembers the last couple of download speeds and displays the average in // the network settings // void setDownloadSpeed(const QString& serverName, int bytesPerSecond); // known servers // ServerList servers() const; // sets the servers // void updateServers(ServerList servers); // for 2.2.1 and before, rewrites the old byte array map to the new format // void updateFromOldMap(); // whether to use a custom command to open links // bool useCustomBrowser() const; void setUseCustomBrowser(bool b); // custom command to open links // QString customBrowserCommand() const; void setCustomBrowserCommand(const QString& s); void dump() const; private: QSettings& m_Settings; // for pre 2.2.1 ini files // ServerList serversFromOldMap() const; // sets the custom command in uibase, called when the settings change // void updateCustomBrowser(); }; enum class EndorsementState { Accepted = 1, Refused, NoDecision }; EndorsementState endorsementStateFromString(const QString& s); QString toString(EndorsementState s); class NexusSettings { public: NexusSettings(Settings& parent, QSettings& settings); // returns whether endorsement integration is enabled // bool endorsementIntegration() const; void setEndorsementIntegration(bool b) const; // returns the endorsement state of MO itself // EndorsementState endorsementState() const; void setEndorsementState(EndorsementState s); // returns whether tracked integration is enabled // bool trackedIntegration() const; void setTrackedIntegration(bool b) const; // returns whether nexus category mappings are enabled // bool categoryMappings() const; void setCategoryMappings(bool b) const; // registers MO as the handler for nxm links // // if 'force' is true, the registration dialog will be shown even if the user // said earlier not to // void registerAsNXMHandler(bool force); std::vector validationTimeouts() const; // dumps nxmhandler stuff // void dump() const; private: Settings& m_Parent; QSettings& m_Settings; }; class SteamSettings { public: SteamSettings(Settings& parent, QSettings& settings); // the steam appid is assigned by the steam platform to each product sold // there. // // the appid may differ between different versions of a game so it may be // impossible for MO to automatically recognize it, though usually it does // QString appID() const; void setAppID(const QString& id); // the steam username is stored in the ini, but the password is in the // windows credentials store; both values are independent and either can be // empty // // if the username exists in the ini, it is assigned to `username`; if not // `username` is set to an empty string // // if the password exists in the credentials store, it is assigned to // `password`; if not, `password` is set to an empty string // // returns whether _both_ the username and password have a value // bool login(QString& username, QString& password) const; // sets the steam login; the username is saved in the ini file and the // password in the credentials store // // if a value is empty, it is removed from its backing store // void setLogin(QString username, QString password); private: Settings& m_Parent; QSettings& m_Settings; }; class InterfaceSettings { public: InterfaceSettings(QSettings& settings); // whether the GUI should be locked when running executables // bool lockGUI() const; void setLockGUI(bool b); // filename of the theme // std::optional styleName() const; void setStyleName(const QString& name); // whether to use collapsible separators when possible // bool collapsibleSeparators(Qt::SortOrder order) const; void setCollapsibleSeparators(bool ascending, bool descending); // whether to highlight mod conflicts and plugins on collapsed // separators // bool collapsibleSeparatorsHighlightTo() const; void setCollapsibleSeparatorsHighlightTo(bool b); // whether to highlight mod conflicts and plugins from separators // when selected but collapsed // bool collapsibleSeparatorsHighlightFrom() const; void setCollapsibleSeparatorsHighlightFrom(bool b); // whether to show icons on collapsed separators // bool collapsibleSeparatorsIcons(int column) const; void setCollapsibleSeparatorsIcons(int column, bool show); // whether each profile should have its own expansion state // bool collapsibleSeparatorsPerProfile() const; void setCollapsibleSeparatorsPerProfile(bool b); // whether to save/restore filter states between runs // bool saveFilters() const; void setSaveFilters(bool b); // whether to collapse groups (separators, categories, ...) after // a delay when hovering (similar to auto-expand) // bool autoCollapseOnHover() const; void setAutoCollapseOnHover(bool b); // whether to check for update after installing a mod // bool checkUpdateAfterInstallation() const; void setCheckUpdateAfterInstallation(bool b); // whether to show compact downloads // bool compactDownloads() const; void setCompactDownloads(bool b); // whether to show meta information for downloads // bool metaDownloads() const; void setMetaDownloads(bool b); // whether to hide downloads after installing them // bool hideDownloadsAfterInstallation() const; void setHideDownloadsAfterInstallation(bool b); // whether to show notifications when downloads complete or fail // bool showDownloadNotifications() const; void setShowDownloadNotifications(bool b); // whether the API counter should be hidden // bool hideAPICounter() const; void setHideAPICounter(bool b); // whether the user wants to see non-official plugins installed outside MO in // the mod list // bool displayForeign() const; void setDisplayForeign(bool b); // short code of the configured language (corresponding to the translation // files) // QString language(); void setLanguage(const QString& name); // whether the given tutorial has been completed // bool isTutorialCompleted(const QString& windowName) const; void setTutorialCompleted(const QString& windowName, bool b = true); // whether to show the confirmation when switching instances // bool showChangeGameConfirmation() const; void setShowChangeGameConfirmation(bool b); // whether to show the menubar when pressing the Alt key // bool showMenubarOnAlt() const; void setShowMenubarOnAlt(bool b); // whether double-clicks on files should try to open previews first // bool doubleClicksOpenPreviews() const; void setDoubleClicksOpenPreviews(bool b); // filter widget options // MOBase::FilterWidget::Options filterOptions() const; void setFilterOptions(const MOBase::FilterWidget::Options& o); private: QSettings& m_Settings; }; class DiagnosticsSettings { public: DiagnosticsSettings(QSettings& settings); // log level for both MO and usvfs // MOBase::log::Levels logLevel() const; void setLogLevel(MOBase::log::Levels level); // log level for loot lootcli::LogLevels lootLogLevel() const; void setLootLogLevel(lootcli::LogLevels level); // crash dump type for both MO and usvfs // env::CoreDumpTypes coreDumpType() const; void setCoreDumpType(env::CoreDumpTypes type); // maximum number of dump files keps, for both MO and usvfs // int maxCoreDumps() const; void setMaxCoreDumps(int n); std::chrono::seconds spawnDelay() const; void setSpawnDelay(std::chrono::seconds t); private: QSettings& m_Settings; }; // manages the settings for MO; the settings are accessed directly through a // QSettings and so are not cached here // class Settings : public QObject { Q_OBJECT; public: // there is one Settings global object for MO when an instance is loaded, but // other Settings objects are required in several places, such as in the // Instance class, the instance dialogs, etc. // // any Settings object created with globalInstance==false won't set the // singleton // // some sub-objects like WidgetSettings and NetworkSettings need to know // whether they were created from a globalInstance, see their constructor // // @param path path to an ini file // @param globalInsance whether this is the global instance; creates the // singleton and asserts if it already exists // Settings(const QString& path, bool globalInstance = false); ~Settings(); // throws if there is no global Settings instance // static Settings& instance(); // returns null if there is no global Settings instance // static Settings* maybeInstance(); // name of the ini file // QString filename() const; // version of MO stored in the ini; this may be different from the current // version if the user just updated // std::optional version() const; // updates the settings to bring them up to date // void processUpdates(const QVersionNumber& current, const QVersionNumber& last); // whether MO has been started for the first time // bool firstStart() const; void setFirstStart(bool b); // configured executables // std::vector> executables() const; void setExecutables(const std::vector>& v); // whether to backup existing mods on install // bool keepBackupOnInstall() const; void setKeepBackupOnInstall(bool b); // blacklisted executables do not get hooked by usvfs; this list is managed // by MO but given to usvfs when starting an executable // QString executablesBlacklist() const; bool isExecutableBlacklisted(const QString& s) const; void setExecutablesBlacklist(const QString& s); QStringList skipFileSuffixes() const; void setSkipFileSuffixes(const QStringList& s); QStringList skipDirectories() const; void setSkipDirectories(const QStringList& s); // ? looks obsolete, only used by dead code // unsigned int motdHash() const; void setMotdHash(unsigned int hash); // registers MO as the handler for download links // // if 'force' is true, the registration dialog will be shown even if the user // said earlier not to // void registerDownloadHandlers(bool force); // registers MO as the handler for modl links // // if 'force' is true, the registration dialog will be shown even if the user // said earlier not to // void registerAsMODLHandler(bool force, bool includeNxm = false); // whether archives should be parsed to show conflicts and contents // bool archiveParsing() const; void setArchiveParsing(bool b); // whether the user wants to check for updates // bool checkForUpdates() const; void setCheckForUpdates(bool b); // whether the user wants to upgrade to pre-releases // bool usePrereleases() const; void setUsePrereleases(bool b); // whether profiles should default to local INIs // bool profileLocalInis() const; void setProfileLocalInis(bool b); // whether profiles should default to local saves // bool profileLocalSaves() const; void setProfileLocalSaves(bool b); // whether profiles should default to automatic archive invalidation // bool profileArchiveInvalidation() const; void setProfileArchiveInvalidation(bool b); // whether to use spascreen or not // bool useSplash() const; void setUseSplash(bool b); // number of threads to use when refreshing // std::size_t refreshThreadCount() const; void setRefreshThreadCount(std::size_t n) const; GameSettings& game(); const GameSettings& game() const; GeometrySettings& geometry(); const GeometrySettings& geometry() const; WidgetSettings& widgets(); const WidgetSettings& widgets() const; ColorSettings& colors(); const ColorSettings& colors() const; PluginSettings& plugins(); const PluginSettings& plugins() const; PathSettings& paths(); const PathSettings& paths() const; NetworkSettings& network(); const NetworkSettings& network() const; NexusSettings& nexus(); const NexusSettings& nexus() const; SteamSettings& steam(); const SteamSettings& steam() const; InterfaceSettings& interface(); const InterfaceSettings& interface() const; DiagnosticsSettings& diagnostics(); const DiagnosticsSettings& diagnostics() const; // makes sure the ini file is written to disk // QSettings::Status sync() const; // last status of the ini file // QSettings::Status iniStatus() const; void dump() const; public slots: // this slot is connected to by various parts of MO // void managedGameChanged(MOBase::IPluginGame const* gamePlugin); signals: // these are fired from outside the settings, mostly by the settings dialog // void languageChanged(const QString& newLanguage); void styleChanged(const QString& newStyle); private: static Settings* s_Instance; mutable QSettings m_Settings; GameSettings m_Game; GeometrySettings m_Geometry; WidgetSettings m_Widgets; ColorSettings m_Colors; PluginSettings m_Plugins; PathSettings m_Paths; NetworkSettings m_Network; NexusSettings m_Nexus; SteamSettings m_Steam; InterfaceSettings m_Interface; DiagnosticsSettings m_Diagnostics; }; // manages global settings in the registry // class GlobalSettings { public: // migrates the old settings from the Tannin key to the new one static void updateRegistryKey(); static QString currentInstance(); static void setCurrentInstance(const QString& s); static bool hideCreateInstanceIntro(); static void setHideCreateInstanceIntro(bool b); static bool hideTutorialQuestion(); static void setHideTutorialQuestion(bool b); static bool hideCategoryReminder(); static void setHideCategoryReminder(bool b); static bool hideAssignCategoriesQuestion(); static void setHideAssignCategoriesQuestion(bool b); // if the key exists from the credentials store, puts it in `apiKey` and // returns true; otherwise, returns false and leaves `apiKey` untouched // static bool nexusApiKey(QString& apiKey); // sets the api key in the credentials store, removes it if empty; returns // false on errors // static bool setNexusApiKey(const QString& apiKey); // removes the api key from the credentials store; returns false on errors // static bool clearNexusApiKey(); // returns whether an API key is currently stored // static bool hasNexusApiKey(); // Retrieves the stored OAuth tokens. Returns false if the credential doesn't exist // or can't be parsed. // static bool nexusOAuthTokens(NexusOAuthTokens& tokens); // Persists the OAuth tokens inside the credentials store, replacing the previous // entry. Returns false on errors. // static bool setNexusOAuthTokens(const NexusOAuthTokens& tokens); // Removes the stored OAuth tokens; returns false on errors. // static bool clearNexusOAuthTokens(); // Returns whether OAuth tokens are currently stored. // static bool hasNexusOAuthTokens(); // resets anything that the user can disable static void resetDialogs(); private: static QSettings settings(); }; // helper class that calls restoreGeometry() in the constructor and // saveGeometry() in the destructor // template class GeometrySaver { public: GeometrySaver(Settings& s, W* w) : m_settings(s), m_widget(w) { m_settings.geometry().restoreGeometry(m_widget); } ~GeometrySaver() { m_settings.geometry().saveGeometry(m_widget); } private: Settings& m_settings; W* m_widget; }; #endif // SETTINGS_H