diff --git a/.gitignore b/.gitignore index 935c42609..39ca36c12 100644 --- a/.gitignore +++ b/.gitignore @@ -12,5 +12,6 @@ edit /msbuild.log /*std*.log /*build +.vscode /src/version.aps diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6a6deb0b3..3e57081a8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -296,6 +296,7 @@ mo2_add_filter(NAME src/register GROUPS ) mo2_add_filter(NAME src/settings GROUPS + extensionsettings settings settingsutilities ) diff --git a/src/extensionsettings.cpp b/src/extensionsettings.cpp new file mode 100644 index 000000000..7e1a9dc8a --- /dev/null +++ b/src/extensionsettings.cpp @@ -0,0 +1,147 @@ +#include "extensionsettings.h" + +#include "settingsutilities.h" + +using namespace MOBase; + +static const QString PLUGINS_GROUP = "Plugins"; +static const QString PLUGINS_PERSISTENT_GROUP = "PluginPersistance"; + +PluginSettings::PluginSettings(QSettings& settings) : m_Settings(settings) {} + +QString PluginSettings::path(const QString& pluginName, const QString& key) +{ + return pluginName + "/" + key; +} + +void PluginSettings::checkPluginSettings(const IPlugin* plugin) const +{ + for (const auto& setting : plugin->settings()) { + const auto settingPath = path(plugin->name(), setting.name()); + + QVariant temp = get(m_Settings, PLUGINS_GROUP, settingPath, QVariant()); + + // No previous enabled? Skip. + if (setting.name() == "enabled" && (!temp.isValid() || !temp.canConvert())) { + continue; + } + + if (!temp.isValid()) { + temp = setting.defaultValue(); + } else if (!temp.convert(setting.defaultValue().metaType())) { + log::warn("failed to interpret \"{}\" as correct type for \"{}\" in plugin " + "\"{}\", using default", + temp.toString(), setting.name(), plugin->name()); + + temp = setting.defaultValue(); + } + } +} + +void PluginSettings::fixPluginEnabledSetting(const IPlugin* plugin) +{ + // handle previous "enabled" settings + // TODO: keep this? + const auto previousEnabledPath = plugin->name() + "/enabled"; + const QVariant previousEnabled = + get(m_Settings, PLUGINS_GROUP, previousEnabledPath, QVariant()); + if (previousEnabled.isValid()) { + setPersistent(plugin->name(), "enabled", previousEnabled.toBool(), true); + + // We need to drop it manually in Settings since it is not possible to remove + // plugin settings: + remove(m_Settings, PLUGINS_GROUP, previousEnabledPath); + } +} + +QVariant PluginSettings::setting(const QString& pluginName, const QString& key, + const QVariant& defaultValue) const +{ + return get(m_Settings, "Settings", path(pluginName, key), defaultValue); +} + +void PluginSettings::setSetting(const QString& pluginName, const QString& key, + const QVariant& value) +{ + const auto settingPath = path(pluginName, key); + const auto oldValue = + get(m_Settings, PLUGINS_GROUP, settingPath, QVariant()); + set(m_Settings, PLUGINS_GROUP, settingPath, value); + emit pluginSettingChanged(pluginName, key, oldValue, value); +} + +QVariant PluginSettings::persistent(const QString& pluginName, const QString& key, + const QVariant& def) const +{ + return get(m_Settings, "PluginPersistance", pluginName + "/" + key, def); +} + +void PluginSettings::setPersistent(const QString& pluginName, const QString& key, + const QVariant& value, bool sync) +{ + set(m_Settings, PLUGINS_PERSISTENT_GROUP, pluginName + "/" + key, value); + + if (sync) { + m_Settings.sync(); + } +} + +void PluginSettings::addBlacklist(const QString& fileName) +{ + m_PluginBlacklist.insert(fileName); + writeBlacklist(); +} + +bool PluginSettings::blacklisted(const QString& fileName) const +{ + return m_PluginBlacklist.contains(fileName); +} + +void PluginSettings::setBlacklist(const QStringList& pluginNames) +{ + m_PluginBlacklist.clear(); + + for (const auto& name : pluginNames) { + m_PluginBlacklist.insert(name); + } +} + +const QSet& PluginSettings::blacklist() const +{ + return m_PluginBlacklist; +} + +void PluginSettings::save() +{ + m_Settings.sync(); + 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"); + } + + ScopedWriteArray swa(m_Settings, "pluginBlacklist", m_PluginBlacklist.size()); + + for (const QString& plugin : m_PluginBlacklist) { + swa.next(); + swa.set("name", plugin); + } +} + +QSet PluginSettings::readBlacklist() const +{ + QSet set; + + ScopedReadArray sra(m_Settings, "pluginBlacklist"); + sra.for_each([&] { + set.insert(sra.get("name")); + }); + + return set; +} diff --git a/src/extensionsettings.h b/src/extensionsettings.h new file mode 100644 index 000000000..80caa9b91 --- /dev/null +++ b/src/extensionsettings.h @@ -0,0 +1,86 @@ +#ifndef EXTENSIONSETTINGS_H +#define EXTENSIONSETTINGS_H + +#include +#include + +#include + +// settings about plugins +// +class PluginSettings : public QObject +{ + Q_OBJECT + +public: + PluginSettings(QSettings& settings); + + // fix enabled settings from previous MO2 installation + // + void fixPluginEnabledSetting(const MOBase::IPlugin* plugin); + + // check that the settings stored for the given plugin are of the appropriate type, + // warning user if not + // + void checkPluginSettings(const MOBase::IPlugin* plugin) const; + + // returns the plugin setting for the given key + // + QVariant setting(const QString& pluginName, const QString& key, + const QVariant& defaultValue = {}) const; + + // sets the plugin setting for the given key + // + void setSetting(const QString& pluginName, const QString& key, const QVariant& value); + + // get/set persistent settings + 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; + QSet m_PluginBlacklist; + + // retrieve the path to the given setting + // + static QString path(const QString& pluginName, const QString& key); + + // commits the blacklist to the ini + // + void writeBlacklist(); + + // reads the blacklist from the ini + // + QSet readBlacklist() const; +}; + +#endif diff --git a/src/organizer_en.ts b/src/organizer_en.ts index 35d281d57..1c3ac2859 100644 --- a/src/organizer_en.ts +++ b/src/organizer_en.ts @@ -6247,7 +6247,6 @@ Continue? <table cellspacing="6"><tr><th>Type</th><th>Active </th><th>Total</th></tr><tr><td>All plugins:</td><td align=right>%1 </td><td align=right>%2</td></tr><tr><td>ESMs:</td><td align=right>%3 </td><td align=right>%4</td></tr><tr><td>ESPs:</td><td align=right>%7 </td><td align=right>%8</td></tr><tr><td>ESMs+ESPs:</td><td align=right>%9 </td><td align=right>%10</td></tr><tr><td>ESHs:</td><td align=right>%11 </td><td align=right>%12</td></tr><tr><td>ESLs:</td><td align=right>%5 </td><td align=right>%6</td></tr></table> - <table cellspacing="6"><tr><th>Type</th><th>Active </th><th>Total</th></tr><tr><td>All plugins:</td><td align=right>%1 </td><td align=right>%2</td></tr><tr><td>ESMs:</td><td align=right>%3 </td><td align=right>%4</td></tr><tr><td>ESPs:</td><td align=right>%7 </td><td align=right>%8</td></tr><tr><td>ESMs+ESPs:</td><td align=right>%9 </td><td align=right>%10</td></tr><tr><td>ESLs:</td><td align=right>%5 </td><td align=right>%6</td></tr><tr><td>Overlay:</td><td align=right>%11 </td><td align=right>%12</td></tr></table> @@ -7538,19 +7537,12 @@ This program is known to cause issues with Mod Organizer, such as freezing or bl - - - - attempt to store setting for unknown plugin "%1" - - - - + Failed - + Failed to start the helper application: %1 @@ -8765,6 +8757,11 @@ If you disable this feature, MO will only display official DLCs this way. Please Preferred Servers (Drag & Drop) + + + Extensions + + Blacklisted Plugins (use <del> to remove): @@ -8928,6 +8925,11 @@ p, li { white-space: pre-wrap; } + + + Back-date BSAs + + Add executables to the blacklist to prevent them from @@ -8947,16 +8949,6 @@ programs you are intentionally running. Executables Blacklist - - - Back-date BSAs - - - - - Extensions - - diff --git a/src/pluginmanager.cpp b/src/pluginmanager.cpp index 4b0e6c649..882c84a86 100644 --- a/src/pluginmanager.cpp +++ b/src/pluginmanager.cpp @@ -383,7 +383,8 @@ IPlugin* PluginManager::registerPlugin(const PluginExtension& extension, plugin->setParent(this); if (m_core) { - m_core->settings().plugins().registerPlugin(pluginObj); + m_core->settings().plugins().fixPluginEnabledSetting(pluginObj); + m_core->settings().plugins().checkPluginSettings(pluginObj); } { // diagnosis plugin @@ -564,8 +565,6 @@ void PluginManager::unloadPlugin(MOBase::IPlugin* plugin, QObject* object) mapNames.erase(plugin->name()); } - m_core->settings().plugins().unregisterPlugin(plugin); - // force disconnection of the signals from the proxies // // this is a safety operations since those signals should be disconnected when the @@ -611,16 +610,6 @@ bool PluginManager::unloadPlugins(const MOBase::PluginExtension& extension) void PluginManager::unloadPlugins() { - if (m_core) { - // this will clear several structures that can hold on to pointers to - // plugins, as well as read the plugin blacklist from the ini file, which - // is used in loadPlugins() below to skip plugins - // - // note that the first thing loadPlugins() does is call unloadPlugins(), - // so this makes sure the blacklist is always available - m_core->settings().plugins().clearPlugins(); - } - bf::for_each(m_plugins, [](auto& t) { t.second.clear(); }); diff --git a/src/settings.cpp b/src/settings.cpp index 71f72b286..4ac8d1c68 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1328,251 +1328,6 @@ QColor ColorSettings::idealTextColor(const QColor& rBackgroundColor) return QColor(iLuminance >= 128 ? 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 auto& setting : plugin->settings()) { - const auto settingPath = plugin->name() + "/" + setting.name(); - - QVariant temp = get(m_Settings, "Plugins", settingPath, QVariant()); - - // No previous enabled? Skip. - if (setting.name() == "enabled" && (!temp.isValid() || !temp.canConvert())) { - continue; - } - - if (!temp.isValid()) { - temp = setting.defaultValue(); - } else if (!temp.convert(setting.defaultValue().metaType())) { - log::warn("failed to interpret \"{}\" as correct type for \"{}\" in plugin " - "\"{}\", using default", - temp.toString(), setting.name(), plugin->name()); - - temp = setting.defaultValue(); - } - - m_PluginSettings[plugin->name()][setting.name()] = temp; - - m_PluginDescriptions[plugin->name()][setting.name()] = - QString("%1 (default: %2)") - .arg(setting.description()) - .arg(setting.defaultValue().toString()); - } - - // Handle previous "enabled" settings: - if (m_PluginSettings[plugin->name()].contains("enabled")) { - setPersistent(plugin->name(), "enabled", - m_PluginSettings[plugin->name()]["enabled"].toBool(), true); - m_PluginSettings[plugin->name()].remove("enabled"); - m_PluginDescriptions[plugin->name()].remove("enabled"); - - // We need to drop it manually in Settings since it is not possible to remove plugin - // settings: - remove(m_Settings, "Plugins", plugin->name() + "/enabled"); - } -} - -void PluginSettings::unregisterPlugin(IPlugin* plugin) -{ - auto it = std::find(m_Plugins.begin(), m_Plugins.end(), plugin); - if (it != m_Plugins.end()) { - m_Plugins.erase(it); - } - m_PluginSettings.remove(plugin->name()); - m_PluginDescriptions.remove(plugin->name()); -} - -std::vector 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(); - } - - return *iterSetting; -} - -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(QObject::tr("attempt to store setting for unknown plugin \"%1\"") - .arg(pluginName)); - } - - QVariant oldValue = m_PluginSettings[pluginName][key]; - - // store the new setting both in memory and in the ini - m_PluginSettings[pluginName][key] = value; - set(m_Settings, "Plugins", pluginName + "/" + key, value); - - // emit signal: - emit pluginSettingChanged(pluginName, key, oldValue, value); -} - -QVariantMap PluginSettings::settings(const QString& pluginName) const -{ - return m_PluginSettings[pluginName]; -} - -void PluginSettings::setSettings(const QString& pluginName, const QVariantMap& map) -{ - auto iterPlugin = m_PluginSettings.find(pluginName); - - if (iterPlugin == m_PluginSettings.end()) { - throw MyException(QObject::tr("attempt to store setting for unknown plugin \"%1\"") - .arg(pluginName)); - } - - QVariantMap oldSettings = m_PluginSettings[pluginName]; - m_PluginSettings[pluginName] = map; - - // Emit signals for settings that have been changed or added: - for (auto& k : map.keys()) { - // .value() return a default-constructed QVariant if k is not in oldSettings: - QVariant oldValue = oldSettings.value(k); - if (oldValue != map[k]) { - emit pluginSettingChanged(pluginName, k, oldSettings.value(k), map[k]); - } - } - - // Emit signals for settings that have been removed: - for (auto& k : oldSettings.keys()) { - if (!map.contains(k)) { - emit pluginSettingChanged(pluginName, k, oldSettings[k], QVariant()); - } - } -} - -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 PluginSettings::persistent(const QString& pluginName, const QString& key, - const QVariant& def) const -{ - if (!m_PluginSettings.contains(pluginName)) { - return def; - } - - return get(m_Settings, "PluginPersistance", pluginName + "/" + key, def); -} - -void PluginSettings::setPersistent(const QString& pluginName, const QString& key, - const QVariant& value, bool sync) -{ - if (!m_PluginSettings.contains(pluginName)) { - throw MyException(QObject::tr("attempt to store setting for unknown plugin \"%1\"") - .arg(pluginName)); - } - - set(m_Settings, "PluginPersistance", pluginName + "/" + key, value); - - if (sync) { - m_Settings.sync(); - } -} - -void PluginSettings::addBlacklist(const QString& fileName) -{ - m_PluginBlacklist.insert(fileName); - writeBlacklist(); -} - -bool PluginSettings::blacklisted(const QString& fileName) const -{ - return m_PluginBlacklist.contains(fileName); -} - -void PluginSettings::setBlacklist(const QStringList& pluginNames) -{ - m_PluginBlacklist.clear(); - - for (const auto& name : pluginNames) { - m_PluginBlacklist.insert(name); - } -} - -const QSet& PluginSettings::blacklist() const -{ - return m_PluginBlacklist; -} - -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()); - } - } - - 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"); - } - - ScopedWriteArray swa(m_Settings, "pluginBlacklist", m_PluginBlacklist.size()); - - for (const QString& plugin : m_PluginBlacklist) { - swa.next(); - swa.set("name", plugin); - } -} - -QSet PluginSettings::readBlacklist() const -{ - QSet set; - - ScopedReadArray sra(m_Settings, "pluginBlacklist"); - sra.for_each([&] { - set.insert(sra.get("name")); - }); - - return set; -} - const QString PathSettings::BaseDirVariable = "%BASE_DIR%"; PathSettings::PathSettings(QSettings& settings) : m_Settings(settings) {} diff --git a/src/settings.h b/src/settings.h index 3f4899942..ba6d8669d 100644 --- a/src/settings.h +++ b/src/settings.h @@ -20,13 +20,16 @@ along with Mod Organizer. If not, see . #ifndef SETTINGS_H #define SETTINGS_H -#include "envdump.h" -#include -#include #include #include +#include #include +#include + +#include "envdump.h" +#include "extensionsettings.h" + #ifdef interface #undef interface #endif @@ -275,102 +278,6 @@ class ColorSettings 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 diff --git a/src/settingsutilities.h b/src/settingsutilities.h index cd55464e5..a5bd9c068 100644 --- a/src/settingsutilities.h +++ b/src/settingsutilities.h @@ -1,43 +1,16 @@ #ifndef SETTINGSUTILITIES_H #define SETTINGSUTILITIES_H -#include +#include + +#include +#include namespace MOBase { class ExpanderWidget; } -template -struct ValueConverter -{ - static const T& convert(const T& t) { return t; } -}; - -template -struct ValueConverter>> -{ - static QString convert(const T& t) - { - return QString("%1").arg(static_cast>(t)); - } -}; - -template <> -struct ValueConverter -{ - static QString convert(const QVariantList& t) - { - return QString("%1").arg(QVariant(t).toStringList().join(",")); - } -}; - -template <> -struct ValueConverter -{ - static QString convert(const QStringList& t) { return t.join(", "); } -}; - bool shouldLogSetting(const QString& displayName); template @@ -47,13 +20,11 @@ void logChange(const QString& displayName, std::optional oldValue, const T& n return; } - using VC = ValueConverter; - if (oldValue) { - MOBase::log::debug("setting '{}' changed from '{}' to '{}'", displayName, - VC::convert(*oldValue), VC::convert(newValue)); + MOBase::log::debug("setting '{}' changed from '{}' to '{}'", displayName, *oldValue, + newValue); } else { - MOBase::log::debug("setting '{}' set to '{}'", displayName, VC::convert(newValue)); + MOBase::log::debug("setting '{}' set to '{}'", displayName, newValue); } }